diff --git a/.agents/skills/migrate-ds-area/SKILL.md b/.agents/skills/migrate-ds-area/SKILL.md new file mode 100644 index 0000000000..d06d40a4f5 --- /dev/null +++ b/.agents/skills/migrate-ds-area/SKILL.md @@ -0,0 +1,330 @@ +--- +name: migrate-ds-area +description: Migrate one Penpot UI area to the Design System components for one group of the DS migration epic (#12375): buttons and icons (icon-button*, button*, icon*), selects (select*) and later groups. Moves the area's SCSS off the deprecated styles, proves every change with a zero-tolerance visual test, then opens the draft PR and updates the group issue (PRs table row, QA comment). Use when the user asks to migrate an area or module to the DS, continue a DS migration group, or names an area of a group issue. +--- + +# Skill: migrate-ds-area + +Move one UI area (one `.cljs` + `.scss` pair, or a few related modules) +from legacy components and raw HTML to the Design System components, +prove with pixels what changed, and ship it as a draft PR that QA can +test. + +The epic is #12375. Each control type is a group with its own issue +(buttons and icons #12376, selects #12377, radios #12378, …). The group +issue holds the plan (order, waiting areas), the changes QA should +expect, and the PRs table where people claim areas. Read it first. + +## Input + +- The group issue (e.g. `#12376`) and the area: a module path (e.g. + `dashboard/fonts`) or an area named in the group issue. +- No area given: take the next unclaimed area in the group issue's plan + order, and say which one. + +## Ground rules + +- Never `git push`. The user pushes; then you open the PR. +- PRs go to `develop`; branches start from `origin/develop`. +- **Stay in the group.** Only the controls of this group change. In the + buttons/icons group, selects, radios, numeric/text inputs, checkboxes, + menus (`%menu-*`, `%dropdown-*`) and modal structure (`%modal-*`) stay; + a checkbox tick or an input label icon stays legacy. +- **Accept the DS look.** Do not add CSS to imitate legacy sizes or + colors. Do keep positions: icons and labels must not move unless the + DS size forces it, and then say so. +- No negative margins to compensate; use positive DS spacing or + `px2rem()`. +- **Gaps in the DS wait.** An area that needs an icon or a feature the + DS lacks keeps its legacy control; say so in the PR. +- Skip legacy shared components that another group will replace + (`context_menu_a11y`, `forms`, `select`, `dropdown`, `radio_buttons`, + `search_bar`, `title_bar`, …): they wait for that group's cleanup, + where they are updated if still used or deleted if not. +- Debug-only UI gets no QA comment and no compare image. + +## Devenv + +Run every test, lint and `pnpm` command inside the workspace container, +never on the host (a host `pnpm` relinks `node_modules` and breaks the +container's watcher). The container is `penpot-devenv-ws-main`, where +`` is the workspace number (`ws0` is the main repo; `wsN` lives in +`~/.penpot/penpot_workspaces/wsN`). Write output to a file, then read it: + +```bash +C=penpot-devenv-ws1-main # your workspace +docker exec -u penpot -w /home/penpot/penpot/frontend $C \ + bash -lc '' > /out.txt 2>&1 +``` + +When the app does not behave as the code says, check the build first: + +- **Stale build after a branch switch** ("is not a function", or a page + with empty `stl/css` classes): `touch` the stale `.cljs`/`.scss`, wait + for the compiled file under `frontend/resources/public/` to update, + hard-reload. +- **A whole area unstyled:** a new `.scss` added after the watcher + started is missing from `main.css` (grep `main.css` for the module + prefix), or `main.css` was written corrupted after many `.scss` + changed at once. Touch a `.scss`; if the module is still missing, + restart the watcher in the workspace's tmux session (`penpot:0`, + `./scripts/watch app`). +- **Blank app after switching between `develop` and `staging`:** + dependencies differ. Run `pnpm install --frozen-lockfile` in the + container and restart the watcher. + +## Workflow + +### 1. Claim the area and branch + +1. Read the group issue: plan, waiting areas, expected changes, PRs table. +2. Add a row to the PRs table with no PR number, the user's @login as + Owner and the state `Claimed` (`gh issue view/edit --body-file`). + Read the body right before editing, and read it again after saving: + when two people edit at the same time, GitHub keeps only the last + edit, so a row can vanish. Put yours back if it is gone. + Skip an area someone else already claimed, unless the claim has had + no PR for 7 days and its owner did not answer a comment asking. +3. `git switch -c -ds-- origin/develop`. +4. Survey the area: every control of the group in the `.cljs`, the + legacy icons, the placeholders and mixins the `.scss` uses, the + `div`/`span` with `:on-click`. + +### 2. Visual baseline + +Add a `test.describe(" controls", …)` block to the matching spec +in `frontend/playwright/ui/visual-specs/` (project `ds`, dark theme). +Put it next to the blocks of the same screen, not always at the end of +the file: two PRs that both append at the end conflict. + +- Every shot: `toHaveScreenshot({ maxDiffPixelRatio: 0, threshold: 0 })`. +- Show every changed control at rest, on hover and open or pressed. + Capture the element or the smallest stable container. +- Locators must work before and after the change: text, role, testid, + `[class*='…']`, or `a.or(b)` when the element type changes (raw + `[class*='menu-btn']` before, `getByRole("button", { name: "Options" })` + after). Scope `.or()` to the area so it cannot match elsewhere. +- Missing data? Add a fixture under `frontend/playwright/data/` and mock + it (`dashboardPage.mockRPC(...)`, `DashboardPage.mockConfigFlags` for + flags). Watch for expired or inconsistent fixtures (dates in the past, + wrong file for a mock). +- Capture with `--update-snapshots=all`: + ```bash + npx playwright test --project=ds -g "" --update-snapshots=all + ``` + Plain `--update-snapshots` keeps a snapshot within the project's 0.5% + tolerance, so a real diff looks like 0. +- Copy the snapshots (`.spec.js-snapshots/-*`) to + `/diffs//before/` and look at them: the state you + wanted must really be on screen. + +### 3. Controls commit + +#### Buttons and icons + +| Legacy | DS | +|---|---| +| icon-only raw button | `[:> icon-button* {:variant "ghost" :aria-label (tr …) :icon i/x :on-click f}]` (always pass `:variant`; `"action"` for 24px icon toggles) | +| pressed/selected | `:aria-pressed (boolean x)` | +| text button | `[:> button* {:variant "primary"/"secondary"/"ghost"/"destructive" :type "button"} label]` | +| `[:input {:type "button"}]` | `button*` | +| modal close | `icon-button*` ghost, `:aria-label (tr "labels.close")`, `:tooltip-class` carrying the corner position | +| legacy icon outside a button | `[:> icon* {:icon-id i/x :class …}]` | + +- Not every raw button changes: rows, tabs, swatches, avatar widgets, + full-height segments, round custom buttons and link-style text buttons + stay raw (their legacy icon still changes). List the kept ones in the + PR. +- A `div`/`span` with `:on-click` in the area becomes a `[:button]` + (with `aria-expanded`/`aria-haspopup` when it opens something). An + `[:a]` without `href` stays; list it in the PR. +- **Tooltip wrapper.** `icon-button*` renders inside a tooltip trigger + wrapper. Position (`position: absolute`, grid placement, margins + against siblings) goes on `:tooltip-class`; look (`--button-height`, + colors) goes on `:class`. +- **Tooltip at the right edge.** Small icon buttons at the right end of + a sidebar row or title bar get `:tooltip-placement "top-left"`; list + it as an expected change. +- **Sizes through custom properties:** `--button-height: #{px2rem(64)}` / + `--button-width`. Sass does not evaluate functions inside custom + properties without `#{}`. +- **A grown `button*` keeps its label at the start**: add + `justify-content: center` when it is wider than its content. +- **Icon colors:** DS icons use `stroke: currentColor`. Set `color` on + the icon class, not `stroke`/`fill`. A legacy `color: transparent` left + on the button or wrapper makes the DS icon invisible: drop it. A class + that redefines the color variable on the icon breaks the hover that + changes it on the button: define the variable on the button. +- **Every DS icon needs a color of its own** (on its class or a parent): + one that inherits none shows in the devenv debug yellow. +- **Icon sizes:** `"s"` draws 12px inside a 16px box (compensate the + margin by 2px per side to keep the drawing in place); `"m"` 16px; `"l"` + 24px; `"xl"` 32px. +- **Stroke width:** DS icons keep the DS stroke width. Never set + `stroke-width` on a DS icon to match the legacy one; a thinner or + thicker line is part of the DS look. +- **Legacy placeholders that style `svg` descendants** (e.g. + `%dropdown-element-base` → `span svg { 12px box; stroke }`) override a + DS icon placed inside them: give the icon class its own box + (`inline-size`/`block-size` 16px) and `stroke: currentcolor` with more + specificity. +- **Flex shrink:** a DS svg inside a small flex button can shrink and be + cut; add `flex-shrink: 0` to the icon class. +- **Rotated legacy arrows:** prefer `i/arrow-left`/`-right`/`-up`/`-down` + over rotating `i/arrow` (same path), unless a rotation class drives the + open/closed state. +- **Menus anchored by the button size** move when the button grows; open + them from the top (`inset-block-start: calc(100% + var(--sp-xs))`) or + fix the context menu offset, and say so. + +#### Selects + +`[:& select {:default-value v :options [{:value :label}] :on-change f}]` +→ `[:> select* {:default-selected :options :on-change f}]` +(`app.main.ui.ds.controls.select`). + +- **Ids are non-blank strings.** A blank id shows a dimmed "--", and an + id that matches no option falls back to the first one. Give mixed, + none and auto their own ids (`"multiple"`, `"none"`, `"auto"`) and map + them back in the handler. Use `""` only where "--" is the wanted look. +- **Convert the value.** The legacy select ran `d/read-string`, so + handlers got keywords, numbers or uuids; `select*` passes the `:id` + string. Convert with `keyword`, `d/parse-integer` or `uuid/parse`. +- **Memoized options, live selection.** `select*` resets its selection + when `:default-selected` or the `:options` identity changes. Build + options with `mf/with-memo` and `mapv` (the schema wants a non-empty + vector) and pass `:default-selected` from the live value, never a + constant. +- **Never pass nil.** In dev builds a present prop with value nil + (`:class`, `:wrapper-class`, `:disabled`, `:default-selected`) throws + `invalid props on component select*`. Use `(boolean x)`, `d/nilv`, or + add the prop only when it has a value. +- **Props the legacy select ignored stay out**: `select*` passes rest + props to its button, so they would reach the DOM. +- **Classes:** `:wrapper-class` for layout, `:class` for look. +- **Portal:** add `:has-portal true` only where the visual test shows the + list cut at the bottom of a panel, and say so in the QA comment. +- **Locators:** the button is a `combobox` with no accessible name; use + `getByRole("combobox").filter({ hasText: "Linear" })` (it matches the + legacy select too) and `getByRole("option", { name: … })`. +- **Keyboard:** the arrows no longer change the value while the list is + closed. It is an expected change, already listed in #12377. + +#### All groups + +- New labels: add the key to `en.po` and `es.po` by hand, in sorted + position, with a `#:` reference to the cljs line. Never run the + translations sync. +- The `.scss` only gets what the new controls need in this commit. +- Some e2e specs are not Prettier-clean on develop: never run + `prettier --write` on a whole spec; format only your block. + +Then wait for the watcher (grep the compiled JS/CSS under +`frontend/resources/public/` for the change), recapture with `=all`, +copy to `after/`, diff: + +```bash +SK=.agents/skills/migrate-ds-area/scripts +python3 -I $SK/diff.py # per-file pixel count + bbox +python3 -I $SK/trip.py # before | after | red diff +``` + +`trip.py` pads images of different sizes, so a taller "after" still +renders. +Explain every diff. Fix what is not a DS-look change (moved icons, cut +icons, lost hover color). Rerun a shot that changes on unchanged code +before trusting it; flaky spots seen so far: the invite modal textarea +border, the search input caret, the user account page, the path handler +menu trigger. + +Lint, then commit (read `mem:workflow/creating-commits` first): + +```bash +npx prettier --check ; npx stylelint ; clj-kondo --lint ; cljfmt check +git add -f # -f: visual-specs/ is gitignored +./scripts/check-commit +``` + +A plain `git add` with an ignored path in the list adds nothing; always +`-f`, and check `git show --stat`. Never commit the screenshots. +Subject: `:recycle: Use DS in `. A pre-existing bug +found and fixed on the way goes in its own `:bug:` commit. + +### 4. SCSS commit + +Run the `refactor-scss` skill on every `.scss` the PR touched (only +those), in its own commit. Keep `refactor/` only for component-family +placeholders (`%modal-*`, `%menu-*`, `%input-*`, `%checkbox-*`) and say +why in a comment above the `@use`. Unused selectors are deleted, not +commented out (list them in the commit body). Typography without an +exact match uses the closest DS typography. Run the skill's +**Self-review** before committing: no bare element selectors (add +classes in the `.cljs`), no copied properties that do nothing, z-index +tokens by purpose. Compare against the controls commit: it should be +0 px; explain anything else (typically a typography or border change). +For files that several modules share or that are huge, stop and propose +a separate PR instead. + +### 5. Hand off, PR and tracking + +1. Report to the user: commits, visible changes, kept raw controls, + pre-existing bugs, and the compare image path + (`/diffs//compare.png`, before | after | diff). Give + the push command: `git push -u origin ` (the branch tracks + `origin/develop`, so a bare `git push` is refused). +2. After the user pushes, check `git rev-parse HEAD origin/`, + then (read `mem:workflow/creating-prs` first): + ```bash + gh pr create --repo penpot/penpot --draft --base develop --head \ + --title "" --body-file \ + --label "WG adoption" --label "no issue required" \ + --milestone --project Main + gh pr edit --repo penpot/penpot --remove-label "needs triage" + ``` + Never add `no changelog` to a PR: that label goes on issues. + Body: AI note, `Part of # (DS migration epic #12375).`, a + short What / How, the kept raw controls, an empty `## Screenshots` + when something is visible (the user attaches the image). Never + `Closes #`. Keep it short: reviewers find long PRs hard to read. +3. In the group issue's PRs table, fill your row: + `| # | : () | @ | Draft |`. +4. Post a QA comment on the group issue: `**#: **`, *Where* (how + to reach it), *Expected changes* (every visible change; QA treats + anything not listed as a bug), *No other change* (what still works + the same). + +Do not mark the draft ready or ask for review: the user decides when. + +### 6. Review fixes + +When the reviewer comments on a PR: + +1. Fix each comment in a new commit on the branch (never amend pushed + commits). A comment you do not apply gets a short reason the user can + post, with an image when it is visual. +2. Rerun the area's visual spec before and after the fix with `=all`. + Build a compare image for every visible change and give the user its + full path, saying which PR it goes to. +3. Update everything the fix makes stale, in the same turn: the QA + comment (*Expected changes* must list the new visible changes), the + PRs table row, and the PR body. +4. When working on several PRs in one go, report per PR: branch, new + commit, what changed, image path. Never mix two branches in one list. +5. Give the push command per branch. If a rebase is coming soon, say + that pushing now costs one extra CI run; the user decides. +6. After a round of review fixes, check whether the same issue exists in + the user's other open PRs of the group, and fix it there too. + +### 7. After merges + +Whenever PRs merge (or go from draft to ready), sync the state column of +the group's PRs table with GitHub for **every** row, not only your own: +read each row's PR (`gh pr view --json state,isDraft`) and set +Merged / In review / Draft. The table drifts when other people merge. + +## Output + +- Branch with 2 commits (controls, SCSS) plus any `:bug:` commit. +- Visual spec block with zero-tolerance shots. +- Compare image in the scratch folder. +- After the push: draft PR, row and QA comment in the group issue. diff --git a/.agents/skills/migrate-ds-area/scripts/diff.py b/.agents/skills/migrate-ds-area/scripts/diff.py new file mode 100644 index 0000000000..9792f37d8a --- /dev/null +++ b/.agents/skills/migrate-ds-area/scripts/diff.py @@ -0,0 +1,25 @@ +"""Pixel diff of two folders of PNG snapshots with the same file names. + +Usage: python3 -I diff.py +Prints, per file, the number of differing pixels and their bounding box, +or the two sizes when they differ. +""" +import os +import sys + +from PIL import Image, ImageChops + +before, after = sys.argv[1], sys.argv[2] +for name in sorted(os.listdir(before)): + other = os.path.join(after, name) + if not os.path.exists(other): + print(name, "missing in", after) + continue + a = Image.open(os.path.join(before, name)).convert("RGB") + b = Image.open(other).convert("RGB") + if a.size != b.size: + print(name, "size", a.size, b.size) + continue + diff = ImageChops.difference(a, b) + count = sum(1 for p in diff.getdata() if p != (0, 0, 0)) + print(name, count, diff.getbbox()) diff --git a/.agents/skills/migrate-ds-area/scripts/trip.py b/.agents/skills/migrate-ds-area/scripts/trip.py new file mode 100644 index 0000000000..dde5c96bf0 --- /dev/null +++ b/.agents/skills/migrate-ds-area/scripts/trip.py @@ -0,0 +1,33 @@ +"""Before | after | red diff image, one row per snapshot. + +Usage: python3 -I trip.py +Both folders hold PNGs with the same names (crop them first if needed). +""" +import os +import sys + +from PIL import Image, ImageChops + +before, after, out, scale = sys.argv[1], sys.argv[2], sys.argv[3], int(sys.argv[4]) +rows = [] +for name in sorted(os.listdir(before)): + a = Image.open(os.path.join(before, name)).convert("RGB") + b = Image.open(os.path.join(after, name)).convert("RGB") + w, h = max(a.width, b.width), max(a.height, b.height) + pad_a = Image.new("RGB", (w, h), (200, 200, 200)); pad_a.paste(a, (0, 0)) + pad_b = Image.new("RGB", (w, h), (200, 200, 200)); pad_b.paste(b, (0, 0)) + mask = ImageChops.difference(pad_a, pad_b).convert("L").point(lambda v: 255 if v else 0) + red = Image.new("RGB", (w, h), (255, 255, 255)) + red.paste((255, 0, 0), mask=mask) + row = Image.new("RGB", (w * 3 + 20, h), (200, 200, 200)) + for i, im in enumerate([pad_a, pad_b, red]): + row.paste(im, (i * (w + 10), 0)) + rows.append(row.resize((row.width * scale, row.height * scale), Image.NEAREST)) +width = max(r.width for r in rows) +height = sum(r.height + 10 for r in rows) +canvas = Image.new("RGB", (width, height), (255, 255, 255)) +y = 0 +for r in rows: + canvas.paste(r, (0, y)) + y += r.height + 10 +canvas.save(out) diff --git a/.agents/skills/refactor-scss/SKILL.md b/.agents/skills/refactor-scss/SKILL.md new file mode 100644 index 0000000000..019dface29 --- /dev/null +++ b/.agents/skills/refactor-scss/SKILL.md @@ -0,0 +1,421 @@ +--- +name: refactor-scss +description: Migrate one Penpot SCSS module off the deprecated styles under frontend/resources/styles/common/refactor/ to the current Design System (ds/ tokens, mixins and px2rem). Pure refactor, no visual change. Use when the user asks to refactor, migrate or clean up a .scss file to the DS. +--- + +# Skill: refactor-scss + +Refactor a single SCSS file so that it no longer depends on the deprecated +styling system, using the current Design System instead. + +This is a **pure refactor**. Do NOT introduce visual or behavioral changes. +Preserve the generated CSS behavior exactly unless a rule below requires +otherwise. The rules that allow a small visible change (closest DS +typography, DS border width) want it shown in the visual check and listed +in the report and the commit body. + +Reviewers read every line of the result. A line that is +copied but does nothing, a bare element selector or a raw z-index comes +back as a review comment, so the rules below are strict about them. + +## Input + +- The path to one `.scss` file (e.g. `frontend/src/app/main/ui/foo/bar.scss`). +- If no path is given, ask for it. Do not pick a file yourself. + +## Workflow + +1. Read the target `.scss` file and the `.cljs` file with the same name next + to it. +2. Read every deprecated source the file uses, so you inline real values, + never remembered ones: + - `frontend/resources/styles/common/refactor/mixins.scss` + - `frontend/resources/styles/common/refactor/design-tokens.scss` + - `frontend/resources/styles/common/refactor/spacing.scss` + - any other file under `frontend/resources/styles/common/refactor/` that + the stylesheet pulls in (placeholders, borders, z-index, fonts...). +3. Read the DS modules you will map to: + - `frontend/src/app/main/ui/ds/_utils.scss` (`px2rem()`) + - `frontend/src/app/main/ui/ds/spacing.scss` + - `frontend/src/app/main/ui/ds/_sizes.scss` + - `frontend/src/app/main/ui/ds/typography.scss` (`use-typography()`) + - `frontend/src/app/main/ui/ds/_borders.scss` + - `frontend/src/app/main/ui/ds/z-index.scss` +4. Apply the rules below and edit the file in place. +5. Verify (see **Verification** and **Visual check**). +6. Report (see **Output**). Do not commit. When the refactor goes with other + changes, it belongs in its own commit, after the task's commits. + +## Primary goal + +After the refactor, the SCSS file must not depend on anything under +`frontend/resources/styles/common/refactor/`. This includes imports, mixins, +placeholder selectors, functions, deprecated variables and design-token +aliases. + +### Partial refactor + +Some placeholders stand for a whole component family that will move to the DS +in its own PR: `%input-element`, `%input-with-label`, `%input-checkbox`, +`%modal-*`, `%menu-dropdown`, `%menu-item-base`, `%shortcut-*` and similar. +Inlining them in a small task produces big, risky diffs. In that case: + +- migrate everything else (tokens, sizes, spacing, small mixins); +- keep the `refactor/` import only for those placeholders; +- list each remaining dependency and the selectors that use it in the report, + so the user can plan it as a separate PR. + +## General rules + +- Keep the resulting SCSS as small and readable as possible. +- Avoid duplicated declarations and duplicated selectors. +- Do not change class names. +- Do not change DOM structure. +- Do not change specificity unless required. +- Follow the project's SCSS formatting (Prettier, see **Verification**). + +## Imports + +Use the DS modules. Import them with `as *` when they expose mixins, +variables or functions: + +```scss +@use "ds/_utils.scss" as *; +``` + +not + +```scss +@use "ds/_utils.scss" as utils; +``` + +Do not introduce namespaces such as `utils.`, `typography.`, `borders.`, +`spacing.` or `sizes.` unless a module explicitly requires one. + +Remove every unused import. + +## Deprecated mixins + +If the stylesheet uses mixins from +`frontend/resources/styles/common/refactor/mixins.scss`: + +- locate the mixin; +- inline its generated CSS (then apply the rest of these rules to it); +- remove the `@include`; +- remove the import. + +Do not leave deprecated mixins. + +## Placeholders + +Replace every `@extend %placeholder` with the generated CSS. Remove every +deprecated placeholder, except the component-family ones described in +**Partial refactor**. + +Inline only what does something on that element. Do not paste a +placeholder (or mixin) body as it is. For each property, check: + +- **It applies to the element.** `fill`, `stroke` and `stroke-width` only + act on SVG shapes: drop them from a `span`, `div` or `button`. + `color: transparent` on a wrapper that only holds an icon does nothing + useful: drop it and set the real color. +- **It is not the default or already given.** `stroke-width: 1px` is the + SVG default; `fill: none` and `stroke: currentColor` are already set by + the DS `icon*` (`.icon`). Drop them. +- **A later rule does not override it.** +- **It is not a stroke width on a DS icon.** DS icons keep the DS + stroke width; drop any `stroke-width` that only imitates the legacy + icon (1.33px, 2.25px...). + +`%button-icon` is the usual case: on a wrapper it leaves `color: +transparent; fill: none; stroke-width: 1px`, and none of the three is +needed. + +## Element selectors + +Do not style HTML elements directly (`svg {}`, `h3 {}`, `button {}`, +`input.x {}`, or `.component { svg {} }`). + +When no class exists, **add one in the `.cljs`**: a DS component takes +`:class (stl/css :x)` (`icon*`, `heading*`, `button*`…), and a plain +element takes `{:class (stl/css :x)}`. Touching the `.cljs` for this is +part of the refactor. A comment like "the icon has no class" is not an +answer. + +Prefer the DS component when one fits: `heading*` for `h1`–`h6` +(`:level`, `:typography`), `text*` for text, `icon*` for icons. + +`element.class` (e.g. `input.element-title`) is not needed to beat the +input defaults: try the class alone and check it in the visual check. + +An element selector stays only for markup you cannot reach (injected +HTML, third-party widgets), with a comment saying so. + +**Icon colors.** A DS icon draws with `stroke: currentColor`. Set +`color` on the icon class or its parent, never `stroke`/`fill` on an +`svg` selector. + +## Color variables + +Whenever a color changes across states, introduce a component-local CSS +variable. + +Instead of + +```scss +.button { + color: var(--color-foreground-primary); + + &:hover { + color: var(--color-accent-primary); + } + + &:disabled { + color: var(--color-foreground-disabled); + } +} +``` + +write + +```scss +.button { + --button-foreground-color: var(--color-foreground-primary); + + color: var(--button-foreground-color); + + &:hover { + --button-foreground-color: var(--color-accent-primary); + } + + &:disabled { + --button-foreground-color: var(--color-foreground-disabled); + } +} +``` + +Use this pattern for `color`, `background-color`, `border-color`, +`outline-color`, `fill`, `stroke` and any similar property. + +## Design token aliases + +Remove every alias defined in +`frontend/resources/styles/common/refactor/design-tokens.scss`. Resolve each +alias to its final DS token. Do not keep intermediate aliases. + +Instead of `color: var(--button-foreground-hover);` write +`color: var(--color-accent-primary);`. + +## Spacing + +Replace deprecated spacing variables using +`frontend/src/app/main/ui/ds/spacing.scss` for `margin`, `padding`, `gap` and +`inset`. + +If no exact spacing token exists, use `px2rem()`. Do not approximate with the +closest token. + +## Sizes + +Replace deprecated size variables using +`frontend/src/app/main/ui/ds/_sizes.scss` for `inline-size`, `block-size`, +`width`, `height` and min/max sizes. + +If no exact size exists, use `px2rem()` from +`frontend/src/app/main/ui/ds/_utils.scss`. Do not leave hardcoded pixel values +unless absolutely necessary. + +## Typography + +When any of `font-size`, `font-weight`, `line-height`, `font-family` or +`letter-spacing` appear, check whether together they match one of the +definitions in `frontend/src/app/main/ui/ds/typography.scss`. + +If they match, replace all the individual declarations with: + +```scss +@include use-typography(""); +``` + +If there is no exact match, use the **closest DS typography** (same role: +a panel title in uppercase → `headline-small`, body text → `body-*`), +even when more than one detail differs. Show the change in the visual +check and state it in the report and in the commit body (e.g. "11px +regular → 12px medium"). + +Keep the original declarations only when no DS typography fits the role +(e.g. a 32px display number in a 14px-only area); then leave a TODO +naming the closest one and ask the user. + +## Borders + +Replace deprecated border radius and border width values using +`frontend/src/app/main/ui/ds/_borders.scss`. A raw width close to a token +(`1.75px`) takes the token (`$b-2`); say so in the report. If nothing is +close, use `px2rem()`. + +## Logical properties + +Prefer logical properties whenever they preserve the existing behavior: + +- `width` → `inline-size` +- `height` → `block-size` +- `max-width` → `max-inline-size` +- `margin-left` → `margin-inline-start` +- `padding-right` → `padding-inline-end` +- `top` → `inset-block-start` +- `left` → `inset-inline-start` + +## Z-index + +Never leave magic z-index values. Use tokens from +`frontend/src/app/main/ui/ds/z-index.scss`, and pick the token **by what +the element is, not by its number**: a dropdown, menu or select → +`--z-index-dropdown`; a toolbar, palette, sidebar or panel → +`--z-index-panels`; and so on (read the comments in that file). A raw `4` +on a dropdown becomes `var(--z-index-dropdown)` even though the number +changes. Check the visual check for overlap. + +Leave a TODO only when the element fits no token's purpose. + +Legacy z-index values between sibling elements that do not overlap can be +removed, but only when the **Visual check** shows no pixel change in every +state that matters (open menus, collapsed, hidden). Say so in the report. + +## Duplicated properties + +Within the same selector, avoid duplicated properties, duplicated CSS +variables and duplicated declarations introduced by the refactor. Allow +duplicates only when they are intentional browser fallbacks. + +## Unused classes + +Open the `.cljs` file with the same name as the SCSS file and check whether +every CSS class is referenced (as `(stl/css :class-name)`, +`(stl/css-case :class-name ...)` or similar). + +CSS modules prefix every class with its module +(`main_ui___`), so only the `.cljs` of the same module can use +it. Before calling a selector unused, also search the whole `frontend/src` +for the class name as a string, a `:global` use, or a dynamic class built +at runtime. + +If a selector is unused after that check: + +- delete it (do not leave it commented out, do not add a TODO); +- list every deleted selector in the report and in the commit body. + +A rule that matches nothing because the markup changed (e.g. `.icon svg` +when the DS icon is the `svg` itself) is deleted the same way. Before +deleting a descendant rule, check every element it matched, not only +the one you changed: `.placeholder .icon svg` can also color a second +icon, which then has no color (in the devenv it shows in the debug +yellow). + +Every DS icon needs a color of its own (`color` on its class or on a +parent with a defined color): an icon that inherits nothing shows in +the debug yellow in the devenv. + +Classes used by markup injected from outside (e.g. an external widget) +stay, with a comment saying so. + +Also check the other direction: every class the `.cljs` passes to +`stl/css` / `stl/css-case` must exist in the `.scss`. A missing one silently +does nothing; report it. + +## Sass and CSS pitfalls + +- Sass does not evaluate functions inside CSS custom properties. Write + `--button-height: calc(var(--height) - #{px2rem(16)});`, not + `... - px2rem(16))`, or the browser gets the literal `px2rem(16)`. +- A `/` between Sass size variables is read as a division + (`grid-template: $sz-32 / auto` breaks). Split it into + `grid-template-rows` / `grid-template-columns` (or areas). +- A CSS-wide keyword cannot be part of a shorthand: `inset-inline: unset 0` + is invalid and stylelint does not catch it. Use the longhands. +- After editing, check the compiled CSS for any of these + (`frontend/resources/public/css/main.css`, search the module class). + +## Uncertainty + +If any transformation is uncertain, never guess. Leave a TODO in the code +and explain why in the report. + +## Self-review + +Before the report, read the final diff as the reviewer would and search +the file for these; each hit must be fixed or have a real reason: + +```bash +rg -n 'svg \{|^\s*(h[1-6]|p|hr|input|button|span|a)\b[^-_]|[a-z]\.[a-z-]+ \{' +rg -n 'stroke-width|color: transparent|fill: none|z-index: [0-9]|TODO' +rg -n '[0-9]\.[0-9]+px|[^(]\b[0-9]+px' +``` + +- an element selector → add a class (see **Element selectors**); +- a property that does not apply or is a default → drop it (see + **Placeholders**); +- a raw z-index → token by purpose; +- a typography TODO → closest DS typography; +- a raw px value → token or `px2rem()`. + +## Verification + +Run the project scripts from `frontend/` inside the devenv container +(`penpot-devenv-ws-main` for workspace `wsN`; never run `pnpm` on the +host), writing output to a file first (never pipe lint output into +filters): + +```bash +docker exec -u penpot -w /home/penpot/penpot/frontend penpot-devenv-ws-main \ + bash -lc 'pnpm run lint:scss; echo LINT=$?; pnpm run check-fmt:scss; echo FMT=$?' \ + > /scss-check.txt 2>&1 +``` + +If the format check fails, run `pnpm run fmt:scss` and check again. + +**Dependency check.** A grep for `refactor/` is not enough. For the final +file, check: + +- `@use` / `@import` sources: all must be `ds/...` (or the sidebar common + module, which is DS-only); +- every `@include`, `@extend` and `%placeholder`; +- every `$variable`: which imported module defines it (`$br-*` exists in both + `ds/_borders.scss` and `refactor/borders.scss`; the `@use` decides); +- every `var(--token)`: defined in the DS (colors, spacing, sizes) or only as a + `refactor/` alias. + +Report any failure as it is; do not hide it. + +## Visual check + +"No visual change" must be shown, not assumed. When a visual spec covers the +component (`frontend/playwright/ui/visual-specs/`, project `ds`), or one can be +added: + +1. Capture before (base branch styles) and after with + `pnpm exec playwright test --project=ds -g "" --update-snapshots=all` + in the container. `=all` is required: plain `--update-snapshots` only + rewrites past the 0.5% tolerance, so a real diff can look like 0. +2. Before each capture, wait until the compiled CSS/JS shows the expected + version (search `main.css` for the changed rule). +3. Compare with zero tolerance. If pixels change, build the before | after | + diff (red) image and explain every change in the report. + +## Output + +1. The diff of the file (`git diff -- `). +2. A summary with: + - removed imports; + - removed deprecated mixins; + - removed placeholders; + - typography replacements; + - design-token aliases removed; + - logical properties introduced; + - deleted unused selectors; + - TODOs requiring manual review, each with its reason; + - remaining `refactor/` dependencies (partial refactor) and where they are + used; + - classes missing from the `.scss`, if any. +3. The verification results, including the visual check (pixel count, or why + it was not possible). diff --git a/.agents/skills/review-plan/SKILL.md b/.agents/skills/review-plan/SKILL.md index c690df6b85..431610db5e 100644 --- a/.agents/skills/review-plan/SKILL.md +++ b/.agents/skills/review-plan/SKILL.md @@ -6,64 +6,40 @@ slash: true # Review Plan -Act as a senior software engineer and perform a thorough review of an -implementation plan. +Act as a senior software engineer and perform a thorough review of an implementation plan. ## When to use -- The user asks to review a plan, in any phrasing: "review this plan", - "does this plan look right?", "second opinion on the plan" — or runs - `/review-plan`. -- A plan was just produced (typically by `/make-a-plan`) and the user - wants it evaluated before executing it. +- The user asks to review a plan, in any phrasing: "review this plan", "does this plan look right?", "second opinion on the plan" — or runs `/review-plan`. +- A plan was just produced (typically by `/make-a-plan`) and the user wants it evaluated before executing it. ## Instructions -1. **Determine the plan under review** from the session context (for example, a - plan just produced by `/make-a-plan`) or from a plan file path given by the - user (typically under `.agents/plans/`). If a file path is given, read the - file first so the complete plan is in context. -2. Delegate the review to the `general` subagent (via the task tool), unless the - user specifies another agent. Include in the prompt the - **`plan-review-criteria`** skill name and all user context. -3. When the subagent returns, output the review to the user verbatim. Do not - summarize it and do not act on its findings. -4. Right after the review, suggest the next step based on the verdict. These - are suggestions — the user decides, and any instruction overrides them: +1. **Determine the plan under review** from the session context (for example, a plan just produced by `/make-a-plan`) or from a plan file path given by the user (typically under `.agents/plans/`). If a file path is given, read the file first so the complete plan is in context. +2. Delegate the review to the `general` subagent (via the task tool), unless the user specifies another agent. Include in the prompt the **`plan-review-criteria`** skill name and all user context. +3. When the subagent returns, output the review to the user verbatim. Do not summarize it and do not act on its findings. +4. Right after the review, suggest the next step based on the verdict. These are suggestions — the user decides, and any instruction overrides them: - **Approve** → suggest `/implement-plan` to execute it. - - **Request changes** → suggest `/make-a-plan` to make a plan to address the - findings. + - **Request changes** → suggest `/make-a-plan` to make a plan to address the findings. ### Hard rule — read-only while reviewing -This flow is read-only **for the duration of the review**: from the moment it -starts until the user considers the review finished (including any feedback, -questions, or clarifications about it). During that period, never fix, -implement, edit files or create commits — not even "obvious" fixes derived from -the findings. Once the user explicitly states the review is done (or moves on to -a different task), this rule no longer applies and you act as a normal build -agent again. +This flow is read-only **for the duration of the review**: from the moment it starts until the user considers the review finished (including any feedback, questions, or clarifications about it). During that period, never fix, implement, edit files or create commits — not even "obvious" fixes derived from the findings. Once the user explicitly states the review is done (or moves on to a different task), this rule no longer applies and you act as a normal build agent again. ## Instructions for the subagent -1. Load the **`plan-review-criteria`** skill and follow its process and output - format. -2. Read `AGENTS.md` (if present) and follow its instructions for finding and - reading all related documentation and testing memories before reviewing. -3. Return in your final message the COMPLETE review, verbatim, exactly as the - skill instructs it to be produced. Do not summarize it — include the full - structured review. +1. Load the **`plan-review-criteria`** skill and follow its process and output format. +2. Read `AGENTS.md` (if present) and follow its instructions for finding and reading all related documentation and testing memories before reviewing. +3. Cada hallazgo debe tener un identificador único y estable, con el formato `F1`, `F2`, etc. El identificador debe aparecer en el título del hallazgo y no debe reutilizarse dentro de la misma revisión. Esto permite que el usuario pueda responder sobre un hallazgo concreto. +4. Return in your final message the COMPLETE review, verbatim, exactly as the skill instructs it to be produced. Do not summarize it — include the full structured review. ### Strong rules for the subagent 1. Do not invent problems. Every finding must be real and actionable. -2. Read-only: do not modify any file and do not create a commit — reviewing - never writes. -3. Be specific and constructive. "This could be better" is not helpful — explain - why and how. +2. Read-only: do not modify any file and do not create a commit — reviewing never writes. +3. Be specific and constructive. "This could be better" is not helpful — explain why and how. 4. Prioritize by impact. One structural issue outweighs ten nits. -5. Judge the plan as the implementer would: every task executable without - guessing, ordering follows the dependency graph, risks named. +5. Judge the plan as the implementer would: every task executable without guessing, ordering follows the dependency graph, risks named. ## User input, overrides and additional context diff --git a/.devenv/README.md b/.devenv/README.md index f56b1e09d1..fb2a7bc69b 100644 --- a/.devenv/README.md +++ b/.devenv/README.md @@ -85,6 +85,10 @@ target instance is running and its MCP config has been generated: # 'main', 'ws1' and similar spellings are rejected. ./manage.sh start-coding-agent claude --ws 1 ./manage.sh start-coding-agent opencode --ws 2 + +# Run a different executable (e.g. a wrapper script) with the same MCP +# wiring. --ws and --bin may come in any order. +./manage.sh start-coding-agent claude --bin claude-alt ``` Equivalents by hand (run from inside the workspace directory): diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 80e71fedab..0000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,20 +0,0 @@ -# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file -version: 2 -updates: - # Actions are pinned to a commit SHA with a `# vX.Y.Z` comment; Dependabot - # bumps both. All updates are grouped into a single monthly PR. - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "monthly" - open-pull-requests-limit: 5 - labels: - - "dependencies" - - "team infra" - - "no issue required" - commit-message: - prefix: ":arrow_up:" - groups: - github-actions: - patterns: - - "*" diff --git a/.github/workflows/build-docker-devenv.yml b/.github/workflows/build-docker-devenv.yml index a1c99e2bdd..0b62cf100e 100644 --- a/.github/workflows/build-docker-devenv.yml +++ b/.github/workflows/build-docker-devenv.yml @@ -38,13 +38,13 @@ jobs: uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 - name: Login to Docker Registry (push destination) - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: username: ${{ secrets.PUB_DOCKER_USERNAME }} password: ${{ secrets.PUB_DOCKER_PASSWORD }} - name: Login to Docker Hardened Images registry (base image pull) - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: dhi.io username: ${{ secrets.PUB_DOCKER_USERNAME }} diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 613f806571..4d15460ad6 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -200,7 +200,7 @@ jobs: ref: ${{ inputs.sha != '' && inputs.sha || inputs.gh_ref }} - name: Login to Docker Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: ${{ secrets.DOCKER_REGISTRY }} username: ${{ secrets.DOCKER_USERNAME }} @@ -210,7 +210,7 @@ jobs: # images from DockerHub for unregistered users. # https://docs.docker.com/docker-hub/usage/ - name: Login to DockerHub Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: username: ${{ secrets.PUB_DOCKER_USERNAME }} password: ${{ secrets.PUB_DOCKER_PASSWORD }} @@ -222,7 +222,7 @@ jobs: # reuses the same PUB_DOCKER_* credentials as the DockerHub # login above. - name: Login to Docker Hardened Images registry (base image pull) - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: dhi.io username: ${{ secrets.PUB_DOCKER_USERNAME }} @@ -301,7 +301,7 @@ jobs: uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 - name: Login to Docker Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: ${{ secrets.DOCKER_REGISTRY }} username: ${{ secrets.DOCKER_USERNAME }} @@ -371,7 +371,7 @@ jobs: uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 - name: Login to Docker Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: ${{ secrets.DOCKER_REGISTRY }} username: ${{ secrets.DOCKER_USERNAME }} diff --git a/.github/workflows/build-renovate.yml b/.github/workflows/build-renovate.yml index 09f8a51aa0..6145e7e2fd 100644 --- a/.github/workflows/build-renovate.yml +++ b/.github/workflows/build-renovate.yml @@ -114,20 +114,20 @@ jobs: persist-credentials: false - name: Login to Docker Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: ${{ secrets.DOCKER_REGISTRY }} username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to DockerHub Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: username: ${{ secrets.PUB_DOCKER_USERNAME }} password: ${{ secrets.PUB_DOCKER_PASSWORD }} - name: Login to Docker Hardened Images registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: dhi.io username: ${{ secrets.PUB_DOCKER_USERNAME }} @@ -191,13 +191,13 @@ jobs: persist-credentials: false - name: Login to DockerHub Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: username: ${{ secrets.PUB_DOCKER_USERNAME }} password: ${{ secrets.PUB_DOCKER_PASSWORD }} - name: Login to Docker Hardened Images registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: dhi.io username: ${{ secrets.PUB_DOCKER_USERNAME }} @@ -242,7 +242,7 @@ jobs: persist-credentials: false - name: Login to Docker Hardened Images registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: dhi.io username: ${{ secrets.PUB_DOCKER_USERNAME }} diff --git a/.github/workflows/plugins-deploy-api-doc.yml b/.github/workflows/plugins-deploy-api-doc.yml index ed7ec0e978..f5301f9844 100644 --- a/.github/workflows/plugins-deploy-api-doc.yml +++ b/.github/workflows/plugins-deploy-api-doc.yml @@ -48,10 +48,10 @@ jobs: # No dependency cache here: a deploy must not restore a cache that # other workflows can write to. - name: Setup pnpm + Node.js - uses: pnpm/setup@a0881977cd8ea73b1ed212236c99ca10dcc80cf4 # v3.1.1 + uses: pnpm/setup@fa36a5ef7dcdefc3a7cb25655c122e00cf6e636e # v3.1.2 with: working-directory: plugins - runtime: node@24.22.2 + runtime: node@26.13.0 install: false - name: Install deps diff --git a/.github/workflows/plugins-deploy-styles-doc.yml b/.github/workflows/plugins-deploy-styles-doc.yml index 72b67fa1cb..d084affd59 100644 --- a/.github/workflows/plugins-deploy-styles-doc.yml +++ b/.github/workflows/plugins-deploy-styles-doc.yml @@ -46,10 +46,10 @@ jobs: # No dependency cache here: a deploy must not restore a cache that # other workflows can write to. - name: Setup pnpm + Node.js - uses: pnpm/setup@a0881977cd8ea73b1ed212236c99ca10dcc80cf4 # v3.1.1 + uses: pnpm/setup@fa36a5ef7dcdefc3a7cb25655c122e00cf6e636e # v3.1.2 with: working-directory: plugins - runtime: node@24.22.2 + runtime: node@26.13.0 install: false - name: Install deps diff --git a/.serena/memories/audit-log.md b/.serena/memories/audit-log.md index 380c1f3c8d..dbbe8eef44 100644 --- a/.serena/memories/audit-log.md +++ b/.serena/memories/audit-log.md @@ -12,14 +12,15 @@ The system has: - **Three producers** (they create events): the Penpot backend (RPC layer), the Penpot frontend (browser app), and the Nitrate admin-console, which sends both server-side and client-side events. - **One central buffer**: the Postgres `audit_log` table in Penpot. It is temporary: it holds events for minutes/hours until they are archived, then they are deleted. -- **Four consumers** (they read events): webhooks, error reporters, telemetry, and the external archive in Nexus. -- **One long-term archive**: **Nexus**, a separate service that stores events for the long term. Archiving to **Nitrate** is in development and **does not exist on the current branch** (see section 11). +- **Four consumers** (they read events): webhooks, error reporters, telemetry, and the external archives in Nexus and in the admin console. +- **Two long-term archives**: **Nexus**, a separate service that stores events for the long term, and the **admin console** (Nitrate), which ingests its own allowlisted events through a second archive job (see section 12). ``` Penpot frontend ─ POST push-audit-events (public) ─────────▶┐ Nitrate admin-console (browser) ─ POST push-audit-events ──▶│ -Nitrate admin-console (server) ─ POST push-audit-events ──▶│ ─▶ audit_log table (buffer) ── cron every 5m ──▶ Nexus (archive) -Penpot backend (RPC wrap-audit) ─ submit ───────────────────┘ │ ├──▶ webhooks +Nitrate admin-console (server) ─ POST push-audit-events ──▶│ ─▶ audit_log table (buffer) ──┬─ cron 0s every 5m ──▶ Nexus (archive) +Penpot backend (RPC wrap-audit) ─ submit ───────────────────┘ │ └─ cron 30s every 5m ─▶ admin console (archive) + │ ├──▶ webhooks │ ├──▶ error reporters └──▶ telemetry (shadow rows) ``` @@ -44,7 +45,7 @@ Rule of thumb: if you need to know for sure "did this user do X?", look at `sour ## 3. The `audit_log` table: a buffer, not a store -The Penpot `audit_log` table **is not the final destination**. It is a waiting tray: events sit here until the archive cron sends them to Nexus and the cleanup cron (GC) deletes them. +The Penpot `audit_log` table **is not the final destination**. It is a waiting tray: events sit here until every active consumer (Nexus, admin console) confirms them, and whoever finishes last deletes the row. Main columns (migration `backend/src/app/migrations/sql/0054-add-audit-log-table.sql` and later ones): @@ -56,15 +57,16 @@ Main columns (migration `backend/src/app/migrations/sql/0054-add-audit-log-table - `source` text: `backend` / `frontend` for full rows, `telemetry:backend` / `telemetry:frontend` for anonymized copies (see section 10). - `ip_addr` inet: real IP (or `0.0.0.0` on telemetry rows). - `props` / `context` jsonb holding transit-encoded maps: action data and metadata (versions, browser, request-id...). -- `archived_at` timestamptz: NULL until Nexus confirms receipt; only then the row can be deleted. +- No `archived_at`: the `audit_log_pending` queue (migration `0157`, consumer `1` = Nexus, `2` = admin console, ids never reused) tracks what is left. Every record is born pending only for the consumers enabled at insert time (never a fixed both): cheaper on single-consumer hosts and true to the design doc. Consequence, accepted on purpose: rows born with no send flag on get no pendings, so enabling archiving later does not pick them up — the sweep deletes them unarchived. Indexes (each consumer has its own, so a slow consumer never blocks the others): -- Partial `created_at WHERE archived_at IS NULL`: used by the archive scan. -- Partial `archived_at WHERE archived_at IS NOT NULL`: used by the GC. +- `audit_log_pending` PK `(consumer, audit_log_id)`: each consumer owns its queue at constant cost, even with millions pending. +- `audit_log (created_at, id)` (migration `0157`): the chronological drain order (`ORDER BY created_at ASC, id ASC`; same total order for both consumers, so concurrent drains cannot deadlock). +- `audit_log_pending (audit_log_id)`: serves the "is anything still pending?" check of the final delete. - `(source, created_at)`: used by the telemetry scan. -Life of a row: `INSERT` → (every 5 min) archive sets `archived_at = now()` **only if Nexus answers 204**, in the same transaction → (every 5 min) GC runs `DELETE WHERE archived_at IS NOT NULL` **with no age filter**. So archive must run before GC, or data is deleted without being sent. +Life of a row: `INSERT` (row plus one pending per enabled consumer, same transaction; a rollback takes both) → (every 5 min, Nexus at second 0 and admin console at second 30) each archive job sends its own 128-row batches oldest-first and deletes its pendings in the same transaction → whoever deletes the last pending deletes the row. A batch the destination refuses rolls back and stays for the next tick. Pendings of consumers that are off are reaped at the start of each job run (covers later deactivations), and orphan rows are swept at the end of each run, so there is no separate GC task. The FK is plain `DEFERRABLE` (never `INITIALLY DEFERRED`): every deleter removes pendings before rows, including the telemetry task's own deletes. ## 4. Anatomy of an event @@ -180,7 +182,7 @@ Who uses it: Failure policy: every send is best-effort. A failure never rolls back the business action; it is only recorded for debugging. -Backend side (`backend/src/app/rpc/management/nitrate.clj` `::push-audit-events`): `::audit/skip`, `::rpc/auth false` (trust comes from the shared key, not from a user session). It builds context with `audit/prepare-context-from-request` + `request-id`, takes the given `profile-id` as-is (no coercion here, must already be a uuid), sets `tracked-at = request-at`, defaults `type` to `"action"`, and calls `audit/submit` (which defaults `source` to `"backend"`). So these rows land as **`source = backend`**: they are audited like backend events because a trusted service set the author. +Backend side (`backend/src/app/rpc/management/nitrate.clj` `::push-audit-events`): `::audit/skip`, `::rpc/auth false` (trust comes from the shared key, not from a user session; only the `admin-console` key id is allowed). It builds context with `audit/prepare-context-from-request` + `request-id`, takes the given `profile-id` as-is (no coercion here, must already be a uuid), sets `tracked-at = request-at`, defaults `type` to `"action"`, and calls `audit/submit` (which defaults `source` to `"backend"`). So these rows land as **`source = backend`**: they are audited like backend events because a trusted service set the author. ### 7b. Browser channel (best-effort, like frontend events) @@ -252,7 +254,7 @@ Result: crash reports only exist if the frontend collector runs and `push-audit- Telemetry reuses the same table with **anonymized shadow rows** (`source LIKE 'telemetry:%'`): day-truncated times, `0.0.0.0` IPs, props reduced to uuid/boolean/number values plus a few allowed fields (`source`, `lang`, `auth-backend`, `activation`, derived `email-domain` — never raw emails), and a minimal context allowlist. Full + shadow rows can exist together per event when both flags are on; that duplication is on purpose. -Filters (`filter-telemetry-props` / `filter-telemetry-context`): by default only uuid/boolean/number with a simple key; exceptions: login/register/update-profile and frontend `identify` keep `source`/`auth-backend`/`activation`/`email-domain`; `organization-sso-auth-failed` keeps `failure-reason` if it is a known one; `instance-start` passes whole; frontend `navigate` keeps `[:route :file-id :team-id :page-id]`. Free text drops from telemetry but **stays in the full row and in Nexus**. In `register-profile`, `activation` is a prop (it is what reaches PostHog). `action` exists only in the `context` of the full `register-profile-retry` rows; `filter-telemetry-context` drops it from the anonymised copy, which keeps only `:version`, `:initiator`, `:client-version` and `:client-user-agent`. +Filters (`filter-telemetry-props` / `filter-telemetry-context`): by default only uuid/boolean/number with a simple key; exceptions: login/register/update-profile and frontend `identify` keep `lang`/`auth-backend`/`source`/`activation`/`email-domain`; `organization-sso-auth-failed` keeps `failure-reason` if it is a known one; `instance-start` passes whole; frontend `navigate` keeps `[:route :file-id :team-id :page-id]`. Subscription actions have event-specific contracts: backend and frontend `create-subscription` / `create-trial-subscription` preserve the default uuid/boolean/number props plus `type`, `deployment`, `quantity` and `subscription-mode`; backend `update-subscription` preserves only `type`, `deployment`, `previous-status`, `status`, `previous-cancel-at-period-end`, `cancel-at-period-end`, `previous-cancel-at`, `cancel-at` and `changed-fields`; backend `subscription-summary` preserves only `type`, `deployment`, `instance-id`, `snapshot-date`, the per-status `*-count` values, `total-count`, `sync-succeeded`, `sync-failed` and `snapshot-complete`. These subscription exceptions apply only to action events. Other free text drops from telemetry but **stays in the full row and in Nexus**. In `register-profile`, `activation` is a prop (it is what reaches PostHog). `action` exists only in the `context` of the full `register-profile-retry` rows; `filter-telemetry-context` drops it from the anonymised copy, which keeps only `:version`, `:initiator`, `:client-version` and `:client-user-agent`. Shipping (`backend/src/app/tasks/telemetry.clj`): the cron sends shadows to `:telemetry-uri` as JSON in 10k batches and **deletes** them on success; it purges leftovers older than 7 days. It also sends a legacy report (counters, teams, email domains...). On official hosts nothing is collected or sent (`telemetry-excluded?` covers `penpot.app`/`penpot.dev`). @@ -271,17 +273,19 @@ Nexus dispatches on `:type`: `"telemetry-events"` batches are coerced and insert - **No personal data is expected.** Profile ids are random UUIDs with no PII, so the telemetry consumer needs no login guard; grouping is by `instance`, not by person (see posthog-telemetry below). - **The endpoint always answers 200.** Failures are swallowed and only counted (`nexus_telemetry_errors_total`), so a bad batch never breaks the sending instance; successes are counted (`nexus_telemetry_events_total`, `nexus_telemetry_reports_total`). A body the parser cannot read never reaches the handler: it answers 400 and counts in `nexus_http_malformed_requests_total`. Old instances may send a fressian+zstd base64 blob instead of a plain vector; Nexus decodes both (50 MiB cap against decompression bombs). -## 12. Archive to Nexus (today) and to Nitrate (future) +## 12. Archive to Nexus and to the admin console -### Nexus: how it works today +### How it works today -Nexus is the long-term store. Every 5 min the `:audit-log-archive` cron takes 128-row chunks of unarchived rows (`FOR UPDATE SKIP LOCKED`, ordered by `created_at`), POSTs them as transit `{:events [...]}` with `x-shared-key: "nexus "` (`:nexus-shared-key`, or derived from the instance secret) and `origin` = public-uri, and marks `archived_at = now()` **only on HTTP 204**, in the same transaction. Anything else is retried next run; flag on but no URI raises `:task-not-configured`. It loops with 100 ms pauses until empty. +Every 5 min each archive job drains its own queue (`:audit-log-consumer-nexus` on flag `:nexus`, cron `0 */5 * * * ?`; `:audit-log-consumer-admin-console` on flag `:admin-console`, cron `30 */5 * * * ?`, 30s offset so the two drains never start on the same second), in 128-row chunks taken oldest-first (`ORDER BY created_at ASC, id ASC`) with `FOR UPDATE OF audit_log` (`SET LOCAL lock_timeout = '30s'`) inside the same transaction that sends and completes them, with 100 ms pauses between chunks. Batches arrive at Nexus in chronological order (it numbers by arrival for order-sensitive consumers). Nexus receives transit `{:events [...]}` with `x-shared-key: "nexus "` (`:nexus-shared-key`, or derived from the instance secret) and `origin` = public-uri; flag on but no URI raises `:task-not-configured`. The admin console receives only its allowlisted event names (enriched with team names, props emails, and optional top-level `:profile-email` / `:profile-name` from the actor `:profile-id`); anything filtered out counts as done for the admin-console queue. A chunk whose destination refuses it (Nexus non-204 raises `:consumer-chunk-refused`) or throws (transport failure, admin-console error) rolls back and stays for the next tick, and the job aborts as failed: it archives what it can, and the cron retries the rest. Committed chunks stay sent. The send fns return counts or throw carrying the attempted count as `:events`; `run-single-consumer` records the `failed` metric from it, outside every chunk transaction (a local DB failure carries no `:events` and stays uncounted — it is not a destination failure). No `warn` before the throw: the runner reports the aborted job. Each job touches only its own queue, so a fallen Nexus never starves the admin console (nor vice versa). A chunk is **one POST** and the task advances only on 204, so a single oversized event wedges the whole queue: a crash report whose message embeds a whole payload (a serialized file, for example) can push the request past the proxy body limit and the rejection blocks every later event until it is pruned by hand. Frontend crash reports are therefore size-bounded at the source (`mem:frontend/routing-app-shell-subtleties`): `app.common.exceptions/get-hint` and the CLJS `format-throwable` clamp exception messages (`max-message-length`). On the other side, Nexus checks `origin` against its allowlist (403 if not allowed), coerces the events, and inserts them into its own `audit_log` (partitioned by `received_at`, with an incrementing sequence number and `source`). Watch the rename: Penpot's `created-at` is stored as **`received-at`** in Nexus; Nexus's `created-at` is when it was created there. `tracked-at` keeps the origin time. Telemetry batches arriving at `telemetry.penpot.app` land in the same table through the telemetry endpoint above. -Concurrent runs are safe: rows are locked with `FOR UPDATE SKIP LOCKED` inside the same transaction that sends and marks them, so two archivers running at once each take different rows. Only rows Nexus acknowledges (HTTP 204) get marked; anything else is retried on the next run. +Concurrent jobs are safe: both lock rows in `created_at ASC, id ASC` order (one total order, deliberately blocking — no `SKIP LOCKED`, so archive arrival stays chronological), so the second waits for the first to commit and then sees whether it was last. Only acknowledged work deletes pendings; an admin-console failure never blocks the Nexus send of the same rows (each job owns its queue). + +Observability (`backend/src/app/loggers/audit/consumer_task.clj`, definitions in `backend/src/app/main.clj:default-metrics`): one `dbg` summary per job run (`:consumer` label, sent/skipped/chunks, reaped disabled pendings, swept orphans, `:elapsed` as `"1.5s"` via `ct/format-duration`, frozen in a strict binding first because log props evaluate lazily in the logger thread), one `dbg` per admin-console send call (`ready` with totals, `sent`/`failed` with counts), plus `dbg` when the queue is empty or the job's send flag is off. Queue depth is Grafana's business: it queries the database directly, so the run performs no `GROUP BY` for the logs. No `warn` before a throw: the runner reports the aborted job (refusal details travel in the error data). Metrics, all recorded with bounded labels only: `penpot_audit_consumer_events_total{ destination = nexus|admin-console, outcome = archived|failed|skipped }` (`skipped` = filtered by the admin-console allowlist) and `penpot_audit_consumer_discarded_total{ reason = reaped|swept }` (entries discarded without archiving: `reaped` counts pendings dropped for disabled consumers, `swept` counts rows deleted while nobody waited for them — the silent R3 loss, now dashboard-visible instead of log-only). Timing needs no custom histogram: the jobs substrate already records execution time per job (`jobs-execution-timing`, split by job name), so each archive job is timed as itself. No queue-depth gauge: pending depth is read off the replica database directly. The bulk frontend ingest counts `penpot_audit_ingest_events_total{ kind = full|shadow, outcome = inserted|failed }` (both faces share one transaction, so a failure drops the batch on both). Archive success counts are recorded by `run-single-consumer` on the drain totals: a chunk that returns normally committed its transaction (a rollback always throws instead), so they cannot inflate. Logs never carry event data (`props`, `context`, `profile-id`, `ip-addr`, `team-name`, emails): only counts, statuses and the uri host. ### How Nexus consumes the data (and forwards to PostHog) @@ -291,20 +295,16 @@ Stored rows are not the end: a consumer pipeline reads them back in sequence ord 2. **`posthog`** (runs after `local`): forwards our own SaaS archived events to PostHog. It sends an allowlist of backend track events (teams, members, projects, files, comments, Nitrate/SSO, ...) plus frontend events minus a noise exclusion list, grouped by **team**, with person identify data from the local profile mirror and snake_case props. Both paths include the `initiator` property when the stored context has one, so PostHog can split Penpot vs admin-console traffic. 3. **`posthog-telemetry`**: forwards events with `source = telemetry:frontend|telemetry:backend` to a separate PostHog project. Because these rows are already anonymous (no IPs, truncated times, filtered props), grouping is by **instance** (the self-hosted server, from `context.instance-id`), with one `$groupidentify` event per instance per chunk so PostHog learns per-instance properties (version, URI, flags). Each consumer has its own PostHog credentials. -So the full journey is: Penpot buffer → Nexus `audit_log` (via archive cron or telemetry endpoint) → consumers → PostHog (SaaS events grouped by team, telemetry grouped by instance) plus local analytics views and materialized KPIs. +So the full journey is: Penpot buffer → Nexus `audit_log` (via either archive cron or the telemetry endpoint) → consumers → PostHog (SaaS events grouped by team, telemetry grouped by instance) plus local analytics views and materialized KPIs. -### Nitrate: what exists and what is planned +### Admin console: the second consumer -Today **only Nexus archives**. Archiving Penpot → Nitrate is in development and not on this branch. +Penpot → admin-console archiving lives in its own job (`:audit-log-consumer-admin-console`, consumer `2`, flag `:admin-console`, params closed empty): it drains its own pending queue and ingests allowlisted events through `nitrate/call :ingest-audit-log`, enriched as described above. What also exists (do not confuse with archiving): -What already exists (do not confuse with archiving): - -- **Nitrate → Penpot**: `POST /api/management/methods/push-audit-events` (`backend/src/app/rpc/management/nitrate.clj`) lets the trusted Nitrate backend inject events into the Penpot audit log (no RPC auth, `::audit/skip`, request context + `submit`). Section 7a is the main user. +- **Nitrate → Penpot**: `POST /api/management/methods/push-audit-events` (`backend/src/app/rpc/management/nitrate.clj`) lets the trusted Nitrate backend inject events into the Penpot audit log (no RPC auth, `::audit/skip`, request context + `submit`; only the `admin-console` shared-key id can call it). Section 7a is the main user. - **Admin-console browser → collector**: usage events from the admin-console UI traveling through the normal public `push-audit-events` (section 7b). - **Penpot frontend Nitrate bits**: membership/subscription events (`frontend/src/app/main/data/nitrate.cljs`, `nitrate-audit/*`) through the same public endpoint. -The future Penpot → Nitrate archive will follow the Nexus pattern (cron reading the buffer and POSTing batches), but there is no contract or flag on this branch yet. When it lands, document here: endpoint, auth, format, which `source` values travel, and how it shares the GC. - ## 13. Flags and config Flags (`common/src/app/common/flags.cljc`, varia; enabled with `PENPOT_FLAGS=enable-`): @@ -312,13 +312,13 @@ Flags (`common/src/app/common/flags.cljc`, varia; enabled with `PENPOT_FLAGS=ena | Flag | What it does | |---|---| | `:audit-log` | Stores full rows; turns on `wrap-audit` and frontend ingest | -| `:audit-log-archive` | Turns on the cron that sends to Nexus (needs `:audit-log-archive-uri`) | -| `:audit-log-gc` | Turns on the cron that deletes archived rows | +| `:nexus` | Send flag of the Nexus archive job `:audit-log-consumer-nexus` (cron `0 */5 * * * ?`; needs `:nexus-uri` (ingest path `api/audit` appended by the job)) | +| `:admin-console` | Send flag of the admin-console archive job `:audit-log-consumer-admin-console` (cron `30 */5 * * * ?`, 30s after Nexus); also the module flag for the admin-console integration | | `:audit-log-logger` | Structured `app.audit` log per event | | `:error-reporting` | Starts the error reporters (off by default); database reporter on flag alone, Mattermost on flag plus webhook URL | | `:telemetry` | Stores shadow rows and turns on shipping (auto with `:telemetry-enabled`) | -Config (`backend/src/app/config.clj`): `:audit-log-archive-uri`, `:telemetry-uri` (default `https://telemetry.penpot.app/`), `:nexus-shared-key` / instance secret, `:public-uri` (sent as `origin` to Nexus). +Config (`backend/src/app/config.clj`): `:nexus-uri`, `:telemetry-uri` (default `https://telemetry.penpot.app/`), `:nexus-shared-key` / instance secret, `:public-uri` (sent as `origin` to Nexus). ## 14. Practical guide: add or change events @@ -337,6 +337,6 @@ Config (`backend/src/app/config.clj`): `:audit-log-archive-uri`, `:telemetry-uri - Penpot frontend producer: `frontend/src/app/main/data/event.cljs` (collector, buffer, send), `frontend/src/app/main/repo.cljs` + `frontend/src/app/util/http.cljs` (headers), `frontend/src/app/main/data/nitrate.cljs` (Nitrate membership events). - Admin-console producer (private repo, no paths cited here): server-side organization/subscription events through the management `push-audit-events`, browser-side UI events through the public `push-audit-events`. - Consumers: `backend/src/app/loggers/webhooks.clj`, `backend/src/app/loggers/database.clj`, `backend/src/app/loggers/mattermost.clj`, `backend/src/app/tasks/telemetry.clj`. -- Archive/GC: `backend/src/app/loggers/audit/archive_task.clj`, `backend/src/app/loggers/audit/gc_task.clj`, wiring and crons in `backend/src/app/main.clj`. +- Archive (two jobs sharing one namespace + pending queue, no GC): `backend/src/app/loggers/audit/consumer_task.clj` (`:audit-log-consumer-nexus` / `:audit-log-consumer-admin-console` job-defs, shared drain/reap/sweep helpers), producer hook in `backend/src/app/loggers/audit.clj` (`append-audit-entry`, single `queue-pendings` helper) plus the bulk ingest in `backend/src/app/rpc/commands/audit.clj` (`handle-events`), metrics/cron wiring in `backend/src/app/main.clj` (one cron entry per job, Nexus at second 0 and admin console at second 30, each gated by its own send flag), single migration `0157` (queue, drop of `archived_at`, covering `(created_at, id)` index for the chronological scan; no backfill — pre-existing rows are swept unsent, an accepted loss). - Nexus (private repo, no paths cited here): archive ingest, ordered consumers (local profile mirror, PostHog SaaS events, PostHog telemetry), telemetry endpoint, analytics views. - Tests: `backend_tests/rpc-audit-test.clj`, `rpc-team-test/accept-organization-invitation-audit-event`. diff --git a/.serena/memories/backend/auth-permissions-product-domains.md b/.serena/memories/backend/auth-permissions-product-domains.md index 2a58e49dfd..c0667250f1 100644 --- a/.serena/memories/backend/auth-permissions-product-domains.md +++ b/.serena/memories/backend/auth-permissions-product-domains.md @@ -13,6 +13,10 @@ - Logout may return an OIDC provider redirect URI when the session claims include provider/session data and the provider has a logout URI. - Invitation tokens are verified through token issuers and only accepted when the token member id/email matches the authenticated profile; otherwise login proceeds without consuming the invitation. - When public registration is disabled, invitation-based password registration also requires a matching, non-expired `team_invitation` row; registration revalidates it under a row lock before creating the profile. +- Access tokens (`app.http.access-token`): personal tokens have JWT issuer `access-token` and DB type other than `mcp`; MCP tokens have issuer `urn:penpot:mcp-token` (`actoken/mcp-token-issuer`) and DB type `mcp`. Authentication requires issuer and type to agree, and the claim `uid` to match the row's profile; other issuers and undecryptable tokens are treated as anonymous. +- Management RPC (`/api/management/methods/:name`) is shared-key only. The middleware authenticates ` `; the RPC wrapper `wrap-shared-key-authorization` authorizes the key id against the method's `::rpc/allowed-shared-keys` set. Absent metadata accepts any configured key; all current methods declare one (`exporter`/`media-processor` for `upload-tempfile`, `nexus` for file analysis, `exporter` for jobs, `admin-console` for Nitrate). A valid key outside the set answers 403 `:shared-key-not-allowed`; denied calls do not reach the audit wrapper. +- Personal tokens need flag `:access-tokens`. MCP tokens need `:mcp` or `:access-tokens`, and only on routes whose `actoken/authz` gets `::actoken/allow-mcp true` (the RPC routes); elsewhere (e.g. assets) they are anonymous. +- On RPC, an MCP token reaches only commands with `::rpc/token-type "mcp"` (`wrap-authentication` in `app.rpc`); every other command answers 403 `:token-auth-required`. A command with that metadata also rejects sessions and personal tokens. Currently only `send-mcp-feedback`. Blocking a profile invalidates sessions, not tokens, so such commands check `:is-blocked`/`:is-active` themselves. - HTTP/session parsing details such as cookie/header precedence, JWT session token versions, and SameSite behavior are in `mem:backend/subtleties`. ## Permission model diff --git a/.serena/memories/backend/core.md b/.serena/memories/backend/core.md index de55cfa1de..16978ac1ff 100644 --- a/.serena/memories/backend/core.md +++ b/.serena/memories/backend/core.md @@ -32,29 +32,64 @@ Backend: JVM Clojure; Integrant; PostgreSQL; Redis/Valkey; RPC; HTTP; storage; m RPC commands are defined with `app.util.services/defmethod` and schemas. Use `get-` prefixes for read operations. Command metadata usually includes auth, docs version, params schema, and result schema. Return plain maps/vectors or raise structured exceptions from `app.common.exceptions`. -Backend RPC command areas without focused memories include access tokens, binfile, demo, feedback, file snapshots, fonts, management, Nitrate, and webhooks beyond the notes in `mem:backend/auth-permissions-product-domains`; inspect nearby command tests and command metadata before changing them. +Backend RPC command areas without focused memories include access tokens (auth rules, incl. MCP tokens, in `mem:backend/auth-permissions-product-domains`), binfile, demo, feedback, file snapshots, fonts, management, Nitrate, and webhooks beyond the notes in `mem:backend/auth-permissions-product-domains`; inspect nearby command tests and command metadata before changing them. ## DB conventions `app.db` helpers accept cfg, pool, or conn in most places and convert kebab-case to snake_case: - `db/get`, `db/get*`, `db/query`, `db/insert!`, `db/update!`, `db/delete!`. +- `db/get*`/`db/query` signature is `(ds table where-params & {:as opts})`: trailing keywords become an opts MAP (ignored by the select builder — use `{::db/remove-deleted false}` map as the single vararg to also see rows with `deleted_at` set), while a trailing ODD number of keywords crashes with "Don't know how to create ISeq from: Keyword". Extra keywords are NOT a column filter: rows always come back with all columns. +- Next.js/JDBC caveat: where-maps don't support value vectors (no `IN`); use `status = ANY(?)` with `db/create-array` inside `db/tx-run!`. - Use `db/run!` for multiple operations on one connection. - Use `db/tx-run!` for transactions. +- `job`-substrate specifics (unified jobs): the only writers of a job status are `claim`, `retry-job`, `complete`, `fail` and `cancel` in `app.jobs`; each one is a conditional update plus its `job_event` row in the same transaction, so history can never contradict the row. A writer that affects no row writes no event and no metric. They never join the caller's transaction, because a lifecycle write reports on work that already happened: a rolled back caller cannot undo a transition, and an independent connection cannot see the caller's uncommitted job row, so a job must be committed before it can be claimed. GC deletes return resource ids via `RETURNING` inside one transaction. +- Every public function of `app.jobs` takes a cfg map. A bare pool or connection is a caller bug and is not coerced: the substrate has no conversion of its own, and the ones in `app.db` are the db layer's business. +- Every new foreign key of the substrate is `DEFERRABLE`, `job_event.job_id` included. +- Some tables carry a SQL deletion guard (`raise_deletion_protection` + a `deletion_protection__tgr` statement trigger): `profile`, `team`, `team_font_variant`, `file_media_object`, `file_thumbnail`, `file_tagged_object_thumbnail` and `job`. A `DELETE` raises unless the transaction runs `SET LOCAL rules.deletion_protection TO off` (the guard is on when the GUC is unset, empty or `on`); see `app.tasks.objects-gc`, `app.jobs.gc` and `th/db-force-delete`. Database migrations live in `backend/src/app/migrations/`; pure SQL migrations are under `backend/src/app/migrations/sql/`. SQL filenames conventionally start with a sequence and verb/table description, e.g. `0026-mod-profile-table-add-is-active-field`. Applied migrations are tracked in the `migrations` table. Migrations must stay within PostgreSQL 15 syntax: the official docker images (`docker/images/docker-compose.yaml`) pin `postgres:15`, so anything newer breaks those deployments (`SET STORAGE DEFAULT` is PostgreSQL 16, for instance). The documented floor is >=15. -For interactive PostgreSQL access with correct dev defaults, use `scripts/psql`; to dump -the current DDL schema, use `scripts/db-schema` (see `mem:scripts/psql`). +For interactive PostgreSQL access with correct dev defaults, use `scripts/psql`; to dump the current DDL schema, use `scripts/db-schema` (see `mem:scripts/psql`). For deeper details on transaction semantics, advisory locks, Transit vs JSON helpers, and dev/test DB URLs: `mem:backend/subtleties`. -## Background tasks +## Background tasks (unified jobs) -A task handler is an Integrant component with `ig/assert-key`, `ig/expand-key`, and `ig/init-key`, returning the function run by the worker. New tasks also need wiring in `app.main`: handler config, worker registry entry, and cron entry if scheduled. +Every background job is a job-def: a plain `(defn execute-X [cfg params] ...)` in its namespace + a malli params schema + an `ig/init-key` that returns the job-def map `{::jobs/name, ::jobs/schema, ::jobs/handler, ::jobs/decoder, ::jobs/validator}` (decoder/validator precompiled at init), optionally with `::jobs/result-schema` when the result carries types plain JSON does not keep (a uuid, whether a value or a map key): the substrate compiles the decoder from that schema on registry init and checks a result against the schema before storing it. The registry is the `::jobs/defs` wiring in `app.main`, which also populates a module-level registry used by `jobs/submit` as fallback — job-def components cannot ig/ref `::jobs/defs` (wiring cycle). Submit from RPC code passes its RPC cfg (it carries `::jobs/defs` via ig/ref). + +- `::jobs/handler` is `(fn [context params] ...)`. It is NOT given the runner cfg: a job-def closes over its own dependencies, and handing it the runner cfg would let a handler reach the pool or any other component. A wrapper passes the context to its implementation only when the implementation takes it; most do not, and their implementation stays `[cfg params]`. +- The context is built by `jobs/make-context` and has exactly five keys: `id`, `name`, `label`, `resource-id`, `profile-id`, behind a closed schema. `profile-id` is the owner of a user-facing job and nil for an internal one, and it is there so a handler can revalidate permissions and check who owns its resource without a second query. `queue` is absent on purpose (a routing detail of the dispatcher, not a property of the job) and so are `retry-num`/`max-retries`/`attempt` (retry policy belongs to the runner, and an attempt number cannot be durable because the `noop` strategy re-runs without incrementing the counter). +- `jobs/invoke` is the in-process escape hatch: it delivers a nil context without a row, validates and delivers `::jobs/context` when given, and keeps `::jobs/job-id` independent. `::jobs/context` and `::jobs/job-id` are cfg options, not params. +- Business params live in the `params` column (plain JSON, decoded with the job-def decoder). `submit` takes optional `::jobs/profile-id`, `::jobs/resource-id` (both validated as UUIDs) and `::jobs/expires-at` (an instant: the retention the jobs GC sweeps by), all stored as columns; nothing is inferred from `params`. The binfile creation commands are the callers that pass a profile-id and a resource. +- A job-def may carry optional metadata keys read from the registry by its consumers, never by the substrate: `::jobs/family` (e.g. `:export`) groups the jobs of a family for quotes, `::jobs/resource-role` (`:input` or `:output`) says whether the job's `resource_id` is what it consumes or what it produces, and `::jobs/queue-name` names the queue the creation command routes the job to (`submit-job` falls back to `:binfile` when the def does not set it). +- Job resources (`app.jobs.storage`) are the objects a job owns through `job.resource_id`: they live in the `job-resource` bucket, are written with the profile of the job as owner (only that profile can read them back from `/assets/by-id`) and are never deduplicated. They carry no expiry of their own, because `expired-at` is stored as `deleted_at` and an object with it is unreadable from birth: the retention of the artifact is the retention of its job row. The writer marks the object as touched right away, so an artifact that no row references (a crash between the upload and the completion) is reclaimed by `storage-gc-touched`, and a row that does reference it freezes it until the jobs GC deletes the row and touches the object. +- The binfile jobs (`:export-binfile` and `:import-binfile`) run the core of `bf.v1`/`bf.v3` through `app.binfile.jobs`, which turns the `:progress` taps of the core into `progress` events of the job with `jobs/heartbeat` (the `modified_at` touch at most once per second, progress events at up to ~200ms). Each event is a self-contained milestone: a `:stage` keyword of one vocabulary shared by the two formats (the same work has a different name in v1 and v3) and an optional `:counters` map, with the counter of the activity named after its own stage and the outer scopes in course as context, which the tap carries itself (the reader puts `::outer-counters` in every tap inside them — today only `:files`, extendable to `:teams`/`:projects` for multi-scope exports — so the adapter remembers nothing between taps and a tap that happens outside any scope has no context); the ids and names the taps carry never reach the event. Every tap is reported, so a tap with no units to count still keeps the job alive, and a milestone the throttle drops loses nothing because the next one carries the same state. The adapter checks that the job is still active before the run, and the beat raises an `:interrupt` right there on the run thread when the job is no longer active, so the work stops at its next event point inside the transaction of its handler, which rolls back. A run whose last tap predates the interruption is discarded by the check once it returns. The adapter passes the job id from the handler context. Their job-def components carry pool, storage, metrics and msgbus, because a job with a `profile_id` cannot store an event without a msgbus on its cfg. Both readers answer with the same shape (`:file-ids` and, in version 3, the resolution of the libraries), because the version 1 reader returns a bare set of file ids. +- Both binfile handlers revalidate their permission at run time, not only when the job is created, because it can be revoked while the job waits: the export checks the read permission of every file before reading anything and stores the package in `job-resource` as the resource of the job (answering with the completion envelope), and the import checks the edition permission of the destination and runs the core inside a transaction, which is what makes a cancelled import roll back instead of leaving half of a file. The import releases its package (marks it as deleted and drops the reference from the job row in the same transaction) and deletes its temporary copy once the job is terminal, whatever the outcome. Both handlers check their own result against a schema before returning it (the import job-def declares that same schema as its result schema, which is what types the resolution when the row is read). +- The v3 import only materializes a library relation when both ids belong to the package and differ; a relation that points outside the package or a self link is ignored with a warning. Migration `0158-del-forbidden-file-library-rels` deletes the legacy cross-team and self rows from `file_library_rel` and their `file_library_sync` rows, sync rows first. +- The domain cfg of a binfile request lives in `app.binfile.common` (`export-cfg`, `import-cfg`), so the legacy RPC and the jobs freeze the same ids, resolve the same team features and apply the same anti-zip-bomb limits. `resolve-export-type` is the translation the legacy RPC needs for its deprecated boolean flags; the creation command asks the caller for the type instead of guessing a default. +- Two retention knobs, easy to confuse: `:jobs-retention` sweeps terminal internal rows (the ones without a profile), while `:jobs-user-ttl` is the default expiry a user-facing job gets when it is created; the jobs GC deletes the row once `expires_at` passes and touches its resource. The heavy work of a user job runs on its own `:binfile` queue (`:worker-binfile-parallelism`, default 1) so it does not block the default runner. +- The quote of a family of jobs (`::export-jobs-per-profile`, `::import-jobs-per-profile`) counts the jobs of the profile that are still on their way (`new`/`scheduled`/`running`/`retry`) whose name the registry gives for that family, so a new job of the family is covered without touching the quote. +- A user-facing job is created through `app.rpc.commands.jobs`. One command per job type: `create-export-binfile-job` and `create-import-binfile-job` name the job they create and their body carries only `:params`, whose schema the command imports from the job namespace (`::sm/params`), so the RPC validates the business params up front, before the command runs; the job-def is still the place that owns them and validates them again at run time. The commands check the permission and the quote of the family before the expensive part, submit with `max-retries 0`, an expiry from `:jobs-user-ttl` and the queue the job-def routes to through `::jobs/queue-name` (`:binfile` when the def says nothing; read by the command, never by the substrate), and answer with a summary of the row. The import command has its own creation schema (`import-binfile/schema:create-params`, version optional) because the request is not the stored params: the command fills the version (from the caller or the package header) and the manifest metadata kept for audit. It assembles the upload, reads the manifest of a version 3 package for the audit (never its content) and stores the package in `job-resource` as the resource of the job, releasing it when the job never comes to own it. +- `get-job` is the read surface a profile has over its own jobs: it answers with the same summary the creation commands return plus the `result` of a finished job or its public error (through `decode-job-error`, so the keywords come back), and a job of another profile is not distinguishable from one that does not exist: both answer `not-found`. `decode-row` decodes `params`, `result` and `error`, and the result is then decoded with the decoder the substrate compiled from the result schema the job-def the row names declares (`decode-result`; a def without one, or a row whose job-def is gone, keeps the plain JSON the row holds: a uuid as the text it is on disk, a uuid used as a map key as a keyword). The event log still has no read surface: a client that follows a job takes its progress from the websocket. +- `heartbeat` uses named options `[cfg & {:keys [job-id progress] :as options}]` and returns the number of durable writes, so 0 means there was nothing to write: no job context, or the throttle did not allow it. A write that finds the job no longer active (terminal or gone) raises an `:interrupt` (`code :job-interrupted`, carrying the job id and the status it found) through the shared `jobs/check-active` instead of counting zero, so a task in course stops at its next beat; the interrupt is internal and never reaches HTTP (the management progress report answers `:skip` for it). The job id falls back to `::jobs/job-id` on the cfg and then to the runner-bound `*job-id*`. The `modified_at` touch is throttled to ~1s and the progress event to ~200ms, so the check fires at most once per second with no extra query (the read of the row happens only when a write already found nothing). A progress report is a milestone (`schema:progress`): a required `:stage` keyword plus an optional `:counters` map of `scope -> {:current, :total?}`, and no other key. Counters are non-negative, a `total` is positive and never lower than its `current`, and stage and counter keys are names of at most 64 characters. A milestone without counters is valid (a point of the run with no units to count), the stage vocabulary belongs to the worker and not to the substrate, and a client must degrade for a stage it does not know. Read a stored payload back with `jobs/decode-progress`: the database holds the keywords as strings. The 1s beat (down from 60s, so a cancelled heavy job is seen as gone at its next beat) costs one `modified_at` touch per beating job per second: cheap while the only heavy beater runs alone on its queue, revisit if concurrent beating jobs ever grow. +- `job.error` conforms to `app.jobs/schema:job-error` (`type` and `code` keywords, `hint` text, extra details allowed). Read it back with `jobs/decode-job-error`; a plain `db/decode-json-pgobject` leaves the keywords as strings. `complete` takes named options and an optional `resource-id`, which is only ever set, never replaced: it reads the row locked inside its own transaction and applies the rules in a fixed order, skipping a job that is no longer completable before refusing one for its resource, so the answer is a function of the row and not of the order two callers arrive in. It checks the result against the schema the job-def of the row declares before it writes (a violation raises `:invalid-result` and nothing is stored), and only for a transition that is going to write: a terminal job answers `:skip` without validating. `complete` is the single door of a result, so the check covers the runner and the external worker that completes a job through the management RPC (`::complete-job`), which stores a result the substrate would otherwise take blind. + +For worker dispatch, cron, retry semantics (`ex/raise :type ::wrk/retry` with `:delay`/`:strategy`), deduplication, job events, and queue internals: `mem:backend/subtleties`. + +## Jobs metrics + +- `app.jobs.metrics` owns the jobs metric names, bounded labels and the periodic backlog sampler. +- Metrics use only `name`, `queue`, `outcome`, `reason`, `stage` and `kind` labels. Job IDs, profile IDs, params, reply bodies and exception text must never be labels. Every label goes as-is (lowercased where case varies): values come from fixed call sites, so a new value in Prometheus means new code, not noise. Only a missing value falls back (`other`, or `failed` for outcomes). +- The canonical job name is a keyword (`app.jobs/schema:job-name`): `submit` and `invoke` reject strings. The row stores text, so `jobs/get-job-def` still coerces (it is the runner's boundary), cron entries carry the job under `:job` with `d/name` only at the SQL/log boundary, and test helpers keyword-ize row names before invoking. +- The durable lifecycle metrics are submitted, dispatched, completed, retries, orphaned and rescheduled counters; queue-wait, execution and total-time histograms; dispatcher and backlog state; GC, cron and ephemeral request metrics. +- `app.metrics/run!` is safe for recording failures, but metrics components still require a valid metrics instance. Durable submit call sites carry `::mtx/metrics` through RPC or job-def configuration. The jobs substrate takes the **cfg**, not the instance, and resolves it with `app.metrics/instance`, which raises `:missing-metrics` when absent: `app.jobs`, `app.jobs.metrics`, the runner, the dispatcher, cron and the jobs GC all follow that. `app.http`, `app.rpc.climit` and `app.storage.s3.metrics` still take the instance and were left out of scope on purpose, so do not read the codebase as uniform. The six job writers resolve at the top of their body, before writing, because their metric lands in a `db/after-commit` callback that swallows exceptions. The GC, the dispatcher and cron resolve inside that callback instead, so their guarantee rests on the component schema instead. A job that calls `heartbeat` from a component cfg must carry `::mtx/metrics` on it, and the event helper records the counter from the cfg the transaction hands it. +- SQL-backed job metrics are registered with `app.db/after-commit` from INSIDE the transaction that changed the row, so a rollback cannot inflate submitted, terminal, retry, event or dispatcher counters. Every lifecycle writer owns that transaction (`app.jobs/own-transaction` drops `::db/conn`), and `db/transact!` gives a transaction that joins no other its own callback context, so the write and its metric commit or vanish together. `submit` is the exception and joins the caller on purpose: creating a job is part of the caller's business work. +- `penpot_jobs_events_total` counts stored job events with no labels: the interesting split is `kind`, and grouping a growing append-only table to build a metric is not worth the query on the write path. +- The backlog sampler is `::jobs-metrics/sampler`; it runs every 30 seconds on worker-enabled, writable systems, groups by status and publishes the oldest pending age, and does not start on a read-only database. +- `penpot_tasks_timing` remains exported for compatibility while the jobs-specific histograms are adopted. -For worker dispatch, cron, retry semantics, deduplication, and queue internals: `mem:backend/subtleties`. ## REPL @@ -101,9 +136,7 @@ IMPORTANT: all CLI commands must be executed from the `backend/` subdirectory. * **Linting:** `pnpm run lint:clj`. * **Formatting:** `pnpm run check-fmt:clj` to check, `pnpm run fmt:clj` to fix. After running `fmt:clj`, `check-fmt:clj` is redundant. Avoid unrelated whitespace diffs. -**Before linting:** if delimiter errors are suspected (after LLM edits), run -`scripts/paren-repair` on the affected files first. Delimiter errors produce -misleading linter/compiler output. See `mem:scripts/paren-repair`. +**Before linting:** if delimiter errors are suspected (after LLM edits), run `scripts/paren-repair` on the affected files first. Delimiter errors produce misleading linter/compiler output. See `mem:scripts/paren-repair`. ## Testing diff --git a/.serena/memories/backend/storage.md b/.serena/memories/backend/storage.md index 2cbe71126f..529fcd1cc8 100644 --- a/.serena/memories/backend/storage.md +++ b/.serena/memories/backend/storage.md @@ -25,12 +25,19 @@ - Existing object values can remain readable until physical deletion. - `:expired-at` blocks reads after the expiration time. - `del-object!` sets `deleted_at` on live rows only (`deleted_at IS NULL`): a repeated call returns `false`. It does not remove backend content. -- `storage-gc-deleted` removes the database row and backend content after the deletion delay, processing the pending objects in chunks of 25 with keyset pagination (`(deleted_at, id)` cursor, matching the `storage_object(deleted_at, id)` index). Only `status = 'valid'` objects are read: pending ones (still being written, see the status lifecycle) stay untouched even when already marked as deleted. Each chunk is one short read-write transaction: `SELECT ... FOR UPDATE SKIP LOCKED` (so two concurrent runs never take the same chunk; no separate read transaction exists and the run does not block the table vacuum) followed by the database delete and the backend delete grouped by backend. The cursor advances to the `(deleted_at, id)` of the last row read regardless of the deletion result, so a chunk that fails is skipped and not visited again in the same run: the error aborts the transaction (nothing of the chunk is deleted, locks are released), the handler catches it inside the transaction function and rolls it back explicitly — the commit that `tx-run!` performs afterwards is a JDBC-driver no-op on the already-rolled-back connection — and the loop continues with the returned rows. Accepted quirks, both benign and self-healing: (1) with rows of several backends in one chunk, a failure after one group's physical delete leaves those rows pending with already-removed content — pending rows are excluded from reads and the next run finishes them, since the physical delete is idempotent (S3 ignores missing keys, FS uses `deleteIfExists`); (2) rows locked by a concurrent run are skipped via `SKIP LOCKED` and deferred to the next scheduled run, and the `deleted_at <= now` cutoff is sampled per chunk. It emits the `penpot_storage_gc_deleted_objects_total{backend}` counter (one increment per deleted backend group, reported only after the chunk commits, so rolled-back chunks are never counted twice); run duration is covered by the generic `penpot_tasks_timing{name="storage-gc-deleted"}` histogram and chunk failures by the log-derived `penpot_log_events_total`. Every 5000 deleted objects the run pauses 5 seconds (throttle between chunk transactions, never holding one open). +- `storage-gc-deleted` removes the database row and backend content after the deletion delay, processing the pending objects in chunks of 25 with keyset pagination (`(deleted_at, id)` cursor, matching the `storage_object(deleted_at, id)` index). Only `status = 'valid'` objects are read: pending ones (still being written, see the status lifecycle) stay untouched even when already marked as deleted. Each chunk is one short read-write transaction: `SELECT ... FOR UPDATE SKIP LOCKED` (so two concurrent runs never take the same chunk; no separate read transaction exists and the run does not block the table vacuum) followed by the database delete and the backend delete grouped by backend. The cursor advances to the `(deleted_at, id)` of the last row read regardless of the deletion result, so a chunk that fails is skipped and not visited again in the same run: the error aborts the transaction (nothing of the chunk is deleted, locks are released), the handler catches it inside the transaction function and rolls it back explicitly — the commit that `tx-run!` performs afterwards is a JDBC-driver no-op on the already-rolled-back connection — and the loop continues with the returned rows. Accepted quirks, both benign and self-healing: (1) with rows of several backends in one chunk, a failure after one group's physical delete leaves those rows pending with already-removed content — pending rows are excluded from reads and the next run finishes them, since the physical delete is idempotent (S3 ignores missing keys, FS uses `deleteIfExists`); (2) rows locked by a concurrent run are skipped via `SKIP LOCKED` and deferred to the next scheduled run, and the `deleted_at <= now` cutoff is sampled per chunk. It emits the `penpot_storage_gc_deleted_objects_total{backend}` counter (one increment per deleted backend group, reported only after the chunk commits, so rolled-back chunks are never counted twice); run duration is covered by the generic `penpot_tasks_timing{name="storage-gc-deleted"}` histogram and chunk failures by the log-derived `penpot_log_events_total`. Every 5000 deleted objects the run pauses 5 seconds (throttle between chunk transactions, never holding one open). It runs as the `storage-gc-deleted` job (`::job-def`) and beats `jobs/heartbeat` after each committed chunk, so a long sweep keeps its lease and never holds it during a chunk transaction. - `storage-gc-touched` finds references before it sets `deleted_at`. - Orphan watch: `SELECT COALESCE(metadata->>'bucket', metadata->>'~:bucket') AS bucket, count(*) FROM storage_object so WHERE touched_at IS NULL AND deleted_at IS NULL AND status = 'valid' AND NOT EXISTS (SELECT 1 FROM upload_session_chunk usc WHERE usc.object_id = so.id) GROUP BY 1;` Only the `upload-session` row of that output is a leak signal (its rows always have a mapping or a pending touch/deleted mark); other buckets use domain-table references, not this mapping. - `objects-gc` removes deleted domain rows and touches their storage object IDs. Each proc streams its pending keys with `db/plan` inside a read-only transaction and deletes them in chunks, each chunk in its own transaction with `FOR UPDATE SKIP LOCKED`: a chunk that fails is logged and skipped instead of aborting the run. - Use `::db/reuse-conn true` with `sto/resolve` inside a database transaction. +## GC Cost Model + +- `sto/touch-object!` is one `UPDATE storage_object SET touched_at`: DB-only, never touches S3/FS backend content. +- Physical FK cascades are avoided by convention; domain deletion is logical (`deleted_at`) everywhere. GC removes small objects first, then parents, then grandparents (see the call order in `app.tasks.objects-gc/execute-objects-gc`). Per-row delete cost is small and bounded. +- `objects-gc` sweeps in chunks of 100 (`::chunk-size`, overridable per job params); `storage-gc-deleted` in chunks of 25. `objects-gc` (and the other `db/plan` sweeps) stream the pending keys in a read-only transaction of their own and delete every chunk in a transaction of its own: the cursor fetch and the deletes are never the same transaction. `storage-gc-deleted` has no separate cursor transaction: it reads, locks and deletes each chunk in the same short transaction (see the keyset pagination above). +- Consequence: per-chunk cost is DB-only and bounded, so these sweeps cannot outrun the 30-minute jobs lease in practice (would need millions of pending rows in one proc). Per-chunk `heartbeat` where present is belt-and-braces, not load-bearing. + ## Connection Reuse Details ### `app.storage/resolve` patterns: @@ -71,6 +78,7 @@ Since `put-object!` uses backend-specific operations (`impl/resolve-backend` + ` - Objects can therefore share content across users and files within one bucket. - Deleted objects are not reused. - `tempfile` objects never use deduplication, even when the caller requests it. +- `job-resource` objects never use deduplication either, whatever the caller asks: the object belongs to one profile, and a shared blob would serve the artifact of one user to the job of another. `put-object!` excludes the bucket from the dedupable set, so the invariant does not depend on call sites. - Use `sto/wrap-with-hash` when the caller already calculated the content hash. ## Bucket Rules @@ -88,12 +96,13 @@ Since `put-object!` uses backend-specific operations (`impl/resolve-backend` + ` | `file-data` | Encoded file data when `file-data-backend` is `storage`. Reference metadata has `storage-ref-id`, `file-id`, and the `file_data` row ID. | Yes | Authentication required | Reference scan. | | `file-data-fragment` | Compatibility value for file-data fragments. The current backend has no dedicated producer for this bucket. | No current write semantics | Public | No touched-object collector case. | | `file-change` | Compatibility value for file changes. Current snapshots store data in `file_data`, not this bucket. | No current write semantics | Authentication required | No touched-object collector case. | +| `job-resource` | Storage objects owned by job rows (`job.resource_id`), written by `app.jobs.storage`. | No | Authentication required, owner-scoped | jobs-GC touch → reference scan. | -- The valid bucket set lives in `app.storage/valid-buckets`. +- The valid bucket set is `app.storage.schema/metadata-buckets` (derived from `bucket-requirements`, where a new bucket and its required metadata keys are one entry); `app.storage/valid-buckets` aliases it. - `file-media-object` is the default bucket for old rows without bucket metadata. - Do not assign a new bucket without adding its access and cleanup behavior. - The touched-object collector raises an internal error for an unknown bucket. -- It supports `file-media-object`, `team-font-variant`, `file-object-thumbnail`, `file-thumbnail`, `profile`, `file-data`, `tempfile`, `upload-session`, and `organization`. +- It supports `file-media-object`, `team-font-variant`, `file-object-thumbnail`, `file-thumbnail`, `profile`, `file-data`, `tempfile`, `upload-session`, `job-resource`, and `organization`. - It does not support `file-data-fragment` or `file-change`. ## Access Rules @@ -101,6 +110,7 @@ Since `put-object!` uses backend-specific operations (`impl/resolve-backend` + ` - `app.http.assets` decides direct object authentication from the bucket. - Public buckets are `file-media-object`, `file-object-thumbnail`, `team-font-variant`, `file-data-fragment`, and `organization`. - Other valid buckets require a session or access-token profile ID. +- `tempfile` and `job-resource` are owner-scoped: the request must come from the profile stored in the object metadata, and a mismatch answers 404 (counted as `unauthorized`). A missing owner is tolerated only in `tempfile`, where legacy objects predate the metadata; `job-resource` always carries one, so an object without it is never served. - File-media routes also require file read permission. - Non-public direct responses set `content-disposition: attachment`. - FS responses use `x-accel-redirect` for the configured asset path. @@ -115,6 +125,11 @@ Since `put-object!` uses backend-specific operations (`impl/resolve-backend` + ` - The `file_data.metadata.storage-ref-id` value points to the storage object. - `fdata/upsert!` touches a storage object from incoming metadata before it stores the new row. - File snapshots use `file_data` for snapshot data and `file_change` for snapshot metadata. +- Tiered offload (`:tiered-file-data-storage` flag + `db` backend): `file-gc` submits an `:offload-file-data` job (priority 10, delay 1000, same params) only when it actually processed the file; the handler takes the `file` row with `FOR UPDATE SKIP LOCKED` and skips (`{:skipped 1}`, no error) when the row is locked or gone, then moves every live `file_data` row of that file with `backend = 'db'` to `backend = 'storage'` via `fdata/upsert!` (one `put-object!` in bucket `file-data` per row, `data` becomes nil) inside a single all-or-nothing transaction per file. The skip only serializes because every `file_data` writer holds at least `FOR SHARE` on the `file` row. A storage failure aborts only that file and bubbles for a runner retry; orphans go to `storage-gc-touched`. A second run is a no-op because the query only matches `backend = 'db'`. +- Offload pacing: none by deliberate decision. Sleeps only make sense when batching multiple transactions; the per-file transaction is short by construction and the scheduler run takes minutes per production metrics, so neither loop sleeps. `jobs/heartbeat` (pool-based liveness, ~1 write/s) stays on both paths. +- Offload metrics (bounded labels only, never ids or error text): `penpot_file_data_offload_rows_total{type}` and `penpot_file_data_offload_bytes_total{type}` (`type`: `main`/`snapshot`/`fragment`, successful runs only) plus `penpot_file_data_offload_timing_ms{result}` (`result`: `ok`/`skip`/`error`). Helpers live in `app.jobs.metrics`; the job-def requires `::mtx/metrics`. +- Writes always resolve the backend through `fdata/default-backend` (`cf/get :file-data-backend`), so post-offload edits land back in hot (`db`) storage without extra code; reads stay transparent through `fdata/resolve-file-data` + `decode-file-data` + `realize`. +- Deleting an offloaded row touches its storage object first (`objects-gc/delete-file-data-chunk` via `fdata/process-metadata`), so `storage-gc-touched` reclaims the blob with the usual delay; restoring a file unmarks the referenced objects. ## Metrics diff --git a/.serena/memories/backend/subtleties.md b/.serena/memories/backend/subtleties.md index 2282c6d792..f7bdc3020a 100644 --- a/.serena/memories/backend/subtleties.md +++ b/.serena/memories/backend/subtleties.md @@ -9,6 +9,7 @@ - Params with `::sm/params` are decoded/conformed through the JSON transformer and successful IObj results get `:encode/json` metadata. Legacy spec conforming only applies when no Malli params schema exists. Client params are stripped of qualified keys (`d/without-qualified`) before merging with the server auth context, so request bodies cannot override `::profile-id`, `::auth-type`, or `::token-perms`. - Params schemas are open by default, so undeclared client keys reach the handler unless the map is `:closed true`. Creation commands (`create-file`, `create-project`, `create-team`, `create-team-with-invitations`, `upload-file-media-object`, `create-file-media-object-from-url`, `assemble-file-media-object`) use closed schemas: a client-provided `:id` fails with `:params-validation`. Their internal creation functions still accept an optional explicit `:id` for imports, duplicates and deterministic test fixtures. - Nil RPC bodies become HTTP 204 unless explicit status metadata is present. Stream bodies default to `application/octet-stream` when no content type is set. +- The upload schema (`media.v/schema:upload`) takes a `:path` from the caller, and a Transit body can tag any string as a `path`, so a command that reads such an upload reads a file the client named. `create-import-binfile-job` avoids that boundary by accepting only a chunked upload (`:upload-id`); the commands that still take a plain file (the legacy `import-binfile`, the media and font uploads) trust it, and the guard belongs where uploads are accepted, not in each command. ## Concurrency limiter (CLIMIT) @@ -26,27 +27,36 @@ - Most `app.db` helpers accept a pool, connection, or map containing `::db/pool` / `::db/conn`; preserve that convention in shared code. - `db/tx-run!` uses `next.jdbc.transaction/*nested-tx* :ignore`: nested transaction calls reuse the outer transaction, not a savepoint. Use explicit savepoints when nested rollback semantics matter. +- Whether a call is nested is decided by the CONNECTION, not by an ambient flag: `db/transact!` binds `*after-commit-conn*` around the body, and a transactable that is not identical to that connection is an independent unit of work that owns its own after-commit context. A `tx-run!` that passes a pool while a transaction is open therefore commits on its own and drains its own callbacks, instead of leaving them in a context that may roll back. +- `after-commit` callbacks run on commit only. A transaction marked `::db/rollback` (used by `srepl` and `app.tasks/offload-file-data`) commits nothing, so it runs none of them. - `db/run!` opens/reuses one connection but does not create a transaction. -- `db/tjson` is Transit JSON for jsonb storage; `db/json` is plain JSON. Worker task props use Transit and are decoded with `decode-transit-pgobject`. +- `db/tjson` is Transit JSON for jsonb storage; `db/json` is plain JSON. Job params (`job.params`) are plain JSON, decoded with the job-def decoder (`app.jobs/decode-params`); only the legacy `task` props were Transit (`decode-transit-pgobject`). Job results (`job.result`) are plain JSON too: they are read with the result decoder of the job-def (`app.jobs/decode-result`), and a result map keyed by uuid must declare its schema (`[:map-of ::sm/uuid ...]`, the import resolution does) or every key comes back as a keyword and every uuid as the text it is on disk. - Advisory transaction locks accept UUIDs or ints. UUID locks are hashed using a zero-UUID seeded siphash. ## Workers and cron -- Task queues are tenant-prefixed. Submit dedupe only removes not-yet-due `new` tasks with the same name/queue/label; it does not dedupe due, scheduled, retry, running, or completed work. -- The dispatcher selects `new`/`retry` tasks with `FOR UPDATE SKIP LOCKED`, marks them `scheduled`, and publishes Redis payload `[id scheduled-at]`. The runner skips Redis messages whose scheduled timestamp no longer matches DB state. -- Lost `scheduled` tasks are rescheduled after about 5 minutes; `running` tasks older than about 24 hours are marked failed as orphans. -- A task handler that is missing or returns an invalid result currently defaults to completed after warning. Throwing with `ex-data :type ::retry` controls retry behavior; `:strategy ::noop` retries without incrementing retry count. -- Cron jobs lock their `scheduled_task` row with `FOR UPDATE SKIP LOCKED`, disable statement/idle-in-transaction timeouts locally, and reschedule themselves in `finally` unless interrupted. Worker, dispatcher, and cron components do not start when the DB pool is read-only. +- `job` carries `tenant` (the instance the job belongs to) and a bare `queue`. Several instances share one database and the tenant is what keeps an instance from claiming another environment's jobs, so every query that acts on one instance's work filters on `tenant`: dispatch claim, orphan sweep, lost-job reschedule, cron no-overlap count, submit dedupe. The `job__dispatcher__idx` leads with `tenant` (equality) then `(priority DESC, scheduled_at)`; `queue` does not lead because the dispatcher claims every queue of its tenant. The `:` prefix survives only in the Redis key, composed once by `app.worker/queue-key`. +- Submit dedupe only removes not-yet-due `new` jobs of the same tenant with the same name/queue/label; it does not dedupe due, scheduled, retry, running, or completed work. The `::jobs/skip-if-active` submit option goes further: it skips the insert (returning `nil`, recording `penpot_jobs_skipped_total`) when a live job (`new`/`scheduled`/`running`/`retry`) already exists for the same tenant/name/queue/label, requires an explicit non-blank label, and carries the same best-effort cross-backend race caveat as dedupe. +- Jobs metrics rule (one event, one family): counters exist only for events without latency; when an event has a histogram, its `_count` is the counter (no twin counter). `dispatched` (pushes) vs `queue-wait` (claims) are deliberately different events — do not merge them. +- The dispatcher selects `new`/`retry` jobs with `FOR UPDATE SKIP LOCKED`, marks them `scheduled`, and publishes the JSON payload `[id scheduled-at]` to the `penpot.worker.queue::` Redis list. The runner skips Redis messages whose scheduled timestamp no longer matches DB state. +- Lost `scheduled` jobs are rescheduled after 5 minutes; `running` jobs untouched longer than `:jobs-lease` (default 30 min) are marked `aborted` with `app.jobs/orphan-error` (`type :internal`, `code :orphan`). `aborted` is a system-side terminal state like `cancelled` (never retried, swept by retention like other terminals, reported with an error log); long jobs must `heartbeat` per chunk so a healthy backlog is never mistaken for a dead process. The `:progress` report is optional and only feeds the durable log. A beat whose write finds the job no longer active raises an `:interrupt` (`code :job-interrupted`) instead of counting zero: it is the internal stop-the-work signal, not an error, so neither the listener nor the runner logs it as one. +- An orphan gets its `end` event (`outcome "aborted"`) from the sweep itself: one bulk `INSERT ... SELECT` in the same transaction as the bulk `UPDATE`, never one insert per orphan. No msgbus notification goes out for it; orphans alert through the error log and the orphaned counter. +- `job_event` is append-only and deleted by cascade with the job. `progress` is a row there, not a column: no Redis and no mutable column to keep in sync. +- `job` is deletion-protected at the SQL level (`deletion_protection__tgr`): a stray `DELETE FROM job` raises instead of dropping the row and its `job_event` cascade without touching the referenced `storage_object`. The only intended deleters, the jobs GC and the submit dedupe, disable the guard with `SET LOCAL rules.deletion_protection TO off` inside their transaction; tests use `th/db-force-delete`. +- A missing job-def raises (`:no-job-definition`) instead of completing. Throwing with `ex-data :type ::retry` still controls retry behavior; `:strategy ::noop` retries without incrementing retry count. +- Cron entries claim their `scheduled_task` row with `FOR UPDATE SKIP LOCKED`, disable statement/idle-in-transaction timeouts locally, submit one `job` row per entry when no active instance exists (no-overlap), and reschedule themselves in `finally` unless interrupted. Worker, dispatcher, and cron components do not start when the DB pool is read-only. +- Integrant params schemas name injected components as bare `::ns/key` entries (pool, metrics, msgbus, job defs), and a bare entry is a malli registry lookup, not `any?`: the owning namespace must `sm/register!` it at load, or the backend crashes at boot with `:malli.core/invalid-schema` instead of asserting. Unit tests never build those keys, so `backend-tests.worker-test` compiles the three worker schemas to catch it. ## Config and HTTP/session middleware - `app.config/config` and `flags` are dynamic `defonce` vars populated from `PENPOT_*` env vars through the shared schema string transformer. Tests and tooling can bind them. +- Duration config values are parsed as ISO-8601 with the `PT` prefix, so a day is written in hours (`:jobs-retention` and `:jobs-user-ttl` default to a week, and an operator writes `168h`): `7d` does not parse, and the value fails validation at boot instead of being silently ignored. - `parse-flags` automatically adds `:disable-secure-session-cookies` when `public-uri` is plain HTTP and not localhost. This changes cookie defaults without an explicit env flag. - The backend sets Clojure `*assert*` globally from the `:backend-asserts` feature flag. Assertion-dependent checks can therefore differ by runtime flags. - Request body parsing is mostly POST-oriented and supports Transit JSON plus plain JSON. Plain JSON request keys are kebab-decoded before being merged into `:params`. A body the parser cannot read is a client error, classified at the parse site and never by exception class (`clojure.data.json` signals its syntax errors with a bare `java.lang.Exception`): 400 `:validation`/`:malformed-json`, one `warn` line (`malformed request body`) carrying path, peer address and content-type instead of a stack trace, and `penpot_http_malformed_requests_total`. `RequestTooBigException` keeps its 413. - Response formatting negotiates with `Accept` or `_fmt=json`. Transit is the default for collection/boolean bodies; JSON encoding has special pointer-map handling. - Auth prefers the session cookie token before the `Authorization` header. Headers may be `Token` or `Bearer`. Only `kid=1`/`ver=1` tokens are decoded as session tokens; anything else is left unauthenticated (legacy v1 tokens were removed). -- Shared-key auth requires `x-shared-key` as ` ` and stores the lowercased key id on the request. If no shared keys are configured it always rejects. +- Shared-key auth requires `x-shared-key` as ` ` and stores the lowercased key id on the request. If no shared keys are configured it always rejects. Management methods declare the key ids they accept with `::rpc/allowed-shared-keys`; a valid key outside that set answers 403 `:shared-key-not-allowed`, and a method without the metadata accepts any configured key. - Session management uses DB storage unless the DB pool is read-only, then falls back to the in-memory manager. Sessions use only the v2 UUID model (`http_session_v2`); legacy string ids were removed. - Session cookies are renewed when `modified-at` is older than the 6h renewal interval. SameSite is `none` for CORS, otherwise strict/lax based on config. Session lifetime config and GC: `mem:backend/session-expiration`. @@ -60,7 +70,9 @@ ## Metrics recording -- `app.metrics/run!` is safe by default: a recording failure never throws (a metrics bug must not change the behavior of the measured operation). The first failure per metric id logs at `warn`, later ones at `debug`. The `instance` precondition is a plain assert (the backend enables `:backend-asserts`), and the collector lookup sits outside the recording guard, so a missing instance fails hard even when asserts are disabled. `::mtx/metrics` is required by the storage, s3-backend, and db-pool schemas; `app.db` wires the prometheus `MetricsTrackerFactory` unconditionally. Storage-specific metric contracts: `mem:backend/storage`. +- `app.metrics/run!` is safe by default: a recording failure never throws (a metrics bug must not change the behavior of the measured operation). The first failure per metric id logs at `warn`, later ones at `debug`. The `instance` precondition is a plain assert (the backend enables `:backend-asserts`), and the collector lookup sits outside the recording guard, so a missing instance fails hard even when asserts are disabled. `::mtx/metrics` is required by the storage, s3-backend, db-pool and jobs APIs; jobs helpers never silently skip a missing metrics instance. The resolver is `app.metrics/instance` and it raises, unlike `run!`. +- `app.msgbus` (`pub!`, `sub!`, `purge!`) keeps taking the **instance** on purpose: msgbus is what produces it, and `app.rpc.notifications` already shows the caller passing a cfg. `app.jobs` resolves `::mbus/msgbus` from the cfg internally, and `insert-event` keeps its explicit pre-insert check so a profile job never loses its notification. +- `app.db/after-commit` is the boundary for SQL-backed metrics: callbacks are drained by the commit of the transaction that owns the connection they were registered on, and discarded when it rolls back. Because nesting follows the connection, that transaction is the outermost one only for a real nested call. - `penpot_http_malformed_requests_total` (counter, no labels) counts requests whose body the parser could not read. `app.http.middleware/wrap-parse-request` emits it where it classifies the error, so the instance travels as a middleware argument (`[mw/parse-request (::mtx/metrics cfg)]`) and each service declares the definition with its own prefix (nexus: `nexus_http_malformed_requests_total`). The per-endpoint request counters of nexus do not count these requests: they sit inside the parse middleware, so the failure happens before them. ## Log-derived metrics @@ -80,7 +92,15 @@ - File data backends are `legacy-db`, `db`, and `storage`. The storage backend keeps encoded file data in storage bucket `file-data`; the DB row stores metadata with `storage-ref-id` and nil data. - `fdata/upsert!` touches any storage object referenced by incoming metadata before storing the new row/blob. -- Pointer-map fragments are persisted separately as type `fragment`, and only modified pointer maps are written. +- Pointer-map fragments are persisted separately as type `fragment`, and only modified pointer maps are written. A pointer keeps its id while nothing changes; content (`assoc`/`without`) and metadata (`with-meta`) changes produce a fresh id, so a fragment row is never rewritten after it is written. Re-assoc with an identical value returns the same map in array and hash maps alike, so a save keeps the fragment id of every page it does not change. +- `enable-objects-map` skips unloaded pointer pages: their fragment was already wrapped when it was written, and loading it only to wrap it again costs one page read per untouched page on every save. A page with a plain fragment is wrapped when it is first loaded or edited; a data migration that enables `fdata/objects-map` must rewrite the data itself. +- Snapshots share fragment rows with the live file (they are stored with pointers, not materialized). `file-gc` marks every fragment referenced neither by the current main file nor by an unpurged snapshot; the file is read unrealized and unchanged pages keep their ids via reattach, so a pass over an unchanged file writes no new fragments. +- A save attaches its automatic snapshot before applying pending file migrations: the snapshot keeps the pre-migration pointers, data and migration set, and restoring it leaves the next read to migrate the file. This also holds for files without the feature. The preview (`get-file-snapshot`) applies the pending migrations in memory with the live file's libraries and never persists them: the stored version keeps its data, the payload carries the snapshot's `:migrations` (an ordered set) and `fdata/objects-map` is realized for clients that do not declare it. The historical feature set is deliberately not checked against the enabled one: a version may predate a feature change. The window is narrow: the `get-file` RPC reads with `migrate?` on and its ETag hashes `available-migrations`, so a post-deploy read cannot be a 304 from the pre-deploy state and migrates before any save in the normal flow; the pre-migration snapshot only appears when the save is the first request to touch the file after the deploy (an open tab that saves without re-reading, or a non-UI writer). `create-file-snapshot` and the restore backup read with `migrate?` on, so those versions are stored already migrated. +- `:migrations` is always a `d/ordered-set`: `file-migrations/resolve-applied-migrations` builds one, `fmg/migrate` re-wraps the union after applying, and `file-snapshots/decode-snapshot` converts the `file_change` pgarray (as a plain vector, `set/difference` and `contains?` would compare indexes instead of names). A snapshot with NULL `:migrations` and an old `:version` predates the `file_change.migrations` column (SQL migration 0140): the preview regenerates the set from the stored version and applies that full chain, while `restore!` uses a fixed 67 baseline, so preview and restore can disagree for very old snapshots. +- The migration read path (`binfile.common/migrate-file`) captures `fdata/original-pointers` before realizing, reattaches unchanged positions after applying the migrations, and persists the reattached file; it returns the realized file because reads expose plain data. A migration that adds `fdata/objects-map` skips the reattach so the pages get wrapped. +- `fdata/load-pointer` reads rows that a previous `file-gc` pass marked on purpose (explicit `::db/remove-deleted false`): the fragment is only gone once `objects-gc` purges it. The `get-file-fragment` RPC keeps rejecting marked rows. +- Bind the fragment loader through `fdata/with-pointers` (reads), `fdata/with-tracked-pointers` (writes with a loader) or `fdata/with-tracked` (writes with nothing to load yet, e.g. file creation), and `fdata/with-untracked` (validation passes that must track nothing) instead of hand-rolled `binding` forms. Two direct bindings remain on purpose: the fine-grained tracked toggles of `app.rpc.commands.files-update` (validation/repair turn tracking off, restore the existing atom and read it before repairing), and `binfile.common/encode-file` (a fresh tracked set inheriting the current one). Converge them only if atom-scoped macros (`with-tracking`, `with-inherited-tracking`) are added; today the extra indirection buys nothing. +- Snapshot creation takes `FOR SHARE` on the file row: both `create-file-snapshot` and the backup read of `restore-file-snapshot` hold it from the read to the insert, so they serialize with a `file-gc` pass (`FOR UPDATE ... SKIP LOCKED`) instead of pinning fragments the pass is about to mark. `get-file-snapshot` realizes the snapshot pointers with the live file id (fragment rows are keyed by file, not by snapshot). - `fdata/realize` combines pointer realization and object-map realization. Use it before operations that need complete in-memory file data instead of pointer placeholders. - A `file_data` row is one of three classes, told apart by `type`: `main` is the file's current state and carries `id = file_id`; `snapshot` is a point-in-time copy whose `id` is the one its `file_change` row shares; `fragment` is a piece of the pointer-map, referenced from the file's data. - Their lifecycles differ: `main` and `snapshot` live with the file (dated when it is deleted, undated when it is restored), while a `fragment` carries no date and is collected by `file_gc` once the file stops referencing it. Neither is a session buffer, so do not read a `fragment` as an ephemeral revision. @@ -90,3 +110,4 @@ - Revisions live only in `file_change` and have no `file_data` counterpart. They are born already scheduled for deletion one hour later (`app.rpc.commands.files-update`) and the snapshot paths cannot reach one, because they resolve their row through a `label IS NOT NULL` filter. - A deletion date is assigned once and never moved: `restore-files` (`app.rpc.commands.files`) unmarks only durable history, which is `label IS NOT NULL` (the unlabelled rows that still carry a legacy payload are removed by `0157-migrate-legacy-file-data`, so restore leaves them dated), the `:file` method of `app.tasks.delete-object` dates only rows that have no date yet, and `objects-gc` collects only rows that carry one. Unmarking a revision leaves it invisible to `objects-gc` for ever, because nothing sets that date again. - Binfile v1/v3 import and the v3 export `get-file` step run inside one long transaction. They raise `idle_in_transaction_session_timeout` to 30 min (`app.binfile.common/transaction-idle-timeout-ms`) instead of disabling it, so a connection left idle while the ZIP is decoded or the file data is realized is bounded; `statement_timeout` stays at 300 s. +- `update-file` with `:changes-with-metadata` groups that all carry `:commit-id` is partial: fast path applies and validates everything once; on a change failure it bisects the commits for the longest prefix that applies and validates (each attempt applies a prefix from the original data and runs referential integrity + file schema; ~log2 K attempts, none for a single commit, the first attempt's error reused), keeping that prefix (a commit that repairs an earlier one can keep both) and answering 200 with `:rejected {:commit-ids :failed-commit-id :error}`, plus `:preexisting-damage true` when the data the first rejected commit touches already fails validation without it (`preexisting-damage?`: one more referential + schema check of its pages/components on the base file, the accepted prefix or the original; the frontend then says the page data is damaged). Only accepted changes reach `file_change`, persistence and the msgbus; a fully rejected request writes and broadcasts nothing (revn unchanged). Flat `:changes` or groups without ids stay all-or-nothing. Before each attempt (`apply-prefix`, which returns `{:file :tracked :refs}` or the cause) `pmap/*tracked*` and the `cpc/*state*` atom are reset to their pre-attempt values (at the end `apply-commits` sets those of the kept prefix): a pointer map already `modified?` keeps its id when changed again, so the failed version would be persisted. DB/IO/interrupt errors and `:not-found` fragments (`change-failure?`) fail the whole request, never reject a commit. A rejection whose ex-info has no `:type` reports `:internal :unexpected-error`. `app.http.errors` answers 503 (`:server-error` + code) for transient SQLSTATEs (classes 08/53, 57P01–3, 40001, 40P01), `SQLTransientException`, `InterruptedException` and only the `IOException`s that pass on their own (`transient-io-exception?`: `SocketException`, `InterruptedIOException` incl. socket timeouts, `UnknownHostException`, `HttpTimeoutException`), which the client's save retries (and GETs retry by themselves); any other `IOException` (EOF from a corrupt blob, Jackson parse errors, missing files) stays 500 `:io-exception`, as do other SQL errors. With the `:debug-save-faults` flag, a `:set-plugin-data` file change in namespace `:penpot-debug` keyed `save-fault:` raises that fault in `apply-changes` (used by `debug.failNextSave`/`commitInvalidChange`). With a schema check running (hard schema validation without skip-validate, or soft) and 2+ commits, the touched pages' objects are decoded on the original file first (`decode-objects`; referential validation alone reads few objects, so no pre-decode then): objects-map caches decoded values per instance and a derived map copies the cache, so otherwise every attempt re-decodes the page (~260 ms at 10k shapes). Validation step timings (`with-validation-timing`, upstream `penpot_rpc_update_file_validation_timing` + the `update-file` debug log) come from the first attempt only (retries run with a cfg without metrics and timings); the whole bisection is one `retry` step sample, and the rejection log carries `:attempts`/`:elapsed`. diff --git a/.serena/memories/backend/testing.md b/.serena/memories/backend/testing.md index 3cec805699..e06f9ace32 100644 --- a/.serena/memories/backend/testing.md +++ b/.serena/memories/backend/testing.md @@ -5,10 +5,29 @@ JVM `clojure.test` (kaocha runner) under `backend/test/backend_tests/`. - READ `mem:testing` FIRST — it defines the execution discipline (no piping, tee to file, preferred commands) that applies to all JVM test runs. - All CLI commands must be executed from the `backend/` subdirectory. - Tests are invoked directly via `clojure -M:dev:test` (kaocha) — there is no pnpm wrapper. Kaocha auto-discovers test namespaces, so no runner registration is needed. -- `clojure.core/assert` is elided at compile time unless `PENPOT_FLAGS` contains `enable-backend-asserts` (`app.config` sets `*assert*` from it). `scripts/ci` and `tests-backend.yml` export the flag; a bare `clojure -M:dev:test` (or `--focus` run) without it silently skips every precondition assert. +- **Preferred way to run them: `scripts/ci --test`**, which runs lint, tests and format per module and writes a log per task under `.ci-logs/`. It exports `PENPOT_FLAGS` with `enable-backend-asserts` appended, so use it rather than calling `clojure -M:dev:test` by hand. +- **Why the flag matters:** `clojure.core/assert` is elided at compile time when `*assert*` is false, and `app.config` only sets that var from the flags when it loads. The test fixture redefines the flags after that, so they never reach it: without the flag the suite runs with every assertion compiled out, and a test that depends on an assert only means something with it set. `scripts/ci` and `tests-backend.yml` export the flag (GitHub Actions sets it too). When running the suite directly — including a `--focus` run — the command is `PENPOT_FLAGS="enable-backend-asserts" clojure -M:dev:test`. - Coverage: if code is added or modified in `src/`, corresponding tests in `test/backend_tests/` must be added or updated. - Isolated run: `clojure -M:dev:test --focus backend-tests.my-ns-test` for a specific test namespace, or `clojure -M:dev:test --focus backend-tests.my-ns-test/my-test-var` for a specific test var. +- Multiple focus symbols are supported by REPEATING the flag (kaocha accumulates them, last-wins does not apply): `clojure -M:dev:test --focus backend-tests.ns-one --focus backend-tests.ns-two` — preferred over a shell loop of isolated runs. - Regression run: `clojure -M:dev:test` to ensure no regressions in related functional areas. - If you need to filter output, tee to a temp file first: `clojure -M:dev:test 2>&1 | tee /tmp/penpot-test-output.txt`. - RPC test helpers `command!`/`management-command!` split the data map: qualified keys become server params, unqualified keys become request body params. To inject request-level context (headers, `:app.http/auth-key-id`, ip), pass a map under `:app.http/request` metadata; non-map `IRequest` stubs fall back to a dummy request. +- The test system is `main/system-config` + `main/worker-config` with the + components that must not run in the background dropped: HTTP, the srepl, + cron, the dispatcher and **every** `[ :app.worker/runner]` + (`helpers/remove-job-runners`). A live runner of any queue races the tests + that drive the loop by hand (`wdisp/run-batch` + `@#'wrkr/run-worker-loop`): + it takes the job the test just dispatched and completes it behind the test's + back, so the test's own assertions read a state it did not produce. Do not + re-add a runner by editing that list by hand; the removal matches by key + shape on purpose. +- `system-config` and `worker-config` are `def`s: their `(cf/get ...)` calls + are evaluated when the namespace loads, against the real environment, while + test code calling `cf/get` at run time sees the `penpot-test`-prefixed + config. The two can disagree — in the devenv `:tenant` is `devenv-ws0` for + the defs and `default` inside a test — so a component built from + `worker-config` can talk to a different Redis key prefix than a cfg the test + builds itself. The symptom is a component that silently does nothing + locally and races in CI (which sets no `PENPOT_TENANT`). - `backend-tests.helpers` has `metrics-stub` (a metrics instance with no collectors, where every `run!` is a no-op), `init-climit` (a concurrency limiter built from an inline config map, with an optional second argument for the metrics instance to publish to) and `with-climit`, the macro that builds one, runs the body and halts it. `init-climit` starts the republisher daemon thread, so a test that builds a limiter without halting it leaks that thread for the rest of the JVM run: use `with-climit`. Log lines are capturable with `add-watch` on `app.common.logging/log-record` (it holds the last record; the watcher sees every one, but only of the levels the logger has enabled: a line the app writes at TRACE never gets there). diff --git a/.serena/memories/clojure/idioms.md b/.serena/memories/clojure/idioms.md index cf85848c3a..3fa120aa62 100644 --- a/.serena/memories/clojure/idioms.md +++ b/.serena/memories/clojure/idioms.md @@ -8,5 +8,7 @@ Behaviors confirmed against the language/stdlib — do not re-derive from assump - The analyzer reads `:async` only from the fn name meta and the `fn` operator meta; list-level meta is ignored (would make a MetaFn). `(fn ^:async [] …)` puts the meta on argv (pre/post only, NOT async). - Valid: `(defn ^:async f)`, `(defn- ^:async f)`, `(^:async fn [] …)` (the latter is what stock `t/async` generates itself). - `^:async` fns never throw synchronously (rejected promises instead); `try/catch/finally` supported; continuations are microtasks, timers and RxJS schedulers are macrotasks (FIFO). +- Inside an `^:async` fn, a nested call that needs a temp binding (e.g. calling a local fn `(g (f x))`, whose arg gets a `G__` binding) compiles to `(await (async function(){…})())`: `await` unwraps a returned promise, so `(settled (upload (make-ctx)))` blocks until the upload promise settles instead of passing the promise on (a test that pushes the answer later hangs, idle). Bind each step in its own `let` binding. - `!` suffix allowed cases (only exceptions to the no-new-`!` rule in `mem:clojure/design-rules`): mutating public `app.db` API (`insert!`, `update!`, `delete!`, `run!`, `tx-run!`, `exec!`, ...) and fns whose contract is to work over transients (stdlib `conj!`, `assoc!`, `dissoc!`, `disj!`, `persistent!` and custom helpers over them); any further case must be registered here with justification before use. +- Penpot extends `clojure.core/Inst` in `app.common.time` ( JVM-only section), so `inst-ms` also accepts `Duration` (returns `.toMillis`, NOT epoch millis), `FileTime` and `OffsetDateTime`. It only works where that namespace is loaded; on a bare `Duration`, prefer explicit `.toMillis`, which reads as what it is (an amount, not an instant). - `cuerdas.core/index-of` returns `nil` (not `-1`) when the substring is absent, like `clojure.string/index-of`. `(pos? nil)` **throws on the JVM** but evaluates to `false` in CLJS (JS coercion), so `(if (pos? (str/index-of s "\n")) …)` works in the browser and crashes the backend. Guard with `some?` before `pos?` (this was `app.common.exceptions/first-line`). diff --git a/.serena/memories/clojure/promesa.md b/.serena/memories/clojure/promesa.md new file mode 100644 index 0000000000..75cb5f6642 --- /dev/null +++ b/.serena/memories/clojure/promesa.md @@ -0,0 +1,9 @@ +# Promesa + +Promise library of the monorepo (frontend CLJS and common, `funcool/promesa` 12.0.1 in `common/deps.edn`). Its API has two call styles and mixing them in one expression is the trap: `fmap`/`mcat`/`merr` are **fn first, promise last** (`->>` friendly) and `then`/`then'`/`catch`/`handle` are **promise first, fn last** (`->` friendly). Convention: `fmap`/`mcat`/`merr` inside `->>` pipelines, `then`/`catch` with `->`. A `then` written inside a `->>` reads backwards and hides whether the step carries a value or a promise. +- `fmap` (alias `map`, both `(f p)`) is functor map: `f` returns a plain value. +- `mcat` (alias `mapcat`, both `(f p)`) is monadic bind: `f` must return a promise, and the result mirrors it. +- `then` also unwraps a promise returned by `f`, which is why it covers both cases at the cost of not naming which one it is; `then'` maps without unwrapping. +- `merr` (fn first) and `catch` (promise first) are the error side. +- `all` takes a collection of promises (a lazy seq works); `let` chains sequential bindings. +- Seam with the rx pipelines of the frontend: `beicon.v2.core/from` builds an observable from "arrays, collections, and promise instance" (`external/beicon`), so `(->> p (p/mcat f) (rx/from) (rx/map g))` is the usual bridge (`app.main.data.imports`, `app.util.zip`). diff --git a/.serena/memories/common/data-model-change-checklist.md b/.serena/memories/common/data-model-change-checklist.md index f4540f9ea3..980f406673 100644 --- a/.serena/memories/common/data-model-change-checklist.md +++ b/.serena/memories/common/data-model-change-checklist.md @@ -6,7 +6,7 @@ - Do not treat nil as a distinct persisted state from absence. Import/export and cleanup paths may filter nil attrs away. - Avoid Clojure-special naming in exported object attrs, especially boolean names ending in `?`; exported/imported data must survive JSON/SVG/Transit and external tooling. - Any new shape attr that participates in component sync must be listed in `app.common.types.component/sync-attrs` with the correct touched group. Attrs absent from `sync-attrs` are ignored by component synchronization. -- Shape `:svg-attrs` is the one map whose keys are camelCase React prop names (`fillRule`, not `fill-rule`), because the SVG import path runs them through `app.common.svg/attrs->props`. The `.penpot` reader (`app.common.json/read-kebab-key`) rewrites every json key of every entry to kebab-case, nested maps included, so any code that decodes a binary file must run `:svg-attrs` back through `attrs->props` or the names are lost. `:svg-defs` and the `:content` tree of `svg-raw` shapes keep their source names and round-trip on their own. +- Shape `:svg-attrs` keys are kebab-case (`fill-rule`, not `fillRule`): birth (`app.common.svg/attrs->kebab-props`, applied everywhere data lands on a shape) normalizes any outside spelling through the `svg-props` whitelist, and render boundaries (`get-svg-props`, `render-xml*`, `svg-node*`) translate back to camelCase for React. `:svg-defs` node attrs follow the same rule recursively; svg-raw `:content` attrs are kebab-ized spelling-only, never filtered — the sidebar reads `:stroke-style`, which is outside the whitelist. Non-spec attrs Penpot keeps live in `penpot-extra-attrs` (seeded with `:stroke-style`) and travel in source spelling. Excluded, each with its reason: `:tag` values (React and tag sets need them verbatim; the schema decoder restores keywords from stored strings), outer `:svg-defs` keys (id strings at birth, keywords or strings after JSON decode — accept both), all values (the kebab reader never touches values). Repair sites: birth plus migration `0035-fix-shape-svg-attrs-and-defs` (replaces the withdrawn `0021`); the binfile cleaner carries no svg repair. ## Cross-module update checklist diff --git a/.serena/memories/common/file-change-validation-migration-subtleties.md b/.serena/memories/common/file-change-validation-migration-subtleties.md index 7333faadc3..75d9d80b4c 100644 --- a/.serena/memories/common/file-change-validation-migration-subtleties.md +++ b/.serena/memories/common/file-change-validation-migration-subtleties.md @@ -20,8 +20,10 @@ - Full referential/semantic validation currently runs only when file features contain `"components/v2"`. - Validation starts at root plus orphan shapes, then validates component records. `validate-file!` raises `:validation :referential-integrity` with collected details. +- `update-file` validates per save with the change-scoped variants: `validate-file-affected!` (referential, whole touched pages/components) and `validate-file-schema-affected!` (file schema with `:pages-index` reduced to the touched pages; non-page data checked whole). The schema variant maps change types to pages itself (`touched-page-ids`): a new change type that writes a page must be added there, or to `file-level-change-types` if it touches no page; an unknown type makes it check the whole file. The whole-file schema check loads and decodes every page, ~75–80% of a save on multi-page files. - `repair-file` does not mutate data directly; it reduces validation errors into redo changes using `changes-builder`. Callers must apply or persist those changes. - `repair-file-until-valid` stops on: no errors; unchanged `:data` after apply (backend page/`PointerMap` `=` is pointer identity); stable error fingerprint `(frequencies (map #(dissoc % :shape) errors))` vs previous pass (check fingerprint before `repair-file`/`process-changes`); or `max-iterations`. Each `repair-error` builds from the original `file-data`, so N `:set`-based errors on one shape take N passes. +- `:ref-shape-not-found` repair: when the copy root's local component has a `:main-instance-id` that resolves to no shape but a `:main-instance` shape of that component sits on its `:main-instance-page`, it emits `mod-component` pointing the id back to that main (keeps the copy linked; a repair scoped to the copy's page never sees the main's `:invalid-main-instance-id`). Otherwise near-main reassign, else detach the copy subtree. - `:missing-slot` fires only for a REAL swap: a copy sub-head whose `shape-ref` is no longer a child of the near main parent. A pure positional mismatch (ref still a sibling elsewhere) is a reorder — valid, realigned by the async component sync; do not "repair" it by assigning swap slots (a slot freezes the child out of normal sync). `fix-missing-swap-slots` (migration 0019) follows the same membership rule. ## Migrations @@ -30,4 +32,5 @@ - Migrations are an ordered set mixing legacy version-derived ids and newer named ids. Keep append order stable; `migrate` applies the set difference between available migrations and file migrations. - `migrate-file` synthesizes legacy migration ids from old numeric versions when `:migrations` is absent, migrates legacy features, and records feature flags created through `cfeat/*new*`. - When a file had no previous `:migrations`, `migrate-file` marks all migrations as migrated in metadata so callers persist the complete migration set, not only transformations that changed data. -- Closed schemas (`schema:stroke-attrs` and others) reject a key that was dropped from the declaration, so removing a declared attr needs a migration in the same change, for data already written with it. \ No newline at end of file +- Closed schemas (`schema:stroke-attrs` and others) reject a key that was dropped from the declaration, so removing a declared attr needs a migration in the same change, for data already written with it. +- Rebase collision on a migration number: upstream keeps the id. Renumber the branch migration to the next free one, keep it last in `available-migrations`, and rename everything that spells the number — its test file and namespace (`files_migrations_00NN_test.cljc`), the test names, and comments in production code or memories that name it. \ No newline at end of file diff --git a/.serena/memories/common/testing.md b/.serena/memories/common/testing.md index 4eda8e871f..b18fa3904b 100644 --- a/.serena/memories/common/testing.md +++ b/.serena/memories/common/testing.md @@ -38,6 +38,10 @@ Useful builders: `add-variant-with-copy` does not accept position params for children; use `gsh/absolute-move` after creation if positions matter. +## Generative sampling caution + +- `sg/sample` is unseeded per run. If a test filters samples by type (e.g. keep only `:svg-raw`) then `first` can yield nil on unlucky seeds; `assoc` onto nil builds a typeless map that fails `:multi` dispatch on `:type`. Pin `:seed` in `sg/sample` opts whenever a test depends on a filtered subtype being present (verified seed 42 deals svg-raw shapes on JVM and JS). + ## Driving production paths For shape mutations, prefer production-path helpers such as `cls/generate-update-shapes` plus `thf/apply-changes`. For component swaps with keep-touched behavior, use `tho/swap-component-in-shape` with `{:keep-touched? true}`. diff --git a/.serena/memories/critical-info.md b/.serena/memories/critical-info.md index 43f678a643..f1c5a2ba4a 100644 --- a/.serena/memories/critical-info.md +++ b/.serena/memories/critical-info.md @@ -8,6 +8,7 @@ You are working on the GitHub project `penpot/penpot`, a monorepo. - Cross-cutting testing principles, TDD workflow, and anti-patterns: `mem:testing`. - Verified Clojure language behaviors that contradict common assumptions (e.g. `int?` covers `Long`; `integer?` is the general predicate): `mem:clojure/idioms` — read before assuming stdlib predicate semantics. - Read `mem:clojure/design-rules` (function shape, boundary checks, naming) before writing or modifying Clojure/ClojureScript code — not only when designing a solution or an API. +- Promises: which call style to use where (`fmap`/`mcat`/`merr` vs `then`/`catch`) and how they bridge to rx: `mem:clojure/promesa` — read before chaining promises or mixing them with observables. # Development workflow @@ -17,7 +18,7 @@ You are working on the GitHub project `penpot/penpot`, a monorepo. - Before `gh pr create` / `gh pr edit` → `mem:workflow/creating-prs` (title format, body structure, "Note:" line) - Before updating `CHANGES.md` → `mem:workflow/updating-changelog` (sources of truth, entry rules, the `:boom:`/`:rocket:` label contracts, pre-flight checks, anomaly report) - Before a repo-wide pnpm version update → `mem:workflow/updating-pnpm` (workspace layout, `scripts/sync-pnpm-version` flow, the stamp-missing-field and ignored-builds gotchas, verification steps) -- Before a Node version update → `mem:workflow/updating-node` (the six pin sites in `.nvmrc`, the devenv and image Dockerfiles and the plugins-deploy workflows, the DHI tag gotcha, what to verify) +- Before a Node version update → `mem:workflow/updating-node` (the six pin sites in `.nvmrc`, the devenv and image Dockerfiles and the plugins-deploy workflows, the DHI tag and digest rules, what to verify) - Before bumping a non-JS dependency pinned in `docker/images/` (JDK, ImageMagick) → `mem:workflow/docker-images` - **Never `git push`, force-push, or modify `git origin`** (or any other remote). The user pushes from their own shell; if a push is required, say so and wait. Never amend a commit that the user has already pushed unless explicitly asked. - **Never edit `CHANGES.md` by hand.** The changelog is generated from GitHub milestones during the release process; update it only via `mem:workflow/updating-changelog` (the `update-changelog` skill flow) or on explicit user request. @@ -64,7 +65,7 @@ The memory is structured in a way that you can get the critical information abou - `scripts/error-reports.mjs` — Query error reports via RPC API with token authentication. Supports list/get operations with filtering and pagination. See `mem:scripts/error-reports`. - `scripts/clean-node-modules` — Remove stale `node_modules` from all pnpm workspaces (root, modules, member packages). Keeps the shared pnpm store at `/.pnpm-store` unless `--store`; ignores `external/` and `.opencode/`. Usage and reinstall steps: `mem:workflow/updating-pnpm`. - `scripts/setup-opencode` — Install the opencode CLI in-container as a global pnpm package in `$PNPM_HOME`. Not part of the devenv image; see `mem:devenv/core`. -- `scripts/ci` — CI orchestration script: runs lint, tests and format checks per module, plus the production build on request (`--build`). Modules: `frontend backend common render-wasm exporter mcp plugins library media-processor`. Logs go to `.ci-logs/`; read the log file on failure. See `mem:scripts/ci`. +- `scripts/ci` — CI orchestration script: runs lint, tests, and format checks per module, plus the production build on request (`--build`). Modules: `frontend backend common render-wasm exporter mcp plugins library media-processor`. Logs go to `.ci-logs/`; read the log file on failure. It is the preferred way to run the backend tests, and it exports `PENPOT_FLAGS` with `enable-backend-asserts`, which is what makes `clojure.core/assert` exist in the compiled test code. See `mem:scripts/ci`. # Dependency graph diff --git a/.serena/memories/devenv/core.md b/.serena/memories/devenv/core.md index 7e9161fff0..3507a5d6e0 100644 --- a/.serena/memories/devenv/core.md +++ b/.serena/memories/devenv/core.md @@ -5,7 +5,7 @@ Compose-based dev environment under `docker/devenv/`, driven by `manage.sh`. Par ## Compose project layout - `penpotdev-infra`: shared `postgres`, `rustfs`, `valkey`, `mailer`, `ldap`. File: `docker-compose.infra.yml`. -- `penpotdev-wsN` (N=0,1,…): per-instance `main` + `redis` (Valkey). File: `docker-compose.main.yml`. ws0 (a.k.a. `main`) binds `$PWD`; ws1+ bind clones at `${PENPOT_WORKSPACES_DIR}/wsN/` (default `~/.penpot/penpot_workspaces/`), maintained by the developer. +- `penpotdev-wsN` (N=0,1,…): per-instance `main` + `redis` (Valkey). File: `docker-compose.main.yml`. ws0 (a.k.a. `main`) binds `$PWD`; ws1+ bind clones at `${PENPOT_WORKSPACES_DIR}/wsN/` (default `${XDG_DATA_HOME:-~/.local/share}/penpot-devenv/workspaces/`, or the former `~/.penpot/penpot_workspaces/` when it already exists), maintained by the developer. - Optional overlay `docker-compose.opencode.yml`: added by `instance-compose` as an extra `-f` only when `PENPOT_OPENCODE_CONFIG_DIR` is set (i.e. `run-devenv --opencode-config-dir DIR` ran in this process). Bind-mounts the host dir at `/home/penpot/.config/opencode` (`:z`). Flag-only, per-call; not read from ambient env. Parser `parse-opencode-config-dir` absolutizes (`~`, realpath) because compose resolves relative bind sources against the compose file's dir. Only instances brought up with the flag get the mount. - All projects join external network `penpot_shared`. Created idempotently by `ensure-devenv-network`, never removed by lifecycle commands. @@ -29,7 +29,7 @@ Compose-based dev environment under `docker/devenv/`, driven by `manage.sh`. Par ## Worker policy -Backend workers run only on ws0. `_env` gates `enable-backend-worker` on `PENPOT_BACKEND_WORKER`; ws1+ inject it as false. Workers are pure fire-and-forget: `wrk/submit!` inserts a row into the shared Postgres `task` table and returns; RPC handlers never wait on completion and workers never publish to msgbus. The reason for "ws0 only" is avoiding multi-instance worker races (cron dedup is best-effort across instances, `wrk/submit!` `dedupe` is racy across submitters); details in `mem:prod-infra/core`. +Backend workers run only on ws0. `_env` gates `enable-backend-worker` on `PENPOT_BACKEND_WORKER`; ws1+ inject it as false. Workers are pure fire-and-forget: `jobs/submit` inserts a row into the shared Postgres `job` table and returns (the legacy `task` table stays dormant); RPC handlers never wait on completion and workers never publish to msgbus. The reason for "ws0 only" is avoiding multi-instance worker races (cron dedup is best-effort across instances, `jobs/submit` `dedupe` is racy across submitters); details in `mem:prod-infra/core`. Each workspace is independent and can be started/stopped in any order. Shared infra (Postgres, RustFS, etc.) is shut down only when no instances remain running. @@ -77,3 +77,42 @@ No `--delete` on the working-tree pass: gitignored caches in the workspace survi - `start-devenv` / `log-devenv` / `drop-devenv`: legacy paths around ws0 + shared infra. `drop-devenv` never removes volumes. `exporter/scripts/run` and `wait-and-start.sh` source `backend/scripts/_env` then `_env.local` if present. + +## Image and dependency pins + +Two Dockerfile trees, both multi-arch (`linux/amd64,linux/arm64`): +`docker/devenv` (dev) and `docker/images` + `docker/imagemagick` (prod). + +- Hand-pinned versions, one source each: + - `docker/devenv/Dockerfile`: `NODE_VERSION` (+ per-arch `NODE_SHA256`), + `PNPM_VERSION` (+ `PNPM_SHA256`), `CADDY_VERSION`, `CLOJURE_VERSION`, + Zulu JDK URL + `ESUM`, `RUSTUP_VERSION`/`RUST_VERSION`/ + `EMSCRIPTEN_VERSION`, `CLJKONDO_VERSION`, `BABASHKA_VERSION`, + `CLJFMT_VERSION`, `PIXI_VERSION`, `GITHUB_CLI_VERSION`, + `AWS_CLI_VERSION` (+ `BINARY_SHA256`), `UV_VERSION`, `SERENA_VERSION`, + `postgresql-client-18`, and the `penpotapp/imagemagick:` + `COPY --from`. + - `docker/images/Dockerfile.backend`: Zulu JDK URL + `ESUM`. The `jlink` + `--add-modules` list must keep `jdk.management` + (`mem:backend/subtleties`). + - `docker/images/Dockerfile.{exporter,mcp,media-processor}`: `ARG + PNPM_VERSION` (+ arch SHAs, same values as devenv). +- Node and pnpm must agree in five places: `.nvmrc`, the devenv tarball pin, + the two `plugins-deploy-*` workflows that set `runtime: node@`, and + the four Dockerfile pins. pnpm procedure: `mem:workflow/updating-pnpm`. +- `dhi.io` base images are pinned `tag@sha256:`; Renovate moves + them (`penpot-renovate[bot]`, commits titled `:arrow_up: Update DHI base + images`). Tag and digest move together: a new tag with the old digest still + pulls the old image, and the digest must be the multi-arch index, not a + per-platform one. +- Every hand-pinned checksum is per architecture; one wrong value breaks half + of the multi-arch build. +- `SERENA_UPDATE_VERSION` in `docker-compose.main.yml` must never be lower than the Dockerfile's `SERENA_VERSION`. Any value makes `start-tmux.sh` install that Serena release on start, overriding the version baked into the image; a stale value silently downgrades Serena (it once replaced 1.7.0 with 1.6.1). If the image already has that version the install is an offline no-op; otherwise it downloads on every start (`run-devenv` recreates the container) and, offline, fails after ~25s and keeps the image version. Bump both together; empty means use the image version. +- Infra images live in `docker-compose.infra.yml`: `rustfs/rustfs` (owns the + S3 data volume), `axllent/mailpit` (SQLite in a volume), `valkey/valkey`, + `postgres:18.6`, `rroemhild/test-openldap`. Keep the volume names stable + when bumping them; replacement services get new volumes and leave the old + data untouched. +- Build locally: `docker build -f docker/devenv/Dockerfile docker/devenv/` + and `cd docker/images && ./build.sh --local`. CI: + `build-docker-devenv.yml`, `build-docker.yml`. diff --git a/.serena/memories/frontend/auth-events.md b/.serena/memories/frontend/auth-events.md index a3deb36a6a..2d532c3003 100644 --- a/.serena/memories/frontend/auth-events.md +++ b/.serena/memories/frontend/auth-events.md @@ -55,8 +55,11 @@ never carries `activation`. Do not "fix" it by inferring a signup. - `login-from-register` accepts an optional `:invitation-token`; `with-register-origin` decorates the profile and `logged-in` does the rest. The four places that used to navigate by hand to `:auth-verify-token` - (`ui/auth/register.cljs` twice, `ui/viewer/login.cljs`, `ui/static.cljs`) emit - `login-from-register` with the token. + (`ui/auth/register.cljs` twice, `ui/static.cljs`, `ui/auth/login_modal.cljs`) + emit `login-from-register` with the token. The modal used to live in + `ui/viewer/login.cljs`; it was extracted to the shared + `ui/auth/login_modal.cljs` (`login-register-modal*`, also used by + `ui/mcp_authorize.cljs`). - The invitation navigates through `get-invitation-nav`, which does not wait for `::dtm/teams-fetched`: `fetch-teams` has no error handling, so depending on it would leave the invitation unaccepted when `get-teams` fails. For invited @@ -66,9 +69,11 @@ never carries `activation`. Do not "fix" it by inferring a signup. invitation JWE in its claims (`backend/src/app/auth/oidc.clj`), so the frontend follows the regular path (`login-from-token` → `logged-in`). That flow visits the token verification screen twice. -- `ui/viewer/login.cljs` and `ui/static.cljs` `:is-active` branches still bypass - `logged-in` (they hide the modal or reload the page); they are a separate - decision, not an oversight. +- `ui/auth/login_modal.cljs` (the register modal: `:is-active` branch calls + `on-close`, or `on-register` when the caller passes one, as in the MCP + authorize flow) and `ui/static.cljs` (`rt/reload true`) `:is-active` + branches still bypass `logged-in`; they are a separate decision, not an + oversight. ## Backend side diff --git a/.serena/memories/frontend/cljs-repl.md b/.serena/memories/frontend/cljs-repl.md index 67bb71ee67..d900ac4bc5 100644 --- a/.serena/memories/frontend/cljs-repl.md +++ b/.serena/memories/frontend/cljs-repl.md @@ -49,16 +49,23 @@ Get `team-id` from `(:current-team-id @app.main.store/state)`. Get file ids from ## Runtime patching with `set!` -Some frontend vars are deliberately mutable escape hatches for runtime instrumentation or circular-dependency patching. From `cljs_repl`, use `set!` for temporary debugging of CLJS vars such as `app.main.store/on-event`, `app.main.errors/reload-file`, `app.main.errors/is-plugin-error?`, `app.main.errors/last-report`, or `app.main.errors/last-exception`. These patches affect only the live browser runtime and disappear on reload or recompilation. +Some frontend vars are deliberately mutable escape hatches for runtime instrumentation or circular-dependency patching. From `cljs_repl`, use `set!` for temporary debugging of CLJS vars such as `app.main.errors/reload-file`, `app.main.errors/is-plugin-error?`, `app.main.errors/last-report`, or `app.main.errors/last-exception`. These patches affect only the live browser runtime and disappear on reload or recompilation. + +`set!` on `app.main.store/on-event` does nothing at runtime: the store captured the function when it was created. To watch events, subscribe to the store stream and dispose the subscription after: ```clojure ;; Log non-noisy Potok events temporarily. -(set! app.main.store/on-event - (fn [event] - (when (potok.v2.core/event? event) - (.log js/console (potok.v2.core/repr-event event))))) +(def sub + (beicon.v2.core/subs! + (fn [event] + (when (potok.v2.core/event? event) + (.log js/console (potok.v2.core/repr-event event)))) + app.main.store/stream)) +;; (beicon.v2.core/dispose! sub) ``` +`cljs_repl` starts every call in `cljs.user`; begin a call with `(in-ns 'my-ns)` to reuse helpers defined in another namespace. + Restore mutable hooks after debugging, or reload the frontend. Use JVM `alter-var-root` only for JVM Clojure; it is not the normal way to patch live CLJS browser vars. ## Browser-console debug namespace diff --git a/.serena/memories/frontend/composable-component-tests.md b/.serena/memories/frontend/composable-component-tests.md index 6618203d88..f0f586aac2 100644 --- a/.serena/memories/frontend/composable-component-tests.md +++ b/.serena/memories/frontend/composable-component-tests.md @@ -150,5 +150,5 @@ The frontend bundle is built once per SHA and restored by this job (`mem:fronten Details: README, "Running in CI". ## Substrate -`mem:common/test-setup`, `mem:common/component-data-model`, `mem:common/component-swap-pipeline`, +`mem:common/testing`, `mem:common/component-data-model`, `mem:common/component-swap-pipeline`, `mem:frontend/testing`. diff --git a/.serena/memories/frontend/dashboard-viewer-subtleties.md b/.serena/memories/frontend/dashboard-viewer-subtleties.md index dfdff4af18..fd5df5962b 100644 --- a/.serena/memories/frontend/dashboard-viewer-subtleties.md +++ b/.serena/memories/frontend/dashboard-viewer-subtleties.md @@ -5,6 +5,7 @@ - Dashboard initialization fetches projects and fonts for the team, then listens to websocket messages only for global topic `uuid/zero` or the current profile id. - Project fetch replaces each project map completely instead of merging, so fields such as `deleted-at` can disappear cleanly. - Dashboard file/project mutations are often optimistic local updates with fire-and-forget RPC watchers. Bulk permanent delete/restore paths use SSE progress and progress notifications. +- The import wizard runs the package as a job of the backend: it reads the manifest of the file in the main thread (`app.main.data.imports`, no web worker) and follows the job, so its header paints the milestone of the job (the stage and its counter) instead of the messages the old SSE stream sent; every file of a container reports the same milestone, so `jp/current-milestone-text` writes it once and the rows never repeat it. Before the job exists, the header paints a client-side `:upload` milestone built from the chunked upload (`uploads/upload-blob-chunked` `:on-progress`, chunks done/total), which never comes from the backend; the chunks go one at a time and it writes them as a percentage of the file, not as a counter. Once created and still waiting for a worker, no row says anything: the header says the import waits for a slot while no job of it has started. The bar of the header measures the whole import, never a file: indeterminate while checking, waiting for a slot or rebuilding, because the client cannot know a global index and total of operations. The upload is the exception — the chunked upload knows its chunks, so the bar is determinate while the containers are sent, weighted by their size (`imp/upload-pct` over `:container-size`, the only percentage the client really knows). The export modal follows the same one-job-per-file model, but every file is queued up front (even the ones whose job is created only after the previous one is over) and its queue ends only with a milestone: `:start`/`:retry` do not end it there. Neither modal paints a generic "…ing file…" line on the rows any more: the rows of the import wizard carry only what is not progress — the name, the size, `Ready` once their container finished uploading, and the error text —, and the button that drops a file only shows while the wizard still offers its import step (the confirmation screen). Above the rows, the import wizard paints a stepper (Check/Upload/Import) plus one phase line: `imp/import-step` derives the step from the dialog status and the entries (upload holds while any file is pending, either `:import-ready` or carrying its own `:upload` milestone), so Check covers analyze, Upload covers the chunked client-side upload, and Import covers queue plus job. A file of a version 3 package shows what it occupies inside its container, computed while analyzing (`imp/file-sizes`): its own `files/…` entries plus the storage objects its media and thumbnails reference, whose metadata is the only link to `objects/…`. The container's own size stays in `:container-size` (what the dropped file weighs) and is what the upload bar weighs, never what a row shows. A file the manifest lists but the container holds no entry of has no size at all, and its row then shows no subtitle: what the package weighs never stands in for it. The subtitle is the size alone, with no extension. While analyzing with no failures nothing renders below the phase header — no rows, no spinner (`imp/awaiting-analysis?`); the header text is the only "Checking…" message. Rows appear once the analysis resolves (one per file inside the package), and a failed analysis takes the error-summary path instead. The ready step and its Continue wait for the end of the analysis (`imp/ready-to-import?`, fed with the completion of `imp/analyze`): a ready file is not enough, because the files still to arrive would never be imported. While rebuilding, the phase title counts the files and `imp/imported-count` counts them done with the job's own `:files` counter, so the number moves while a container rebuilds its files one by one. The `:error` of an import entry is user-facing text (the backend hint, or a translated label), never an i18n key: the wizard renders it as-is, it does not translate it again. The link-later step takes the file id from the `:id` of its resolution entry, never from the key of the resolution map: the map comes from the job result, so what a key holds depends on the result schema of the job-def that wrote it, while the wizard indexes its selection by the id of the entry. The legacy SSE import stays in the backend for other clients. - File creation/duplication strips file `:data` before putting file summaries into dashboard state. ## Viewer @@ -12,6 +13,6 @@ - `app.main.ui.ui` parses the viewer's `file-id` query param with `uuid/parse*`, so a missing or malformed id arrives as nil. `viewer-page*` calls `check-file-id!` (same namespace) during render, before mounting `viewer*`: it raises `ex/raise :type :not-found :code :missing-file-id`, which the error boundary turns into the not-found page. Without the guard the `dv/initialize` assertion fails on the nil file-id and the user gets an internal-error page. Test: `frontend-tests.ui.viewer-test/check-file-id-raises-not-found-for-invalid-file-id`. - Viewer initialization sets `:current-file-id`, `:current-share-id`, and `:viewer-local`, then fetches the view-only bundle. Comment threads are fetched only for logged-in users. - Viewer bundle fetch sends the full supported feature set because anonymous shared viewers may not know team-enabled features. -- View-only bundles can contain pointer values in `:pages-index` and file data. Viewer resolves those fragments with `:get-file-fragment` before storing the bundle. +- View-only bundles can contain pointer values in `:pages-index` and file data. Viewer resolves those fragments with `:get-file-fragment` before storing the bundle, through `app.main.features.pointer-map/resolve-pointer` (share-link scoped; the workspace and library loaders use `resolve-file`, which resolves the same two levels). Both fail loudly: a fragment that cannot be resolved rejects the stream and is logged with file id and fragment id instead of silently dropping the page. - `bundle-fetched` indexes pages and precomputes viewer frames/all-frames, stores libraries/users/thumbnails/permissions under `:viewer`, then navigates to frame id, query index, or auto-selected frame. - Viewer zoom and interaction mode changes update both `:viewer-local` and the `:viewer` route query params. `update-zoom-querystring` guards with a query-param comparison (`not= current expected`) and uses `::rt/replace`; without that guard the load sequence (`bundle-fetched` → `zoom-to-fill` → `update-zoom-querystring` → `rt/nav`) re-runs on every navigation and crashes with React "maximum update depth exceeded" (2.18.0-RC5 regression, fixed in `31b73460c3`; regression test: `bundle-fetched-with-zoom-fill-url-does-not-navigate`). \ No newline at end of file diff --git a/.serena/memories/frontend/plugin-api-to-cljs-binding.md b/.serena/memories/frontend/plugin-api-to-cljs-binding.md index 8536ad245c..080c4b009c 100644 --- a/.serena/memories/frontend/plugin-api-to-cljs-binding.md +++ b/.serena/memories/frontend/plugin-api-to-cljs-binding.md @@ -23,6 +23,9 @@ - Public Plugin API objects are lightweight handles, not durable snapshots. Most getters locate fresh state from `app.main.store/state` using hidden `$id`, `$file`, `$page`, etc. - `not-valid` logs by default but throws when the plugin flag `throwValidationErrors` is enabled. The MCP execute-code handler deliberately enables that flag while running code. +- A write to a removed shape always throws `Shape not found`, whatever that flag: setters through the shape proxy's `:on-set u/check-shape-writable` (runs outside the error handling), and methods that must not vanish silently (`resize`, `rotate`) call `u/check-shape-located` first. Methods run inside `:on-error (u/handle-error ...)`, which logs instead of throwing when the flag is off, so it rethrows errors whose `ex-data :type` is `::u/shape-not-found` or `::r/read-only`. +- A write while the workspace cannot take it (`r/read-only-reason`: save-failure lock `[:workspace-global :persistence-lock]`, `[:permissions :can-edit]` false, version preview `:preview-id`, lost render context `[:render-state :lost]`) always throws, whatever that flag. The `[:workspace-global :read-only?]` flag alone does not block: the Inspect tab sets it and plugins may still write. `r/check-permission` with `content:write`/`library:write` calls `r/check-writable` first, which covers most writers; writers without a permission check (most top-level context methods in `api.cljs`: `create*`, `group`, `align*`, …; z-order methods; text range `applyTypography`; ruler guide `remove`) call `r/check-writable` themselves, and shape setters get it through `u/check-shape-writable`. A new writer without a write-permission check must call it too. An async writer calls it again after each server await, before the next server write or commit (`rx/tap` on the response stream; `dwm/create-svg-shape-with-images` takes it as `:check-writable`), so a lock that lands mid-way rejects the promise with the read-only error. `r/has-permission?` is the check without it, used by the undo blocks so a block opened before a lock still closes. Comments (`comment:write`) stay writable: they do not go through file saves. +- `penpotMgmt` (`app.plugins.management`) checks permissions with `r/has-permission?` (`granted?`), never `r/check-permission`: its calls act on files through the backend, often from the dashboard with no file open, so the workspace read-only check (`check-writable`) must not block them. - `naturalChildOrdering` and `throwValidationErrors` are stored per plugin under `[:plugins :flags plugin-id ...]`; changing default behavior affects automation and MCP diagnostics. - Plugin data is stored under keyword namespaces: private data uses `(keyword "plugin" plugin-id)`, shared data uses `(keyword "shared" namespace)`. @@ -31,4 +34,5 @@ - Plugin listeners are watches on the global store and callbacks are debounced about 10ms. Callback exceptions are caught and logged so plugin code does not crash the app. - `selectionchange` callbacks receive arrays of shape id strings, while `filechange`, `pagechange`, and `shapechange` return proxies. - `contentsave` fires only when persistence status transitions to `:saved`; it calls the callback with no value. +- `contentrollback` is not a state watch: it subscribes to `st/stream` for `::dps/rolled-back` (kept in `subscriptions`, disposed by `remove-listener`), not debounced, delivered async so the reverted state is in place. Payload `{fileIds pageIds shapeIds}` from `pcm/rollback-targets` (page shapes only, from the reverted commits' redo changes). When the rollback falls back to reloading the file, `::dps/rolled-back` waits for the reloaded data, so the callback never reads the rejected data. The runtime's `validEvents` must list every event name, or `penpot.on` rejects it; a stale runtime build rejects new events. The MCP plugin forwards these rollbacks with the next task response and the server puts a warning first in that tool response (`RollbackNotices`). Unsent ones survive a tab or plugin reload in `penpot.localStorage` (key `rollbacks/`, `LocalStorageRollbackStore`; the MCP manifest in `app.main.data.workspace.mcp` and `mcp/packages/plugin/public/manifest.json` grant `allow:localstorage`, which the runtime checks on access); the entry goes once a response carried them. - Plugin history `undoBlockBegin` creates a workspace undo transaction with a JS `Symbol`; `undoBlockFinish` commits that symbol. Missing finish eventually relies on the workspace transaction timeout. \ No newline at end of file diff --git a/.serena/memories/frontend/routing-app-shell-subtleties.md b/.serena/memories/frontend/routing-app-shell-subtleties.md index d033919683..8c43a30161 100644 --- a/.serena/memories/frontend/routing-app-shell-subtleties.md +++ b/.serena/memories/frontend/routing-app-shell-subtleties.md @@ -29,3 +29,4 @@ For general store mechanics such as `emit!`, `last-events`, persistence, and und - Websocket initialization uses `cf/public-uri` joined with `ws/notifications`, converting `http/https` to `ws/wss`, and includes the current `session-id` as query param. - Reinitializing or finalizing websocket stops the previous receive stream. Incoming websocket payloads become Potok data events under `app.main.data.websocket/message`. +- A job the client created is followed with `app.main.data.jobs/watch-job`: it emits the `:job-event` messages of that job (filtered by `:job-id`) and one final outcome read from the row with `get-job`, which is read when the stream starts and again on every `opened` of the websocket, so a job that ended before the client started following it, or while the socket was down, still answers. The stream ends after that outcome, and a read that fails reaches the caller: the repo retries the transient transport errors of a GET, and anything else is a real failure the flow has to show. The `:stage` of a progress event is a stable key of the vocabulary of the worker, never a display string: `app.main.ui.jobs.progress` resolves it with a `case` of literal `tr` calls, falls back to a generic label for a stage it does not know, and the counter of the activity is named after its own stage (the counter of `:files` is the outer context). diff --git a/.serena/memories/frontend/testing.md b/.serena/memories/frontend/testing.md index 7c6bfec777..37f5304395 100644 --- a/.serena/memories/frontend/testing.md +++ b/.serena/memories/frontend/testing.md @@ -39,6 +39,7 @@ To assert over emitted event sequences, observe termination: subscribe through ` - Auto-answering mocks lose deadline expressiveness (can't time answers), need teardown timer-cancellation, and post-teardown deliveries hit real implementations — explicit pushes + async delivery + `wait-for` won on every axis. - Teardown belongs to the terminal continuation, never to `finally`-around-triggers (it would dispose in-flight flows). - A body that awaits must be `(^:async fn …)` even if the rest is sync; sync sequences are atomic vs the event loop. +- The `:esm` test build cannot load the CommonJS `sax` module behind `tubax`, so SVG parsing fails in tests (`dwm/valid-svg-string?` answers false, `dwm/svg->clj` errors): mock both with the parsed data. From `frontend/`: - Full unit test run (always builds, suppressed output): `pnpm run test:quiet`. diff --git a/.serena/memories/frontend/translations.md b/.serena/memories/frontend/translations.md index 7053af9d4b..ab6c4b1bde 100644 --- a/.serena/memories/frontend/translations.md +++ b/.serena/memories/frontend/translations.md @@ -7,10 +7,11 @@ high-coverage support reference, never the base. ## Workflow - Canonicalize with `node ./scripts/translations.js sync -l ` from `frontend/`: sorts entries, syncs `#:` comments from `en`, deletes keys missing in `en`. +- `sync` never ADDS a key missing in the locale: it only syncs comments (for keys that already exist) and drops keys absent from `en`. A key added to `en` alone stays missing in `es` and its text silently falls back to English. Every new key is born in `en` AND `es` in the same change; compare the `msgid` sets to check (`comm -23 <(grep '^msgid ' en.po | sort -u) <(grep '^msgid ' es.po | sort -u)` must be empty). - `sync` copies ALL comment flags from `en`, including `#, fuzzy`. Translated entries must NOT stay fuzzy: strip the flag after translating (mirror `es.po`, which keeps fuzzy only on genuinely untranslated entries). Fuzzy entries are excluded from `msgfmt --statistics` translated counts. - Canonical files carry NO `#~` obsolete blocks (`en`/`es` have zero). Drop them; `sync` does not resurrect them. -- Definition of done: `msgfmt --check .po` exit 0 and `msgfmt --statistics` shows 0 fuzzy, 0 untranslated. -- `rehash` scans `frontend/src` AND `common/src` for `(tr "key"` occurrences (any line counts, including `;;` comments) and refreshes `#:` refs in `en`, marking unreferenced keys `#, unused`. +- Definition of done: `msgfmt --check .po` exit 0, `msgfmt --statistics` shows 0 fuzzy, 0 untranslated, and the locale's `msgid` set is identical to `en.po`'s (no key missing, none extra). +- `rehash` scans `frontend/src` AND `common/src` for `(tr "key"` occurrences (any line counts, including `;;` comments) and refreshes `#:` refs in `en`, marking unreferenced keys `#, unused`. The refs in `en` are broadly stale, so a repo-wide `rehash` rewrites hundreds of lines (measured: ~487 on `en` alone): it is a maintenance action on its own, never a step to run inside a change that only adds or edits a key. - Dynamic keys are invisible to `rehash`: eliminate them instead of declaring them. Preference order: literal `(tr "key")` args only; never put a branch inside `tr`, hoist it out (`(if cond (tr "a") (tr "b"))`); @@ -28,6 +29,12 @@ high-coverage support reference, never the base. ## Entry rules +- Placement: a key names the feature that renders it (`dashboard.import.*`, + `files-export-modal.*`, `jobs.*`); `labels.*` is the bucket for the short + generic words reused across screens (Accept, Cancel, Download…), never the + home of a feature's own vocabulary. Keep a feature's strings together: the + durable jobs write their progress stages as `jobs.progress.stage.*` and + their row statuses as `jobs.*`, not half in `labels.*`. - Keys resolved dynamically from server-provided data carry a `#, backend` flag (set it in `en`, `sync` copies it to the locales): do not remove or rename these entries, and keep their `%s` placeholders (the backend only @@ -40,7 +47,6 @@ high-coverage support reference, never the base. - Preserve verbatim: `%s`/`%d`, `{var}`/`{{...}}`, markdown `[text](%s)`, HTML tags, `\n` positions, brand names (Penpot), key names (Ctrl/Shift/Alt), technical terms (SVG, CSS, HSV, RGB). ## Catalan (ca) conventions - - Normative IEC/Termcat Catalan. Address the user in VOSALTRES (2nd person plural): "Deseu", "Creeu", "Ja teniu un compte?". Buttons/menus use short imperatives ("Crea", "Mou", "Restaura"). - Established glossary (reuse exactly, do not re-coin): layer=capa, board=tauler, stroke=traç, fill=Emplenat, blur=Difuminat, shadow=Ombra, clipboard=porta-retalls, delete=Elimina, rename=Canvia el nom, shortcut=drecera, grid=graella (keep "grid" where the file already does, e.g. grid-layout editing), plugins/extensions UI=extensions, layout=Disposició, gradient=Degradat, wireframing kept as loanword. - Ela geminada uses the middle dot: Cancel·la, paral·lel, al·lega. @@ -48,6 +54,20 @@ high-coverage support reference, never the base. - Shortcut/action names (`shortcuts.*`) are noun/infinitive labels, not sentences. Error strings are direct, no hedging. - Same English source in different contexts may legitimately differ (verb "Copia" vs noun "Còpia"; "Desactivat" vs "Deshabilitada" agreeing with "drecera"). Normalize only true duplicates. +## Russian (ru) conventions + +- Formal address with capital ВЫ ("Вы уверены…", "Вы собираетесь…"). Buttons/menus use infinitives ("Создать", "Удалить", "Дублировать"), never imperatives. +- Established glossary (reuse exactly, do not re-coin): layer=слой, board=доска (EN `board` is NEVER кадр — normalized in lote1: toolbar/viewer/shape-menu/header-menu/export-frames/undo/num-of-frames), path/draw tool=контур, stroke=обводка, fill=заливка, blur=размытие, shadow=тень, grid=сетка, clipboard=буфер обмена, shortcuts=сочетания клавиш (normalized in lote1: title/show-shortcuts/not-found), plugin=плагин, layout=макет (`labels.layout` fixed in lote1), gradient=градиент, token=токен, set=набор, theme=тема, file=файл, project=проект, team=команда, library=библиотека, component=компонент. +- No external RU style guide applies verbatim (Mozilla ru guide is marketing-only, GNOME glossary unmaintained, Adobe uses монтажная область for artboard but `ru.po` majority already says доска — keep доска for consistency). +- `words.ru.txt` `[function]` DELIBERATELY omits 1-letter words (в/с/к/о/у/а/и) and ending-colliding particles (на/по/не/ли/же/бы/его/их/те/или…): Russian inflects (проект→проекта, запросили=запрос+или), so listing them flags every inflected noun/verb. Never "complete" that list; put tripped valid words in `[ok]`. +- Never abbreviate т. д. / т. е. in `ru.po`: the lone т/д tokens poison the glue detector's frequency map and flag short words (тон→т+он). Write и так далее out. +- Tokens-domain glossary (lote2, reuse): alias=алиас, remap=переназначить, viewport=вьюпорт, set=набор, theme=тема, spread (shadow)=распространение, margins=внешние отступы, paddings=внутренние отступы, gaps=промежутки, type identifiers stay in English (Line Height, Font Size, innerShadow/dropShadow, none/Uppercase/Lowercase/Capitalize, thin/light/regular/bold/Italic). +- Editor-domain glossary (lote3, reuse): variant=вариант, property=свойство, guide=направляющая, board guide=направляющая доски, MCP server=MCP-сервер, size preset=пресет размера, border-radius section=скругление, path handler=маркер, canvas=холст, render=рендер/рендеринг, plugin (never extensión)=плагин. +- Avoid standalone те (those) in phrasing ("те же" → "такие же"): one lone те validates те+мы splits and flags every темы. Same poisoning class as т. д. +- `words.ru.txt` `[function]` also omits пока (dropped in lote4): with standalone за anywhere in the file, пока+за flags показа. Rare-typo detection loss accepted. +- Billing-domain glossary (lote4, reuse): plan names follow shipped `ru.po`, NOT es: Enterprise=Корпоративный, Professional=Профессиональный, Unlimited=Безлимитный, trial=пробный; subscription=подписка, storage=хранилище, seat editors=редакторы, viewer role=наблюдатель, amounts as `175 $/мес.` / `10 ГБ`; access token=токен доступа, MCP key=MCP-ключ (regenerate=перевыпустить), markdown `[text|target:self](%s)` kept verbatim. +- Closing-domain glossary (lote5, reuse): shortcut noun everywhere=сочетание (shortcuts section titles are noun/infinitive labels, never ярлыки); node=узел, segment=сегмент, measurements=размеры; WebGL unavailable flow uses гайд/руководство по неполадкам + рендеринг WebGL выключен/включён; `ru.po` reached 2275/2275 keys (100%) with 0 fuzzy, 0 untranslated per `msgfmt --statistics`. + ## QA before commit - Run `node ./scripts/translations.js check -l ` from `frontend/` (no default locale: pass `-l` explicitly): 0 errors required; review warnings by hand. Word lists live in `frontend/scripts/check-translations/words..txt` (`[elision]` `[function]` `[common]` `[ok]` `[brands]`); new valid words that trip the gate go to `[ok]`; `check --self-test` covers the detector rules. Without a catalog only the universal checks run. `#, fuzzy` entries are skipped (known-pending, owned elsewhere). diff --git a/.serena/memories/frontend/ui-conventions-and-style-system.md b/.serena/memories/frontend/ui-conventions-and-style-system.md index 1161b55d3a..59390c27e7 100644 --- a/.serena/memories/frontend/ui-conventions-and-style-system.md +++ b/.serena/memories/frontend/ui-conventions-and-style-system.md @@ -12,7 +12,7 @@ - Co-located SCSS modules are preferred. Use `app.main.style/stl` helpers from CLJS and design-system SCSS tokens/mixins instead of legacy global selectors or high-specificity nesting. - Keep CSS specificity low. Avoid nested selectors unless they target elements the component owns; CSS Modules already prevent class-name collisions. -- Prefer CSS logical properties for directional spacing/layout (`padding-inline-start`, etc.). Physical `width`/`height` are still acceptable where they are clearer. +- Prefer CSS logical properties for directional spacing/layout (`padding-inline-start`, etc.). Physical `width`/`height` are still acceptable where they are clearer. Never mix: a stylesheet logical property (`inline-size`) and an inline physical `width` on the same element cascade separately, and the logical mapping wins — a dynamically driven width must be `width` on both sides (import modal progress bar hit this). And an inline style passed from CLJS must be a JS object (`#js {:width …}`): for plain `[:div …]` elements the Rumext macro does not convert the `:style` map at runtime — a plain CLJS map reaches React raw and React never finds the keys (static style literals are converted at compile time, which is why `:style` maps seem to work elsewhere). - Use named design-system variables/tokens for spacing, borders, fixed dimensions, colors, and typography. Avoid hardcoded px/rem values and deprecated `resources/styles/common/refactor/spacing.scss` variables. - Use component-local CSS custom properties for variants and theming instead of one-off Sass variables when a component has multiple visual states. - Prefer DS typography components (`heading*`, `text*`) and typography mixins instead of plain text wrappers or deprecated typography mixins. diff --git a/.serena/memories/frontend/workspace-state-persistence-subtleties.md b/.serena/memories/frontend/workspace-state-persistence-subtleties.md index eee6de48b8..7e8196080f 100644 --- a/.serena/memories/frontend/workspace-state-persistence-subtleties.md +++ b/.serena/memories/frontend/workspace-state-persistence-subtleties.md @@ -7,6 +7,7 @@ - `ongoing-tasks` controls `window.onbeforeunload`: any non-empty set blocks tab unload. - `app.main.streams/wasm-modifiers` and `workspace-selrect` are behavior subjects used for high-frequency interactive preview state that bypasses normal store updates and lenses. - Keyboard modifier streams merge a window blur signal so stuck modifier-key state is cleared after focus loss. +- Focus mode (`dws/toggle-focus-mode`) keeps a watcher on page objects that adds new shapes to `:workspace-focus-selected` and drops removed ones; when the set empties (delete, rollback) `update-focus-shapes` leaves focus mode (`::focus-mode-left` stops the watcher) and both exits restore the pre-focus selection without removed shapes. Without it the next drawn shape re-armed focus mode alone. ## Repo calls @@ -14,29 +15,37 @@ - `repo/retryable-types` is public and is the single source of truth for the transient transport classification. `persistence/transient-error?` and `errors/environment-error-types` both derive from it; extend that set, never copy it. - GET retry is limited to transient `:network`, `:bad-gateway`, `:service-unavailable`, `:gateway-error` (504, Cloudflare 52x), and `:offline` errors with exponential backoff. Mutations are not retried. - A server SSE response is only accepted when the command is configured `:stream?`; otherwise it raises an unexpected-response assertion. +- `rp/cmd!` is a multimethod: to spy on a command from the REPL, `defmethod` it around `(get-method rp/cmd! :default)` and `remove-method` after; `set!` on the var or its arity property breaks every call. ## Commits, undo, persistence -- `commit-changes` refuses to create commits unless `:permissions :can-edit` is true. It captures file revn/vern, selected-before, features, tags, undo group, and translation flag into a `::commit` event. A refusal answers with an empty stream, never nil, so callers subscribe uniformly instead of branching on nil. -- Applying a remote commit first rolls back pending local commits, applies the remote changes, then replays pending local redo changes. Index updates are emitted for undo, remote redo, and replayed redo paths. +- `commit-changes` refuses to create commits unless `:permissions :can-edit` is true, and while the save-failure lock holds (`plk/locked?`) unless the params carry `:ignore-save-lock true`: only `compensate-commits` (rollbacks run while locked: a role-change release, a slow-cycle answer `:rejected`), the two `#{:position-data}` commits and `dwl/touch-component` (derived from commits already made). Every other commit, user edit or not (deleted-font fix, stale-text resize, component sync), is refused while locked; never add an origin list. Remote commits go through `dch/commit`, never refused. It captures file revn/vern, selected-before, features, tags, undo group, and translation flag into a `::commit` event. A refusal answers with an empty stream, never nil, so callers subscribe uniformly instead of branching on nil. +- Applying a remote commit first rolls back the pending local commits of its own file (`pcm/pending-commits`: queued entries, then `[:persistence :buffer]`; other files' commits never touch its data), applies the remote changes, then replays pending local redo changes. Index updates are emitted for undo, remote redo, and replayed redo paths. Leaving the buffer out makes a remote change to the same attr win locally but lose on the server. - Local commits are independently consumed by undo, persistence, WASM model updates, thumbnail/library watchers, and text position-data recalculation. -- Persistence buffers local commits: status becomes pending after about 200ms, commits are flushed after about 3s or `::force-persist`, and buffered commits are merged per file before `:update-file`. +- Persistence buffers local commits in the store (`buffer-commit` → `[:persistence :buffer]`, stamped with `:remote-seq`): status becomes pending after about 200ms; the 3s debounce or `::force-persist` emits `flush-buffer`, which merges the buffer per file into `append-commit`s, and each append removes its own commits from the buffer (a commit arriving mid-flush stays). `initialize-persistence-state` flushes a leftover buffer. Queue entries (`pcm/queue-entry`) hold the entry keys of the first commit (id, created-at, origin, features, file-id/revn/vern) and `:commits` (id, origin, undo-group, redo/undo changes, remote-seq of each merged commit), no merged change lists; every reader goes through `:commits`. The save sends `:changes-with-metadata`, one group per commit with `:commit-id`, never flat `:changes`. +- A save answered with `:rejected` (backend kept the commits before the first failing one, see `mem:backend/subtleties`) is not a failure: `rollback-rejected` runs before `commit-persisted`. The unit is the undo group (one user action, which can commit to several files, e.g. `update-component` writes the copy's file and the library, and `component-changed` syncs inherit the edit's group; multi-file actions must share a non-nil group): the pure bookkeeping lives in `app.main.data.persistence.commits` (`pcm`): `pcm/rollback-plan` reverts, in the saved file, the pending commits from the first rejected one on, and in any file the pending commits from the first one of a reverted group on, iterating until no new group appears; other files' unrelated commits stay. `pcm/without-rolled-back` drops pending reverted commits from queue entries (partial entries trimmed, empty ones leave the queue) and the buffer; saved commits of a reverted group (the head's kept ones, and `:saved`, which `keep-saved-commits` fills on `discard-commit` with saved commits whose group a pending commit still shares) are undone by a new `save-undo? false` commit (`compensate-commits`, flushed like any edit); for a file whose data is not loaded the undo goes straight to the queue as an entry, nothing applied locally. Per file, `dch/rebase-commits` undoes every pending commit newest first, re-applies the remote changes kept since the oldest pending commit, and redoes the pending commits that stay. Undo changes hold absolute values from commit time, so undoing only the rejected commits would overwrite a peer's later change to the same attr. Remote changes are numbered (`record-remote-changes`, `[:persistence :remote-seq]`; it also keeps the highest received revn per file in `:remote-revn`) and kept per file under `:remote-changes` only while the file has pending commits; `prune-remote-changes` (discard, release) forgets those at or below the oldest pending stamp, since re-applying them would undo saved edits. Then `dwu/forget-undo-groups groups undo-index` (cut history before the first entry whose `:undo-groups` — every group the entry holds, filled by `append-undo`, transactions and stacking — meets a reverted group; no match keeps the history; the open transaction is emptied only when cut or holding one, keeping its group for later commits; a reverted Ctrl+Z/Ctrl+Shift+Z commit, which carries real `:undo-changes` and `:undo-index` (the index it moved from, kept by `pcm` commit-keys), puts the index back to the plan's `:undo-index`, the oldest one's; background commits with `:undo-changes []` (position-data, component `modified-at`, deleted-font fixes) stay on screen after a rollback by design), selection pruned and edition mode left when the edited shape is gone, or when it is a path being edited that the rollback changed: `discard-path-edit` drops `[:workspace-local :edit-path id]` first, so `finalize-path-content` sees no `:old-content` and the editor's stale copy is never saved (`prune-removed-shapes` takes the edited object from before the rebase to compare). Text editions stay open: the v3 WASM editor already shows the reverted text, and the v2 editor resets its DOM root from the shape on `::rolled-back` when the text differs (and, under WASM, emits `v2-sync-wasm-text-layout` so the canvas and size follow), since its unmount `on-blur` would otherwise save the stale DOM content (`prune-removed-shapes`; `v2-update-text-shape-content` also skips a missing shape, so the editor's exit commits nothing), `errors/flash` `:unhandled` toast, text/tag/timeout from `rejection-notice`: a file other than `:current-file-id` is named (`errors.save-rejected-changes-undone-in-file`, tag `:persistence-rejected-other-file`; generic text when `:files` has no name); a `:preexisting-damage` answer says the page data is damaged (`errors.save-rejected-damaged-data`); otherwise `errors.save-rejected-changes-undone` (says the latest changes were undone: edits made while the save was in flight go too), tag `dps/rejected-tag`, hidden after `rejected-timeout-ms` (10 s). Single-toast store: the backend repair notice (`dps/notify-repaired`, emitted by `handle-file-change` on `:repaired`, sticky, tag `dps/file-repaired-tag`) and a rejection notice of the open file fold into one sticky warning (`workspace.notifications.file-repaired-changes-undone`, tag `dps/repaired-rejected-tag`) whichever comes second, keeping the rejection's report link, so neither is lost. It ends with `(ptk/data-event ::rolled-back (pcm/rollback-targets plan))` (`:file-ids :page-ids :shape-ids :comment-thread-ids`), emitted by `notify-rolled-back` once the reverted data is in the state: when a rebase failed (its deref has `:failed true`) it waits for the reload's `::dw/bundle-fetched` (`::dw/all-libraries-resolved` when a library failed) and is dropped on `finalize-workspace`; the reload replaces the whole file, so shapes outside the listed ids may change too. Comment positions are saved by their own RPC, not by `update-file`, so `dwcm/watch-comment-thread-position-changes` reacts to it: threads of reverted `:set-comment-thread-position` changes get the restored page-data position in `:comment-threads` and on the server (`update-comment-thread-position`), and threads left on a frame the rollback removed move to `uuid/zero` (`update-comment-thread-frame`, as `detach-comment-thread` on delete). `dwl/sync-file` also waits for it or `wait-persisted` and marks the library synced only on a landed save (on rollback it restores `:synced-at` and re-checks updates after the rollback's own save; on a failed save it restores `:synced-at` and does the same re-check on the file's next `::rolled-back`, i.e. the role-change release, until `finalize-workspace`). `update-status` ignores `:pending` when buffer and queue are empty (`nothing-to-save?`): a dropped commit's 200 ms pending debounce would otherwise leave the status stuck and `wait-persisted-or-error` waiting forever. `prune-removed-shapes` also closes a `:colorpicker` modal. `rebase-commits` is not a commit (not saved/broadcast/undoable, commit watchers miss it) and falls back to `::dw/reload-current-file` if applying throws; once it ran, its deref gives `{:file-id :changes :failed}`. Thumbnails read that: `dwl/watch-component-changes` checks each rebase against the data before it (`ch/components-changed`) and re-renders and uploads the thumbnails of the mains it changed (`update-component-thumbnail`), and the SVG `dwth/watch-state-changes` takes rebases as commits (clears and re-renders frame thumbnails). The WASM `render-thumbnail :persist? true` skips the upload when the main's subtree changed during the render (that change brings its own render), so a render of rejected data that finishes after the rollback is not stored. `debug.commitInvalidChange({kind, before, after})` forces such a rejection live. - Persistence sends revn as the max of the commit revn and locally tracked latest revn; remote commits update that revn tracker. +- Save answer `{:revn (pre-update) :lagged [rows revn > sent revn, created-at order, own row last] :rejected?}`. `settle-saved-commit` replaces a direct `commit-persisted`: for the current file, when the last `:lagged` row is this session's (`:session-id state`) and a row of another session (a peer, or the backend repair session, whose `:file-change` `:repaired` message also reaches the saver) has revn > `:remote-revn`, the head entry gets `::awaiting` (attempt-state `:in-flight`, no resend) and stays pending until a remote commit with that revn arrives, so the normal remote rebase puts that row under the saved commits, as the server ordered them; then `commit-persisted`. Bounded: `unseen-changes-timeout-ms` (10 s, `rx/timer`) acknowledges and emits `::dw/reload-current-file`; `finalize-workspace` acknowledges at once. Repair changes are never applied from the HTTP answer; ws messages per file arrive in publish order, so max received revn is the seen test. +- A reload to another version (peer restore; `pending-file-changes` already skips other-vern edits): `bundle-fetched` calls `dps/forget-other-versions` → `pcm/without-other-versions`: other-vern buffered and `:saved` commits dropped, other-vern queue entries kept but emptied (`:commits []`), remote changes pruned, a `:pending` status with nothing left turns `:saved`. So remote rebases and rollbacks never replay them on the reloaded data. `stale-commit?` (entry/commit `:file-vern` ≠ loaded file `:vern`, never when the file has no `:vern`): attempt-state `:stale` → acknowledged unsent; `rollback-rejected` of a stale entry does nothing (no rebase, toast or `::rolled-back`); `save-attempt-failed` drops a stale commit's failure (e.g. `:vern-conflict`) instead of halting. `merge-commit` groups the buffer by file and vern (one save carries one vern). - Persistence is skipped in version preview/read-only mode or without edit permission. -- A terminal failure halts the queue on purpose: the edits stay queued so `wait-persisted-or-error` keeps rejecting, and `workspace/versions.cljs` relies on that to avoid snapshotting a file whose edits were never saved. `recover-persistence` lifts the halt when a file is opened again or the role changes. A transport failure still inside its retry window (`slow-retry-pending?`) is resent once (`resume-persistence true`), never released; any other halt emits `release-failed-file` (drops every queued commit of the file that failed) then `resume-persistence`. Opening a file also replays the queued edits of its version onto the loaded data (`pending-file-changes` → `restore-pending-changes` in `bundle-fetched`); a replay that throws keeps the server data and the queue. That is what unblocks the editor without a reload; because the queue is shared, dropping the failed file is also what lets other files' pending edits drain. `release-failed-file` runs at recovery time, never at failure time — clearing the error eagerly would make waiters resolve. +- The copy the backend keeps before a repair is a system snapshot labeled `internal/repair/` (`dwv/repair-snapshot?`). The History panel lists it as its own per-day group, "Before automatic repair, " (`workspace.versions.repair.version`), above that day's autosaves, and the preview banner uses the same label, so the repair notice's pointer to the version history can be followed. +- Failing saves lock the workspace (`app.main.data.persistence.lock`, alias `plk`): `plk/failing?` (status `:error`, or `:recovering` while a slow-cycle attempt is in flight as `:saving`) drives `sync-failure-lock` (also on `change-team-role`), which locks only while `[:permissions :can-edit]`: a viewer, already read-only, gets the `:save-permission-denied` toast and never the save-lock banner; `lock-on-failure` stores the previous read-only value in `[:workspace-global :persistence-lock]` and sets `:read-only?`, then emits `::plk/cancel-gestures` before `:interrupt`: drags built on `plk/gesture-stopper` (transforms: move, resize, rotate, line endpoint; drawing: box, curve, line) see `cancelled?` and drop their preview (`dwt/cancel-transform`, `dwdc/cancel-drawing`) instead of committing; the pen tool and path edition rely on the lock's `clear-edition-mode` (drops the drawing object; `finalize-path-content` is refused); component gestures key on read-only: guides (`use-guide`) and flex padding/margin/gap (`fcc/use-read-only-cancel`) drop the drag when read-only turns on and ignore pointer-down while read-only, the grid editor's `use-drag` cancels on unmount mid-drag; the v2 text editor resets its DOM and WASM layout to the committed content on `::plk/cancel-gestures` (its typing commits debounced); `options-content*` shows only the read-only Inspect panel while the lock exists (it keys on the lock, never on `:read-only?`, which the Inspect tab sets too); `unlock-after-failure` restores it (into `[:render-state :pre-context-lost-read-only?]` instead while the WebGL context is lost, which keeps its own save/restore). `set-workspace-read-only` writes into the lock while it is held, so an Inspect/Design switch is not lost. `view-only-bar*` (`ui/workspace/viewport/top_bar.cljs`, shared by both renderers) shows the save error while the lock exists (text per `dps/save-retrying?`), hides its Done/exit button and takes priority over the context-loss message. No save-failure popup: page navigation, pan, zoom and inspection stay available. Once unlocked, the usual Inspect/context-loss message and action return. Plugin writes throw (`register/check-writable`) while locked, as a viewer, in a version preview and with the render context lost, but not for `:read-only?` alone (Inspect); async writes (`createShapeFromSvgWithImages`, `uploadMediaData`/`uploadMediaUrl`, `connectLibrary`) call `r/check-writable` again after their server await and reject with the read-only error (an image already uploaded stays on the server, unused); any other commit made while locked is refused at `commit-changes`. A peer's `:file-restored` (backend adds `:profile-id`, `:created-at` and, for a version a user named, `:label`) reloads the file and opens the `:file-restored` modal: who restored (profile, else the session's presence, else "A teammate"), the version by label, else date (`PPp`), and the "latest changes couldn't be saved" line when `dps/pending-file-changes` held edits of the replaced version, which the reload drops. Reloads (`::dw/reload-current-file`: restore, rebase fallback, `errors.cljs`) re-run `initialize-workspace` without `finalize-workspace` (that would drop file data, the save lock, modals and toasts). Every stream it starts (undo `append-undo`, position-data, stale-text resize, comment positions) also stops on a newer `::initialize-workspace`, and `dwsl/initialize-shape-layout`/`dwtxt/initialize-text-reflow` on a newer instance of themselves, so a reload never doubles undo entries or handlers. It emits a new `dwn/initialize`: a new `::dwn/initialize` ends the previous subscription; for the same file it sends no `unsubscribe-file` (the server keeps one file subscription per connection, so a late unsubscribe would end the new one), for another file it does. Without it each reload doubled message handling. +- A terminal failure halts the queue on purpose: the edits stay queued so `wait-persisted-or-error` keeps rejecting, and `workspace/versions.cljs` relies on that to avoid snapshotting a file whose edits were never saved. Its rejection carries `::errors/handled? true` (the failure toast already showed), so `create-version` catches `:type :persistence` and shows `workspace.versions.create-failed-unsaved` instead of failing silently; the plugin variant rejects its promise. `recover-persistence` lifts the halt when a file is opened again or the role changes. A transport failure still inside its retry window (`slow-retry-pending?`) is resent once (`resume-persistence true`), never released; any other halt emits `release-failed-file` (drops every queued commit of the file that failed) then `resume-persistence`. Opening a file also replays the queued, then buffered, edits of its version onto the loaded data (`pending-file-changes` → `restore-pending-changes` in `bundle-fetched`); a replay that throws keeps the server data and the queue. That is what unblocks the editor without a reload; because the queue is shared, dropping the failed file is also what lets other files' pending edits drain. `release-failed-file` runs at recovery time, never at failure time — clearing the error eagerly would make waiters resolve. - The split between `initialize-persistence-state` and `recover-persistence` is deliberate: initialization deliberately does NOT lift a halt, so undeliverable edits are released when the user acts, not when the view mounts. -- **Every non-transport terminal save failure is permanent.** There is no "stale but valid" category: permission lost, file gone, and server-rejected changes all fail again on a resend. Losing edit permission is not temporary — the backend raises `:not-found` for it (`check-edition-permissions!` → `perms/make-check-fn`), and `:not-found` routes to the exception page. So `:save-permission-denied` (a client-side `can-edit` check) must never be resumed: replaying the save to discover that routes the user to the exception page. Release it instead. -- Save failures split transient vs terminal (`transient-error?`: the repo retryable types `:network`/`:offline`/`:bad-gateway`/`:service-unavailable`/`:gateway-error`, plus `:invalid-save-response`, plus save-only `busy-server-error?`: `:rate-limit` (429 from rlimit/climit — a save parked on another save's file lock holds the profile's single `:update-file/by-profile` permit, so the user's saves to other files 429 after 60 s) and backend-written 504 `:server-error` with `:statement-timeout`/`:idle-in-transaction-timeout` (Postgres cancels the lock wait at 300 s, before nginx; rolled back, so resend is safe). Kept out of `rp/retryable-types` on purpose so GETs do not retry them; everything else is terminal). Terminal keeps the `:error` halt + `flash-persistence` path; transient enters a `:retrying` episode: the head commit stays queued and resends with backoff 2s/8s/20s (3 retries, then the `:error` path). A transient failure that outlasts the backoff arms `slow-retry-cycle`: one silent resend (`:recovering`) every `slow-retry-delay-ms` (30s) until a save lands, `retry-give-up-ms` (24h since `:failing-since`) passes, or the head commit collects `max-slow-failures` (3) attempts that failed after `slow-failure-ms` (90s) or more (`:slow-failures`, counted in `persistence-transient-failure` from the attempt's elapsed time): a gateway timeout may leave the backend applying the save, so a save that keeps failing slowly must not be replayed for a day, while fast failures (offline) never count; the cycle stops on `::resume-persistence`, `::initialize-persistence` and `finalize-workspace`. Leaving the workspace also emits `suspend-retries` (`:suspended` in `:persistence`, drops `:retry-token` so a pending backoff timer goes stale): no backoff, slow cycle or `online` resend runs outside the workspace; only `workspace-initialized` (not a role change) lifts it via `recover-persistence` (`opened-file?`), which also resends a `:retrying` episode whose backoff was cut short. Its toast is `errors.save-still-retrying` without a reload link; a landing save hides the failure toast (`errors/persistence-failed-tag`). Replaying a save is safe because changes are idempotent, so there is no unknown-outcome guard: a commit whose earlier request left `active-requests` is simply sent again under the same `:commit-id`; only a request still in flight blocks a resend (`:in-flight` stays silent). Tests hold the slow cycle by stubbing `rx/timer` to never fire for `slow-retry-delay-ms`, or instant timers loop forever. Retry timers carry the episode token; a superseded token stays silent. Status stays `:retrying` through re-entries (`next-status` refuses `:pending`/`:saving` from it); waiters (`wait-persisted-or-error`) wait through it and reject only on `:error`. -- One reconnect notice per episode: sticky toast tagged `:persistence-reconnecting` (single-toast store, re-show replaces), hidden by tag on drain (`:saved`) and on terminal failure; recovery is silent. Header indicator has a `:retrying` state (`workspace.header.retrying`). +- **Every non-transport terminal save failure is permanent** (a backend without commit-id groups still answers an invalid batch with a terminal `:validation`). There is no "stale but valid" category: permission lost, file gone, and server-rejected changes all fail again on a resend. Losing edit permission is not temporary — the backend raises `:not-found` for it (`check-edition-permissions!` → `perms/make-check-fn`), and a save's `:not-found` is a terminal save error that keeps the workspace open (`errors/delegated-persistence-types` is only `#{:authentication}`; a missing data fragment answers it too). So `:save-permission-denied` (a client-side `can-edit` check) must never be resumed. A role change that leaves `can-edit` false keeps it halted (the edits stay queued and on screen; `notify-kept-failure` shows the count toast again, since the single-toast store lets the role-change info toast replace it); the role change that brings edit rights back releases it. A release on a role change (`release-failed-file` with `revert?`, i.e. not on opening a file, whose data comes fresh from the server) also rolls the released commits back locally with `pcm/release-plan` (a `rollback-plan` rejecting every pending commit of the file) through `apply-rollback-plan`: rebase, compensation of saved commits of the same groups, undo-history cut, `prune-removed-shapes`, `::rolled-back`. Without it the canvas keeps showing edits the server never got. Its toast `errors.save-permission-lost` is plural over `:unsaved-commits` (`count-unsaved-commits`); plural keys need `(tr k (i18n/c n))`, a bare number renders the fallback text. +- Save failures split transient vs terminal (`transient-error?`: the repo retryable types `:network`/`:offline`/`:bad-gateway`/`:service-unavailable`/`:gateway-error`, plus `:invalid-save-response`, plus save-only `busy-server-error?`: `:rate-limit` (429 from rlimit/climit — a save parked on another save's file lock holds the profile's single `:update-file/by-profile` permit, so the user's saves to other files 429 after 60 s) and backend-written 504 `:server-error` with `:statement-timeout`/`:idle-in-transaction-timeout` (Postgres cancels the lock wait at 300 s, before nginx; rolled back, so resend is safe). Kept out of `rp/retryable-types` on purpose so GETs do not retry them; everything else is terminal). Terminal keeps the `:error` halt + `flash-persistence` path; transient enters a `:retrying` episode: the head commit stays queued and resends with backoff 2s/8s/20s (3 retries, then the `:error` path). A transient failure that outlasts the backoff arms `slow-retry-cycle`: one silent resend (`:recovering`) every `slow-retry-delay-ms` (30s) until a save lands, `retry-give-up-ms` (24h since `:failing-since`) passes, or the head commit collects `max-slow-failures` (3) attempts that failed after `slow-failure-ms` (90s) or more (`:slow-failures`, counted in `persistence-transient-failure` from the attempt's elapsed time; the cap is checked there before any resend, fast backoff included, so a commit at the cap goes to `persistence-failed` at once; when the cycle stops, `end-stopped-slow-cycle` clears `:recovering`, so `save-retrying?` turns false and the banner shows the stopped copy): a gateway timeout may leave the backend applying the save, so a save that keeps failing slowly must not be replayed for a day, while fast failures (offline) never count; the cycle stops on `::resume-persistence`, `::initialize-persistence` and `finalize-workspace`. Leaving the workspace also emits `suspend-retries` (`:suspended` in `:persistence`, drops `:retry-token` so a pending backoff timer goes stale): no backoff, slow cycle or `online` resend runs outside the workspace; only `workspace-initialized` (not a role change) lifts it via `recover-persistence` (`opened-file?`), which also resends a `:retrying` episode whose backoff was cut short. Its toast is `errors.save-still-retrying` without a reload link; a landing save hides the failure toast (`errors/persistence-failed-tag`). Replaying a save is safe because changes are idempotent, so there is no unknown-outcome guard: a commit whose earlier request left `active-requests` is simply sent again under the same `:commit-id`; only a request still in flight blocks a resend (`:in-flight` stays silent). Tests hold the slow cycle by stubbing `rx/timer` to never fire for `slow-retry-delay-ms`, or instant timers loop forever. Retry timers carry the episode token; a superseded token stays silent. Status stays `:retrying` through re-entries (`next-status` refuses `:pending`/`:saving` from it); waiters (`wait-persisted-or-error`) wait through it and reject only on `:error`. `terminal-status?` releases waiters only with an empty queue and an empty buffer: `run-persistence-task` sets `:saved` as soon as the queue empties, even with commits still buffered (a slow save landing while later edits wait for the 3 s flush). +- One reconnect notice per episode: sticky toast tagged `:persistence-reconnecting` (single-toast store, re-show replaces), hidden by tag on drain (`:saved`) and on terminal failure; recovery is silent. Its text follows the first failure: `busy-server-error?` (429, DB timeouts) says the server is busy (`errors.save-retrying-busy`), anything else that the connection was lost (`errors.save-retrying`); the slow-cycle toast does the same through `flash-persistence :busy` (`errors.save-still-retrying-busy`). Header indicator has a `:retrying` state (`workspace.header.retrying`). - Resume triggers: backoff timer and the browser `online` event (guarded by `exists? js/window`; re-enters the runner only for a live `:retrying` episode). New local edits during `:retrying` only join the queue: the episode keeps its `:run-id`, so `append-commit` does not re-enter, and the live runner (still waiting on the head's `commit-persisted`) sends them after the head. Re-entering on edits would bypass the backoff and spend an attempt per batched commit. - Tests instant-trigger retries by stubbing `rx/timer` (recording delays to assert the schedule); dynamic bindings do not survive `await` continuations, so no dynamic var for delays. - `save-request-timeout-ms` is 7 minutes and wraps the `update-file` request in `rx/timeout`, failing it as a transient `:network` error so the retry path resends instead of blocking the queue forever. It is deliberately longer than `saving-stall-timeout-ms` (5 min) so the stalled report fires before the retry. -- `sync-unload-guard` keeps `:persistence` in `st/ongoing-tasks` while edits are buffered or queued, which is what holds the browser leave-page warning on. It syncs on `::append-commit`, `::discard-commit` and `::release-failed-file` (every queue change). It is the one effect that must stay in `store.cljs`'s `omitset`, and `store.cljs`'s `ongoing-tasks` watcher is guarded by `exists? js/window` because tests run without a window. -- `leave-signals` (document `visibilitychange`→hidden, window `blur`) emits `::force-persist` when edits are buffered, so leaving the tab/window sends the 3 s batch at once; tests mock `dps/leave-signals` BEFORE `initialize-persistence` runs. pagehide/beforeunload are too late for an async save. +- `sync-unload-guard` keeps `:persistence` in `st/ongoing-tasks` while edits are buffered or queued, which is what holds the browser leave-page warning on. It reads `[:persistence :buffer]` and `:queue` and syncs on `::buffer-commit`, `::append-commit`, `::discard-commit` and `::release-failed-file` (every buffer and queue change). It is the one effect that must stay in `store.cljs`'s `omitset`, and `store.cljs`'s `ongoing-tasks` watcher is guarded by `exists? js/window` because tests run without a window. +- `leave-signals` (document `visibilitychange`→hidden, window `blur`) emits `persist-buffered`, which forces a save when `[:persistence :buffer]` is not empty, so leaving the tab/window sends the 3 s batch at once; tests mock `dps/leave-signals` BEFORE `initialize-persistence` runs. pagehide/beforeunload are too late for an async save. +- Exports (exporter and client/WASM PDF/SVG/PNG alike), the dashboard `dd/duplicate-files` and the `.penpot` download call `force-persist-files-and-wait file-ids timeout` first. A client export draws local data, so its warning says the export includes unsaved changes (`workspace.export.unsaved-changes-included`); exporter exports and the duplicate copy server data and warn they miss them. `duplicate-files` owns its one toast, shown once every copy exists: the plural `dashboard.duplicate-unsaved-changes` warning when the files had unsaved changes before the copy request, else the success toast; a failed copy errors the stream, so neither shows (the file menu must not emit its own success toast, the single-toast store would let it replace the warning). - Exports and the `.penpot` download call `force-persist-files-and-wait file-ids timeout` (waits only for those files, ends on `:error` or timeout, never raises) and then `unsaved-changes? state file-ids` to warn; it reads `refs/persistence`, so tests mock it with `(l/derived :persistence store)`. -- `persistence-failed` emits a `save-failed` audit event (`:cause-type :code :retrying :queued-commits`) whenever the user is notified (`notify-failure?`, same rule as the toast), including delegated auth/not-found failures. +- `persistence-failed` emits a `save-failed` audit event (`:cause-type :code :retrying :queued-commits`) whenever the user is notified (`notify-failure?`, same rule as the toast), including delegated auth failures. +- `start-undo-transaction` stamps the transaction with a fresh `:commit-group` (dropped from the history entry); `commit-changes` uses `(or undo-group (:undo-group tx) (:commit-group tx) (uuid/next))`, so all commits of one transaction are one rollback unit. Pre-setting `:undo-group` instead breaks entries whose group is set later (sync transactions). - Undo transactions can stay open only temporarily; timed-out pending transactions are force-committed after about 20s. Undo entries are capped at 50. -- Undo/redo are ignored while a normal editor/drawing interaction is active, except grid-layout edition handles undo through this path. +- Undo/redo are ignored while a normal editor/drawing interaction is active, except grid-layout edition handles undo through this path. `undo`, `redo` and `undo-to-index` also do nothing while the save lock holds: `materialize-undo` would move the index while the commit is refused. - After local commits and when render-wasm is active, text shapes get derived `:position-data` recomputed in a separate commit tagged `#{:position-data}`; that tag is excluded from the position-data watcher to avoid loops. ## Refs diff --git a/.serena/memories/frontend/workspace-transform-subtleties.md b/.serena/memories/frontend/workspace-transform-subtleties.md index 958e121c9c..c96e68bca3 100644 --- a/.serena/memories/frontend/workspace-transform-subtleties.md +++ b/.serena/memories/frontend/workspace-transform-subtleties.md @@ -6,6 +6,7 @@ - `apply-modifiers*` is the lower-level commit path once object/text modifiers are ready. It updates frame guides, frame comment threads, and then emits `update-shapes` with `:reg-objects? true`. - Transform commits restrict diff attrs to `transform-attrs` to avoid scanning unrelated shape attrs. - Text transforms may carry derived `:position-data`; `assoc-position-data` attaches it while preserving the original text shape context. +- A drag commits when its `mse/drag-stopper` fires, `:interrupt` (Escape) included. The save-failure lock emits `::plk/cancel-gestures` first: drags built on `plk/gesture-stopper` (start-move, start-resize, start-rotate, start-move-line-point, box/curve/line drawing) see a `cancelled?` flag set before the stop and emit `dwt/cancel-transform` (`clear-local-transform` + `finish-transform`) or `cancel-drawing` instead. A new drag gesture must use it (or, in a component, drop the drag when read-only turns on): `commit-changes` refuses the commit anyway, but the preview (WASM modifiers/props) would stay. ## Component-copy touched suppression diff --git a/.serena/memories/mcp/core.md b/.serena/memories/mcp/core.md index a9d0b2164e..a13d2a2946 100644 --- a/.serena/memories/mcp/core.md +++ b/.serena/memories/mcp/core.md @@ -13,19 +13,26 @@ enabling advanced AI-driven features in Penpot. - Build Tool: TypeScript Compiler (tsc) + esbuild - Package Manager: pnpm -## General Principles +## Principles & Code Style IMPORTANT: Use an idiomatic, object-oriented style. -In particular, this implies that, for any non-trivial interfaces, you use interfaces that expect explicitly typed abstractions -rather than mere functions (i.e. use the strategy pattern, for example). +* You keep each concern in exactly one home. + A mechanism whose parts are only correct in combination is implemented as a single component/class, and its parts are private to it; + expose a minimal public surface. Java-like encapsulation: If helper functions and constants are only used by one abstraction, + make them internal to the respective class. +* For any non-trivial interfaces, you use functions/methods that expect explicitly typed abstractions + rather than mere functions (i.e. use the strategy pattern, for example). +* Prefer declaring and instantiating concrete classes over defining only interfaces and using object literals. Comments: -When describing parameters, methods/functions and classes, you use a precise style, where the initial (elliptical) phrase -clearly defines *what* it is. Any details then follow in subsequent sentences. +* When describing parameters, methods/functions and classes, you use a precise style, where the initial (elliptical) phrase + clearly defines *what* it is. Any details then follow in subsequent sentences. +* When describing what blocks of code do, you also use an elliptical style and start with a lower-case letter unless + the comment is a lengthy explanation with at least two sentences (in which case you start with a capital letter, as is + required for sentences). -When describing what blocks of code do, you also use an elliptical style and start with a lower-case letter unless -the comment is a lengthy explanation with at least two sentences (in which case you start with a capital letter, as is -required for sentences). +Syntax: +* Never use if blocks (or other blocks) without braces, even for single-line cases. ## Project Structure (Excerpt) @@ -102,6 +109,19 @@ From the `mcp/` directory, run stdout directly. `NODE_TEST_CONTEXT` is the env var `node --test` sets. * Cross-cutting testing principles and anti-patterns: `mem:testing`. +## OAuth for remote MCP clients + +- Authorization/discovery URLs, self-contained client IDs, consent and PKCE/code binding, backend Redis state, deployment wiring, and compatibility limits: `mem:mcp/oauth`. + +## Backend tools (`submit_feedback`) + +- Tools that call the Penpot backend directly (not through the plugin) register only when `PENPOT_MCP_BACKEND_URI` is nonblank (`PenpotMcpServer.initTools`). Currently only `SubmitFeedbackTool`. +- `PenpotBackendClient` owns the HTTP call and the status-to-message mapping; it posts to `api/main/methods/send-mcp-feedback` with `Authorization: Token `. The token comes from the session context (OAuth bearer or `?userToken=`); without one the tool fails before calling the backend. +- Tool zod schemas mirror the backend malli schema (lengths, blank, control characters); keep both in sync. The backend is the authority. +- Success means the mail job was queued, not delivered; the tool text says so. `formatArgs` hides the feedback from logs. +- Backend side: `send-mcp-feedback` in `app.rpc.commands.feedback`, configured by `PENPOT_MCP_FEEDBACK_EMAIL`; MCP-token auth rules in `mem:backend/auth-permissions-product-domains`. +- User docs: `mcp/README.md` (env tables), `docs/mcp/index.md` (tool list), `docs/technical-guide/configuration.md` (MCP feedback section). + ## Devenv plugin/server wiring In the normal Penpot devenv MCP path, the browser plugin does not discover or route through Postgres. The frontend provides the plugin extension API with `mcp.getServerUrl()`, currently derived from `frontend/src/app/config.cljs` as `penpotMcpServerURI` if set, otherwise `/mcp/ws`. The MCP plugin opens a direct WebSocket to that URL and appends the current MCP access token as a query parameter. @@ -113,5 +133,10 @@ For parallel devenvs, prefer same-origin MCP routing: each Penpot instance shoul ## Plugin reconnect policy - The plugin treats WebSocket close code `1008` (policy violation) as terminal: it stops auto-reconnecting and stays disconnected until the user explicitly reconnects. Other close codes keep the capped-backoff retry. The decision lives in `ReconnectPolicy.ts` (`shouldReconnectAfterClose`), kept as a pure module so it is unit-testable without DOM/CSS. -- The MCP server emits `1008` for a duplicate connection on the same user token (`PluginBridge`) and for a missing `userToken` in multi-user mode. -- A tab rejected with `1008` never reaches `connected`, so the frontend's 60s reconnect watcher (`start-reconnect-watcher` in `app.main.data.workspace.mcp`, started only on `connected`) does not engage; recovery is manual via "Connect here". +- The MCP server emits `1008` for a missing `userToken` in multi-user mode, for missing/invalid connection metadata, and to close a connection displaced by a newer one with the same user and session ID. +- Session IDs are deterministic per tab and file, so a reconnect reuses its ID while the old socket may be half-open: the newest connection wins (`PluginBridge.displaceConnection`). Across instances, each new connection publishes a session claim on a per-user Redis channel; an instance displaces its local connection only on a foreign claim that arrives after its own claim (channel order, no clocks). Discovery deduplicates by session ID. +- A tab rejected with `1008` never reaches `connected`, so the frontend's 60s reconnect watcher (`start-reconnect-watcher` in `app.main.data.workspace.mcp`, started only on `connected`) does not engage; recovery is manual via the MCP menu. + +## Rollback warnings + +- The plugin's `RollbackLog` (`packages/plugin/src/RollbackLog.ts`) holds the `contentrollback` events not yet sent with a task response; `Task.sendResponse` attaches them and acknowledges them once sent, and the server's `unclaimed-rollbacks` message (a response that came after its task timed out) puts them back. With a `RollbackStore` (`LocalStorageRollbackStore` over `penpot.localStorage`, one entry per file) every change is written through, so a reload of the tab or plugin keeps them; storage failures fall back to memory. Plugin tests run with `node --experimental-strip-types`, which rejects TypeScript parameter properties: declare fields explicitly. diff --git a/.serena/memories/mcp/oauth.md b/.serena/memories/mcp/oauth.md new file mode 100644 index 0000000000..110372cb4c --- /dev/null +++ b/.serena/memories/mcp/oauth.md @@ -0,0 +1,47 @@ +# MCP OAuth + +## Contract and scope + +- MCP-specific authorization; existing Penpot login identifies the user on the approval page. No application vetting or persistent consent policy. Access requires user approval; registration alone grants nothing. +- Return the existing fixed MCP user token. On approval, enable `:mcp-enabled` and create the token only if no usable one exists; preserve an existing usable token. A token is unusable, and replaced, when expired (settings can give MCP tokens an expiry), when its JWT issuer is not `urn:penpot:mcp-token` (tokens issued before MCP tokens had their own issuer), or when this instance cannot decrypt it (e.g. secret key changed); the backend rejects all three. No refresh token, `expires_in`, or new token lifetime. +- No new client database table or schema migration. Client IDs must remain usable across separate authorization flows without server-side registration storage. Do not replace them with expiring Redis registrations. +- MCP transport continues matching user tokens to connected Penpot plugins; no new per-request token introspection or independent semantic validation. The logged-in Penpot instance remains the content-access boundary. Do not silently add resource-server validation or change token semantics. +- README scope: user/developer usage instructions; document `PENPOT_MCP_AUTH_URI` in its environment-variable table. Keep OAuth architecture and protocol details here and non-obvious invariants in source comments. + +## Deployment and discovery + +- MCP process: `PENPOT_MCP_AUTH_URI=https://penpot.example/api/mcp/oauth` with `--multi-user`. Unset/empty disables OAuth; configured OAuth requires multi-user. Absolute HTTPS, or HTTP on loopback for development; no userinfo, query, or fragment. Invalid configuration fails startup; startup logs OAuth status. +- Devenv: `mcp/scripts/start-mcp-devenv-oauth` starts multi-user mode with `PENPOT_MCP_AUTH_URI` derived from `PENPOT_PUBLIC_URI`. Manual opt-in only: agentic tooling needs the single-user `start-mcp-devenv`. +- Public MCP transport: `/mcp/stream`, proxied to Node `/mcp`. Missing credentials with OAuth enabled: 401 plus `WWW-Authenticate` referencing `/resource` and scope `mcp`. +- Bearer header takes precedence over legacy `?userToken=`. Malformed headers do not fall back to the query token. Existing query-token clients remain supported. +- Protected-resource metadata: `/api/mcp/oauth/resource`; issuer: `/api/mcp/oauth`; authorization-server metadata: `/.well-known/oauth-authorization-server/api/mcp/oauth`. All reach the backend. Bundled devenv/production nginx configs forward the well-known path; custom proxies must do likewise. Keep Penpot public URI and external URLs consistent. +- Node implementation: `mcp/packages/server/src/McpAuthentication.ts`, integrated by `PenpotMcpServer.ts`; OAuth-enabled tools advertise `_meta.securitySchemes`. + +## Client identification + +- `backend/src/app/mcp/oauth_clients.clj`: dynamic registration at `/api/mcp/oauth/register`, HTTPS client metadata documents (CIMD), and registrations obtained in advance via `(app.mcp.oauth-clients/register! {} metadata)`. +- DCR returns `penpot-mcp-v1.` plus unpadded base64url JSON: normalized callback URLs, optional name, authentication method, random nonce, optional secret hash. Fixed grant/response/scope values are reconstructed. Nonce distinguishes registrations with otherwise identical metadata. +- IDs are public, unsigned, unencrypted, non-expiring metadata; validate on every resolution. Client secrets are generated randomly and returned separately; only the hash enters the ID. No signing/encryption key or client record in PostgreSQL/Redis. Old experimental database-backed UUID IDs require registration again. +- An edited payload changes the ID. Authorization-code exchange compares the exact ID string against the approved request before resolving metadata. Do not replace this with comparison of decoded fields; code ownership and PKCE binding must survive metadata edits. +- Supported authentication: `none`, `client_secret_basic`, `client_secret_post`; no `private_key_jwt`. CIMD selects public-client `none` from the document's supported methods; other listed methods do not imply support here. Scope `mcp`; grant `authorization_code`; response `code`. CIMD may advertise additional grants (e.g. Codex lists `refresh_token`); require `authorization_code` support and normalize to the grant Penpot implements, rather than reject broader client capabilities. +- Entire ID capped at 2,048 ASCII characters. DCR returns `invalid_client_metadata` if encoded metadata exceeds this; shorter/fewer callback URLs reduce size. OAuth defines no fixed ID-length maximum (RFC 6749 §2.2); stateless IDs are explicitly described by RFC 7591 §A.5.2. Installed MCP client SDK 2.3.1 accepts a 2,048-character ID; this does not establish Codex/Claude compatibility. +- Redirect matching is exact except loopback HTTP port changes for native clients (`127.0.0.1`, `[::1]`, exact `localhost`). Host, path and query must still match; reject fragments/userinfo and lookalike localhost domains. Allow HTTPS, loopback HTTP, and reverse-domain native schemes. +- CIMD: fetch with existing SSRF-checked `app.http.client/req`, no redirect following, require matching document `client_id`, bounded 64-KiB body and request/body-read timeouts. Cache max 256 entries/one minute, further limited by HTTP max-age; no-store/no-cache/no max-age means no reuse. + +## Approval and exchange + +- `backend/src/app/http/mcp_oauth.clj`: separate router branch from ordinary RPC middleware; preserve OAuth snake_case JSON and form encoding. Session auth applies to approval request/decision only. Public metadata/register/token endpoints allow noncredentialed CORS; MCP transport CORS was not added. +- Errors: every failure answers with an OAuth `{"error": ...}` body, never the shared Penpot error shape. Failures without `:oauth-error` are also passed to `app.http.errors/handle` for logging only; its response is discarded. `wrap-errors` also wraps the session middleware of `/request` and `/decision`. Safe to log because these routes never set request `:params`. +- `/authorize`: validate client/callback, `code`, PKCE S256, scope/resource/state; store pending request for 600 seconds; redirect to `?screen=mcp-authorize&request=`. +- `frontend/src/app/main/ui/mcp_authorize.cljs`: existing login with return URL; show application and account details; submit explicit Allow/Cancel. No fixed MCP token in frontend state or browser redirect. Approval refreshes profile and broadcasts MCP enablement; does not connect a plugin automatically. +- `/request`: first authenticated read binds request to profile plus hashed login-session ID and CSRF value, using compare-and-replace. Re-read after the attempt: concurrent GETs may win/lose binding; only the matching session may proceed. +- `/decision`: check exact Origin, session binding and CSRF, then atomically consume the request. Denial creates no token/enables nothing. Approval calls transactional `access-token/ensure-mcp-access!`, then stores code for 120 seconds. Code state holds token row ID, never the fixed token. Callback echoes OAuth `state` and RFC 9207 `iss`. +- `/token`: validate grant, exact client ID/callback/resource and PKCE; resolve current client metadata/authentication method and verify any secret; atomically consume validated code; fetch the unexpired token by original row ID/profile/type. Regeneration deletes the old token row, so a pending code cannot silently yield the replacement token. Return fixed `access_token`, `token_type: Bearer`, scope `mcp`. +- `backend/src/app/mcp/oauth_state.clj` and Lua: tenant/kind/hash-prefixed keys; random handles; SET NX EX must succeed before returning handle. Reads retain raw JSON for atomic comparison. Consume/delete and bind/replace compare the validated raw record in Lua; replacement preserves TTL. Redis failures fail closed. Backend Redis is required independently of Node's optional Redis plugin bridge. +- `backend/src/app/rpc/commands/access_token.clj`: `ensure-mcp-access!` decodes the stored token with `ex/ignoring` to apply the usable-token rule above. Lock profile row in both MCP generation and OAuth provisioning, including when no token exists. Serializes simultaneous approvals and settings regeneration. Provisioning must run within a transaction. + +## Verification and limits + +- Tests: `mcp_oauth_clients_test.clj`, `mcp_oauth_http_test.clj`, `mcp_oauth_state_test.clj`, OAuth provisioning in `rpc_access_tokens_test.clj`; MCP authentication/HTTP tests; frontend approval tests. Cover no-storage registration, size boundaries, edited-ID rejection, code replay/concurrency, session/CSRF binding, and fixed-token reuse. +- Live OpenAI/Codex/Claude compatibility and marketplace acceptance remain unverified. In particular, verify token persistence without `expires_in` and acceptance of the existing matching-only resource boundary; do not claim protocol conformance from SDK tests alone. +- Protocol references: https://www.rfc-editor.org/rfc/rfc6749.html#section-2.2 ; https://www.rfc-editor.org/rfc/rfc7591.html#appendix-A.5.2 ; https://www.rfc-editor.org/rfc/rfc7636.html ; https://www.rfc-editor.org/rfc/rfc9207.html . diff --git a/.serena/memories/prod-infra/core.md b/.serena/memories/prod-infra/core.md index 4441f0175c..d8ced132ca 100644 --- a/.serena/memories/prod-infra/core.md +++ b/.serena/memories/prod-infra/core.md @@ -4,32 +4,36 @@ Backend (`app.config`, `PENPOT_*` env vars) is parameterized; deployments choose ## Services -- **PostgreSQL**: durable store. Profiles, teams, files, sessions, audit, `storage_object` metadata, the `task` queue, `scheduled_task` cron registry, migrations. File-data also lives here when the file-data backend is `legacy-db`/`db`. One shared DB across all backends. +- **PostgreSQL**: durable store. Profiles, teams, files, sessions, audit, `storage_object` metadata, the `job` work queue (plus the dormant legacy `task` table), `scheduled_task` cron registry, migrations. File-data also lives here when the file-data backend is `legacy-db`/`db`. One shared DB across all backends. - **Redis (Valkey-compatible)**: per-backend message bus and cache. Concrete uses: msgbus Pub/Sub for collaborative-editing broadcasts and team/profile-org notifications fired by RPC handlers (`app.rpc.notifications`, `files_update`, `teams`, `websocket`); file-summary cache gated by `enable-redis-cache`; rate-limit counters; and the dispatcher→runner work hand-off list `penpot.worker.queue::`. `PENPOT_REDIS_URI`. A burst of client-side `RedisCommandTimeoutException` (2s on `DEL`/`SET`, 10s on `BLPOP`) with the exporter healthy is the JVM paused or starved, not Redis: the timeout fires because nothing reads the reply. - **Object storage**: backends `:s3` and `:fs`. S3 in prod; devenv uses RustFS. Holds uploaded media, file-data when the file-data backend is `storage`, exports. Backend-side details (resolve, dedup, bucket set, object lifecycle, and file-data backends): `mem:backend/storage`. -- **SMTP mailer**: invitations, password resets, email verification (sent via the `:sendmail` worker task). +- **SMTP mailer**: invitations, password resets, email verification (sent via the `:sendmail` worker job). - **LDAP** (optional auth provider): helpers in `app.auth.*`, gated by `enable-login-with-ldap`. ## Task queue and worker model -Async tasks are enqueued via `wrk/submit!` (`app.worker`), which inserts a row into the shared Postgres `task` table tagged with `queue = ":"`. Submission is **fire-and-forget** — RPC handlers never poll, never wait, and workers never publish to msgbus. The only completion signal is the `task` row's `status` / `completed_at` columns, which nothing in `rpc/` reads. Soft-delete RPCs return immediately after marking the top-level row, leaving the cascade and reaping to workers. +Async jobs are enqueued via `jobs/submit` (`app.jobs`), which inserts a row into the shared Postgres `job` table tagged with `queue = ":"`. Submission is **fire-and-forget** — RPC handlers never poll and never wait. The completion signal is the `job` row's `status` / `completed_at` columns, which the creation commands read back to answer with the state of the job they just created. A job submitted with `::jobs/profile-id` also publishes a `:job-event` message on that profile's msgbus topic after each transaction commits, and the binfile commands (`create-export-binfile-job`, `create-import-binfile-job`) are the callers that pass that owner, so the progress of their jobs does reach the msgbus. Soft-delete RPCs return immediately after marking the top-level row, leaving the cascade and reaping to workers. -Workers run on backends with `enable-backend-worker` in `PENPOT_FLAGS`. Each worker-enabled backend has a `dispatcher` (polls `task` with `FOR UPDATE SKIP LOCKED`, marks status='scheduled', RPUSHes claimed task IDs into **its own** Redis list) and one or more `runner`s per queue (BLPOP from that same local list, execute, update the Postgres row). The Redis hand-off list is purely intra-backend — cross-backend coordination happens at the Postgres row level. +Workers run on backends with `enable-backend-worker` in `PENPOT_FLAGS`. Each worker-enabled backend has a `dispatcher` (polls `job` with `FOR UPDATE SKIP LOCKED`, marks status='scheduled', RPUSHes the JSON `[id scheduled-at]` payloads into **its own** Redis list) and one or more `runner`s per queue (BLPOP from that same local list, execute, update the Postgres row). The Redis hand-off list is purely intra-backend — cross-backend coordination happens at the Postgres row level. The queues are `default`, `webhooks`, `cron` and `binfile`: the last one carries the heavy work of a user job (`:worker-binfile-parallelism`, default 1), so a long export or import does not block the default runner. ## Cross-backend safety -Postgres row locking is the only correctness primitive: `task` claims via `FOR UPDATE SKIP LOCKED`, cron firing via `FOR UPDATE SKIP LOCKED` on the `scheduled_task` row, plus task-handler-internal locks (e.g. `file_gc_scheduler` locks candidate file rows). This makes the work-claim path safe across any number of worker-enabled backends. +Postgres row locking is the only correctness primitive: `job` claims via `FOR UPDATE SKIP LOCKED`, cron firing via `FOR UPDATE SKIP LOCKED` on the `scheduled_task` row, plus job-handler-internal locks (e.g. `file_gc_scheduler` locks candidate file rows). This makes the work-claim path safe across any number of worker-enabled backends. Two known race patterns survive multi-backend operation: -- **Cron dedup is best-effort.** The lock on `scheduled_task` is released when the task body finishes. If two backends' cron timers fire for the same scheduled instant with a gap larger than the task body's runtime, both execute it. Penpot's cron entries are idempotent (`session-gc`, `objects-gc`, `storage-gc-*`, `tasks-gc`, `upload-session-gc`, `file-gc-scheduler`); the exceptions are `:telemetry` (would double-report) and `:audit-log-archive` (depends on archive target idempotency). -- **`wrk/submit! ::dedupe true`** does a non-atomic `DELETE` then `INSERT`. Concurrent cross-backend submits can both bypass the `DELETE` (each sees the other's uncommitted insert as absent) and end up with duplicate `'new'` rows. Each row claims and runs once independently, so the underlying work is fine; the "at most one pending" guarantee weakens. +- **Cron dedup is best-effort.** The lock on `scheduled_task` is released when the task body finishes. If two backends' cron timers fire for the same scheduled instant with a gap larger than the task body's runtime, both execute it. Penpot's cron entries are idempotent (`session-gc`, `objects-gc`, `storage-gc-*`, `storage-pending-gc`, `jobs-gc`, `file-gc-scheduler`); the exceptions are `:telemetry` (would double-report) and `:audit-log-consumer-nexus` / `:audit-log-consumer-admin-console` (depend on archive target idempotency). +- **`jobs/submit ::dedupe true`** does a non-atomic `DELETE` then `INSERT`. Concurrent cross-backend submits can both bypass the `DELETE` (each sees the other's uncommitted insert as absent) and end up with duplicate `'new'` rows. Each row claims and runs once independently, so the underlying work is fine; the "at most one pending" guarantee weakens. Penpot in production lives with both: horizontal-scale deployments accept "exactly-once" as "essentially-once for idempotent operations." Devenv parallel instances handle it by running workers only on ws0 (see `mem:devenv/core`). +## Jobs observability + +Worker-enabled, writable backends publish unified jobs metrics through the normal Prometheus registry. Counters describe submit, dispatch, terminal outcomes, retries, orphan recovery, rescheduling and total stored job events (unlabelled); histograms separate queue wait, handler execution and total job age. SQL-backed events are recorded after the outer PostgreSQL transaction commits. The `::jobs-metrics/sampler` component publishes backlog by status and the oldest pending-job age every 30 seconds. All labels are bounded operational values; job IDs, profile IDs, params and error text are excluded. Read-only backends do not start the sampler or the workers. + ## Draining one instance -Each backend has a per-process availability flag (`app.health`, a global atom). While offline, `/readyz` answers `503 OFFLINE` without querying the database, so the load balancer removes that instance from rotation while requests in flight still finish. Operator surface: `app.srepl.main/set-offline` / `set-online`; a restart comes back online. In-flight worker tasks are safe: tasks claimed as `scheduled` return to `new` after 5 minutes via `sql:reschedule-lost` and another instance picks them up. +Each backend has a per-process availability flag (`app.health`, a global atom). While offline, `/readyz` answers `503 OFFLINE` without querying the database, so the load balancer removes that instance from rotation while requests in flight still finish. Operator surface: `app.srepl.main/set-offline` / `set-online`; a restart comes back online. In-flight worker jobs are safe: jobs claimed as `scheduled` return to `new` after 5 minutes via `sql:reschedule-lost` and another instance picks them up. ## Touching production data @@ -46,5 +50,4 @@ Production runs the deployed toolbox, not the devenv one: - Devenv composition and the ws0-only worker placement: `mem:devenv/core`. - Storage backend resolution, dedup, bucket behavior, object lifecycle, and file-data lifecycle: `mem:backend/storage`. - Storage operation metrics (S3 API calls, logical ops, dedup, asset requests) and the logical-bucket vs physical-target distinction: `mem:backend/storage` (Metrics section). -- Storage operation metrics (S3 API calls, logical ops, dedup, asset requests) and the logical-bucket vs physical-target distinction: `mem:backend/storage` (Metrics section). - Grafana (datasource ids and how to use them, main dashboard, panel conventions and gotchas): `mem:prod-infra/grafana`. diff --git a/.serena/memories/scripts/ci.md b/.serena/memories/scripts/ci.md index d236bad383..be509b7a22 100644 --- a/.serena/memories/scripts/ci.md +++ b/.serena/memories/scripts/ci.md @@ -5,6 +5,12 @@ the production build of each module, for one or more monorepo modules, and prints a per-task summary. It is the local equivalent of CI; use it to verify changes before declaring work done. +It also exports `PENPOT_FLAGS` with `enable-backend-asserts` appended (keeping +whatever the caller already set), which is what puts `clojure.core/assert` +into the compiled test code. Prefer it over calling `clojure -M:dev:test` by +hand for that reason alone: without the flag the suite passes while every +assertion is compiled out. GitHub Actions sets the same flag. + ## When to use - After implementing or fixing code in a module: run its checks before diff --git a/.serena/memories/workflow/updating-node.md b/.serena/memories/workflow/updating-node.md index 5a05836745..f8b803d9b5 100644 --- a/.serena/memories/workflow/updating-node.md +++ b/.serena/memories/workflow/updating-node.md @@ -1,15 +1,16 @@ # Updating Node Across the Toolchain -No single source of truth: nothing reads `.nvmrc` at build time, and the version is repeated in a different shape per file. Six sites move together. +No single source of truth: nothing reads `.nvmrc` at build time, and the version is repeated in a different shape per file. Six sites move together, and the image ones carry a digest beside the tag. ## Pin sites - `.nvmrc` → `v`, with the leading `v`. -- `docker/devenv/Dockerfile`, `setup-node` stage → `ENV NODE_VERSION=v`; downloads `https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-{arm64,x64}.tar.gz` and verifies each with `NODE_SHA256` in the same arch case. Bumping Node means recomputing both hashes from the published tarballs; only the arch SHAs change, the structure stays. +- `docker/devenv/Dockerfile`, `setup-node` stage → `ENV NODE_VERSION=v`; downloads `https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-{arm64,x64}.tar.gz`, one tarball per arch, and verifies each against a `NODE_SHA256` right below its `BINARY_URL`. Both hashes come from `SHASUMS256.txt` in that same dist directory, so a bump must recompute the two; only the arch SHAs change, the structure stays. - `docker/images/Dockerfile.mcp` → two stages: `FROM dhi.io/node:-debian13-dev AS build` and `FROM dhi.io/node:-debian13 AS image`. The release stage carries no `-dev`. - `docker/images/Dockerfile.media-processor` and `docker/images/Dockerfile.exporter` → single `FROM dhi.io/node:-debian13-dev`. - `.github/workflows/plugins-deploy-api-doc.yml` and `.github/workflows/plugins-deploy-styles-doc.yml` → `runtime: node@` in the `pnpm/setup` step. Its `working-directory: plugins` makes pnpm's own version come from the manifest, so this pin is the Node one; keep it equal to `.nvmrc` minus the `v`. - DHI tags take the bare version; only the devenv `NODE_VERSION` carries the `v`. +- Every `dhi.io` base image in the repo (the node ones above, plus `debian-base` and `nginx`) is pinned by digest: `FROM dhi.io/:@sha256:`. Tag and digest are a pair; Renovate's `Update DHI base images` commits rewrite both in one go, so a hand-made tag bump that leaves the digest behind keeps the old image. ## Gotchas @@ -22,5 +23,5 @@ No single source of truth: nothing reads `.nvmrc` at build time, and the version ## Verification -- `rg ''` finds nothing outside `CHANGES.md` and untracked scratch files (a repo-wide grep without path filters hits multi-megabyte ClojureScript analyzer caches: always scope it). +- `rg ''` finds nothing outside `CHANGES.md` and untracked scratch files (a repo-wide grep without path filters hits multi-megabyte ClojureScript analyzer caches: always scope it). Grep `dhi\.io/node` as well: a line whose tag moved but whose digest did not still pulls the old image. - After a devenv rebuild, `node --version` matches `.nvmrc` and `pnpm --version` matches `PNPM_VERSION` in `docker/devenv/Dockerfile`. diff --git a/.serena/memories/workflow/updating-pnpm.md b/.serena/memories/workflow/updating-pnpm.md index 44462d5e7a..4a7f5eec7e 100644 --- a/.serena/memories/workflow/updating-pnpm.md +++ b/.serena/memories/workflow/updating-pnpm.md @@ -23,7 +23,7 @@ file (never pipe tool output through filters). - The store survives `node_modules` cleans. It is content-addressed and integrity-verified, so it cannot go stale; staleness lives in node_modules. Only `scripts/clean-node-modules --store` removes it. -- Every first-party `package.json` (35 files: workspace roots plus +- Every first-party `package.json` (34 files: workspace roots plus members) must carry a `packageManager` field with the identical `pnpm@+sha512.` value. Do not let them drift. `external/` (vendored trees with their own lifecycles), `.opencode/`, @@ -60,7 +60,7 @@ file (never pipe tool output through filters). 3. From the repo root, on a host whose system pnpm is the target version (rebuilt devenv), run `scripts/sync-pnpm-version`. It resolves the integrity hash via `pnpm view` + node (no npm, no corepack, no - python3) and stamps the identical field into all 35 files, replacing + python3) and stamps the identical field into all 34 files, replacing only the value line (each file keeps its own indent) and inserting the key after `"name"`/`"version"` where missing. Explicit version instead: `scripts/sync-pnpm-version `. The explicit form works @@ -89,8 +89,8 @@ file (never pipe tool output through filters). so a nested one silently forks install and lockfile behavior. Do not reintroduce it. - `media-processor/target/package.json` is a generated, gitignored copy that - the script also stamps, so stamping and `--check` report 36 files while - only 35 are tracked. A stale copy there makes `--check` fail with nothing + the script also stamps, so stamping and `--check` report 35 files while + only 34 are tracked. A stale copy there makes `--check` fail with nothing wrong in the repo: delete `media-processor/target` and re-run. - Expect metadata-only lockfile diffs when only the pnpm version moves: the pnpm self-reference entries, plus a new `packageManagerDependencies` diff --git a/.serena/project.yml b/.serena/project.yml index 4b63be5624..0eabf5ed6a 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -1,42 +1,54 @@ # the name by which the project can be referenced within Serena/when chatting with the LLM. project_name: "penpot" -# list of languages for which language servers are started (LSP backend only); choose from: -# ada al angular ansible bash -# bsl clojure cpp cpp_ccls crystal -# csharp csharp_omnisharp cue dart elixir -# elm erlang fortran fsharp gdscript -# go groovy haskell haxe hlsl -# html java json julia kotlin -# latex lean4 lua luau markdown -# matlab msl nix ocaml pascal -# perl php php_phpactor php_phpantom powershell -# python python_jedi python_pyrefly python_ty r -# rego ruby ruby_solargraph rust scala -# scss solidity svelte swift systemverilog -# terraform toml typescript typescript_vts vue -# yaml zig +# list of language servers to start when using the LSP backend; choose from: +# ada al angular ansible astro +# bash bsl clojure cpp cpp_ccls +# crystal csharp csharp_omnisharp cue dart +# deno elixir elm erlang fortran +# fsharp gdscript gleam go groovy +# haskell haxe hlsl html java +# json julia julia_fatou kotlin latex +# lean4 lua lua_emmylua luau markdown +# matlab msl nextflow nix ocaml +# pascal perl php php_devsense php_phpactor +# php_phpantom powershell python python_basedpyright python_jedi +# python_pyrefly python_ty qml r rego +# ruby ruby_solargraph rust scala scss +# solidity svelte swift systemverilog terraform +# toml typescript typescript_vts vue wolfram +# yaml zig # (This list may be outdated; generated with scripts/print_language_list.py; -# For the current list, see values of Language enum here: +# For the current list, see values of the LanguageServerId enum here: # https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py) -# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# For some languages, there are several alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) # Note: # - For C, use cpp # - For JavaScript, use typescript # - For Angular projects, use angular (subsumes typescript+html; requires `npm install` in the project root) +# - For Astro projects, use astro (subsumes typescript/javascript for .astro projects; requires npm) # - For Svelte projects, use svelte (subsumes typescript/javascript for .svelte projects; requires npm) +# - For Deno projects, use deno (serves the same .ts/.js files as typescript; requires the deno CLI on PATH) # - For SCSS / Sass / plain CSS, use scss (some-sass-language-server handles all three) # - For Free Pascal/Lazarus, use pascal +# - External Python adapters may add further registered IDs; install the adapter package first +# and then use its ID here, for example: example # Special requirements: -# Some languages require additional setup/installations. +# Some language servers require additional setup/installations. # See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers -# When using multiple languages, the first language server that supports a given file will be used for that file. -# The first language is the default language and the respective language server will be used as a fallback. +# When using multiple language servers, the first language server that supports a given file will be used for that file. +# The first language server is the default language and the respective language server will be used as a fallback. # Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +language_servers: + - clojure + - typescript + - rust + +# same list for Serena 1.x, which only reads this key; keep both lists equal languages: -- clojure -- typescript -- rust + - clojure + - typescript + - rust # the encoding used by text files in the project # For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings @@ -177,3 +189,18 @@ activation_command: # maximum time in seconds to wait for activation_command to complete before killing it (default 180s). # must be a positive number. activation_command_timeout: 180.0 + +# list of APIs (facades or facade methods, e.g. "lsp" or "lsp.get_diagnostics_for_symbol") to include in the REPL +# that would otherwise be disabled (particularly optional methods, which are disabled by default). +# This extends the existing inclusions (e.g. from the global configuration). +included_apis: [] + +# list of APIs (facades or facade methods, e.g. "lsp" or "lsp.find_symbol") to exclude from the REPL. +# This extends the existing exclusions (e.g. from the global configuration). +excluded_apis: [] + +# The interface through which the agent (LLM) accesses Serena's functionality (overrides the global setting). +# Valid values: tools, REPL (see the global configuration for details); leave empty to use the global setting. +# Note: the interface is fixed at startup. If a project is activated post-init, its setting is not applied. +agent_interface: REPL + diff --git a/AGENTS.md b/AGENTS.md index c39c02dcaf..4e4bf1fec1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,7 +33,7 @@ Do this **before planning, before coding, before touching any file**: -1. Read `critical-info` (use `serena_read_memory critical-info` or read `.serena/memories/critical-info.md`). +1. Read `critical-info` through Serena (see "Accessing memories" below) or read `.serena/memories/critical-info.md`. It describes the project structure and tells you which modules exist. 2. From `critical-info`, identify which modules your task affects. 3. Read each affected module's **core memory** — the name is `/core` @@ -104,9 +104,12 @@ When working on a task: ## Accessing memories -- **If `serena_read_memory` / `serena_list_memories` tools are available**: use them. - `serena_read_memory` takes a memory name (e.g. `critical-info`, `frontend/core`). -- **If tools are NOT available**: read the filesystem directly. +- **If Serena's `serena_repl` tool is available** (Serena 2.x): call `s.mem.read_memory(name)` + and `s.mem.list_memories()` through it. +- **If Serena's `read_memory` / `list_memories` tools are available** (Serena 1.x; some clients + show them as `serena_read_memory` / `serena_list_memories`): use them. +- Either way, `read_memory` takes a memory name (e.g. `critical-info`, `frontend/core`). +- **If Serena is NOT available**: read the filesystem directly. Memory name `mem:foo/bar` maps to file `.serena/memories/foo/bar.md`. ## Cross-reference convention diff --git a/backend/package.json b/backend/package.json index f573defead..ff67eed620 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,7 +4,7 @@ "license": "MPL-2.0", "author": "Kaleidos INC Sucursal en España SL", "private": true, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "repository": { "type": "git", "url": "https://github.com/penpot/penpot" diff --git a/backend/pnpm-lock.yaml b/backend/pnpm-lock.yaml index 8d3278fb84..503657d9a7 100644 --- a/backend/pnpm-lock.yaml +++ b/backend/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' diff --git a/backend/resources/climit.edn b/backend/resources/climit.edn index 1a5c8f1ecc..2d93080a19 100644 --- a/backend/resources/climit.edn +++ b/backend/resources/climit.edn @@ -69,5 +69,11 @@ :import-binfile/by-profile {:permits 1 :queue 2 :timeout 60000} + :create-import-binfile-job/global + {:permits 4 :queue 10 :timeout 60000} + + :create-import-binfile-job/by-profile + {:permits 1 :queue 2 :timeout 60000} + :profile-plugin-ops/by-profile {:permits 1 :queue 2 :timeout 30000}} diff --git a/backend/scripts/_env b/backend/scripts/_env index 0544911a02..989fb52c43 100644 --- a/backend/scripts/_env +++ b/backend/scripts/_env @@ -62,13 +62,14 @@ export PENPOT_FLAGS="\ disable-tiered-file-data-storage \ enable-file-validation \ enable-file-schema-validation \ + enable-debug-save-faults \ enable-redis-cache \ enable-link-preview \ enable-subscriptions"; # Uncomment for nexus integration testing -# export PENPOT_FLAGS="$PENPOT_FLAGS enable-audit-log-archive"; -# export PENPOT_AUDIT_LOG_ARCHIVE_URI="http://localhost:6070/api/audit"; +# export PENPOT_FLAGS="$PENPOT_FLAGS enable-nexus"; +# export PENPOT_NEXUS_URI="http://localhost:6070/"; # Default deletion delay for devenv export PENPOT_DELETION_DELAY="24h" @@ -92,6 +93,7 @@ export JAVA_OPTS="\ -XX:+UseShenandoahGC \ -XX:+UseCompactObjectHeaders \ -XX:ShenandoahGCMode=generational \ + -XX:NativeMemoryTracking=summary \ -XX:-OmitStackTraceInFastThrow \ --sun-misc-unsafe-memory-access=allow \ --enable-preview \ diff --git a/backend/src/app/auth/oidc.clj b/backend/src/app/auth/oidc.clj index c4e79f8251..55aeff66fc 100644 --- a/backend/src/app/auth/oidc.clj +++ b/backend/src/app/auth/oidc.clj @@ -629,7 +629,7 @@ (l/warn :hint "received incomplete user info object (please set correct scopes)" :info info) (ex/raise :type :internal :code :incomplete-user-info - :hint "inconmplete user info" + :hint "incomplete user info" :info info))) ;; If the provider is OIDC, we can proceed to check @@ -1037,7 +1037,9 @@ (when (and session organization-id) (let [props (-> (or (:props session) {}) (update :sso assoc organization-id exp))] - (session/update-session (::session/manager cfg) (assoc session :props props)))) + (session/update-session (::session/manager cfg) (assoc session :props props)) + (submit-organization-sso-auth-event + cfg request (:profile-id session) organization-id "organization-sso-token-issued"))) (submit-organization-sso-auth-event cfg request (:profile-id session) organization-id "organization-sso-auth-succeeded") (redirect-response dest-url)) diff --git a/backend/src/app/binfile/cleaner.clj b/backend/src/app/binfile/cleaner.clj index a7fcd456db..305150f979 100644 --- a/backend/src/app/binfile/cleaner.clj +++ b/backend/src/app/binfile/cleaner.clj @@ -9,7 +9,7 @@ for recently imported shapes." (:require [app.common.data :as d] - [app.common.svg :as csvg] + [app.common.geom.matrix :as gmt] [app.common.types.shape :as cts] [app.common.uuid :as uuid])) @@ -106,14 +106,11 @@ :reverse-column :column-reverse dir)))) -(defn- fix-svg-attrs - "The json reader of the binfile rewrites every key of every entry to - kebab-case, but `:svg-attrs` keys are react prop names and are stored - in camelCase. `attrs->props` is the transform the svg import path - already applies to them, so running it again restores the names; it - is idempotent, so shapes that come in correct are left untouched." +(defn- fix-svg-transform + "The json reader decodes `:svg-transform` as a plain map; the svg defs + renderer multiplies it as a matrix, so turn it back into one." [shape] - (d/update-when shape :svg-attrs csvg/attrs->props)) + (d/update-when shape :svg-transform gmt/coerce)) (defn clean-shape-post-decode "A shape procesor that expected to be executed after schema decoding @@ -123,7 +120,7 @@ (fix-shape-shadow-color) (fix-root-shape) (fix-legacy-flex-dir) - (fix-svg-attrs))) + (fix-svg-transform))) (defn- fix-container [container] diff --git a/backend/src/app/binfile/common.clj b/backend/src/app/binfile/common.clj index 95241f062f..efc6174d4b 100644 --- a/backend/src/app/binfile/common.clj +++ b/backend/src/app/binfile/common.clj @@ -74,11 +74,11 @@ ;; Maximum total decompressed size allowed for all JSON/text zip entries ;; combined within a single import job: 500MiB. Bounds the case where many ;; entries, each individually under default-max-text-entry-size, still sum -;; to an unreasonable total. Set well above the 200MiB it replaces because -;; legitimate giant exports exist: hundreds of thousands of KB-sized -;; entries (pages, shapes) that never trip the per-entry cap but sum past -;; 400MiB. The per-entry cap still bounds single-entry DEFLATE -;; amplification, so this only moves the cumulative ceiling. +;; to an unreasonable total. Set this high because legitimate giant +;; exports exist: hundreds of thousands of KB-sized entries (pages, +;; shapes) that never trip the per-entry cap but sum past 400MiB. The +;; per-entry cap still bounds single-entry DEFLATE amplification, so this +;; only moves the cumulative ceiling. (def ^:const default-max-text-total-size (* 1024 1024 500)) @@ -239,24 +239,30 @@ (defn- migrate-file [{:keys [::db/conn] :as cfg} {:keys [read-only?]} {:keys [id] :as file}] - (binding [pmap/*load-fn* (partial fdata/load-pointer cfg id) - pmap/*tracked* (pmap/create-tracked)] - (let [libs (delay (get-resolved-file-libraries cfg file)) - ;; For avoid unnecesary overhead of creating multiple - ;; pointers and handly internally with objects map in their - ;; worst case (when probably all shapes and all pointers - ;; will be readed in any case), we just realize/resolve them - ;; before applying the migration to the file. - file (-> (fdata/realize cfg file) - (fmg/migrate-file libs))] + (fdata/with-tracked-pointers cfg id + (let [libs (delay (get-resolved-file-libraries cfg file)) + ;; Capture the pointer layout before realizing: the migration + ;; runs on plain data, and unchanged positions must keep their + ;; original fragment ids instead of being re-wrapped with new + ;; ones on persist. + original (fdata/original-pointers (:data file)) + objects-map-before? (contains? (:features file) "fdata/objects-map") + realized (-> (fdata/realize cfg file) + (fmg/migrate-file libs)) + objects-map-added? (and (not objects-map-before?) + (contains? (:features realized) "fdata/objects-map"))] (if (or read-only? (db/read-only? conn)) - file - (do ;; When file is migrated, we break the rule of no - ;; perform mutations on get operations and update the - ;; file with all migrations applied + realized + (let [file (if objects-map-added? + realized + (fdata/reattach-unchanged original realized))] + ;; When the file is migrated, we break the rule of not + ;; performing mutations on get operations and update the file + ;; with all migrations applied. The caller still receives the + ;; realized file: the read contract is plain data. (update-file! cfg file) - (fmigr/resolve-applied-migrations cfg file)))))) + (fmigr/resolve-applied-migrations cfg realized)))))) (defn- get-file* [{:keys [::db/conn] :as cfg} id @@ -281,7 +287,7 @@ (assert (db/connection? conn) "expected cfg with valid connection") (when (and (not load-data?) - (or lock-for-share? lock-for-share? skip-locked?)) + (or lock-for-share? lock-for-update? skip-locked?)) (throw (IllegalArgumentException. "locking is incompatible when `load-data?` is false"))) (let [sql @@ -946,7 +952,12 @@ (let [library-ids (->> (get-file-libraries conn (:id file)) (map :id) (cons (:id file))) - load-fn #(get-file cfg % :migrate? false)] + ;; Fragment rows are file-local and callers bind the loader of + ;; the file they work on, so each library realizes its pointers + ;; here, with its own id, before anything walks its data. + load-fn (fn [library-id] + (->> (get-file cfg library-id :migrate? false) + (fdata/realize-pointers cfg)))] (weak/loadable-weak-value-map library-ids load-fn {id file}))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1064,3 +1075,46 @@ {:bytes bytes :size (alength ^bytes bytes) :hash (sto/calculate-hash (java.io.ByteArrayInputStream. bytes))}))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; REQUEST CFG +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn resolve-export-type + "The canonical export type of a request on the legacy RPC: an explicit + `type` wins, and the deprecated boolean flags are translated. The + creation command of a job does not need it, because it asks the caller + for the type instead of guessing it." + [{:keys [type include-libraries embed-assets]}] + (cond + (some? type) type + (true? include-libraries) :include-libraries + (true? embed-assets) :merge-libraries + :else :detach-libraries)) + +(defn export-cfg + "The domain cfg of an export: the frozen set of files, how their + libraries are handled, and where the package is written." + [cfg {:keys [file-ids export-type output]}] + (-> cfg + (assoc ::file-ids file-ids) + (assoc ::export-type export-type) + (assoc ::output output))) + +(defn import-cfg + "The domain cfg of an import: the destination team/profile/project, the + name, the input and the anti-zip-bomb limits. The caller resolves the + team, because what allows it to import there differs between the RPC and + a job; what both must share is fixed here." + [cfg {:keys [profile-id project-id team name input]}] + (-> cfg + (assoc ::features (cfeat/get-team-enabled-features cf/flags team)) + (assoc ::project-id project-id) + (assoc ::profile-id profile-id) + (assoc ::team-id (:id team)) + (assoc ::name name) + (assoc ::input input) + (assoc ::import-max-binary-entry-size (cf/get :binfile-import-max-binary-entry-size)) + (assoc ::import-max-text-entry-size (cf/get :binfile-import-max-text-entry-size)) + (assoc ::import-max-text-total-size (cf/get :binfile-import-max-text-total-size)) + (assoc ::import-max-zip-entries (cf/get :binfile-import-max-zip-entries)))) diff --git a/backend/src/app/binfile/jobs.clj b/backend/src/app/binfile/jobs.clj new file mode 100644 index 0000000000..3bdfe668db --- /dev/null +++ b/backend/src/app/binfile/jobs.clj @@ -0,0 +1,120 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.binfile.jobs + "The progress bridge of the binfile jobs. + + A job has no SSE channel, so the `:progress` points that `bf.v1` and + `bf.v3` emit become `progress` events of the job through + `jobs/heartbeat`, which throttles them and drops the ones that do not + fit. The adapter turns a tap into a self-contained milestone: a `:stage` + of a single vocabulary (the two formats name the same work differently) + and the counters the tap carries, with the outer scopes in course as + context. The reader puts the outer counters in every tap inside them, + so the adapter holds no state between taps. The ids the taps carry are + not part of the job event contract. + + The handlers in `app.tasks.export-binfile` and + `app.tasks.import-binfile` run the core through `run` and + guard it with `check-active`: a job that was cancelled, or that + already reached a terminal state, raises an `:interrupt` right there on + the run thread, so the work stops at its next event point and the + transaction of its handler rolls back with it. A run whose last tap + happened before the interruption is discarded once it returns, which is + what makes that transaction roll back too." + (:require + [app.jobs :as jobs] + [app.util.events :as events])) + +(defn check-active + "Raise an `:interrupt` when the job is no longer active. + + The substrate owns both the check and the error; this is the adapter's + entry point to it, used by the two handlers before their run and by the + tests. Answers with the cfg, so a handler can thread it." + [cfg context] + (jobs/check-active cfg (:id context))) + +(def ^:private stage-of + "The stage a tap section becomes: one vocabulary for the two formats, so + a client resolves each stage once. The counter of the activity is named + after its own stage, which is what lets a client render + `counters[stage]` without a table of equivalences." + {:file :files + :page :pages + :media :media + :thumbnail :thumbnails + :thumbnails :thumbnails + :component :components + :color :colors + :typography :typographies + :tokens-lib :tokens + :tokens-status :tokens + :storage-object :storage-objects + :storage-objects :storage-objects + :relations :relations + :manifest :manifest + :v1/metadata :metadata + :v1/files :files + :v1/rels :relations + :v1/sobjects :storage-objects}) + +(defn- stage + "The stage of a tap section; a section without a name of its own keeps + it, so a new tap is never dropped from the stream." + [section] + (get stage-of section section)) + +(defn- counter + "The counter a tap reports, when it reports one: a tap that only marks a + point of the run has no units to count." + [{:keys [current total]}] + (when (some? current) + (cond-> {:current current} + (some? total) + (assoc :total total)))) + +(defn- milestone + "The milestone a tap becomes: what the job is doing now and the counters + the tap carries, with the outer scopes in course as context. + + The tap of a scope carries its outer counters, so the milestone is + self-contained and the adapter remembers nothing between taps: a client + that misses one (the substrate throttles the events) still renders the + truth from the next." + [{:keys [section outer-counters] :as tap}] + (let [stage (stage section) + own (counter tap)] + (cond-> {:stage stage} + (or (some? outer-counters) (some? own)) + (assoc :counters (cond-> (or outer-counters {}) + (some? own) (assoc stage own)))))) + +(defn run + "Run `f` with the progress taps of the core turned into milestones of the + job. + + `f` is called with the cfg, so the adapter forwards what it receives + and a caller can pass the core directly. + + Every tap is reported: the substrate throttles the durable events, and + a milestone that does not fit loses no information because the next one + carries the same state. + + The taps run on this same thread: the sink calls the heartbeat inline, + so an `:interrupt` it raises aborts the core right there, inside the + transaction of its handler, which rolls back." + [cfg context f] + (let [job-id (:id context) + sink (fn [[type data]] + (when (= :progress type) + (jobs/heartbeat cfg + :job-id job-id + :progress (milestone data))))] + (binding [events/*sink* sink] + (let [result (f cfg)] + (check-active cfg context) + result)))) diff --git a/backend/src/app/binfile/v1.clj b/backend/src/app/binfile/v1.clj index 649494a2c6..76fc870f5d 100644 --- a/backend/src/app/binfile/v1.clj +++ b/backend/src/app/binfile/v1.clj @@ -329,13 +329,13 @@ [:v1/metadata :v1/files :v1/rels :v1/sobjects])))) (defmethod write-section :v1/metadata - [{:keys [::output ::bfc/ids ::bfc/include-libraries] :as cfg}] - (if-let [fids (get-files cfg ids)] + [{:keys [::output ::bfc/file-ids ::bfc/include-libraries] :as cfg}] + (if-let [fids (get-files cfg file-ids)] (let [lids (when include-libraries - (bfc/get-libraries cfg ids)) - ids (into fids lids)] - (write-obj! output {:version cf/version :files ids}) - (vswap! bfc/*state* assoc :files ids)) + (bfc/get-libraries cfg file-ids)) + file-ids (into fids lids)] + (write-obj! output {:version cf/version :files file-ids}) + (vswap! bfc/*state* assoc :files file-ids)) (ex/raise :type :not-found :code :files-not-found :hint "unable to retrieve files for export"))) @@ -689,12 +689,12 @@ `::bfc/embed-assets`: instead of including the libraries, embed in the same file library all assets used from external libraries." - [{:keys [::bfc/ids] :as cfg} output] + [{:keys [::bfc/file-ids] :as cfg} output] (dm/assert! - "expected a set of uuid's for `::bfc/ids` parameter" - (and (set? ids) - (every? uuid? ids))) + "expected a set of uuid's for `::bfc/file-ids` parameter" + (and (set? file-ids) + (every? uuid? file-ids))) (dm/assert! "expected instance of jio/IOFactory for `input`" diff --git a/backend/src/app/binfile/v3.clj b/backend/src/app/binfile/v3.clj index 15173a885c..9ec984e524 100644 --- a/backend/src/app/binfile/v3.clj +++ b/backend/src/app/binfile/v3.clj @@ -231,6 +231,32 @@ (def validate-file-thumbnail (sm/check-fn schema:file-thumbnail)) +;; --- PROGRESS + +(defn- tap-progress + "Emit a progress tap of the core, with the counters of the outer scopes + in course when the cfg has any. + + The outer counters live in `::outer-counters` of the cfg while a scope + is being processed (today only `:files`, tomorrow `:teams` and + `:projects` too), so every tap inside carries them as `:outer-counters`: + the stages inside do not repeat them, and whoever reads the taps (the + jobs adapter) builds its report from one tap alone instead of + remembering the last position it saw. A tap that happens outside any + scope carries no outer counters, which is the truth about it." + [cfg data] + (events/tap :progress + (cond-> data + (some? (::outer-counters cfg)) + (assoc :outer-counters (::outer-counters cfg))))) + +(defn- with-outer-counter + "Return cfg with the counter of `scope` set to `position`, keeping the + counters of the other outer scopes already in course, so entering a + nested scope never wipes the ones above it." + [cfg scope position] + (assoc-in cfg [::outer-counters scope] position)) + ;; --- EXPORT IMPL (defn- write-entry! @@ -269,8 +295,10 @@ (defn- export-storage-objects [{:keys [::output] :as cfg}] - (let [storage (sto/resolve cfg)] - (doseq [id (-> bfc/*state* deref :storage-objects not-empty)] + (let [storage (sto/resolve cfg) + ids (-> bfc/*state* deref :storage-objects not-empty) + total (count ids)] + (doseq [[index id] (d/enumerate ids)] (let [sobject (sto/get-object storage id) smeta (meta sobject) ext (cmedia/mtype->extension (:content-type smeta)) @@ -282,7 +310,8 @@ (write-entry! output path params) - (events/tap :progress {:section :storage-object :id id}) + (tap-progress cfg {:section :storage-object :id id + :current (inc index) :total total}) (with-open [input (sto/get-object-data storage sobject)] (.putNextEntry ^ZipOutputStream output (ZipEntry. (str "objects/" id ext))) @@ -310,7 +339,7 @@ thumbnails (bfc/get-file-object-thumbnails cfg file-id)] - (events/tap :progress {:section :file :id file-id :name (:name file)}) + (tap-progress cfg {:section :file :id file-id :name (:name file)}) (vswap! bfc/*state* update :files assoc file-id {:id file-id @@ -344,7 +373,8 @@ (write-entry! output path page) - (events/tap :progress {:section :page :id page-id :name (:name page) :file-id file-id}) + (tap-progress cfg {:section :page :id page-id :name (:name page) :file-id file-id + :current (inc index) :total (count pages)}) (doseq [[shape-id shape] objects] (let [path (str "files/" file-id "/pages/" page-id "/" shape-id ".json") @@ -355,30 +385,33 @@ (vswap! bfc/*state* bfc/collect-storage-objects media) (vswap! bfc/*state* bfc/collect-storage-objects thumbnails) - (doseq [{:keys [id] :as media} media] - (let [path (str "files/" file-id "/media/" id ".json") - media (encode-media media)] + (doseq [[index {:keys [id] :as item}] (d/enumerate media)] + (let [path (str "files/" file-id "/media/" id ".json") + encoded (encode-media item)] - (events/tap :progress {:section :media :id id :file-id file-id}) - (write-entry! output path media))) + (tap-progress cfg {:section :media :id id :file-id file-id + :current (inc index) :total (count media)}) + (write-entry! output path encoded))) - (doseq [thumbnail thumbnails] + (doseq [[index thumbnail] (d/enumerate thumbnails)] (let [data (cth/parse-object-id (:object-id thumbnail)) path (str "files/" file-id "/thumbnails/" (:tag data) "/" (:page-id data) "/" (:frame-id data) ".json") data (-> data (assoc :media-id (:media-id thumbnail)) (encode-file-thumbnail))] - (events/tap :progress {:section :thumbnails :id (:object-id thumbnail) :file-id file-id}) + (tap-progress cfg {:section :thumbnails :id (:object-id thumbnail) :file-id file-id + :current (inc index) :total (count thumbnails)}) (write-entry! output path data))) - (doseq [[id component] components] + (doseq [[index [id component]] (d/enumerate components)] (let [path (str "files/" file-id "/components/" id ".json") component (encode-component component)] - (events/tap :progress {:section :component :id id :file-id file-id}) + (tap-progress cfg {:section :component :id id :file-id file-id + :current (inc index) :total (count components)}) (write-entry! output path component))) - (doseq [[id color] colors] + (doseq [[index [id color]] (d/enumerate colors)] (let [path (str "files/" file-id "/colors/" id ".json") color (-> (encode-color color) (dissoc :file-id)) @@ -386,20 +419,22 @@ (and (contains? color :path) (str/empty? (:path color))) (dissoc :path))] - (events/tap :progress {:section :color :id id :file-id file-id}) + (tap-progress cfg {:section :color :id id :file-id file-id + :current (inc index) :total (count colors)}) (write-entry! output path color))) - (doseq [[id object] typographies] + (doseq [[index [id object]] (d/enumerate typographies)] (let [path (str "files/" file-id "/typographies/" id ".json") typography (encode-typography object)] - (events/tap :progress {:section :typography :id id :file-id file-id}) + (tap-progress cfg {:section :typography :id id :file-id file-id + :current (inc index) :total (count typographies)}) (write-entry! output path typography))) (when (and tokens-lib (not (ctob/empty-lib? tokens-lib))) (let [path (str "files/" file-id "/tokens.json") encoded-tokens (encode-tokens-lib tokens-lib)] - (events/tap :progress {:section :tokens-lib :file-id file-id}) + (tap-progress cfg {:section :tokens-lib :file-id file-id}) (write-entry! output path encoded-tokens))) (when tokens-status @@ -415,18 +450,19 @@ encoded-status (-> (encode-tokens-status tokens-status) (cfo/ids->names effective-tokens-lib))] - (events/tap :progress {:section :tokens-status :file-id file-id}) + (tap-progress cfg {:section :tokens-status :file-id file-id}) (write-entry! output path encoded-status))))) (defn- export-files - [{:keys [::bfc/ids ::bfc/export-type ::output] :as cfg}] + [{:keys [::bfc/file-ids ::bfc/export-type ::output] :as cfg}] - (let [original-ids ids - ids (into ids (when (= export-type :include-libraries) (bfc/get-libraries cfg ids))) - rels (if (= export-type :include-libraries) - (->> (bfc/get-files-rels cfg ids) - (mapv (juxt :file-id :library-file-id))) - []) + (let [original-ids file-ids + file-ids (into file-ids (when (= export-type :include-libraries) + (bfc/get-libraries cfg file-ids))) + rels (if (= export-type :include-libraries) + (->> (bfc/get-files-rels cfg file-ids) + (mapv (juxt :file-id :library-file-id))) + []) ;; Compute external libraries: referenced by original files but ;; not included in the export set. Only relevant for :link-later. @@ -451,10 +487,11 @@ (vswap! bfc/*state* assoc :files (d/ordered-map)) ;; Write all the exporting files - (doseq [[index file-id] (d/enumerate ids)] + (doseq [[index file-id] (d/enumerate file-ids)] (-> cfg (assoc ::file-id file-id) - (assoc ::file-seqn index) + (with-outer-counter :files {:current (inc index) + :total (count file-ids)}) (export-file))) ;; Write manifest file @@ -747,17 +784,19 @@ (defn- read-file-colors [{:keys [::bfc/input ::entries-index] :as cfg} file-id] - (->> (get-in entries-index [:colors (str file-id)]) - (reduce (fn [result {:keys [id entry]}] - (let [object (->> (read-entry cfg input entry) - (decode-color) - (validate-color))] - (events/tap :progress {:section :color :id id :file-id file-id}) - (if (= id (:id object)) - (assoc result id object) - result))) - {}) - (not-empty))) + (let [entries (get-in entries-index [:colors (str file-id)])] + (->> (d/enumerate entries) + (reduce (fn [result [index {:keys [id entry]}]] + (let [object (->> (read-entry cfg input entry) + (decode-color) + (validate-color))] + (tap-progress cfg {:section :color :id id :file-id file-id + :current (inc index) :total (count entries)}) + (if (= id (:id object)) + (assoc result id object) + result))) + {}) + (not-empty)))) (defn- read-file-components [{:keys [::bfc/input ::entries-index] :as cfg} file-id] @@ -776,15 +815,18 @@ (reduce-kv (fn [objects id shape] (assoc objects id (bfl/clean-shape-pre-decode shape))) objects - objects))))] + objects)))) + entries + (get-in entries-index [:components (str file-id)])] - (->> (get-in entries-index [:components (str file-id)]) - (reduce (fn [result {:keys [id entry]}] + (->> (d/enumerate entries) + (reduce (fn [result [index {:keys [id entry]}]] (let [object (->> (read-entry cfg input entry) (clean-component-pre-decode) (decode-component) (clean-component-post-decode))] - (events/tap :progress {:section :component :id id :file-id file-id}) + (tap-progress cfg {:section :component :id id :file-id file-id + :current (inc index) :total (count entries)}) (if (= id (:id object)) (assoc result id object) result))) @@ -793,22 +835,24 @@ (defn- read-file-typographies [{:keys [::bfc/input ::entries-index] :as cfg} file-id] - (->> (get-in entries-index [:typographies (str file-id)]) - (reduce (fn [result {:keys [id entry]}] - (let [object (->> (read-entry cfg input entry) - (decode-typography) - (validate-typography))] - (events/tap :progress {:section :typography :id id :file-id file-id}) - (if (= id (:id object)) - (assoc result id object) - result))) - {}) - (not-empty))) + (let [entries (get-in entries-index [:typographies (str file-id)])] + (->> (d/enumerate entries) + (reduce (fn [result [index {:keys [id entry]}]] + (let [object (->> (read-entry cfg input entry) + (decode-typography) + (validate-typography))] + (tap-progress cfg {:section :typography :id id :file-id file-id + :current (inc index) :total (count entries)}) + (if (= id (:id object)) + (assoc result id object) + result))) + {}) + (not-empty)))) (defn- read-file-tokens-lib* [{:keys [::bfc/input ::entries-index] :as cfg} file-id] (when-let [{:keys [entry]} (first (get-in entries-index [:tokens (str file-id)]))] - (events/tap :progress {:section :tokens-lib :file-id file-id}) + (tap-progress cfg {:section :tokens-lib :file-id file-id}) (->> (read-entry cfg input entry nil) (decode-tokens-lib) (validate-tokens-lib)))) @@ -829,7 +873,7 @@ (defn- read-file-tokens-status [{:keys [::bfc/input ::entries-index] :as cfg} file-id tokens-lib] (when-let [{:keys [entry]} (first (get-in entries-index [:tokens-status (str file-id)]))] - (events/tap :progress {:section :tokens-status :file-id file-id}) + (tap-progress cfg {:section :tokens-status :file-id file-id}) (->> (read-entry cfg input entry nil) (cfo/names->ids tokens-lib) (decode-tokens-status) @@ -851,19 +895,21 @@ (defn- read-file-pages [{:keys [::bfc/input ::entries-index] :as cfg} file-id] - (->> (get-in entries-index [:pages (str file-id)]) - (keep (fn [{:keys [id entry]}] - (let [page (->> (read-entry cfg input entry) - (decode-page)) - page (dissoc page :options)] - (events/tap :progress {:section :page :id id :file-id file-id}) - (when (= id (:id page)) - (let [objects (read-file-shapes cfg file-id id)] - (assoc page :objects objects)))))) - (sort-by :index) - (reduce (fn [result {:keys [id] :as page}] - (assoc result id (dissoc page :index))) - (d/ordered-map)))) + (let [entries (get-in entries-index [:pages (str file-id)])] + (->> (d/enumerate entries) + (keep (fn [[index {:keys [id entry]}]] + (let [page (->> (read-entry cfg input entry) + (decode-page)) + page (dissoc page :options)] + (tap-progress cfg {:section :page :id id :file-id file-id + :current (inc index) :total (count entries)}) + (when (= id (:id page)) + (let [objects (read-file-shapes cfg file-id id)] + (assoc page :objects objects)))))) + (sort-by :index) + (reduce (fn [result {:keys [id] :as page}] + (assoc result id (dissoc page :index))) + (d/ordered-map))))) (defn- read-file-thumbnails [{:keys [::bfc/input ::entries-index] :as cfg} file-id] @@ -904,8 +950,11 @@ :plugin-data plugin-data})) (defn- import-file - [{:keys [::db/conn ::bfc/project-id ::manifest] :as cfg} {file-id :id file-name :name}] - (let [file-id' (bfc/lookup-index file-id) + [{:keys [::db/conn ::bfc/project-id ::manifest] :as cfg} + {file-id :id file-name :name} + position] + (let [cfg (with-outer-counter cfg :files position) + file-id' (bfc/lookup-index file-id) file (read-file cfg file-id) media (read-file-media cfg file-id) thumbnails (read-file-thumbnails cfg file-id)] @@ -917,16 +966,19 @@ :version (:version file) ::l/sync? true) + (tap-progress cfg {:section :file :file-id file-id}) + (vswap! bfc/*state* update :index bfc/update-index media :id) - (doseq [item media] + (doseq [[index item] (d/enumerate media)] (let [params (-> item (update :id bfc/lookup-index) (assoc :file-id file-id') (d/update-when :media-id bfc/lookup-index) (d/update-when :thumbnail-id bfc/lookup-index))] - (events/tap :progress {:section :media :id (:id params) :file-id file-id}) + (tap-progress cfg {:section :media :id (:id params) :file-id file-id + :current (inc index) :total (count media)}) (l/dbg :hint "inserting media object" :file-id (str file-id') @@ -939,7 +991,7 @@ (db/insert! conn :file-media-object params ::db/on-conflict-do-nothing? (::bfc/overwrite cfg)))) - (doseq [item thumbnails] + (doseq [[index item] (d/enumerate thumbnails)] (let [media-id (bfc/lookup-index (:media-id item)) object-id (-> (assoc item :file-id file-id') (cth/fmt-object-id)) @@ -953,13 +1005,12 @@ :media-id (str media-id) ::l/sync? true) - (events/tap :progress {:section :thumbnail :file-id file-id :object-id object-id}) + (tap-progress cfg {:section :thumbnail :file-id file-id :object-id object-id + :current (inc index) :total (count thumbnails)}) (db/insert! conn :file-tagged-object-thumbnail params ::db/on-conflict-do-nothing? true))) - (events/tap :progress {:section :file :file-id file-id}) - (let [data (-> (read-file-data cfg file-id (:tokens-source file)) (d/without-nils) (assoc :id file-id') @@ -988,9 +1039,12 @@ (defn- import-file-relations [{:keys [::db/conn ::manifest ::bfc/timestamp] :as cfg}] - (events/tap :progress {:section :relations}) - (let [file-ids (into #{} (map :id) (:files manifest))] - (doseq [[file-id libr-id] (:relations manifest)] + (let [relations (:relations manifest) + file-ids (into #{} (map :id) (:files manifest))] + (doseq [[index [file-id libr-id]] (d/enumerate relations)] + (tap-progress cfg {:section :relations + :current (inc index) :total (count relations)}) + (if (and (not= file-id libr-id) (contains? file-ids file-id) (contains? file-ids libr-id)) @@ -1010,8 +1064,6 @@ (defn- import-storage-objects [{:keys [::bfc/input ::entries-index ::bfc/timestamp] :as cfg}] - (events/tap :progress {:section :storage-objects}) - ;; IMPORTANT: we strongly do not reuse the main connection that can ;; run inside a transaction because the storage upload process can ;; fail in the middle of uploading and leave garbage on the underlying @@ -1020,9 +1072,12 @@ ;; what the storage subsystem registers in other parallel ;; transaction (let [storage (sto/resolve cfg) - entries (:objects entries-index)] + entries (:objects entries-index) + total (count entries)] - (doseq [{:keys [id entry]} entries] + (doseq [[index {:keys [id entry]}] (d/enumerate entries)] + (tap-progress cfg {:section :storage-objects + :current (inc index) :total total}) (let [object (-> (read-entry cfg input entry) (decode-storage-object) (d/update-when :content-type bfc/normalize-content-type) @@ -1204,12 +1259,15 @@ (vswap! bfc/*state* update :index assoc old-lib-id library-id))) (let [files (get manifest :files) - file-ids (reduce (fn [result file] + total (count files) + file-ids (reduce (fn [result [index file]] (let [name' (get file :name) - file (assoc file :name name')] - (conj result (import-file cfg file)))) + file (assoc file :name name')] + (conj result (import-file cfg file + {:current (inc index) + :total total})))) [] - files) + (d/enumerate files)) ;; Build map of file-id to file-name for resolution files-info (into {} (map (fn [file-id manifest-file] [file-id (:name manifest-file)]) @@ -1266,7 +1324,7 @@ bfc/*reference-file* ref-file] (import-storage-objects cfg) - (import-file cfg file) + (import-file cfg file {:current 1 :total 1}) (invalidate-thumbnails cfg file-id) (bfm/apply-pending-migrations! cfg) @@ -1321,7 +1379,7 @@ (cfeat/check-supported-features! features))) - (events/tap :progress {:section :manifest}) + (tap-progress cfg {:section :manifest}) (binding [bfc/*state* (volatile! {:media [] :index {}})] (if (::bfc/file-id cfg) @@ -1332,24 +1390,25 @@ (defn export-files! "Do the exportation of a specified file in custom penpot binary - format. There are some options available for customize the output: + format. The cfg carries what `bfc/export-cfg` froze: `::bfc/file-ids` + (the files to export), `::bfc/export-type` (how linked libraries are + handled) and `::bfc/output` (where the package is written). - `::bfc/export-type`: determines how linked libraries are handled. - Valid values: - `:include-libraries` (include linked libraries), + `::bfc/export-type` determines how linked libraries are handled. + Valid values: `:include-libraries` (include linked libraries), `:merge-libraries` (embed library assets in the file), `:detach-libraries` (treat assets as basic objects), `:link-later` (preserve component metadata for relinking on import)." - [{:keys [::bfc/ids] :as cfg} output] + [{:keys [::bfc/file-ids ::bfc/output] :as cfg}] (assert - (and (set? ids) (every? uuid? ids)) - "expected a set of uuid's for `::bfc/ids` parameter") + (and (set? file-ids) (every? uuid? file-ids)) + "expected a set of uuid's for `::bfc/file-ids` parameter") (assert (satisfies? jio/IOFactory output) - "expected instance of jio/IOFactory for `input`") + "expected instance of jio/IOFactory for `::bfc/output`") (let [id (uuid/next) tp (ct/tpoint) @@ -1375,6 +1434,11 @@ (vreset! ab true) nil) + (catch clojure.lang.ExceptionInfo cause + (if (= :interrupt (:type (ex-data cause))) + (vreset! ab true) + (throw cause))) + (catch Throwable cause (vreset! cs cause) (vreset! ab true) diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj index 8939dbfc48..99f47ea40c 100644 --- a/backend/src/app/config.clj +++ b/backend/src/app/config.clj @@ -77,6 +77,11 @@ :telemetry-uri "https://telemetry.penpot.app/" + :jobs-lease (ct/duration {:minutes 30}) + :jobs-retention (ct/duration {:days 7}) + :jobs-user-ttl (ct/duration {:days 7}) + :jobs-request-timeout (ct/duration {:minutes 2}) + :media-max-file-size (* 1024 1024 30) ; 30MiB :font-max-file-size (* 1024 1024 30) ; 30MiB @@ -97,12 +102,44 @@ :quotes-upload-sessions-per-profile 5 :quotes-upload-chunks-per-session 20 + :quotes-export-jobs-per-profile 10 + :quotes-import-jobs-per-profile 10 :upload-max-chunk-size (* 1024 1024 30) ; 30MiB ;; SSRF protection :ssrf-allowed-hosts #{} :ssrf-extra-blocked-cidrs #{}}) +(def schema:tenant + "Tenant identifier: hostname-label-style, lowercase (letters, digits + and hyphens). The value is normalized to lowercase when the + configuration is read, so an operator writing `PENPOT_TENANT=ACME` and + one writing `acme` name the same tenant: the value is compared as an + exact string in the keys and rows it lands in, and one uppercase + letter would otherwise split one tenant into two. + + It is interpolated into Redis keys separated by dots (msgbus + topics, rate-limit buckets, exporter job keys, which are also matched + with a glob pattern), so `.` and whitespace are rejected at startup + because one tenant would then be able to read or overwrite another's + keys. + + `%`, `_` and `:` are rejected too, and the reason they were added is + gone: the job queue used to be filtered with a LIKE over a + `:` prefix and now has a `tenant` column. They are kept + because narrowing the rule is a startup contract change with no real + tenant asking for it, and a tenant that has one would have to be + renamed. Tenants using those characters must be renamed before + upgrading." + [:re #"^[a-z0-9-]+$"]) + +(defn- normalize-tenant + "Lowercase the configured tenant before it is validated, so + `PENPOT_TENANT=ACME` and `acme` mean the same tenant everywhere." + [config] + (cond-> config + (string? (:tenant config)) (update :tenant str/lower))) + (def schema:config (do #_sm/optional-keys [:map {:title "config"} @@ -110,7 +147,7 @@ [:admins {:optional true} [::sm/set ::sm/email]] [:secret-key {:optional true} :string] - [:tenant {:optional false} :string] + [:tenant {:optional false} schema:tenant] [:is-saas ::sm/boolean] [:public-uri {:optional false} ::sm/uri] [:host {:optional false} :string] @@ -173,22 +210,29 @@ [:deletion-delay {:optional true} ::ct/duration] [:file-clean-delay {:optional true} ::ct/duration] + [:jobs-lease {:optional true} ::ct/duration] + [:jobs-retention {:optional true} ::ct/duration] + [:jobs-user-ttl {:optional true} ::ct/duration] + [:jobs-request-timeout {:optional true} ::ct/duration] [:telemetry-enabled {:optional true} ::sm/boolean] [:default-blob-version {:optional true} ::sm/int] [:allow-demo-users {:optional true} ::sm/boolean] [:error-report-webhook {:optional true} :string] [:user-feedback-destination {:optional true} :string] + [:mcp-feedback-email {:optional true} ::sm/email] [:default-rpc-rlimit {:optional true} [::sm/vec :string]] [:rpc-rlimit-config {:optional true} ::fs/path] [:rpc-climit-config {:optional true} ::fs/path] - [:audit-log-archive-uri {:optional true} :string] + [:nexus-uri {:optional true} ::sm/uri] [:audit-log-http-handler-concurrency {:optional true} ::sm/int] [:default-executor-parallelism {:optional true} ::sm/int] ;; REVIEW [:worker-default-parallelism {:optional true} ::sm/int] [:worker-webhook-parallelism {:optional true} ::sm/int] + [:worker-cron-parallelism {:optional true} ::sm/int] + [:worker-binfile-parallelism {:optional true} ::sm/int] [:database-password {:optional true} [:maybe :string]] [:database-uri {:optional true} ::sm/uri] @@ -207,6 +251,8 @@ [:quotes-font-variants-per-team {:optional true} ::sm/int] [:quotes-comment-threads-per-file {:optional true} ::sm/int] [:quotes-comments-per-file {:optional true} ::sm/int] + [:quotes-export-jobs-per-profile {:optional true} ::sm/int] + [:quotes-import-jobs-per-profile {:optional true} ::sm/int] [:quotes-snapshots-per-file {:optional true} ::sm/int] [:quotes-snapshots-per-team {:optional true} ::sm/int] [:quotes-team-access-requests-per-team {:optional true} ::sm/int] @@ -368,6 +414,7 @@ [& {:keys [prefix default] :or {prefix "penpot"}}] (->> (read-env prefix) (merge default) + (normalize-tenant) (decode-config))) (def version @@ -422,6 +469,41 @@ [& segments] (apply join-uri (c/get config :public-uri) segments)) +(defn get-jobs-lease + "Max time a job can run without touching modified_at (heartbeat or + progress) before the dispatcher marks it as `aborted` (system-side + terminal, never retried, reported with an error log)." + [] + (or (c/get config :jobs-lease) + (ct/duration {:minutes 30}))) + +(defn get-jobs-request-timeout + "Default timeout for the ephemeral request! calls (waiting for the + reply-key blpop); can be overridden per call. Any override is applied + by raising the pooled connection command timeout for the duration of + the call, which the pool restores on return." + [] + (or (c/get config :jobs-request-timeout) + (ct/duration {:minutes 2}))) + +(defn get-jobs-retention + "How long terminal (completed/failed/cancelled/aborted) internal job rows are + kept before the jobs GC deletes them. The legacy `task` table is not + touched by the jobs GC: while both versions run in parallel it is + cleaned by the legacy `tasks-gc` of that version." + [] + (or (c/get config :jobs-retention) + (ct/duration {:days 7}))) + +(defn get-jobs-user-ttl + "How long a user-facing job, and the artifact it owns, is kept before + the jobs GC deletes the row and marks the object for the storage GC. + Not the same as `:jobs-retention`, which only sweeps terminal internal + rows." + [] + (or (c/get config :jobs-user-ttl) + (ct/duration {:days 7}))) + (defn get "A configuration getter. Helps code be more testable." ([key] diff --git a/backend/src/app/db.clj b/backend/src/app/db.clj index d34911ff32..c9998b49b8 100644 --- a/backend/src/app/db.clj +++ b/backend/src/app/db.clj @@ -583,12 +583,70 @@ (l/trc :hint "explicit rollback requested (savepoint)") (.rollback conn sp)))) +(def ^:dynamic *after-commit-context* nil) + +;; the connection the ambient transaction owns, bound by `transact!` for +;; the extent of its body. It is what tells a nested call (same +;; connection) from an independent one (any other). +(def ^:dynamic *after-commit-conn* nil) + +(defn- run-after-commit-callbacks + [callbacks] + (doseq [callback callbacks] + (try + (callback) + (catch Throwable cause + (l/wrn :hint "after-commit callback failed" :cause cause))))) + +(defn after-commit + "Run a callback after the current transaction commits. + + The callback runs immediately when called outside a transaction. + + Nesting follows the connection, not an ambient flag. A transaction + opened on the connection the surrounding one owns is part of it and + shares its context, so callbacks are never drained before the + transaction that owns the connection commits. A transaction opened on + any other connection is an independent unit of work: it owns its + context, drains its callbacks at its own commit and drops them if it + rolls back." + [f] + (if *after-commit-context* + (swap! *after-commit-context* conj f) + (f))) + +(defn- joins-transaction? + "True when `transactable` is the very connection the ambient + transaction owns, which is what makes this call part of it." + [transactable] + (boolean + (and *after-commit-conn* + (identical? transactable *after-commit-conn*)))) + (defn transact! "A lower-level function for executing function in a transaction" ([transactable f] (transact! transactable f {})) ([transactable f opts] - (binding [next.jdbc.transaction/*nested-tx* :ignore] - (jdbc/transact transactable f opts)))) + (if (joins-transaction? transactable) + (binding [next.jdbc.transaction/*nested-tx* :ignore] + (jdbc/transact transactable f opts)) + (let [context (atom [])] + (try + (let [result (binding [*after-commit-context* context] + (binding [next.jdbc.transaction/*nested-tx* :ignore] + (jdbc/transact + transactable + (fn [conn] + (binding [*after-commit-conn* conn] + (f conn))) + opts)))] + ;; a rollback-only transaction commits nothing, so its + ;; callbacks have nothing to follow + (when-not (:rollback-only opts) + (run-after-commit-callbacks @context)) + result) + (finally + (reset! context []))))))) (defn tx-run! "Run a function in a transaction." diff --git a/backend/src/app/email.clj b/backend/src/app/email.clj index 8efa36ffbb..ede5d54bb6 100644 --- a/backend/src/app/email.clj +++ b/backend/src/app/email.clj @@ -6,6 +6,7 @@ (ns app.email "Main api for send emails." + (:refer-clojure :exclude [send]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -16,8 +17,8 @@ [app.config :as cf] [app.db :as db] [app.db.sql :as sql] + [app.jobs :as jobs] [app.util.template :as tmpl] - [app.worker :as wrk] [clojure.java.io :as io] [cuerdas.core :as str] [integrant.core :as ig]) @@ -296,23 +297,39 @@ [email-factory params] (email-factory params)) -(defn send! - "Schedule an already defined email to be sent using asynchronously - using worker task." - [{:keys [::conn ::factory] :as params}] - (assert (db/connectable? conn) "expected a valid database connection or pool") +(defn send + "Schedule an already defined email to be sent asynchronously + using the unified jobs machinery. The first `cfg` parameter is the + connectable context that provides the `::jobs/defs` registry (an RPC + method cfg or the system) and can provide a default connection; the + second `params` provides the email data and optionally `::factory` + and `::reuse-conn`. When `::reuse-conn` is true the submission + reuses the caller's existing `::db/conn` (from cfg) instead of + acquiring a fresh one from the pool. + NOTE: this renders the email before submitting, so the full subject + and body are stored as job params. Prefer `send-template` for + templated emails: it stores only the template id plus the raw data + and renders inside the worker. `send` is kept for raw (non + templated) emails and for draining jobs submitted in the old + format." + [cfg {:keys [::reuse-conn ::factory] :as params}] + (when (and reuse-conn (nil? (::db/conn cfg))) + (ex/raise :type :validation + :code :missing-connection + :hint "reuse-conn requires the caller cfg to carry ::db/conn (call inside a transaction)")) (let [email (if factory (factory params) (-> params - (dissoc params) + (dissoc ::reuse-conn ::factory) (check-params)))] - (wrk/submit! {::wrk/task :sendmail - ::wrk/delay 0 - ::wrk/max-retries 4 - ::wrk/priority 200 - ::db/conn conn - ::wrk/params email}))) + (jobs/submit (cond-> (dissoc cfg ::db/conn) + reuse-conn (assoc ::db/conn (::db/conn cfg))) + {::jobs/name :sendmail + ::jobs/delay 0 + ::jobs/max-retries 4 + ::jobs/priority 200 + ::jobs/params email}))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SENDMAIL FN / TASK HANDLER @@ -320,39 +337,44 @@ (declare send-to-logger!) +(defn- sendmail-impl + [cfg params] + (when (contains? cf/flags :smtp) + (let [session (create-smtp-session cfg)] + (with-open [transport (.getTransport session (if (::ssl cfg) "smtps" "smtp"))] + (.connect ^Transport transport + ^String (::host cfg) + ^String (::port cfg) + ^String (::username cfg) + ^String (::password cfg)) + + (let [^MimeMessage message (create-smtp-message cfg session params)] + (l/dbg :hint "sendmail" + :id (:id params) + :to (:to params) + :subject (str/trim (:subject params))) + + (.sendMessage ^Transport transport + ^MimeMessage message + (.getAllRecipients message)))))) + + (when (contains? cf/flags :log-emails) + (send-to-logger! cfg params))) + (defmethod ig/init-key ::sendmail [_ cfg] - (fn [params] - (when (contains? cf/flags :smtp) - (let [session (create-smtp-session cfg)] - (with-open [transport (.getTransport session (if (::ssl cfg) "smtps" "smtp"))] - (.connect ^Transport transport - ^String (::host cfg) - ^String (::port cfg) - ^String (::username cfg) - ^String (::password cfg)) + ;; the job handler: [context params]. The smtp deps are closed over + ;; here, and this job has no use for the context. + (fn [_context params] + (sendmail-impl cfg params))) - (let [^MimeMessage message (create-smtp-message cfg session params)] - (l/dbg :hint "sendmail" - :id (:id params) - :to (:to params) - :subject (str/trim (:subject params))) - - (.sendMessage ^Transport transport - ^MimeMessage message - (.getAllRecipients message)))))) - - (when (contains? cf/flags :log-emails) - (send-to-logger! cfg params)))) - -(defmethod ig/assert-key ::handler - [_ params] - (assert (fn? (::sendmail params)) "expected valid sendmail handler")) - -(defmethod ig/init-key ::handler - [_ {:keys [::sendmail]}] - (fn [{:keys [props] :as task}] - (sendmail props))) +(defmethod ig/init-key ::job-def + [_ {sendmail ::sendmail}] + {::jobs/name :sendmail + ::jobs/schema schema:params + ::jobs/handler sendmail + ::jobs/decoder (sm/decoder schema:params sm/json-transformer) + ::jobs/validator (sm/validator schema:params)}) (defn- send-to-logger! [_ email] @@ -379,7 +401,7 @@ [:feedback-subject ::sm/text] [:feedback-type ::sm/text] [:feedback-content ::sm/text] - [:profile :map]]) + [:profile [:map [:id ::sm/uuid]]]]) (def user-feedback "A profile feedback email." @@ -578,6 +600,125 @@ :id ::comment-notification :schema schema:comment-notification)) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TEMPLATED SEND (render inside the worker) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- template-entry + "Registry entry for a templated email: the factory that renders it and + the JSON encoder/decoder of its data schema. The job `params` column + is plain JSON, so values without a JSON form (e.g. the URIs of an + organization) are encoded to strings before submit and decoded back + to their types before the factory validates and renders them." + [factory schema] + {::factory factory + ::encode (sm/encoder schema sm/json-transformer) + ::decode (sm/decoder schema sm/json-transformer)}) + +;; Registry of template id to entry. This map is the single source of +;; truth for templated mail: `send-template` validates membership and +;; encodes the data here before submitting (so a job that could never +;; be rendered is rejected up front), and the worker decodes the data +;; and renders it with the entry factory. Callers pass the id string +;; itself (e.g. `"invite-to-team"`); keys stay `::eml/`-namespaced and +;; values stay plain data, following the params convention. +(def templates + {"feedback" (template-entry user-feedback schema:feedback) + "register" (template-entry register schema:register) + "password-recovery" (template-entry password-recovery schema:password-recovery) + "password-changed" (template-entry password-changed schema:password-changed) + "change-email" (template-entry change-email schema:change-email) + "invite-to-team" (template-entry invite-to-team schema:invite-to-team) + "invite-to-organization" (template-entry invite-to-organization schema:invite-to-organization) + "organization-setup-sso" (template-entry organization-setup-sso schema:organization-setup-sso) + "renewal-notice" (template-entry renewal-notice schema:renewal-notice) + "join-team" (template-entry join-team schema:join-team) + "request-file-access" (template-entry request-file-access schema:request-file-access) + "request-file-access-yourpenpot" (template-entry request-file-access-yourpenpot schema:request-file-access) + "request-file-access-yourpenpot-view" (template-entry request-file-access-yourpenpot-view schema:request-file-access) + "request-team-access" (template-entry request-team-access schema:request-team-access) + "comment-mention" (template-entry comment-mention schema:comment-mention) + "comment-thread" (template-entry comment-thread schema:comment-thread) + "comment-notification" (template-entry comment-notification schema:comment-notification)}) + +(defn render-template + "Render the templated email `template-id` from its JSON-decoded data, + as the worker does before sending it." + [template-id params] + (let [{:keys [::factory ::decode]} (get templates template-id)] + (factory (decode params)))) + +;; Envelope of the job params only: the template data travels encoded +;; by the template schema and is decoded by `render-template`. +(def ^:private schema:templated-params + [:map {:title "Templated Email Params"} + [:template (into [:enum] (sort (keys templates)))] + [:to [:or ::sm/email [::sm/vec ::sm/email]]] + [:reply-to {:optional true} ::sm/email] + [:from {:optional true} ::sm/email] + [:lang {:optional true} ::sm/text] + [:priority {:optional true} [:enum :high :low]] + [:extra-data {:optional true} ::sm/text] + [:attachments {:optional true} + [:map-of :string :string]]]) + +(defn send-template + "Schedule a templated email to be sent asynchronously using the + unified jobs machinery. Unlike `send`, this does NOT render the + email: it stores only the template id plus the raw template data as + `:send-templated-mail` job params, and the worker renders and sends + the email. The `::template` param is the template id string (one of + the keys of `templates`, e.g. `invite-to-team`); + `::reuse-conn` behaves like in `send`." + [cfg {:keys [::template ::reuse-conn] :as params}] + (when (and reuse-conn (nil? (::db/conn cfg))) + (ex/raise :type :validation + :code :missing-connection + :hint "reuse-conn requires the caller cfg to carry ::db/conn (call inside a transaction)")) + (when (nil? template) + (ex/raise :type :validation + :code :missing-template + :hint "template is mandatory for templated emails")) + (when-not (contains? templates template) + (ex/raise :type :validation + :code :unknown-email-template + :hint (str/ffmt "unknown email template: %", template))) + (let [encode (get-in templates [template ::encode]) + recipe (-> params + (dissoc ::reuse-conn ::template) + ;; Present-but-nil extras (e.g. `:file-id` when there is + ;; no file) would fail the job schema, while the old + ;; inline render simply ignored them. + (d/without-nils) + (encode))] + (jobs/submit (cond-> (dissoc cfg ::db/conn) + reuse-conn (assoc ::db/conn (::db/conn cfg))) + {::jobs/name :send-templated-mail + ::jobs/delay 0 + ::jobs/max-retries 4 + ::jobs/priority 200 + ::jobs/params (cond-> (assoc recipe :template template) + ;; `:public-uri` arrives as a URI record + ;; from config, which plain JSON cannot + ;; roundtrip; the templates only need its + ;; string form. + (some? (:public-uri recipe)) + (update :public-uri str))}))) + +(defmethod ig/init-key ::templated-job-def + [_ {sendmail ::sendmail}] + {::jobs/name :send-templated-mail + ::jobs/schema schema:templated-params + ::jobs/handler (fn [context params] + (let [template-id (:template params)] + (when-not (contains? templates template-id) + (ex/raise :type :internal + :code :unknown-email-template + :template-id template-id)) + (sendmail context (render-template template-id (dissoc params :template))))) + ::jobs/decoder (sm/decoder schema:templated-params sm/json-transformer) + ::jobs/validator (sm/validator schema:templated-params)}) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; BOUNCE/COMPLAINS HELPERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/backend/src/app/features/fdata.clj b/backend/src/app/features/fdata.clj index 43484a696b..d18da11ea6 100644 --- a/backend/src/app/features/fdata.clj +++ b/backend/src/app/features/fdata.clj @@ -51,10 +51,19 @@ (update file :data process-objects (partial into {}))) (defn enable-objects-map + "Ensure every page holds an ObjectsMap. Unloaded pointers are left + untouched: the fragment they point to was written by an `encode-file` + that already wrapped its objects, and loading it only to wrap it again + costs one page read per untouched page on every save. A page whose + fragment predates the feature is wrapped when it is first loaded or + edited; a data migration that enables the feature must rewrite the + data itself." [file & _opts] (let [update-page (fn [page] - (update page :objects omap/wrap)) + (if (and (pmap/pointer-map? page) (not (pmap/loaded? page))) + page + (update page :objects omap/wrap))) update-data (fn [fdata] @@ -264,9 +273,13 @@ (defn load-pointer "A database loader pointer helper" [cfg file-id id] + ;; Keep the include-marked behavior explicit: `db/get*` only filters + ;; rows by `deleted_at` when that column is selected, and a fragment a + ;; previous pass marked must still load until `objects-gc` purges it. (let [fragment (some-> (db/get* cfg :file-data {:id id :file-id file-id :type "fragment"} - {::sql/columns [:data :backend :id :metadata]}) + {::sql/columns [:data :backend :id :metadata] + ::db/remove-deleted false}) (update :metadata decode-metadata))] (l/trc :hint "load pointer" @@ -297,6 +310,40 @@ :type "fragment" :data content}))))) +(defmacro with-pointers + "Bind the fragment loader for `file-id` around `body`. All pointer + derefs must happen inside the body; a pointer derefed outside of it + raises because there is no loader bound." + [cfg file-id & body] + `(binding [pmap/*load-fn* (partial load-pointer ~cfg ~file-id)] + ~@body)) + +(defmacro with-tracked + "Bind a fresh tracked set around `body`, without a fragment loader. + For write paths that create pointers before any file exists to load + from (e.g. file creation): wrapping tracks new pointers for + persistence, while any deref still fails loudly." + [& body] + `(binding [pmap/*tracked* (pmap/create-tracked)] + ~@body)) + +(defmacro with-untracked + "Run `body` with pointer tracking disabled. For validation passes + that must not track anything for persistence: pointers created + inside are not collected, and derefs still go through the bound + loader when there is one." + [& body] + `(binding [pmap/*tracked* nil] + ~@body)) + +(defmacro with-tracked-pointers + "Bind a fresh tracked set plus the fragment loader for `file-id` + around `body`. For write paths that create or modify pointers." + [cfg file-id & body] + `(binding [pmap/*tracked* (pmap/create-tracked) + pmap/*load-fn* (partial load-pointer ~cfg ~file-id)] + ~@body)) + (defn process-pointers "Apply a function to all pointers on the file. Usuly used for dereference the pointer to a plain value before some processing." @@ -311,14 +358,20 @@ (defn realize-pointers "Process a file and remove all instances of pointers realizing them to - a plain data. Used in operation where is more efficient have the - whole file loaded in memory." - [cfg {:keys [id] :as file}] - (binding [pmap/*load-fn* (partial load-pointer cfg id)] - (update file :data process-pointers deref))) + plain data. Accepts a file map (arity 1) or raw file data with its + file id (arity 2): a snapshot map carries the snapshot id, so always + pass the live file id explicitly." + ([cfg {:keys [id] :as file}] + (update file :data #(realize-pointers cfg id %))) + ([cfg file-id data] + (with-pointers cfg file-id + (process-pointers data deref)))) (defn get-used-pointer-ids - "Given a file, return all pointer ids used in the data." + "Given a file data map, return all pointer ids used in the data. + This is a plain readout with no liveness meaning of its own: file-gc + unions it over the main file and every unpurged snapshot (see + `app.tasks.file-gc/clean-fragments!` for the actual liveness rule)." [fdata] (->> (concat (vals fdata) (vals (:pages-index fdata))) @@ -345,3 +398,50 @@ (->> file (realize-pointers cfg) (realize-objects cfg))) + +(defn realize-entry + "Realize a file data entry (a page or the components map) to plain + data for value comparison: deref pointers and convert objects-maps + with the same `into {}` production uses. Requires *load-fn* bound." + [entry] + (let [entry (if (pmap/pointer-map? entry) (deref entry) entry)] + (if (and (map? entry) (contains? entry :objects)) + (let [objects (:objects entry)] + (assoc entry :objects (if (omap/objects-map? objects) + (into {} objects) + objects))) + entry))) + +(defn original-pointers + "Snapshot the current pointer layout of file data, mapping each + pointer position to its PointerMap. No loading involved." + [fdata] + {:root (into {} (filter (fn [[_ v]] (pmap/pointer-map? v))) fdata) + :pages (into {} (filter (fn [[_ v]] (pmap/pointer-map? v))) + (:pages-index fdata {}))}) + +(defn reattach-unchanged + "Put back the original fragment pointers wherever the current value + is value-equal to the stored fragment, so unchanged positions keep + their ids and no new fragment rows are persisted for them. `current` + may be a PointerMap (file-gc) or a plain page (the migration read + path); `orig` always comes from `original-pointers`. Anything added, + removed or actually changed keeps the current value. Requires + *load-fn* bound." + [{:keys [root pages]} file] + (letfn [(reattach [current orig] + (if (or (identical? current orig) + (= (realize-entry current) (realize-entry orig))) + orig + current)) + (reattach-all [m origs] + (reduce-kv (fn [m k orig] + (if (contains? m k) + (update m k reattach orig) + m)) + m origs))] + (update file :data (fn [data] + (cond-> (reattach-all data root) + (contains? data :pages-index) + (update :pages-index reattach-all pages)))))) + diff --git a/backend/src/app/features/file_snapshots.clj b/backend/src/app/features/file_snapshots.clj index 9be4b1b572..2370758d28 100644 --- a/backend/src/app/features/file_snapshots.clj +++ b/backend/src/app/features/file_snapshots.clj @@ -19,9 +19,9 @@ [app.db :as db] [app.db.sql :as-alias sql] [app.features.fdata :as fdata] + [app.jobs :as jobs] [app.storage :as sto] [app.util.blob :as blob] - [app.worker :as wrk] [cuerdas.core :as str])) (def sql:snapshots @@ -48,11 +48,19 @@ AND fd.type = 'snapshot') WHERE c.label IS NOT NULL") +;; `:migrations` is an ordered set everywhere (see `common.types.file`), +;; and `need-migration?`/`migrate` use set semantics on it: the pgarray +;; must never surface as a vector, where `contains?`/`set/difference` +;; would compare indexes instead of names. +(defn- decode-migrations + [value] + (into (d/ordered-set) (db/decode-pgarray value []))) + (defn- decode-snapshot [snapshot] (some-> snapshot (-> (d/update-when :metadata fdata/decode-metadata) - (d/update-when :migrations db/decode-pgarray []) + (d/update-when :migrations decode-migrations) (d/update-when :features db/decode-pgarray #{})))) (def ^:private sql:get-minimal-file @@ -376,8 +384,13 @@ :snapshot-id (str (:id snapshot))) ;; In the same way, on reseting the file data, we need to restore - ;; the applied migrations on the moment of taking the snapshot - (bfc/update-file! cfg file ::bfc/reset-migrations? true) + ;; the applied migrations on the moment of taking the snapshot. + ;; The snapshot data may hold shared pointer-mapped pages; the + ;; loader stays bound while persisting so any page the encode + ;; path does load keeps working (`enable-objects-map` skips the + ;; unloaded ones). + (fdata/with-pointers cfg file-id + (bfc/update-file! cfg file ::bfc/reset-migrations? true)) ;; FIXME: this should be separated functions, we should not have ;; inline sql here. @@ -406,12 +419,12 @@ (assert (uuid? file-id) "missing file-id") (assert (ct/inst? deleted-at) "missing deleted-at") - (wrk/submit! {::db/conn (db/get-connection cfg) - ::wrk/task :delete-object - ::wrk/params {:object :snapshot - :deleted-at deleted-at - :file-id file-id - :id id}}) + (jobs/submit cfg + {::jobs/name :delete-object + ::jobs/params {:object :snapshot + :deleted-at deleted-at + :file-id file-id + :id id}}) (db/update! cfg :file-change {:deleted-at deleted-at} {:id id :file-id file-id} @@ -441,7 +454,12 @@ (defn reduce-snapshots "Process the file snapshots using efficient reduction; the file - reduction comes with all snapshots, including maked as deleted" + reduction comes with all snapshots, including maked as deleted. + + Snapshot data may hold pointer-mapped pages, so the fragment loader + is bound while reducing: consumers that walk page content (media + refs, stats) transparently load fragments, consumers that only read + pointer ids never trigger a load." [cfg file-id xform f init] (let [conn (db/get-connection cfg) xform (comp @@ -449,5 +467,6 @@ (map (partial fdata/decode-file-data cfg)) xform)] - (->> (db/plan conn [sql:get-snapshots file-id] {:fetch-size 1}) - (transduce xform f init)))) + (fdata/with-pointers cfg file-id + (->> (db/plan conn [sql:get-snapshots file-id] {:fetch-size 1}) + (transduce xform f init))))) diff --git a/backend/src/app/http.clj b/backend/src/app/http.clj index aa6950fdeb..83af60c3d2 100644 --- a/backend/src/app/http.clj +++ b/backend/src/app/http.clj @@ -19,6 +19,7 @@ [app.http.errors :as errors] [app.http.link-preview :as-alias link-preview] [app.http.management :as mgmt] + [app.http.mcp-oauth :as-alias mcp-oauth] [app.http.middleware :as mw] [app.http.security :as sec] [app.http.session :as session] @@ -236,6 +237,7 @@ [::ws/routes schema:routes] [::rpc/routes schema:routes] [::oidc/routes schema:routes] + [::mcp-oauth/routes schema:routes] [::assets/routes schema:routes] [::link-preview/routes schema:routes] [::debug/routes schema:routes] @@ -278,4 +280,7 @@ (::ws/routes cfg) (::oidc/routes cfg) - (::rpc/routes cfg)]])) + (::rpc/routes cfg)] + ;; OAuth owns form parsing, protocol JSON names and cross-origin handling; + ;; it must not inherit the RPC transformations and session middleware above. + (::mcp-oauth/routes cfg)])) diff --git a/backend/src/app/http/access_token.clj b/backend/src/app/http/access_token.clj index 57f19fdda8..017b042bd4 100644 --- a/backend/src/app/http/access_token.clj +++ b/backend/src/app/http/access_token.clj @@ -14,14 +14,20 @@ [app.setup :as-alias setup] [app.tokens :as tokens])) +(def mcp-token-issuer "urn:penpot:mcp-token") + +(def ^:private valid-issuers + #{"access-token" mcp-token-issuer}) + (defn decode-token [cfg token] (try - (tokens/verify cfg {:token token :iss "access-token"}) - (catch Throwable cause - (l/trc :hint "exception on decoding token" - :token token - :cause cause)))) + (let [claims (tokens/verify cfg {:token token})] + (when (contains? valid-issuers (:iss claims)) + claims)) + ;; The cause is not logged because its data carries the token + (catch Throwable _ + (l/trc :hint "exception on decoding token")))) (def sql:get-token-data "SELECT perms, profile_id, expires_at, type @@ -34,32 +40,48 @@ [pool claims] (when-not (db/read-only? pool) (when-let [token-id (get claims :tid)] - (some-> (db/exec-one! pool [sql:get-token-data token-id]) - (update :perms db/decode-pgarray #{}))))) + (let [token (db/exec-one! pool [sql:get-token-data token-id]) + mcp-claims? (= mcp-token-issuer (:iss claims)) + mcp-token? (= "mcp" (:type token))] + (when (and (= (:uid claims) (:profile-id token)) + (= mcp-claims? mcp-token?)) + (update token :perms db/decode-pgarray #{})))))) (defn- wrap-authz - [handler {:keys [::db/pool]}] - (fn [request] - (let [{:keys [type claims]} (get request ::http/auth-data)] - (if (= :token type) - (let [{:keys [perms profile-id expires-at type]} (some->> claims (get-token-data pool)) - token-id (get claims :tid)] - (handler (cond-> request - (some? perms) - (assoc ::perms perms) - (some? profile-id) - (assoc ::profile-id profile-id) - (some? expires-at) - (assoc ::expires-at expires-at) - (some? token-id) - (assoc ::id token-id) - (some? type) - (assoc ::type type)))) + "Authenticate requests carrying an access token. Personal tokens need the + `access-tokens` flag. MCP tokens are restricted to specific commands, so + only RPC routes opt in (the RPC wrapper enforces those restrictions); they + need either the `mcp` or the `access-tokens` flag." + [handler {:keys [::db/pool ::allow-mcp]}] + (let [personal-enabled? (contains? cf/flags :access-tokens) + mcp-enabled? (and allow-mcp + (or personal-enabled? + (contains? cf/flags :mcp)))] + (fn [request] + (let [{:keys [type claims]} (get request ::http/auth-data)] + (if (and (= :token type) + (if (= mcp-token-issuer (:iss claims)) + mcp-enabled? + personal-enabled?)) + (let [{:keys [perms profile-id expires-at type]} (some->> claims (get-token-data pool)) + token-id (get claims :tid)] + (handler (cond-> request + (some? perms) + (assoc ::perms perms) + (some? profile-id) + (assoc ::profile-id profile-id) + (some? expires-at) + (assoc ::expires-at expires-at) + (some? token-id) + (assoc ::id token-id) + (some? type) + (assoc ::type type)))) - (handler request))))) + (handler request)))))) (def authz {:name ::authz :compile (fn [& _] - (when (contains? cf/flags :access-tokens) + (when (or (contains? cf/flags :access-tokens) + (contains? cf/flags :mcp)) wrap-authz))}) diff --git a/backend/src/app/http/assets.clj b/backend/src/app/http/assets.clj index e2c41d0886..f09c694eb4 100644 --- a/backend/src/app/http/assets.clj +++ b/backend/src/app/http/assets.clj @@ -100,7 +100,7 @@ headers (cond-> {"location" (str url) "x-host" (cond-> host port (str ":" port)) "x-mtype" (-> obj meta :content-type) - "cache-control" (str "max-age=" (inst-ms cch-max-age))} + "cache-control" (str "max-age=" (ct/seconds cch-max-age))} (not public?) (assoc "content-disposition" "attachment"))] {::yres/status 307 @@ -115,7 +115,7 @@ bucket (:bucket mdata) headers (cond-> {"x-accel-redirect" (:path purl) "content-type" (:content-type mdata) - "cache-control" (str "max-age=" (inst-ms cch-max-age))} + "cache-control" (str "max-age=" (ct/seconds cch-max-age))} (not (public-bucket? bucket)) (assoc "content-disposition" "attachment"))] {::yres/status 204 @@ -150,14 +150,22 @@ [request] (some? (request-profile-id request))) -(defn- tempfile-owner-match? - "Check if the request's profile-id matches the tempfile's stored owner. - Returns true if no profile-id was stored (legacy objects)." +(def ^:private owner-scoped-buckets + "Buckets whose objects belong to a profile: a request from anybody else + must not reach them." + #{sto/tempfile-bucket sto/job-resource-bucket}) + +(defn- owner-match? + "Check that the request belongs to the profile that owns the object. + A missing owner is only tolerated in `tempfile`, where objects predate + the metadata; the `job-resource` bucket always carries one, so an + object without it fails closed." [obj request] - (let [stored-profile-id (:profile-id (meta obj)) - request-profile-id (request-profile-id request)] - (or (nil? stored-profile-id) - (= stored-profile-id request-profile-id)))) + (let [stored (-> obj meta :profile-id) + owner (request-profile-id request) + legacy? (= sto/tempfile-bucket (-> obj meta :bucket))] + (or (and legacy? (nil? stored)) + (= stored owner)))) (defn- serve-object-measured "Serve `obj`, recording one asset metric per outcome. A failure is @@ -175,7 +183,9 @@ "Handler that serves storage objects by id. For non-public buckets (e.g. profile), requires authentication via session cookie or access token. - For tempfile bucket, also requires ownership (profile-id match)." + For the owner-scoped buckets (tempfile, job-resource), also requires + ownership: the request must come from the profile the object belongs + to." [{:keys [::sto/storage] :as cfg} request] (let [id (get-id request) obj (sto/get-object storage id)] @@ -193,8 +203,8 @@ ;; The response stays 404 to avoid leaking existence, but the ;; metric records the internal 401 outcome. - (and (= (-> obj meta :bucket) sto/tempfile-bucket) - (not (tempfile-owner-match? obj request))) + (and (contains? owner-scoped-buckets (-> obj meta :bucket)) + (not (owner-match? obj request))) (do (emit-asset! cfg "by-id" obj 401) {::yres/status 404}) diff --git a/backend/src/app/http/debug.clj b/backend/src/app/http/debug.clj index 6995464290..d62520706f 100644 --- a/backend/src/app/http/debug.clj +++ b/backend/src/app/http/debug.clj @@ -359,10 +359,11 @@ (let [path (tmp/tempfile :prefix "penpot.export." :min-age "30m")] (with-open [output (io/output-stream path)] (-> cfg - (assoc ::bfc/ids file-ids) + (assoc ::bfc/file-ids file-ids) (assoc ::bfc/embed-assets embed?) (assoc ::bfc/include-libraries libs?) - (bf.v3/export-files! output))) + (assoc ::bfc/output output) + (bf.v3/export-files!))) (if clone? (let [profile (profile/get-profile pool profile-id) @@ -784,8 +785,12 @@ (db/tx-run! (assoc cfg ::db/rollback true) (fn [cfg] - (let [file (bfc/get-file cfg file-id) - libs (bfc/get-resolved-file-libraries cfg file-id)] + ;; Validation walks every page and component, so the + ;; file is read realized; the library map is built + ;; from the file (not its id) and its own loader + ;; resolves each library's pointers. + (let [file (bfc/get-file cfg file-id :realize? true) + libs (bfc/get-resolved-file-libraries cfg file)] (if file (let [errors (cfv/validate-file file libs)] {::yres/status 200 diff --git a/backend/src/app/http/errors.clj b/backend/src/app/http/errors.clj index b8ef12eb74..7acad1f9f4 100644 --- a/backend/src/app/http/errors.clj +++ b/backend/src/app/http/errors.clj @@ -17,6 +17,7 @@ [app.http.session :as-alias session] [app.util.inet :as inet] [clojure.spec.alpha :as s] + [cuerdas.core :as str] [yetti.request :as yreq] [yetti.response :as yres])) @@ -223,6 +224,25 @@ "25P03" "The transaction was idle too long and was cancelled" "A database error occurred")) +(defn- transient-sql-state? + "True for SQLSTATE codes of failures that pass on their own: lost or + refused connections, lack of resources, a server shutting down, and + serialization failures or deadlocks of concurrent transactions." + [state] + (and (string? state) + (or (str/starts-with? state "08") + (str/starts-with? state "53") + (contains? #{"57P01" "57P02" "57P03" "40001" "40P01"} state)))) + +(defn- unavailable + "Response for a failure that passes on its own: the request can be sent + again." + [code hint] + {::yres/status 503 + ::yres/body {:type :server-error + :code code + :hint hint}}) + (defmethod handle-exception org.postgresql.util.PSQLException [error request parent-cause] (let [state (.getSQLState ^java.sql.SQLException error) @@ -231,6 +251,9 @@ (l/error :hint "postgresql error" :cause cause) (cond + (transient-sql-state? state) + (unavailable :database-unavailable "The database is not available right now") + (= state "57014") {::yres/status 504 ::yres/body {:type :server-error @@ -271,13 +294,37 @@ (assoc :type :server-error) (update :code #(or % :unhandled)))})))) +(defmethod handle-exception java.sql.SQLTransientException + [cause request _] + (binding [l/*context* (request->context request)] + (l/wrn :hint "transient database error" :cause cause) + (unavailable :database-unavailable "The database is not available right now"))) + +(defn- transient-io-exception? + "True for I/O failures that pass on their own: a lost, refused or timed + out connection, or a host name that did not resolve. A parse error or + data that cannot be read fails again on every attempt." + [cause] + (or (instance? java.net.SocketException cause) + (instance? java.io.InterruptedIOException cause) + (instance? java.net.UnknownHostException cause) + (instance? java.net.http.HttpTimeoutException cause))) + (defmethod handle-exception java.io.IOException [cause request _] (binding [l/*context* (request->context request)] (l/wrn :hint "io exception" :cause cause) - {::yres/status 500 - ::yres/body {:type :server-error - :code :io-exception}})) + (if (transient-io-exception? cause) + (unavailable :io-exception "An input/output operation failed") + {::yres/status 500 + ::yres/body {:type :server-error + :code :io-exception}}))) + +(defmethod handle-exception InterruptedException + [cause request _] + (binding [l/*context* (request->context request)] + (l/wrn :hint "interrupted request" :cause cause) + (unavailable :interrupted "The request was interrupted"))) (defmethod handle-exception java.util.concurrent.CompletionException [cause request _] diff --git a/backend/src/app/http/mcp_oauth.clj b/backend/src/app/http/mcp_oauth.clj new file mode 100644 index 0000000000..5184cc7e65 --- /dev/null +++ b/backend/src/app/http/mcp_oauth.clj @@ -0,0 +1,353 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.http.mcp-oauth + "MCP-specific OAuth authorization server. + + The authorize endpoint stages a request; the existing Penpot session identifies + the user on the approval page. Approval creates a one-use code, and token + exchange returns the existing fixed MCP user token after verifying PKCE. + Client registration, login sessions, and authorization codes are distinct." + (:require + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.http.errors :as errors] + [app.http.middleware :as mw] + [app.http.session :as session] + [app.mcp.oauth-clients :as clients] + [app.mcp.oauth-state :as state] + [app.rpc.commands.access-token :as access-token] + [app.util.json :as json] + [clojure.string :as str] + [integrant.core :as ig] + [yetti.request :as yreq] + [yetti.response :as yres]) + (:import + java.io.InputStream + java.net.URI + java.net.URLDecoder + java.net.URLEncoder + java.nio.charset.StandardCharsets + java.util.Base64)) + +(defn issuer [] (str (cf/get-public-uri "api/mcp/oauth"))) +(defn resource [] (str (cf/get-public-uri "mcp/stream"))) + +(defn authorization-metadata + "OAuth capabilities and endpoints advertised to MCP clients." + [] + {:issuer (issuer) + :authorization_endpoint (str (issuer) "/authorize") + :token_endpoint (str (issuer) "/token") + :registration_endpoint (str (issuer) "/register") + :response_types_supported ["code"] + :grant_types_supported ["authorization_code"] + :code_challenge_methods_supported ["S256"] + :token_endpoint_auth_methods_supported ["none" "client_secret_basic" "client_secret_post"] + :scopes_supported ["mcp"] + :authorization_response_iss_parameter_supported true + :client_id_metadata_document_supported true}) + +(defn resource-metadata + "Protected-resource metadata referenced by the MCP server's 401 challenge." + [] + {:resource (resource) + :authorization_servers [(issuer)] + :scopes_supported ["mcp"] + :bearer_methods_supported ["header"] + :resource_name "Penpot MCP"}) + +(defn- response + ([body] (response 200 body)) + ([status body] + {::yres/status status + ::yres/headers {"content-type" "application/json" + "cache-control" "no-store" + "pragma" "no-cache" + "referrer-policy" "no-referrer"} + ::yres/body (json/encode-str body)})) + +(defn- redirect + [url] + {::yres/status 303 + ::yres/headers {"location" url "cache-control" "no-store" + "referrer-policy" "no-referrer"} + ::yres/body ""}) + +(defn- encode [^String value] (URLEncoder/encode value StandardCharsets/UTF_8)) +(defn- decode + [^String value] + (try (URLDecoder/decode value StandardCharsets/UTF_8) + (catch IllegalArgumentException _ (clients/invalid! "invalid_request")))) + +(defn- parse-form + "Bounded OAuth form/query parser preserving protocol parameter names. + Rejects duplicate keys so validation never depends on which value wins." + [query] + (when (> (count query) 16384) (clients/invalid! "invalid_request")) + (reduce (fn [result part] + (let [[key value] (str/split part #"=" 2) + key (keyword (decode key))] + (when (contains? result key) (clients/invalid! "invalid_request")) + (assoc result key (decode (or value ""))))) + {} + (remove str/blank? (str/split (or query "") #"&")))) + +(defn- read-body + [request content-type] + (when-not (str/starts-with? (or (yreq/get-header request "content-type") "") content-type) + (clients/invalid! "invalid_request")) + (with-open [^InputStream body (yreq/body request)] + (when-not body (clients/invalid! "invalid_request")) + (let [bytes (.readNBytes body 16385)] + (when (> (alength bytes) 16384) (clients/invalid! "invalid_request")) + (String. bytes StandardCharsets/UTF_8)))) + +(defn- append-query + [url params] + (str url (if (str/includes? url "?") "&" "?") + (str/join "&" (for [[key value] params :when (some? value)] + (str (encode (name key)) "=" (encode value)))))) + +(defn- limit! + [cfg request kind limit] + (when-not (state/allow-request! cfg kind (str (yreq/remote-addr request)) limit) + (clients/invalid! "temporarily_unavailable" 429))) + +(defn- authorize + "Validate and stage an authorization request before user login or consent. + The browser receives only an opaque handle to the ten-minute Redis record." + [cfg request] + (limit! cfg request :authorize-rate 60) + (let [params (parse-form (yreq/query request)) + client (clients/resolve-client cfg (:client_id params)) + callback (:redirect_uri params)] + ;; Never redirect until the client and its callback have been checked. + (when-not (clients/redirect-matches? client callback) + (clients/invalid! "invalid_request")) + (when-not (and (= "code" (:response_type params)) + (= "S256" (:code_challenge_method params)) + (string? (:code_challenge params)) + (re-matches #"[A-Za-z0-9_-]{43}" (:code_challenge params)) + (= "mcp" (get params :scope "mcp")) + (= (resource) (get params :resource (resource))) + (<= (count (:state params)) 2048)) + (clients/invalid! "invalid_request")) + (let [handle (state/create! cfg :request + {:client-id (:client_id client) + :client-name (:client_name client) + :logo-uri (:logo_uri client) + :redirect-uri callback + :challenge (:code_challenge params) + :state (:state params) + :resource (resource) + :scope "mcp"} + 600)] + (redirect (append-query (str (cf/get-public-uri)) + {:screen "mcp-authorize" :request handle}))))) + +(defn- session-binding + "Profile and login-session identity used to bind one approval request." + [request] + (let [session (session/get-session request)] + (when-not (:profile-id session) (clients/invalid! "login_required" 401)) + {:profile-id (str (:profile-id session)) + :session-id (state/digest (str (:id session)))})) + +(defn- approval-request + "Bind a pending request to its first authenticated session and return UI data. + Compare-and-replace prevents another session from taking over that request." + [cfg request] + (let [binding (session-binding request) + handle (:request (parse-form (yreq/query request))) + record (state/read! cfg :request handle)] + (when-not record (clients/invalid! "invalid_request")) + (let [value (:value record)] + (if (:session-id value) + (when-not (= binding (select-keys value [:profile-id :session-id])) + (clients/invalid! "access_denied" 403)) + (state/replace! cfg :request handle record + (merge value binding {:csrf (state/random-secret)})))) + ;; Another GET may have won the binding race. Read the winning record so + ;; same-session retries share its CSRF value; other sessions cannot proceed. + (let [value (:value (state/read! cfg :request handle))] + (when-not (= binding (select-keys value [:profile-id :session-id])) + (clients/invalid! "invalid_request")) + (response (select-keys value [:client-id :client-name :logo-uri :csrf :scope]))))) + +(defn- same-origin? + [request] + (let [public (URI. (str (cf/get-public-uri))) + origin (str (.getScheme public) "://" (.getRawAuthority public))] + (= origin (yreq/get-header request "origin")))) + +(defn- decision + "Accept one session-bound approval/denial and return the client callback. + Origin and CSRF checks protect the cookie-authenticated decision endpoint." + [cfg request] + (when-not (same-origin? request) (clients/invalid! "access_denied" 403)) + (let [binding (session-binding request) + params (parse-form (read-body request "application/x-www-form-urlencoded")) + handle (:request params) + record (state/read! cfg :request handle) + value (:value record)] + (when-not (and record + (= binding (select-keys value [:profile-id :session-id])) + (state/secret= (:csrf value) (:csrf params)) + (contains? #{"approve" "deny"} (:decision params))) + (clients/invalid! "invalid_request")) + ;; Consume before provisioning: concurrent clicks must not issue two codes. + ;; A later failure requires a fresh authorization attempt. + (when-not (state/consume! cfg :request handle record) + (clients/invalid! "invalid_request")) + (let [result (if (= "deny" (:decision params)) + {:error "access_denied"} + (let [token (db/tx-run! cfg access-token/ensure-mcp-access! + (uuid/parse (:profile-id value))) + ;; Retain the token row ID, never the fixed token itself. + code (state/create! cfg :code + (-> value + (dissoc :csrf :session-id :client-name) + (assoc :token-id (str (:id token)))) + 120)] + {:code code}))] + (response {:redirect-uri (append-query (:redirect-uri value) + (merge result {:state (:state value) + :iss (issuer)}))})))) + +(defn- client-authentication + "Extract one OAuth client-authentication method, rejecting mixed credentials. + Basic credentials are form-decoded after splitting the decoded header." + [request params] + (let [header (yreq/get-header request "authorization")] + (if header + (do + (when (or (:client_secret params) (not (str/starts-with? header "Basic "))) + (clients/invalid! "invalid_client" 401)) + (let [decoded (try (String. (.decode (Base64/getDecoder) (subs header 6)) + StandardCharsets/UTF_8) + (catch IllegalArgumentException _ + (clients/invalid! "invalid_client" 401))) + [id secret] (str/split decoded #":" 2) + id (decode id)] + (when (or (nil? secret) + (and (:client_id params) (not= id (:client_id params)))) + (clients/invalid! "invalid_client" 401)) + {:id id :secret (decode secret) :method "client_secret_basic"})) + {:id (:client_id params) :secret (:client_secret params) + :method (if (:client_secret params) "client_secret_post" "none")}))) + +(defn- token + "Exchange a code for the fixed MCP token after client and PKCE verification." + [cfg request] + (limit! cfg request :token-rate 120) + (let [params (parse-form (read-body request "application/x-www-form-urlencoded")) + auth (client-authentication request params) + record (state/read! cfg :code (:code params)) + value (:value record)] + (when-not (= "authorization_code" (:grant_type params)) + (clients/invalid! "unsupported_grant_type")) + ;; Compare the exact client ID, not decoded metadata: a self-contained ID + ;; edited after approval identifies a different client for this exchange. + (when-not (and record + (= (:id auth) (:client-id value)) + (= (:redirect_uri params) (:redirect-uri value)) + (= (:resource value) (get params :resource (:resource value))) + (string? (:code_verifier params)) + (re-matches #"[A-Za-z0-9._~-]{43,128}" (:code_verifier params)) + (state/secret= (:challenge value) (state/digest (:code_verifier params)))) + (clients/invalid! "invalid_grant")) + ;; Resolve again because an HTTPS metadata document may have changed + ;; since approval; its current callback and authentication method apply. + (let [client (clients/resolve-client cfg (:id auth))] + (when-not (and (= (:method auth) (:token_endpoint_auth_method client)) + (clients/redirect-matches? client (:redirect-uri value)) + (or (= "none" (:method auth)) + (and (string? (:secret auth)) + (state/secret= (:secret-hash client) + (state/digest (:secret auth)))))) + (clients/invalid! "invalid_client" 401))) + ;; Validate before consuming so an invalid verifier/secret cannot burn the + ;; code. Atomic consumption allows only one valid exchange to succeed. + (when-not (state/consume! cfg :code (:code params) record) + (clients/invalid! "invalid_grant")) + ;; Regeneration deletes the old token row. Never substitute the profile's + ;; latest token for the one this approval authorized, nor return it expired. + (let [row (db/exec-one! (::db/pool cfg) + [(str "SELECT token FROM access_token" + " WHERE id = ? AND profile_id = ? AND type = 'mcp'" + " AND (expires_at IS NULL OR expires_at > now())") + (uuid/parse (:token-id value)) + (uuid/parse (:profile-id value))])] + (when-not row (clients/invalid! "invalid_grant")) + (response {:access_token (:token row) :token_type "Bearer" :scope "mcp"})))) + +(defn- register + "Issue a self-contained client registration; does not grant user access." + [cfg request] + (limit! cfg request :registration-rate 10) + (let [body (read-body request "application/json") + metadata (try (json/decode body) + (catch Exception _ (clients/invalid! "invalid_request")))] + (response 201 (clients/register! cfg metadata)))) + +(defn- wrap-errors + "Answer every failure with an OAuth error body. Unexpected failures also go + through the shared HTTP error handler, only for its logging; these routes + never set request params, so codes and credentials stay out of the log." + [handler] + (fn [request] + (try (handler request) + (catch Exception cause + (let [{:keys [oauth-error status]} (ex-data cause) + status (or status (if (and oauth-error (not= oauth-error "temporarily_unavailable")) 400 503))] + (when-not oauth-error + (errors/handle cause request)) + (cond-> (response status {:error (or oauth-error "temporarily_unavailable")}) + (and (= status 401) (= oauth-error "invalid_client")) + (assoc-in [::yres/headers "www-authenticate"] "Basic realm=\"Penpot MCP\""))))))) + +(defn- wrap-public + "CORS for discovery, registration and exchange without browser credentials. + Cookie-authenticated approval endpoints must not use this wrapper." + [handler] + (fn [request] + (let [response (if (= :options (yreq/method request)) + {::yres/status 204} + (handler request))] + (update response ::yres/headers merge + {"access-control-allow-origin" "*" + "access-control-allow-methods" "GET, POST, OPTIONS" + "access-control-allow-headers" "Content-Type, Authorization" + "access-control-expose-headers" "WWW-Authenticate"})))) + +(defn- endpoint + [cfg handler method public?] + {:handler (partial handler cfg) + :middleware (into (if public? [{:compile (constantly wrap-public)}] []) + [{:compile (constantly wrap-errors)} [mw/restrict-methods]]) + :allowed-methods (cond-> #{method} public? (conj :options))}) + +;; OAuth endpoints parse forms and emit snake_case JSON independently of RPC +;; middleware. Only the approval UI endpoints require a Penpot login session. +(defmethod ig/init-key ::routes + [_ cfg] + ["" + ["/.well-known/oauth-authorization-server/api/mcp/oauth" + (endpoint cfg (fn [_ _] (response (authorization-metadata))) :get true)] + ["/api/mcp/oauth" + ["/resource" (endpoint cfg (fn [_ _] (response (resource-metadata))) :get true)] + ["/authorize" (endpoint cfg authorize :get false)] + ["/register" (endpoint cfg register :post true)] + ["/token" (endpoint cfg token :post true)] + ;; Error handling wraps the session middleware too, so its failures also + ;; get OAuth error bodies and logging. + ["" {:middleware [{:compile (constantly wrap-errors)} + [mw/auth {:cookie (partial session/decode-token cfg)}] + [session/authz cfg]]} + ["/request" (endpoint cfg approval-request :get false)] + ["/decision" (endpoint cfg decision :post false)]]]]) diff --git a/backend/src/app/http/session.clj b/backend/src/app/http/session.clj index c5909ca432..4e9ce3bd77 100644 --- a/backend/src/app/http/session.clj +++ b/backend/src/app/http/session.clj @@ -18,6 +18,7 @@ [app.http :as-alias http] [app.http.auth :as-alias http.auth] [app.http.session.tasks :as-alias tasks] + [app.jobs :as jobs] [app.main :as-alias main] [app.setup :as-alias setup] [app.setup.clock :as clock] @@ -329,7 +330,7 @@ ;; TASK: SESSION GC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defmethod ig/assert-key ::tasks/gc +(defmethod ig/assert-key ::job-def [_ params] (assert (db/pool? (::db/pool params)) "expected valid database pool") (assert (ct/duration? (::tasks/max-age params))) @@ -341,7 +342,7 @@ (throw (IllegalArgumentException. "absolute session max-age must be greater than or equal to idle max-age")))) -(defmethod ig/expand-key ::tasks/gc +(defmethod ig/expand-key ::job-def [k v] (let [max-age (cf/get :auth-token-cookie-max-age default-cookie-max-age) max-age-absolute (cf/get :auth-token-cookie-max-age-absolute @@ -368,10 +369,25 @@ :deleted result) result)) -(defmethod ig/init-key ::tasks/gc - [_ {:keys [::tasks/max-age ::tasks/max-age-absolute] :as cfg}] - (l/dbg :hint "initializing session gc task" - :max-age max-age - :max-age-absolute max-age-absolute) - (fn [_] - (db/tx-run! cfg collect-expired-tasks))) +(declare execute-session-gc) + +(def schema:session-gc-params + "Params map (no params needed; config-derived only)." + [:map {:closed true}]) + +(defmethod ig/init-key ::job-def + [_ cfg] + {::jobs/name :session-gc + ::jobs/schema schema:session-gc-params + ::jobs/handler + (fn [_context params] + (execute-session-gc cfg params)) + ::jobs/decoder (sm/decoder schema:session-gc-params sm/json-transformer) + ::jobs/validator (sm/validator schema:session-gc-params)}) + +(defn execute-session-gc + "Plain job handler: delete expired http sessions." + ([cfg] (execute-session-gc cfg nil)) + ([cfg _params] + (jobs/heartbeat cfg) + (db/tx-run! cfg collect-expired-tasks))) diff --git a/backend/src/app/http/sse.clj b/backend/src/app/http/sse.clj index 0fc7c385bd..4c1420876e 100644 --- a/backend/src/app/http/sse.clj +++ b/backend/src/app/http/sse.clj @@ -60,14 +60,13 @@ (partial write! output) (partial pu/close! output))] (try - (binding [events/*channel* channel] - (let [result (handler)] - (events/tap :end result))) - - (catch Throwable cause - (let [result (errors/handle' cause request)] - (events/tap channel :error result))) - + (binding [events/*sink* channel] + (try + (let [result (handler)] + (events/tap :end result)) + (catch Throwable cause + (let [result (errors/handle' cause request)] + (events/tap :error result))))) (finally (sp/close! channel) (px/await! listener))))))})) diff --git a/backend/src/app/jobs.clj b/backend/src/app/jobs.clj new file mode 100644 index 0000000000..271414ebcc --- /dev/null +++ b/backend/src/app/jobs.clj @@ -0,0 +1,1439 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC Sucursal en España SL + +(ns app.jobs + "Public API for the unified jobs substrate. + + Every job is defined as a job-def map returned by the `ig/init-key` of its + module: {::name, ::schema, ::handler, ::decoder, ::validator} — decoder and + validator are precompiled at init time. A def whose result needs its types + back declares the optional ::result-schema: the substrate compiles the + decoder from that schema and checks a result against it before storing it, + while a def without one answers with the JSON the row holds. A job-def may + also carry the optional metadata {::family, ::resource-role}, which the + consumers of the job (quotes, storage helpers) read from the registry: the + substrate never reads them. The registry of job-defs is plain integrant + wiring (::jobs/defs); submit, dispatch and management consume it by + reference. + + Two execution modes are provided: + - `submit` (durable): validates + JSON-encodes params and inserts a row + into the `job` table; the dispatcher/runner machinery does the rest. + - `request` (ephemeral): synchronous request/response over the redis + queues with a reply-key and a dedicated, unbounded connection pool; + external workers answer with `reply`. + + Params payloads are stored as plain JSON (not transit) in the `params` jsonb + column and decoded back to typed Clojure values using the job-def decoder. + Results are plain JSON in the `result` jsonb column: they are decoded with the + decoder the substrate compiled from the schema the def declares, and a result + that does not conform to that schema is refused before it is stored. Without a + schema a uuid comes back as the text it is on disk and a uuid used as a map + key as a keyword, so a def that keys a result map by uuid must declare it. + + Any job that can run longer than `:jobs-lease` must call `heartbeat` + on its loop, otherwise the dispatcher marks it `aborted` (a system-side + terminal state, like `cancelled` but set by the dispatcher instead of + a user: never retried, kept for triage and reported with an error + log) while its side effects continue. A beat that finds the job no + longer active raises an `:interrupt`, an internal signal that the task + should stop the work in course, never an error. `heartbeat` also accepts + an optional `progress` report, which is stored as a `progress` row of + `job_event`: progress is durable history, not a mutable column, and + needs no Redis. + + Events of a job with a `profile_id` publish a `:job-event` message on the + topic of that profile after the transaction commits, so a client can + follow the job without polling the database. + + Reserved ledger columns (`profile_id`, `error`, `result`, `resource_id`, + `expires_at`) are only written by `submit` (profile, resource and expiry + references) and by the terminal writers; `submit` never infers them from + `params`." + (:require + [app.common.data :as d] + [app.common.exceptions :as ex] + [app.common.generic-pool :as gpool] + [app.common.json :as json] + [app.common.logging :as l] + [app.common.schema :as sm] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs.metrics :as metrics] + [app.metrics :as mtx] + [app.msgbus :as mbus] + [app.redis :as rds] + [app.worker :as wrk] + [cuerdas.core :as str] + [integrant.core :as ig]) + (:import + java.lang.AutoCloseable)) + +(set! *warn-on-reflection* true) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; HANDLER CONTEXT +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private schema:context + "What a handler is allowed to know about the job it is running. The + schema is closed on purpose: a handler must not be able to reach the + whole row, and adding a key here is a decision, not an accident. + + `queue` is deliberately absent: it is a routing detail of the + dispatcher, not a property of the job. `name` already says which job + this is, and a handler has no use for the queue it happened to be + scheduled on. + + The retry keys are absent too. Retry policy belongs to the runner: it + is the one comparing `retry-num` with `max-retries` and deciding + whether to schedule again. A handler has no use for the counter, and + the attempt number would lie anyway: the `noop` retry strategy runs + again without incrementing it, so a durable \"which execution is this\" + number would need a second counter that nothing bounds. + + `profile-id` is the owner of a user-facing job and nil for an internal + one. It travels here so a handler can revalidate permissions and check + who owns the resource it is working on without a second query." + [:map {:closed true + :title "job-context"} + [:id ::sm/uuid] + [:name ::sm/text] + [:label [:maybe ::sm/text]] + ;; technical reference kept for garbage collection + [:resource-id [:maybe ::sm/uuid]] + ;; owner of a user-facing job; nil for an internal job + [:profile-id [:maybe ::sm/uuid]]]) + +(def check-context + "Validate a handler context; raises with the malli explanation when it + does not match the closed schema." + (sm/check-fn schema:context)) + +(defn make-context + "Build the handler context from a job row: exactly the five keys a + handler may see, and nothing else. The result is a plain map, not the + database row, and it is not modified afterwards. + + The empty label is the `submit` default for \"no label\", so it becomes + nil here: a handler sees either a label or nothing." + [job] + (check-context {:id (:id job) + :name (:name job) + :label (if (str/blank? (:label job)) nil (:label job)) + :resource-id (:resource-id job) + :profile-id (:profile-id job)})) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; JOB DEFINITIONS (registry) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def schema:job-name + "Canonical job name: a keyword, and only a keyword. Callers pass + `:sendmail`, never \"sendmail\": `submit` and `invoke` reject anything + else. The database still stores a string (`d/name` at the submit + boundary), so `get-job-def` coerces back at that boundary." + :keyword) + +(def ^:private schema:job-def + [:map {:title "job-def"} + [::name schema:job-name] + ;; Optional metadata of the job family, read from the registry by the + ;; consumers that need it (quotes, storage helpers, a future job + ;; listing). `app.jobs` itself never reads them: they are not part of + ;; the execution contract. + [::family {:optional true} ::sm/keyword] + [::resource-role {:optional true} ::sm/keyword] + [::schema any?] + ;; every handler is [context params]. A job-def already closes over its + ;; own dependencies, so nothing else is handed to it: a handler cannot + ;; reach the runner cfg, the pool or any other component. + ;; + ;; A wrapper forwards the context to its implementation only when that + ;; implementation takes it. Most jobs have no use for it, so most + ;; wrappers are (fn [_context params] (execute-X cfg params)) and their + ;; implementation stays [cfg params]. + [::handler ::sm/fn] + [::decoder ::sm/fn] + [::validator ::sm/fn] + ;; A result crosses a plain JSON column on its way out of the job, so a + ;; uuid comes back as the text it is on disk and a uuid used as a map + ;; key comes back as a keyword. A def that declares the schema of its + ;; result gets its types back when the row is read (the substrate + ;; compiles the decoder from the schema), and its result is checked + ;; against that schema before it is stored. + [::result-schema {:optional true} any?]]) + +(def ^:private schema:job-defs + [:map-of :keyword schema:job-def]) + +;; The worker components name this registry as a bare `::defs` entry in +;; their integrant params schemas, and a bare entry is a registry lookup: +;; without this the backend crashes at boot with +;; `:malli.core/invalid-schema` instead of asserting the config. +(sm/register! ::defs schema:job-defs) + +;; Module-level registry of the job-defs, populated by the ::jobs/defs +;; ig component on init. It exists for the submit call-sites that run +;; inside components that cannot reference `::jobs/defs` by ig/ref +;; (the job-def components themselves are part of the registry; an ig +;; ref would create a wiring cycle). Call-sites that can provide the +;; registry via cfg take precedence over this global one. +(def ^:private defs-registry (atom {})) + +(defn get-defs + "The registry for submit/lookup: the one provided on the cfg has + precedence over the module-level one (used by tests)." + [cfg] + (or (get cfg ::defs) @defs-registry)) + +(def ^:private definitions-validator (sm/validator schema:job-defs)) + +(defmethod ig/assert-key ::defs + [_ defs] + (assert (definitions-validator defs) "expected valid job-defs map") + (doseq [[name job-def] defs] + (when-not (= (d/name name) (d/name (::name job-def))) + (ex/raise :type :assertion + :code :job-def-name-mismatch + :hint "job-def name mismatch" + :job (d/name name) + :expected (d/name (::name job-def)))))) + +(defn- compile-result-schema + "Compile the decoder of a job-def from the result schema it declares, so the + pair cannot drift from the schema and a def without one keeps working + untouched. The write path checks the result against the schema itself." + [job-def] + (if-let [schema (::result-schema job-def)] + (assoc job-def ::result-decoder (sm/decoder schema sm/json-transformer)) + job-def)) + +(defmethod ig/init-key ::defs + [_ defs] + (let [defs (reduce-kv (fn [acc name job-def] + (assoc acc name (compile-result-schema job-def))) + {} + defs)] + (reset! defs-registry defs) + (l/inf :hint "job definitions initialized" :jobs (count defs)) + defs)) + +(defmethod ig/halt-key! ::defs + [_ defs] + (reset! defs-registry {}) + (l/inf :hint "job definitions halted" :jobs (count defs))) + +(defn get-job-def + "Resolve the job-def for the provided job name; raises if missing. + + Accepts a string as well as a keyword, but only because the runner + resolves from the database row (whose `name` column is text): every + other caller passes the canonical keyword." + [defs name] + (or (get defs (keyword name)) + (ex/raise :type :not-found + :hint "no job definition found" + :code :no-job-definition + :name (d/name name)))) + +(defn decode-params + "Decode the raw JSON params (pgobject or decoded map) into typed params + using the precompiled decoder of the job-def." + [job-def params] + (-> (cond-> params + (db/pgobject? params) + db/decode-json-pgobject) + ((::decoder job-def)))) + +(defn decode-result + "Decode the raw JSON result (pgobject or decoded map) with the decoder the + substrate compiled from the result schema of the job-def. A def without one + answers with the JSON the row holds: every key keywordized and every uuid as + the text it is on disk." + [job-def result] + (when (some? result) + (let [value (cond-> result + (db/pgobject? result) + db/decode-json-pgobject)] + (if-let [decoder (::result-decoder job-def)] + (decoder value) + value)))) + +(defn- check-result + "Validate the result of a job against the schema its job-def declares, with + the malli explanation on failure. A job-def without a result schema has + nothing to check, and so has a job whose definition is gone." + [job-def result] + (when-let [schema (::result-schema job-def)] + (sm/check schema result + :hint "invalid result of a job" + :type :validation + :code :invalid-result)) + result) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; SUBMIT API +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private sql:insert-new-job + "insert into job (id, name, tenant, params, queue, label, priority, + max_retries, profile_id, resource_id, expires_at, + created_at, modified_at, scheduled_at) + values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + returning id") + +;; `tenant` is part of the match, not an extra: without it this DELETE +;; reaches the not-started jobs of every other instance sharing the +;; database. +(def ^:private sql:remove-not-started-jobs + "DELETE FROM job + WHERE name=? + AND tenant=? + AND queue=? + AND label=? + AND status = 'new' + AND scheduled_at > ?") + +;; The cron no-overlap check and the skip-if-active submit option share +;; this shape: both look for jobs of one tenant with the same name, queue +;; and label that have not reached a terminal state yet. The +;; `job__name_label__idx` partial index serves both queries. +(def ^:private sql:count-active-jobs + "SELECT count(*) AS n + FROM job + WHERE name = ? + AND tenant = ? + AND queue = ? + AND label = ? + AND status IN ('new', 'scheduled', 'running', 'retry')") + +(def ^:private schema:options + [:map {:title "submit-options"} + [::name schema:job-name] + [::label {:optional true} ::sm/text] + [::delay {:optional true} + [:or ::sm/int ::ct/duration]] + [::queue {:optional true} [:or ::sm/text :keyword]] + [::priority {:optional true} ::sm/int] + [::max-retries {:optional true} ::sm/int] + [::dedupe {:optional true} ::sm/boolean] + ;; Skip the insert when a live job already covers the same + ;; (tenant, name, queue, label). Requires an explicit label: the + ;; default empty one would block every flagged submission at once. + [::skip-if-active {:optional true} ::sm/boolean] + ;; Owner of a user-facing job. Its events are published on the msgbus + ;; topic of this profile; internal jobs omit it. + [::profile-id {:optional true} ::sm/uuid] + ;; Technical reference (storage object) kept for garbage collection. It + ;; is a column, never part of `params`, and it is never inferred from it. + [::resource-id {:optional true} ::sm/uuid] + ;; Retention of the job: when it is set, the jobs GC deletes the row + ;; (and reclaims its resource) once the instant has passed. User-facing + ;; jobs set it at creation; internal jobs leave it nil. + [::expires-at {:optional true} ::ct/inst]]) + +(def check-options + (sm/check-fn schema:options)) + +(defn validate-params + "Validate the params with the precompiled validator of the job-def; on + failure raises with the malli explanation. Shared by `submit` (raw + params) and the runner (decoded params). `invoke` deliberately stays + lenient: it is the in-process escape hatch used by tests (legacy task + params via `run-task!`) and the REPL." + [job-def params] + (when-not ^boolean ((::validator job-def) params) + (sm/check (::schema job-def) params)) + params) + +(defn submit + "Submit a durable job: validates the params with the job-def validator, + encodes them as plain JSON and inserts a row into the `job` table. + Fire-and-forget: returns the job id immediately. + + The optional ledger columns (`::profile-id`, `::resource-id` and + `::expires-at`) are validated and stored as columns; nothing is inferred + from `params`. + + With `::skip-if-active`, the insert is skipped (and `nil` returned) + when a live job (`new`, `scheduled`, `running` or `retry`) already + exists for the same tenant, name, queue and label. The check and the + insert share one transaction. A skip deletes nothing and inserts + nothing; it records a skipped metric instead of a submitted one, so + dashboards can tell withheld work apart from new work. The option + needs an explicit non-blank `::label`, otherwise it raises. + + NOTE: the dedupe DELETE and the INSERT run atomically: joined to + the caller's transaction when the cfg provides `::db/conn`, wrapped + in their own transaction otherwise. Concurrent cross-backend + submissions can, in rare race conditions, produce duplicated 'new' + rows (accepted risk, see prod-infra documentation). The same caveat + covers the skip-if-active check: a race between backends can slip + two inserts past the count." + [cfg {:keys [::params ::name ::delay ::queue ::priority ::max-retries + ::dedupe ::skip-if-active ::label + ::profile-id ::resource-id ::expires-at] + :or {delay 0 queue :default priority 100 max-retries 3 label ""} + :as options}] + + (assert (mtx/instance cfg) "missing ::mtx/metrics on the jobs cfg") + ;; Before the schema check so the failure names the option, not the + ;; shape: the schema would reject a blank label with a generic + ;; "check error" either way. + (when (and skip-if-active (or (not (string? label)) (str/blank? label))) + (ex/raise :type :validation + :code :skip-if-active-requires-label + :hint "skip-if-active requires an explicit non-blank label" + :name (d/name name))) + (check-options options) + (let [job-def (get-job-def (get-defs cfg) name) + params (validate-params job-def params) + delay (ct/duration delay) + now (ct/now) + scheduled-at (-> (ct/plus now delay) + (ct/truncate :millisecond)) + ;; The :rollback? testing escape hatch must never persist on a + ;; durable row (the runner would roll everything back yet mark + ;; the job completed); it stays available in-process via + ;; invoke/run-task!. Validation above is untouched. + ;; Duration values are normalized to millis: a Duration object + ;; does not survive JSON encoding (schemas still accept it for + ;; in-process callers). + payload (db/json (-> (dissoc params :rollback?) + (update-vals #(if (ct/duration? %) + (inst-ms %) + %)))) + id (uuid/next) + tenant (cf/get :tenant) + job-name (d/name name) + queue (d/name queue) + ;; Dedupe is best-effort: we delete not-started jobs of this + ;; tenant with the same name/queue/label, then insert. A race + ;; between backends could create duplicates, but this is + ;; acceptable: cross-backend races are rare, jobs are idempotent, + ;; and dedupe is best-effort. + ;; Skip-if-active is best-effort like dedupe: the count and the + ;; insert share this transaction, but a race between backends + ;; can still slip two inserts past the count. + active? (fn [conn] + (when skip-if-active + (pos? (or (:n (db/exec-one! conn [sql:count-active-jobs + job-name tenant queue label])) + 0)))) + insert! (fn [conn] + (let [deleted (when dedupe + ;; the DELETE below is intentional: + ;; disable the `job` deletion-protection + ;; guard inside this transaction + (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) + (-> (db/exec-one! conn [sql:remove-not-started-jobs + job-name tenant queue label now]) + (db/get-update-count)))] + (l/trc :hint "submit" + :name job-name + :job-id (str id) + :tenant tenant + :queue queue + :label label + :dedupe (boolean dedupe) + :delay (ct/format-duration delay) + :replace (or deleted 0)) + (db/exec-one! conn [sql:insert-new-job id job-name tenant payload queue + label priority max-retries + profile-id resource-id expires-at + now now scheduled-at])))] + ;; Both statements always run inside db/tx-run!: joined to the + ;; caller's transaction when the cfg provides a connection, + ;; wrapped in their own otherwise (a failed INSERT can never + ;; orphan a committed DELETE, even on an autocommit caller conn). + ;; A skip returns nil: no DELETE, no INSERT, no submitted metric. + (db/tx-run! cfg + (fn [{:keys [::db/conn]}] + (if (active? conn) + (do (db/after-commit + #(metrics/record-skipped cfg job-name queue)) + (l/trc :hint "submit skipped, active job exists" + :name job-name + :tenant tenant + :queue queue + :label label + :skipped true)) + (let [result (insert! conn)] + (db/after-commit + #(metrics/record-submitted cfg job-name queue)) + result + id)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; JOB API +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- decode-json-col + [row key] + (cond-> row + (db/pgobject? (get row key)) + (assoc key (db/decode-json-pgobject (get row key))))) + +(defn- decode-row + [row] + (-> row + (decode-json-col :params) + (decode-json-col :result) + (decode-json-col :error))) + +(defn- decode-result-of + "Decode the result of a row with the decoder of the job-def that owns it. + A kind the registry no longer knows (a retired job-def) answers with the + row untouched instead of raising." + [cfg row] + (if-let [job-def (get (get-defs cfg) (keyword (:name row)))] + (update row :result #(decode-result job-def %)) + row)) + +(defn get-job + "Retrieve the job row, with params and result decoded to plain maps (the + result only when its job-def declares a result decoder)." + [cfg job-id] + (when-let [row (db/get* cfg :job {:id job-id})] + (decode-result-of cfg (decode-row row)))) + +(defn get-user-status + "Map the internal job status to the user-facing status." + [status] + (let [status (d/name status)] + (case status + ("new" "scheduled" "retry") "pending" + "running" "running" + "completed" "completed" + ("failed" "cancelled" "aborted") "failed"))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; JOB EVENTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; `job_event` is an append-only log: it is the only durable place for +;; progress, and the place where the lifecycle of a job is recorded. There +;; is no Redis key and no mutable column to keep in sync. + +;; mirrors the `job_event.kind` CHECK constraint of migration 0154 +(def ^:private known-event-kinds + #{"start" "progress" "retry" "end"}) + +(def ^:private known-outcomes + ;; mirrors the `job_event` end outcomes. `aborted` is only ever written + ;; by the dispatcher orphan sweep (bulk insert in the same transaction, + ;; see `app.worker.dispatcher/mark-orphan-jobs`): no `app.jobs` writer + ;; emits it. + #{"completed" "failed" "cancelled" "aborted"}) + +(def ^:private known-retry-reasons + #{"backoff" "noop"}) + +(def ^:private max-progress-key-length + "Longest accepted `:stage` or counter key. They name a vocabulary, they + are not a place for data, so the cap only keeps a runaway key out of the + log." + 64) + +(def schema:progress-counter + "Counter of one scope of a job: `current` is how many units are done and + `total` how many there are, when the worker knows it." + [:map {:closed true + :title "job-progress-counter"} + [:current ::sm/int] + [:total {:optional true} ::sm/int]]) + +(def schema:progress + "Progress milestone of a job: the activity in course and the counters of + the scopes around it, each one under its own key. + + `:stage` is a stable keyword the client resolves (a translation, an + icon...), never a display string, and `:counters` is open on purpose: a + worker reports the scopes it knows and names the counter of the activity + after its own stage, so a client can render `counters[stage]` without a + table of equivalences. Neither is a place for names, params or exception + text: the payload is keywords and integers only." + [:map {:closed true + :title "job-progress"} + [:stage ::sm/keyword] + [:counters {:optional true} [:map-of ::sm/keyword schema:progress-counter]]]) + +(def check-progress + "Validate the shape of a progress milestone." + (sm/check-fn schema:progress)) + +(defn- check-counter + "Check the range rules of one counter: a non-negative `current` and a + positive `total` never lower than its `current`." + [key counter] + (when (neg? (:current counter)) + (ex/raise :type :validation + :code :invalid-progress + :hint "progress counter current must not be negative" + :counter key + :progress counter)) + (when-let [total (:total counter)] + (when (or (not (pos? total)) + (> (:current counter) total)) + (ex/raise :type :validation + :code :invalid-progress + :hint "progress counter total must be positive and not lower than current" + :counter key + :progress counter)))) + +(defn validate-progress + "Check what the schema cannot express: non-negative counters, a positive + total never lower than its `current`, and stage and counter keys short + enough to be a name and not a payload." + [progress] + (let [progress (check-progress progress)] + (doseq [key (cons (:stage progress) (keys (:counters progress)))] + (when (> (count (name key)) max-progress-key-length) + (ex/raise :type :validation + :code :invalid-progress + :hint (str "progress key must not be longer than " + max-progress-key-length " characters") + :key key + :progress progress))) + (doseq [[key counter] (:counters progress)] + (check-counter key counter)) + progress)) + +(defn decode-progress + "Read back a stored progress payload as a Clojure map: `:stage` and the + counter keys come back as the strings the database holds, so they are + keywordized here, and a payload that is already a map is returned as is." + [progress] + (when (some? progress) + (-> (cond-> progress + (db/pgobject? progress) + (db/decode-json-pgobject)) + (d/update-when :stage keyword) + (d/update-when :counters + (fn [counters] + (into {} (map (fn [[k v]] [(keyword k) v])) counters)))))) + +(def ^:private event-payload-keys + {"start" #{:attempt} + "progress" #{:stage :counters} + "retry" #{:attempt :reason} + "end" #{:outcome}}) + +(defn- validate-event-payload + "Reject anything the durable log must never store: unknown kinds, unknown + outcomes or retry reasons, extra keys, params or exception text." + [kind payload] + (let [reject (fn [hint details] + (ex/raise :type :validation + :code :invalid-job-event + :hint hint + :kind kind + :details details)) + reject-attempt #(reject "job event attempt must be a non negative integer" + payload) + allowed (get event-payload-keys kind)] + (when-not (contains? known-event-kinds kind) + (reject "unknown job event kind" kind)) + (when (seq (remove allowed (keys payload))) + (reject "job event payload has unexpected keys" (keys payload))) + (case kind + "start" (do (when-not (nat-int? (:attempt payload)) (reject-attempt)) + payload) + "retry" (do (when-not (nat-int? (:attempt payload)) (reject-attempt)) + (when-not (contains? known-retry-reasons (:reason payload)) + (reject "unknown job event retry reason" (:reason payload))) + payload) + "end" (do (when-not (contains? known-outcomes (:outcome payload)) + (reject "unknown job event outcome" (:outcome payload))) + payload) + "progress" (validate-progress payload)))) + +(def ^:private sql:insert-job-event + "INSERT INTO job_event (job_id, kind, payload) + VALUES (?, ?, ?) + RETURNING id, created_at") + +(def ^:private sql:job-event-owner + "SELECT profile_id FROM job WHERE id = ?") + +(defn- notify-event + "Publish a `:job-event` message on the topic of the job profile. Runs + after the commit that inserted the event: a listener that is not + connected never rolls back durable history. Takes the cfg and resolves + the msgbus from it, like `record-terminal` does with the metrics." + [{:keys [::mbus/msgbus]} + {:keys [profile-id job-id event-id kind payload created-at]}] + (mbus/pub! msgbus + :topic profile-id + :message {:type :job-event + :profile-id profile-id + :job-id job-id + :event-id event-id + :kind kind + :payload payload + :created-at created-at})) + +(defn- insert-event + "Insert a `job_event` row, then count it and notify after the commit. + + Must be called inside a transaction that owns the job row. When the job + has a `profile_id` the cfg must carry a msgbus: the check happens + before the insert, so a profile job never loses its notification + silently. Jobs without profile store the event and publish nothing. + + The counter also runs after the commit, so a rolled back transaction + never inflates it." + [{:keys [::mbus/msgbus] :as cfg} job-id kind payload] + (assert (db/connection-map? cfg) + "expected cfg with valid connection: insert-event must run inside a transaction") + (assert (some? db/*after-commit-context*) + "expected transaction context: insert-event must run inside a transaction") + (let [payload (validate-event-payload kind payload) + connectable (db/get-connectable cfg) + profile-id (:profile-id (db/exec-one! connectable [sql:job-event-owner job-id]))] + (when (and profile-id + (not (mbus/msgbus? msgbus))) + (ex/raise :type :assertion + :code :missing-msgbus + :hint "job events of a profile job require ::mbus/msgbus on the cfg" + :job-id job-id + :profile-id profile-id)) + (let [{:keys [id created-at]} + (db/exec-one! connectable [sql:insert-job-event job-id kind (db/json payload)])] + (db/after-commit #(metrics/record-event cfg)) + (when profile-id + (db/after-commit + #(notify-event cfg {:profile-id profile-id + :job-id job-id + :event-id id + :kind kind + :payload payload + :created-at created-at}))) + {:event-id id :created-at created-at :kind kind :payload payload}))) + +(defn- own-transaction + "A cfg for a job write that must own its transaction. + + A lifecycle write reports on work that already happened elsewhere, so + it never joins the transaction of whoever called it: the `::db/conn` is + dropped and `db/tx-run!` opens its own transaction on its own + connection. Because that transaction joins no other, it owns its + after-commit context too, so the write and its metric commit or vanish + together and a rollback of the surrounding work can undo neither. + + `submit` is the deliberate exception: creating a job is part of the + business unit of work of whoever asks for it." + [cfg] + (dissoc cfg ::db/conn)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; HEARTBEAT / PROGRESS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private heartbeat-interval + ;; one second, down from a minute: a heavy job that was cancelled must + ;; be seen as gone at its next beat. The price is one `modified_at` + ;; touch per beating job per second (60x the old rate): cheap while the + ;; only heavy beater runs alone on its queue, revisit if concurrent + ;; beating jobs ever grow. + (ct/duration {:seconds 1})) +(def ^:private progress-interval (ct/duration {:millis 200})) + +(def ^:private prune-threshold 10000) +(def ^:private prune-window (ct/duration {:hours 1})) + +;; Throttle state atoms (public for testing) +(def heartbeats (atom {})) +(def progresses (atom {})) + +(def ^:dynamic *job-id* + "Job id of the job being executed on the current thread. The runner + binds it around handler invocations; handlers call `heartbeat` without + knowing the id. Nil means no job context (in-process `invoke` without a + row), in which case the throttled writes become no-ops." + nil) + +(defn- should-write? + "Throttle gate: true when the last recorded write for `job-id` is older + than `interval` (or when there is none). Maintains a bounded in-memory + registry of the last write time per job. Uses a volatile inside swap! to + communicate the decision — the volatile is dereferenced after the swap + completes, which is safe." + [state-ref job-id now interval] + (let [decision (volatile! false)] + (swap! state-ref + (fn [m] + (let [m (if (> (count m) prune-threshold) + ;; Remove stale entries (older than prune-window) + (into {} + (remove (fn [[_ last-inst]] + (> (- (inst-ms now) (inst-ms last-inst)) + (inst-ms prune-window)))) + m) + m) + last-inst (get m job-id)] + (if (and last-inst + (< (- (inst-ms now) (inst-ms last-inst)) + (inst-ms interval))) + m + (do + (vreset! decision true) + (assoc m job-id now)))))) + @decision)) + +(defn cleanup-throttle + "Remove `job-id` from the heartbeat and progress throttle atoms. + Called by the runner after a job completes (success or failure) to + prevent completed job IDs from accumulating in memory. The prune + fallback in `should-write?` remains as defense-in-depth." + [job-id] + (swap! heartbeats dissoc job-id) + (swap! progresses dissoc job-id)) + +(def ^:private active-statuses + "The statuses of a job that is still on its way. It mirrors the filter + of the heartbeat and progress writes below: a row outside this set is no + longer active and nothing can report to it." + #{"new" "scheduled" "running" "retry"}) + +(def ^:private sql:touch-heartbeat + "UPDATE job + SET modified_at = ? + WHERE id = ? + AND status IN ('new', 'scheduled', 'running', 'retry')") + +(def ^:private sql:lock-active-job + "SELECT id FROM job + WHERE id = ? + AND status IN ('new', 'scheduled', 'running', 'retry') + FOR UPDATE") + +(defn- touch-job + [connectable job-id now] + (-> (db/exec-one! connectable [sql:touch-heartbeat now job-id]) + (db/get-update-count))) + +(defn- report-progress + "Insert a progress event on an active job. + + The row is locked first: a concurrent complete, fail or cancel blocks + here, so a progress event can never land on an already terminal job. + Returns 1 when the event was stored. + + Like every lifecycle write it owns its transaction (see `own-transaction`), + so a beat survives the rollback of the work around it." + [cfg job-id progress] + (db/tx-run! (own-transaction cfg) + (fn [{:keys [::db/conn] :as tx-cfg}] + (if (db/exec-one! conn [sql:lock-active-job job-id]) + (do + (insert-event tx-cfg job-id "progress" progress) + 1) + 0)))) + +(defn check-active + "Raise an `:interrupt` when the job is no longer active. + + A job is active while its status is one of `active-statuses`; a row + that already reached a terminal state, or that no longer exists, is + not. The status is read from the row, because a write can only tell + that it matched no row, not why. + + The interrupt is an internal signal, not an error: the caller is a task + that must stop the work in course. `heartbeat` calls this when one of + its writes finds the job gone, and a caller can also ask explicitly, + which is what the binfile adapter does before and after its run. + Answers with the cfg, so a caller can thread the check into its run." + [cfg job-id] + (let [job (get-job cfg job-id)] + (when-not (and (some? job) (contains? active-statuses (:status job))) + (ex/raise :type :interrupt + :code :job-interrupted + :hint "the job is no longer active" + :job-id job-id + :status (or (:status job) "gone"))) + cfg)) + +(defn heartbeat + "Keep a running job alive and, optionally, report its progress. + + Named options: + + - `:job-id` explicit job id; defaults to `::jobs/job-id` on the cfg + and then to the runner-bound `*job-id*`. + - `:progress` optional progress report (see `schema:progress`). + - `::force?` internal: skips only the progress throttle. + + Returns the number of durable writes performed, so 0 means there was + nothing to write: no job context, or the throttle did not allow it. A + write that finds the job no longer active raises an `:interrupt` (see + `check-active`) instead of counting zero, so a task in course stops at + its next beat. + + Both writes go through the connection pool (`::db/pool` on the cfg), + never the caller transaction, so a beat survives a rollback of the + surrounding work: the `modified_at` touch is throttled to ~1s while + progress events go out at up to ~200ms, so the user sees movement + without hammering the row. External workers report progress as sparse + milestones, while handlers may beat on every iteration. + + Like every other function here the cfg is a cfg: a bare pool or + connection is a caller bug and is not accommodated. + + A progress report never touches a terminal row (beating one would + silently extend its retention window). The payload itself is validated + before any write, on every route: a malformed report is a caller bug, + not a transient failure, so it always raises. Only the insert itself is + forgiving on the handler path, where it is logged and ignored; the + forced path used by the management API propagates it so the external + worker can retry." + [cfg & {:keys [job-id progress] :as options}] + (let [job-id (or job-id (get cfg ::job-id) *job-id*) + ;; a malformed report is a caller bug: never swallow it + progress (some-> progress validate-progress)] + (when (uuid? job-id) + (let [connectable (::db/pool cfg) + now (ct/now) + force? (boolean (::force? options)) + writes (volatile! 0)] + (when (should-write? heartbeats job-id now heartbeat-interval) + (let [n (touch-job connectable job-id now)] + ;; the touch is conditional on the row still being active: a + ;; write that matched no row is what raises the interrupt + (when (zero? n) + (check-active cfg job-id)) + (vswap! writes + n))) + (when (and (some? progress) + (or force? + (should-write? progresses job-id now progress-interval))) + (let [store #(report-progress cfg job-id progress) + n (if force? + (store) + (try + (store) + (catch Throwable cause + (l/err :hint "unable to persist job progress" + :tenant (cf/get :tenant) + :job-id (str job-id) + :cause cause) + 0)))] + ;; the raise stays outside the catch above: an interrupt is + ;; not a transient failure and must not be swallowed with it + (when (zero? n) + (check-active cfg job-id)) + (vswap! writes + n))) + (int @writes))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; JOB WRITERS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The only path that changes the status of a job. Every writer changes +;; the row and stores its event in the same transaction, so the durable +;; history can never contradict the row: if the event cannot be stored, +;; the transition is rolled back. A writer that affects no row writes no +;; event and no metric, which is what makes concurrent transitions safe: +;; the first one wins and the losers see zero. +;; +;; Every metric is registered with `db/after-commit` from inside the +;; transaction, next to the event, so it lands on the outermost commit +;; whatever that is: a rolled back transition is never counted. Each +;; writer owns that transaction (see `own-transaction`), so the commit +;; in question is always its own. +;; +;; That callback swallows exceptions, which is why every writer below +;; opens with `(assert (mtx/instance cfg) ...)`. It reads like a +;; redundant assertion because the instance is not used here, but it is +;; the only thing that makes a miswired cfg fail loudly: resolve it +;; inside the callback instead and the row is persisted while the count +;; disappears into a log line. + +(defn encode-result + "Serialize a job result to JSON, dropping unserializable values to nil + with a warning instead of throwing (a throw here would leave the row + stuck in `running` until the orphan lease fires)." + [job-name result] + (try + (db/json result) + (catch Throwable cause + (l/err :hint "unable to serialize job result to JSON" + :job-name (some-> job-name str) + :cause cause) + nil))) + +(defn- record-terminal + "Record the total time of a terminal outcome. Runs after the commit + that made the job terminal, so a rolled back transition is never + counted. The histogram `_count` is the terminal total per outcome: + no separate counter. Takes the cfg and resolves the metrics + instance from it, so the callback is not tied to an instance read + at registration time." + [cfg job outcome] + (when job + (when (ct/inst? (:created-at job)) + (metrics/record-total + cfg + (:name job) + (:queue job) + outcome + (- (inst-ms (ct/now)) (inst-ms (:created-at job))))))) + +(def schema:job-error + "Structured error of a job that did not complete. `type` and `code` are + keywords so they decode back from the stored JSON, `hint` is the human + message. The map stays open: these three keys are the contract, and + anything else is a detail the failing component adds for triage." + [:map {:title "job-error"} + [:type ::sm/keyword] + [:code ::sm/keyword] + [:hint ::sm/text]]) + +(def check-job-error + "Validate a job error against its schema; raises with the malli + explanation when it does not match." + (sm/check-fn schema:job-error)) + +(def orphan-error + "Error of a job whose execution process died or stopped reporting. + Nothing is alive to report it, so the dispatcher stores this and marks + the job `aborted` (system-side terminal, never retried) with its `end` + event in the same transaction: the durable state of the job is the + whole story." + (check-job-error {:type :internal + :code :orphan + :hint "job execution lease expired"})) + +(def ^:private decode-job-error-value + (sm/decoder schema:job-error sm/json-transformer)) + +(defn decode-job-error + "Read back a stored `job.error` as a Clojure map, applying the + transformation of `schema:job-error` so `type` and `code` come back as + keywords instead of the strings the database holds." + [error] + (when (some? error) + (decode-job-error-value (cond-> error + (db/pgobject? error) + (db/decode-json-pgobject))))) + +;; --- CLAIM + +(def ^:private sql:claim-job + "UPDATE job + SET status='running', started_at=now(), modified_at=now() + WHERE id=? + AND scheduled_at=? + AND status IN ('new','scheduled','retry') + RETURNING retry_num") + +(defn claim + "Claim a job on behalf of a worker, internal or external, and record the + `start` event. + + Only transitions a pending row (new/scheduled/retry) to `running`, and + requires an exact `scheduled-at` match with the value advertised in the + queue payload, so a stale payload (row rescheduled or claimed in the + meantime) affects 0 rows and must be skipped. Returns the number of + affected rows. + + The event carries `attempt` = `retry_num + 1`: the number of the + attempt this run consumes from the retry budget." + [cfg job-id scheduled-at] + (assert (mtx/instance cfg) "missing ::mtx/metrics on the jobs cfg") + (db/tx-run! (own-transaction cfg) + (fn [{:keys [::db/conn] :as tx-cfg}] + (if-let [{:keys [retry-num]} + (db/exec-one! conn [sql:claim-job job-id scheduled-at])] + (do + (insert-event tx-cfg + job-id "start" {:attempt (inc (long retry-num))}) + 1) + 0)))) + +;; --- RETRY + +(def ^:private sql:retry-job + "UPDATE job + SET status='retry', modified_at=?, scheduled_at=?, retry_num=?, error=? + WHERE id=? + AND status IN ('running','retry') + RETURNING retry_num") + +(defn retry-job + "Schedule the next attempt of a running job and record the `retry` event. + + `next-retry-num` is the counter after the transition and `reason` says + how we got there, one of `:backoff` or `:noop`. A backoff increments the + counter, a noop keeps it, so the event always reports the attempt that + counts against `max-retries`: a noop retry re-reports the same number, + because it consumed no budget. Returns the number of affected rows; a + terminal job is left untouched and writes no event." + [cfg job-id next-retry-num scheduled-at error reason] + (assert (mtx/instance cfg) "missing ::mtx/metrics on the jobs cfg") + (let [job (get-job cfg job-id) + encoded (db/json (check-job-error error))] + (db/tx-run! (own-transaction cfg) + (fn [{:keys [::db/conn] :as tx-cfg}] + (if-let [{:keys [retry-num]} + (db/exec-one! conn [sql:retry-job (ct/now) scheduled-at + next-retry-num encoded job-id])] + (do + (insert-event tx-cfg + job-id "retry" {:attempt (inc (long retry-num)) + :reason (name reason)}) + (db/after-commit + #(metrics/record-retry tx-cfg + (:name job) + (:queue job) + reason)) + 1) + 0))))) + +;; --- COMPLETE + +(def ^:private sql:complete-job + "UPDATE job + SET status='completed', completed_at=?, modified_at=?, result=?, + error=NULL, resource_id=coalesce(resource_id, ?) + WHERE id=? + AND status IN ('running','retry') + RETURNING id") + +(def ^:private sql:lock-job + "SELECT * FROM job + WHERE id = ? + FOR UPDATE") + +(defn- check-resource-id + "Validate a `resource-id` on its own, before any database work." + [resource-id] + (when (some? resource-id) + (when-not (uuid? resource-id) + (ex/raise :type :validation + :code :invalid-resource-id + :hint "resource-id must be a uuid" + :resource-id resource-id)))) + +(defn complete + "Mark a running job as completed and record the `end` event. + + Named options: + + - `:job-id` the job to complete. + - `:result` the JSON-encodable result, nil when the job returns + nothing; a result that does not conform to the schema + its job-def declares is refused. + - `:resource-id` optional storage object to associate with the job. A + resource is only ever set, never replaced: passing one + for a job that already has a resource is a validation + error, and a nil value leaves the column untouched. + + The result, the resource, the status and the event share one + transaction, so a job never ends up completed with a resource that was + not stored or an event that is missing. + + Conditional on the non-terminal running/retry states (first-terminal + wins: a row already marked failed/cancelled/aborted — e.g. an orphan + aborted by the dispatcher — is never overwritten) and writes no event. Returns + the number of affected rows. + + The row is read locked inside that same transaction, so the rules are + applied in a fixed order against the row and not against the clock: a + job that is no longer completable is skipped, only a job that is still + completable can be refused for its resource, and the result is checked + against the schema its job-def declares before it is stored (a def + without one has nothing to check). A concurrent completion waits for + the lock and then sees the outcome." + [cfg & {:keys [job-id result resource-id]}] + (assert (mtx/instance cfg) "missing ::mtx/metrics on the jobs cfg") + (check-resource-id resource-id) + (let [n (db/tx-run! (own-transaction cfg) + (fn [{:keys [::db/conn] :as tx-cfg}] + (let [job (some-> (db/exec-one! conn [sql:lock-job job-id]) + decode-row) + encoded (when (some? result) + (encode-result (:name job) result))] + (if-not (contains? #{"running" "retry"} (:status job)) + 0 + (do + (when (and (some? resource-id) + (:resource-id job)) + (ex/raise :type :validation + :code :resource-already-associated + :hint "the job already has a resource, it cannot be replaced" + :job-id job-id + :resource-id (:resource-id job))) + (check-result (get (get-defs tx-cfg) + (keyword (:name job))) + result) + (if (db/exec-one! conn [sql:complete-job (ct/now) (ct/now) + encoded resource-id job-id]) + (do + (insert-event tx-cfg + job-id "end" {:outcome "completed"}) + (db/after-commit + #(record-terminal tx-cfg job :completed)) + 1) + 0))))))] + (cleanup-throttle job-id) + n)) + +;; --- FAIL + +(def ^:private sql:fail-job + "UPDATE job + SET status='failed', modified_at=?, error=? + WHERE id=? + AND status IN ('running','retry') + RETURNING id") + +(defn fail + "Mark a running job as failed and record the `end` event. + + The error must conform to `schema:job-error`; it is validated here, not + only at the RPC boundary, so every path writes the same shape. The + error never reaches the event: `job.error` is where the whole thing + lives, and the event only says the job failed. + + Conditional on the non-terminal running/retry states (first-terminal + wins) and writes no event when it affects no row. Returns the number of + affected rows." + [cfg job-id error] + (assert (mtx/instance cfg) "missing ::mtx/metrics on the jobs cfg") + (let [job (get-job cfg job-id) + encoded (db/json (check-job-error error)) + n (db/tx-run! (own-transaction cfg) + (fn [{:keys [::db/conn] :as tx-cfg}] + (if (db/exec-one! conn [sql:fail-job (ct/now) encoded job-id]) + (do + (insert-event tx-cfg + job-id "end" {:outcome "failed"}) + (db/after-commit + #(record-terminal tx-cfg job :failed)) + 1) + 0)))] + (cleanup-throttle job-id) + n)) + +;; --- CANCEL + +(def ^:private sql:cancel-job + "UPDATE job + SET status='cancelled', modified_at=? + WHERE id=? + AND status IN ('new','scheduled','retry','running') + RETURNING id") + +(defn cancel + "Cancel a pending or running job (new/scheduled/retry/running) and + record the `end` event. + + A running job stops at its next heartbeat: the beat finds no active + row and raises an `:interrupt`, which the runner treats as a normal + stop while the handler rolls back. A terminal job is left untouched + and writes no event. Returns the number of affected rows." + [cfg job-id] + (assert (mtx/instance cfg) "missing ::mtx/metrics on the jobs cfg") + (let [job (get-job cfg job-id) + n (db/tx-run! (own-transaction cfg) + (fn [{:keys [::db/conn] :as tx-cfg}] + (if (db/exec-one! conn [sql:cancel-job (ct/now) job-id]) + (do + (insert-event tx-cfg + job-id "end" {:outcome "cancelled"}) + (db/after-commit + #(record-terminal tx-cfg job :cancelled)) + 1) + 0)))] + (when (pos? n) + (cleanup-throttle job-id)) + n)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; REQUEST (ephemeral request/response, no row, no dispatcher) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NOTE (2026-09-17): this path intentionally has no producer yet; it +;; is kept for an upcoming phase. Do not re-raise as dead code until +;; that phase wires a consumer. + +(def ^:private request-command-timeout-margin (ct/duration {:seconds 30})) +(def ^:private reply-expire-seconds 60) + +(def reply-key-prefix "penpot.worker.reply") + +(defn get-request-context + [cfg] + (or (::request-pool cfg) + (ex/raise :type :assertion + :code :missing-request-pool + :hint "missing ::jobs/request-pool on provided cfg"))) + +(defmethod ig/expand-key ::request-pool + [k v] + {k (-> (d/without-nils v) + (assoc ::command-timeout + (ct/plus (cf/get-jobs-request-timeout) + request-command-timeout-margin)))}) + +(def ^:private schema:request-pool + [:map + [::command-timeout ::ct/duration] + ::rds/client + ::mtx/metrics]) + +(defmethod ig/assert-key ::request-pool + [_ cfg] + (sm/check schema:request-pool cfg)) + +(defmethod ig/init-key ::request-pool + [_ {::rds/keys [client] ::mtx/keys [metrics] ::keys [command-timeout]}] + ;; pool without a max size: gpool/get creates a connection when no + ;; idle one is available and never blocks; the in-flight concurrency + ;; is bounded upstream by the RPC concurrency limits. Connections are + ;; created with a command timeout above the per-call request timeout; + ;; the dispose-fn restores it on return to the pool. + {::pool (rds/pool {::rds/client client + ::mtx/metrics metrics} + {:timeout command-timeout}) + ::mtx/metrics metrics}) + +(def ^:private schema:request-options + [:map {:title "request-options"} + [::queue [:or ::sm/text :keyword]] + [::cmd [:or ::sm/text :keyword]] + [::params any?] + [::timeout {:optional true} [:or ::sm/int ::ct/duration]]]) + +(def check-request-options + (sm/check-fn schema:request-options)) + +(defn reply + "Respond to an ephemeral request: push the JSON response to the + reply-key and set a short TTL as a safety net for late replies (a + reply pushed after the caller timeout would otherwise live forever). + Response shape: `{:ok ...}` or `{:error {...}}`. Accepts a connectable + cfg (a redis pool under ::rds/pool)." + [cfg reply-key response] + (rds/run! cfg + (fn [{:keys [::rds/conn]}] + (rds/rpush conn reply-key [(json/encode response)]) + (rds/expire conn reply-key reply-expire-seconds)))) + +(defn request + "Ephemeral request/response (no job row, no dispatcher): pushes a JSON + payload [request-id, reply-key, cmd, params] to the target queue and + blocks on the reply-key with a per-call timeout (defaults to + :jobs-request-timeout). Any per-call override is applied by raising + the pooled connection command timeout for the duration of the call, + which the pool dispose-fn restores on return. + + On success returns the decoded `:ok` payload; an `:error` reply + propagates as an exception; on timeout raises `:request-timeout` and + the reply-key is deleted (in finally, also on error). The connection + is always returned to the pool." + [cfg + {:keys [::queue ::cmd ::params ::timeout] :as options}] + + (check-request-options options) + + (let [context (get-request-context cfg) + pool (::pool context) + tenant (cf/get :tenant) + timeout (or timeout (cf/get-jobs-request-timeout)) + request-id (uuid/next) + reply-key (str/ffmt "%:%:%" reply-key-prefix tenant request-id) + queue-key (wrk/queue-key tenant queue) + payload (json/encode [(str request-id) + reply-key + (d/name cmd) + params])] + + (with-open [^AutoCloseable pooled (gpool/get pool)] + (let [conn @pooled + tpoint (ct/tpoint) + outcome (volatile! :error)] + (try + ;; raise the connection command timeout above the per-call + ;; blpop timeout; the pool dispose-fn restores the default on + ;; return. + (rds/set-timeout conn (ct/plus timeout request-command-timeout-margin)) + + (rds/rpush conn queue-key [payload]) + + (let [[_ reply] (rds/blpop conn [reply-key] timeout)] + (if (nil? reply) + (do + (vreset! outcome :timeout) + (ex/raise :type :timeout + :code :request-timeout + :hint "timeout waiting for the job reply" + :tenant tenant + :queue queue + :timeout timeout)) + (let [response (json/decode reply :key-fn keyword)] + (if-let [error (:error response)] + (do + (vreset! outcome :error) + (ex/raise :type :internal + :code (get error :code) + :hint (or (get error :hint) "request failed") + :tenant tenant + :queue queue + :response response)) + (do + (vreset! outcome :replied) + (get response :ok)))))) + + (finally + ;; the context, not `cfg`: the caller's cfg only carries the + ;; request pool, and the metrics instance comes with it + (metrics/record-request context + @outcome + (inst-ms (tpoint))) + (l/trc :hint "request" + :tenant tenant + :queue (d/name queue) + :cmd (d/name cmd) + :outcome @outcome + :elapsed (inst-ms (tpoint))) + (rds/del conn reply-key) + (rds/reset-timeout conn))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IN-PROCESS INVOCATION +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn invoke + "Execute a job handler in-process (no row, no dispatch): decodes the + params with the job-def decoder and invokes the handler bound to the + `*job-id*` dynamic (or the provided ::job-id, which makes the + throttled heartbeat/progress writes work against the row). Options: + + {::name :delete-object + ::params {...} ;; raw (JSON-shaped) params + ::defs {...} ;; the ::jobs/defs registry + ::context {...}} ;; optional, validated and delivered as is + ::job-id } ;; optional, only when the row already exists + + Without ::context the handler receives a nil context: there is no row + to describe. ::context and ::job-id are independent, and providing a + context does not turn this into a durable execution: only the job-id + makes heartbeat and progress reach a row. + + Returns the handler result." + [cfg] + (let [name (get cfg ::name)] + (when-not (keyword? name) + (ex/raise :type :validation + :code :invalid-job-name + :hint "job name must be a keyword" + :name name)) + (let [job-def (get-job-def (get-defs cfg) name) + context (some-> (get cfg ::context) check-context) + decoded (decode-params job-def (get cfg ::params))] + (binding [*job-id* (get cfg ::job-id)] + ((::handler job-def) context decoded))))) diff --git a/backend/src/app/jobs/gc.clj b/backend/src/app/jobs/gc.clj new file mode 100644 index 0000000000..04a89a6400 --- /dev/null +++ b/backend/src/app/jobs/gc.clj @@ -0,0 +1,164 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.jobs.gc + "A maintenance cron task for the unified `job` table: + + - expiration: deletes rows with `expires_at` in the past (any status, + user or internal). Expiration is the mechanism for user-facing jobs + and for jobs carrying volatile resources. + - retention: deletes internal terminal rows (no profile, status in + completed/failed/cancelled/aborted) older than the `:jobs-retention` delay. + The dormant legacy `task` table is cleaned by the parallel legacy + version until the next release, which is what allows PRE and HOURLY to + run side by side on the same database and the deploy to roll back. + User-facing terminal rows (profile_id NOT NULL) are + NOT swept by retention: they are governed by `expires_at` (or stay + as ledger history). + + Rows carrying a `resource_id` touch their storage object (touched_at + set in the same transaction, mirroring `sto/touch-object!`) so + `storage-gc-touched` reclaims it: deleting the job row does not touch + the `storage_object` (ON DELETE SET NULL only fires when the object + row is deleted, not the job row), and gc-touched only analyzes objects + already marked with `touched_at` — without the explicit touch the + object would be orphaned. + + Each batch deletes and touches in its own transaction: a crash + leaves the remaining batches for the next tick, and an object can + never lose its referencing row without being marked for reclaim. + + `job` carries a deletion-protection trigger, so each batch disables + it first (`SET LOCAL rules.deletion_protection TO off`): the GC is + the intended deleter, and the guard only exists so a stray + `DELETE FROM job` cannot drop a row without touching its object." + (:require + [app.common.logging :as l] + [app.common.schema :as sm] + [app.common.time :as ct] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.jobs.metrics :as metrics] + [integrant.core :as ig])) + +(def ^:private sql:touch-objects + "UPDATE storage_object + SET touched_at = ? + WHERE id = ANY(?::uuid[])") + +(def ^:private gc-batch-size 1000) + +(def ^:private sql:delete-expired-jobs + "DELETE FROM job + WHERE id IN (SELECT id + FROM job + WHERE expires_at < now() + AND status NOT IN ('running', 'retry') + LIMIT ?) + RETURNING resource_id") + +(def ^:private sql:delete-retained-jobs + "DELETE FROM job + WHERE id IN (SELECT id + FROM job + WHERE status IN ('completed', 'failed', 'cancelled', 'aborted') + AND profile_id IS NULL + AND modified_at < now() - ?::interval + LIMIT ?) + RETURNING resource_id") + +(defn- touch-resources + [conn resource-ids] + (if (seq resource-ids) + (-> (db/exec-one! conn [sql:touch-objects (ct/now) + (db/create-array conn "uuid" resource-ids)]) + (db/get-update-count)) + 0)) + +(defn- delete-jobs + [cfg kind sql & params] + (loop [deleted 0 + touched 0] + (let [[rows touched-now] + (db/tx-run! cfg + (fn [{:keys [::db/conn]}] + (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) + (let [rows (db/exec! conn (conj (into [sql] params) + gc-batch-size)) + resource-ids (into [] (keep :resource-id) rows) + touched (touch-resources conn resource-ids)] + (db/after-commit + #(do + (metrics/record-gc-rows + cfg kind :deleted (count rows)) + (metrics/record-gc-rows + cfg kind :touched touched))) + [rows touched]))) + deleted' (+ deleted (count rows)) + touched' (+ touched touched-now)] + (if (< (count rows) gc-batch-size) + [deleted' touched'] + (do (jobs/heartbeat cfg) + (recur deleted' touched')))))) + +(declare execute-jobs-gc) + +(def schema:jobs-gc-params + "min-age: duration object, integer millis or duration string + in-process; integer millis or duration string over the job pipeline + (a duration object does not survive JSON encoding). The handler + always normalizes with ct/duration." + [:map + [:min-age {:optional true} [:or :int :string ::ct/duration]]]) + +(defmethod ig/init-key ::job-def + [_ cfg] + {::jobs/name :jobs-gc + ::jobs/schema schema:jobs-gc-params + ::jobs/handler (fn [_context params] + (execute-jobs-gc cfg params)) + ::jobs/decoder (sm/decoder schema:jobs-gc-params sm/json-transformer) + ::jobs/validator (sm/validator schema:jobs-gc-params)}) + +(defn execute-jobs-gc + "Plain job handler: delete expired rows (expires_at) and retained + internal terminal rows, marking the storage resources of the deleted + rows as touched. + + Deletes run in bounded batches of 1000 rows, each batch in its own + transaction, with a heartbeat between batches so long sweeps neither + spike the WAL nor outrun the job lease." + [cfg params] + (let [min-age + (ct/duration (or (:min-age params) + (cf/get-jobs-retention))) + + expired-tpoint + (ct/tpoint) + + [deleted-expired touched-expired] + (delete-jobs cfg :expired sql:delete-expired-jobs) + + retained-tpoint + (ct/tpoint) + + [deleted-retained touched-retained] + (delete-jobs cfg :retained sql:delete-retained-jobs + (db/interval min-age))] + + (metrics/record-gc-duration cfg :expired (inst-ms (expired-tpoint))) + (metrics/record-gc-duration cfg :retained (inst-ms (retained-tpoint))) + (l/dbg :hint "finished" + :deleted-expired deleted-expired + :touched-expired touched-expired + :deleted-retained deleted-retained + :touched-retained touched-retained) + {:deleted-expired deleted-expired + :touched-expired touched-expired + :deleted-retained deleted-retained + :touched-retained touched-retained})) + diff --git a/backend/src/app/jobs/metrics.clj b/backend/src/app/jobs/metrics.clj new file mode 100644 index 0000000000..ab1941b1b7 --- /dev/null +++ b/backend/src/app/jobs/metrics.clj @@ -0,0 +1,288 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.jobs.metrics + "Metric names, labels and the jobs backlog sampler. + + Labels are bounded by construction, not by allowlist: every value + recorded here comes from a fixed call site (a job name from the + registry, a queue from submit, an outcome from the lifecycle + writers), never from params, IDs or exception text. A new value + showing up in Prometheus means new code, which is exactly what you + want to see: folding it into some other bucket would only hide it. + + This namespace is intentionally free of job handlers. Callers pass only + values already known at their boundary; IDs, props and exception text never + become metric labels." + (:require + [app.common.data :as d] + [app.common.logging :as l] + [app.common.schema :as sm] + [app.db :as db] + [app.metrics :as mtx] + [cuerdas.core :as str] + [integrant.core :as ig] + [promesa.exec :as px])) + +(set! *warn-on-reflection* true) + +(defn queue-label + "Coerce a queue to its label: lowercase, with \"other\" only when there + is no queue at all. A new queue shows up as-is: that means new code." + [queue] + (str/lower (mtx/label queue "other"))) + +;; Job names go to the metric as-is: the registry is fixed (one job-def +;; per name, wired in `app.main`), so cardinality is bounded by it. +;; Anything without a name (nil) still falls back to "other". +(defn- name-label [name] + (mtx/label name "other")) + +(defn- gc-kind-label [kind] + (str/lower (or (some-> kind d/name) "other"))) + +(defn- outcome-label [outcome] + (str/lower (or (some-> outcome d/name) "failed"))) + +(defn- retry-reason-label [reason] + (str/lower (or (some-> reason d/name) "backoff"))) + +(defn- stage-label [stage] + (str/lower (or (some-> stage d/name) "execution"))) + +(defn- record-value [metrics id labels value] + (mtx/run! metrics :id id :labels labels :val value)) + +(defn- record-count [metrics id labels amount] + (mtx/run! metrics :id id :labels labels :inc amount)) + +(defn record-submitted + [cfg name queue] + (record-count (mtx/instance cfg) :jobs-submitted + [(name-label name) (queue-label queue)] + 1)) + +(defn record-skipped + [cfg name queue] + (record-count (mtx/instance cfg) :jobs-skipped + [(name-label name) (queue-label queue)] + 1)) + +(defn record-dispatched + [cfg name queue amount] + (record-count (mtx/instance cfg) :jobs-dispatched + [(name-label name) (queue-label queue)] + amount)) + +(defn record-retry + [cfg name queue reason] + (record-count (mtx/instance cfg) :jobs-retries + [(name-label name) (queue-label queue) + (retry-reason-label reason)] + 1)) + +(defn record-event + "Total number of job events stored. No labels: the interesting split is + `kind`, and grouping a growing append-only table to build a metric is not + worth the query on the write path." + [cfg] + (record-count (mtx/instance cfg) :jobs-events-total [] 1)) + +(defn record-orphan + [cfg queue] + (record-count (mtx/instance cfg) :jobs-orphaned + [(queue-label queue)] + 1)) + +(defn record-rescheduled + [cfg queue] + (record-count (mtx/instance cfg) :jobs-rescheduled + [(queue-label queue)] + 1)) + +(defn record-queue-wait + [cfg name queue millis] + (record-value (mtx/instance cfg) :jobs-queue-wait-timing + [(name-label name) (queue-label queue)] + (max 0 (long millis)))) + +(defn record-execution + [cfg name queue millis] + (record-value (mtx/instance cfg) :jobs-execution-timing + [(name-label name) (queue-label queue)] + (max 0 (long millis)))) + +(defn record-legacy-execution + "Histogram `penpot_tasks_timing`, from the pre-jobs `task` queue. + + It only carries the job name label, with no queue to tell jobs of the + same name apart. Use `record-execution`, which adds the queue label, + for anything new. Kept exported only so the dashboards and the alerts + that already read it keep working: like every other jobs metric, + the label is the raw job name." + [cfg name millis] + (record-value (mtx/instance cfg) :tasks-timing + [(mtx/label name "other")] + (max 0 (long millis)))) + +(defn record-total + [cfg name queue outcome millis] + (record-value (mtx/instance cfg) :jobs-total-timing + [(name-label name) (queue-label queue) + (outcome-label outcome)] + (max 0 (long millis)))) + +(defn record-dispatcher-batch + [cfg stage outcome millis] + (record-value (mtx/instance cfg) :jobs-dispatcher-timing + [(stage-label stage) (outcome-label outcome)] + (max 0 (long millis)))) + +(defn record-dispatcher-size + [cfg queue size] + (record-value (mtx/instance cfg) :jobs-dispatcher-batch-size + [(queue-label queue)] + size)) + +(defn record-gc-rows + [cfg kind action amount] + (record-count (mtx/instance cfg) :jobs-gc-rows + [(gc-kind-label kind) + (str/lower (or (some-> action d/name) "deleted"))] + amount)) + +(defn record-gc-duration + [cfg kind millis] + (record-value (mtx/instance cfg) :jobs-gc-timing + [(gc-kind-label kind)] + (max 0 (long millis)))) + +(defn record-cron + [cfg outcome reason] + (record-count (mtx/instance cfg) :jobs-cron-total + [(str/lower (or (some-> outcome d/name) "error")) + (str/lower (or (some-> reason d/name) "none"))] + 1)) + +(defn record-request + [cfg outcome millis] + (let [outcome (str/lower (or (some-> outcome d/name) "error")) + metrics (mtx/instance cfg)] + ;; No separate counter: the timing `_count` is the request total + ;; per outcome. + (record-value metrics :jobs-request-timing [outcome] + (max 0 (long millis))))) + +(defn- offload-result-label + "Coerce an offload outcome to its label: lowercase, with `failed` + only when there is no outcome at all. Values come from the fixed + call sites of the offload handler (`ok`, `skip`, `error`), never + from params, IDs or exception text." + [result] + (str/lower (mtx/label result "failed"))) + +(defn- offload-type-label + "Coerce a file data type to its label: lowercase, with `other` only + when there is no type at all. Values are the fixed `file_data.type` + values (`main`, `snapshot`, `fragment`)." + [type] + (str/lower (mtx/label type "other"))) + +(defn record-offload-rows + "Count file data rows moved by the offload job, broken down by row + type. Only successful runs emit rows, so there is no result label." + [cfg type amount] + (record-count (mtx/instance cfg) :file-data-offload-rows + [(offload-type-label type)] + amount)) + +(defn record-offload-bytes + "Count file data bytes moved by the offload job, broken down by row + type. Only successful runs emit bytes, so there is no result label." + [cfg type amount] + (record-count (mtx/instance cfg) :file-data-offload-bytes + [(offload-type-label type)] + amount)) + +(defn record-offload-duration + "Time one offload run from lock to summary (milliseconds). Recorded + for every outcome (`ok`, `skip`, `error`), so its count is the run + total per outcome." + [cfg result millis] + (record-value (mtx/instance cfg) :file-data-offload-timing + [(offload-result-label result)] + (max 0 (long millis)))) + +(def ^:private backlog-statuses + ["new" "scheduled" "running" "retry" "completed" "failed" "cancelled" "aborted"]) + +(def ^:private sql:backlog + "SELECT status, count(*) AS n + FROM job + GROUP BY status") + +(def ^:private sql:oldest-pending + "SELECT coalesce(extract(epoch FROM (now() - min(created_at))), 0) AS age + FROM job + WHERE status IN ('new', 'scheduled', 'retry')") + +(defn sample-backlog + "Update the current job backlog gauges. The query is intentionally small + and runs periodically rather than from the worker hot path." + [{:keys [::db/pool] :as cfg}] + (let [rows (db/exec! pool [sql:backlog]) + by-id (into {} (map (juxt :status #(or (:n %) 0))) rows) + age (-> (db/exec-one! pool [sql:oldest-pending]) + :age + (max 0) + (double)) + metrics (mtx/instance cfg)] + (doseq [status backlog-statuses] + (record-value metrics :jobs-backlog [status] (get by-id status 0))) + (record-value metrics :jobs-oldest-pending-age [] age) + age)) + +(defn- sample-jobs-metrics [cfg] + (try + (sample-backlog cfg) + (catch Throwable cause + (l/warn :hint "unable to sample jobs metrics" :cause cause)))) + +(def ^:private sample-interval-ms 30000) + +(defn create-sampler + "Create the daemon scheduler used by the jobs metrics component." + [cfg] + (let [scheduler (px/scheduled-executor + :parallelism 1 + :factory (px/thread-factory :prefix "penpot/jobs-metrics/" + :daemon true)) + sample (fn sample [] + (try + (sample-jobs-metrics cfg) + (finally + (px/schedule scheduler sample-interval-ms sample))))] + (px/schedule scheduler 0 sample) + scheduler)) + +(def ^:private schema:sampler + [:map + ::db/pool + ::mtx/metrics]) + +(defmethod ig/assert-key ::sampler + [_ cfg] + (assert (sm/check schema:sampler cfg))) + +(defmethod ig/init-key ::sampler + [_ {:keys [::db/pool] :as cfg}] + (if (db/read-only? pool) + (l/warn :hint "not started (db is read-only)") + (create-sampler cfg))) + +(defmethod ig/halt-key! ::sampler + [_ sampler] + (some-> sampler px/shutdown-now)) diff --git a/backend/src/app/jobs/storage.clj b/backend/src/app/jobs/storage.clj new file mode 100644 index 0000000000..dd33252cb7 --- /dev/null +++ b/backend/src/app/jobs/storage.clj @@ -0,0 +1,152 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.jobs.storage + "Storage helpers for the objects a job owns through `job.resource_id`. + + A job resource lives in the `job-resource` bucket and belongs to the + profile that owns the job, so only that profile can read it back (the + asset route checks the owner, not just the session). + + The job row is what keeps the object alive: `storage-gc-touched` + freezes it while a row references it and deletes it once no row does, + and the jobs GC marks it as touched when it removes the row. The object + carries no expiry of its own: `expired-at` is stored as `deleted_at`, + which makes the object unreadable from birth, so the retention of the + artifact is the retention of its job." + (:require + [app.common.exceptions :as ex] + [app.common.logging :as l] + [app.common.time :as ct] + [app.config :as cf] + [app.db :as db] + [app.storage :as sto] + [app.storage.tmp :as tmp])) + +(defn- resource-uri + "The url a user downloads the artifact from. A string, because the + descriptor ends up in the JSON result of the job." + [id] + (cf/get-public-uri "assets/by-id/" (str id))) + +(defn put-resource + "Store an artifact of a job in the `job-resource` bucket and return the + descriptor that identifies it. + + Named options: + + - `:content` the artifact, an `app.storage/content` value built from a + temporary file or from bytes in memory + - `:filename` the name the user will see + - `:mtype` the content type of the artifact + + `profile-id` is the owner of the object: the profile that owns the job, + for both the package a job consumes and the artifact it produces. + + The object is marked as touched right away, so an artifact that no job + row ever references (a crash between the upload and the completion) is + reclaimed by the storage GC instead of leaking; while a row references + it, `storage-gc-touched` freezes it." + [cfg profile-id {:keys [content filename mtype]}] + (assert (uuid? profile-id) "expected the profile that owns the resource") + (assert (string? mtype) "expected the content type of the resource") + (let [storage (sto/resolve cfg) + object (sto/put-object! storage + {::sto/content content + ::sto/touched-at (ct/now) + :bucket sto/job-resource-bucket + :profile-id profile-id + :content-type mtype})] + {:resource-id (:id object) + :resource-uri (resource-uri (:id object)) + :filename filename + :mtype mtype + :size (:size object)})) + +(defn- check-input + "The object a job consumes must exist, be a job resource and belong to + the profile that owns the job. Raises `:not-found` otherwise: a handler + never reads another profile's package." + [context object] + (when (or (nil? object) + (not= sto/job-resource-bucket (:bucket (meta object))) + (not= (:profile-id context) (:profile-id (meta object)))) + (ex/raise :type :not-found + :code :job-resource-not-found + :hint "the input of the job is not available" + :job-id (:id context) + :resource-id (:resource-id context))) + object) + +(defn load-input + "Download the package a job consumes to a local temporary file and + return its path, ready for the binfile reader. + + An object that was released or expired is already invisible to + `sto/get-object`, so it lands in the same `:not-found` as a missing + one." + [cfg context] + (let [storage (sto/resolve cfg) + resource-id (:resource-id context) + object (check-input context + (when (uuid? resource-id) + (sto/get-object storage resource-id)))] + (if-let [input (sto/get-object-data storage object)] + (tmp/tempfile-from input) + (ex/raise :type :not-found + :code :job-resource-not-found + :hint "the input of the job is not readable" + :job-id (:id context) + :resource-id resource-id)))) + +(defn release-resource + "Release a job resource. + + The object is marked as deleted and, when it belongs to a job, the + row stops pointing at it in the same transaction: a row never + references an object that is already gone, and the foreign key is not + what ends up clearing the reference. A resource no job owns (the + package of a creation that never submitted) is released with a nil + job id, and then no row is touched at all. + + Best effort: a failure is logged and never changes the terminal state + of the job, and releasing without a resource is a no-op. Returns true + when the object was marked as deleted or a stale reference was + cleared." + ([cfg resource-id] + (release-resource cfg resource-id nil)) + ([cfg resource-id job-id] + (when (uuid? resource-id) + (try + (db/tx-run! + cfg + (fn [tx-cfg] + (let [storage (sto/resolve tx-cfg ::db/reuse-conn true) + deleted? (sto/del-object! storage resource-id) + ;; the job no longer owns the package: the pointer goes with + ;; it, so clearing it does not depend on the object being + ;; physically deleted later + cleared? (when (some? job-id) + (db/update! tx-cfg :job + {:resource-id nil :modified-at (ct/now)} + ["id = ? AND resource_id = ?" + job-id resource-id] + {::db/return-keys [:id]}))] + (or deleted? (some? cleared?))))) + (catch Throwable cause + (l/wrn :hint "unable to release the input of a job" + :job-id (str job-id) + :resource-id (str resource-id) + :cause cause) + false))))) + +(defn release-input + "Release the package a job consumed. + + Delegates to `release-resource` with the id of the job: the object + and the pointer are dropped together. Best effort, like there." + [cfg context] + (release-resource cfg (:resource-id context) (:id context))) diff --git a/backend/src/app/loggers/audit.clj b/backend/src/app/loggers/audit.clj index 78a77fc15b..0d9a0b560f 100644 --- a/backend/src/app/loggers/audit.clj +++ b/backend/src/app/loggers/audit.clj @@ -13,19 +13,21 @@ [app.common.logging :as l] [app.common.schema :as sm] [app.common.time :as ct] + [app.common.transit :as t] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] [app.email :as email] [app.http :as-alias http] [app.http.access-token :as-alias actoken] + [app.jobs :as jobs] + [app.loggers.audit.consumer-task :as consumer] [app.loggers.audit.tasks :as-alias tasks] [app.loggers.webhooks :as-alias webhooks] [app.rpc :as-alias rpc] [app.setup :as-alias setup] [app.util.inet :as inet] [app.util.services :as-alias sv] - [app.worker :as wrk] [cuerdas.core :as str] [yetti.request :as yreq])) @@ -46,6 +48,46 @@ "incomplete-user-info" "unexpected-error"}) +(def ^:private subscription-creation-events + #{"create-subscription" "create-trial-subscription"}) + +(def ^:private subscription-creation-telemetry-props + #{:type + :deployment + :quantity + :subscription-mode}) + +(def ^:private subscription-update-telemetry-props + #{:type + :deployment + :previous-status + :status + :previous-cancel-at-period-end + :cancel-at-period-end + :previous-cancel-at + :cancel-at + :changed-fields}) + +(def ^:private subscription-summary-telemetry-props + #{:type + :deployment + :instance-id + :snapshot-date + :trialing-count + :active-count + :active-cancel-scheduled-count + :past-due-count + :canceled-count + :unpaid-count + :incomplete-count + :incomplete-expired-count + :paused-count + :other-count + :total-count + :sync-succeeded + :sync-failed + :snapshot-complete}) + (def ^:private safe-backend-context-keys #{:version :initiator @@ -240,8 +282,13 @@ (update :props db/tjson) (update :context db/tjson) (update :ip-addr db/inet)) - params (select-keys params event-keys)] - (db/insert! cfg :audit-log params))) + params (select-keys params event-keys) + row (db/insert! cfg :audit-log params)] + ;; The consumer queue follows the row in the same transaction: one + ;; pending per enabled consumer. A rollback takes both on its way + ;; out, so a row never outlives its queue. + (consumer/queue-pendings cfg [(:id row)]) + row)) (def ^:private xf:filter-telemetry-props "Transducer that keeps only map entries whose values are UUIDs, @@ -305,19 +352,24 @@ :else label) dedupe? (boolean (and batch-key batch-timeout))] - (wrk/submit! (-> cfg - (assoc ::wrk/task :process-webhook-event) - (assoc ::wrk/queue :webhooks) - (assoc ::wrk/max-retries 0) - (assoc ::wrk/delay (or batch-timeout 0)) - (assoc ::wrk/dedupe dedupe?) - (assoc ::wrk/label label) - (assoc ::wrk/params (-> event - (d/without-qualified) - (dissoc :source) - (dissoc :context) - (dissoc :ip-addr) - (dissoc :type))))))) + (jobs/submit cfg + {::jobs/name :process-webhook-event + ::jobs/queue :webhooks + ::jobs/max-retries 0 + ::jobs/delay (or batch-timeout 0) + ::jobs/dedupe dedupe? + ::jobs/label label + ;; The event travels as an opaque transit blob: + ;; transit preserves the instant/UUID/set types + ;; that plain JSON props cannot carry. + ::jobs/params {:event-blob + (t/encode-str + (-> event + (d/without-qualified) + (dissoc :source) + (dissoc :context) + (dissoc :ip-addr) + (dissoc :type)))}}))) event)) (defn submit* @@ -336,6 +388,24 @@ (defn filter-telemetry-props [{:keys [source name props type] :as params}] (cond + (and (= type "action") + (contains? subscription-creation-events name) + (or (= source "backend") + (= source "frontend"))) + (let [props' (into {} xf:filter-telemetry-props props) + props' (merge props' (select-keys props subscription-creation-telemetry-props))] + (assoc params :props props')) + + (and (= source "backend") + (= type "action") + (= name "update-subscription")) + (assoc params :props (select-keys props subscription-update-telemetry-props)) + + (and (= source "backend") + (= type "action") + (= name "subscription-summary")) + (assoc params :props (select-keys props subscription-summary-telemetry-props)) + (and (= source "backend") (= name "organization-sso-auth-failed")) (let [props' (into {} xf:filter-telemetry-props props) @@ -578,6 +648,8 @@ logic." [cfg event] (when (contains? cf/flags :audit-log) + ;; Transactional (not a bare connection): the row and its + ;; pendings commit or vanish together (see append-audit-entry). (let [tnow (ct/now) event (-> event (assoc :created-at tnow) @@ -588,4 +660,4 @@ (update :source d/nilv "backend") (select-keys event-keys) (check-event))] - (db/run! cfg append-audit-entry event)))) + (db/tx-run! cfg append-audit-entry event)))) diff --git a/backend/src/app/loggers/audit/archive_task.clj b/backend/src/app/loggers/audit/archive_task.clj deleted file mode 100644 index 1c5f953d44..0000000000 --- a/backend/src/app/loggers/audit/archive_task.clj +++ /dev/null @@ -1,141 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; Copyright (c) KALEIDOS SUBSIDIARY SL - -(ns app.loggers.audit.archive-task - (:require - [app.common.exceptions :as ex] - [app.common.logging :as l] - [app.common.schema :as sm] - [app.common.transit :as t] - [app.config :as cf] - [app.db :as db] - [app.http.client :as http] - [app.setup :as-alias setup] - [integrant.core :as ig] - [promesa.exec :as px])) - -;; This is a task responsible to send the accumulated events to -;; external service for archival. - -(defn- decode-row - [{:keys [props ip-addr context] :as row}] - (cond-> row - (db/pgobject? props) - (assoc :props (db/decode-transit-pgobject props)) - - (db/pgobject? context) - (assoc :context (db/decode-transit-pgobject context)) - - (db/pgobject? ip-addr "inet") - (assoc :ip-addr (db/decode-inet ip-addr)))) - -(def ^:private event-keys - [:type - :name - :source - :created-at - :tracked-at - :profile-id - :ip-addr - :props - :context]) - -(defn- row->event - [row] - (select-keys row event-keys)) - -(defn- send! - [{:keys [::uri] :as cfg} events] - (let [skey (-> cfg ::setup/shared-keys :nexus) - body (t/encode {:events events}) - headers {"content-type" "application/transit+json" - "origin" (str (cf/get :public-uri)) - "x-shared-key" (str "nexus " skey)} - params {:uri uri - :timeout 12000 - :method :post - :headers headers - :body body} - resp (http/req cfg params {:skip-ssrf-check? true})] - - (if (= (:status resp) 204) - true - (do - (l/error :hint "unable to archive events" - :resp-status (:status resp) - :resp-body (:body resp)) - false)))) - -(defn- mark-archived! - [{:keys [::db/conn]} rows] - (let [ids (db/create-array conn "uuid" (map :id rows))] - (db/exec-one! conn ["update audit_log set archived_at=now() where id = ANY(?)" ids]))) - -(def ^:private xf:create-event - (comp (map decode-row) - (map row->event))) - -(def ^:private sql:get-audit-log-chunk - "SELECT * - FROM audit_log - WHERE archived_at IS NULL - ORDER BY created_at ASC - LIMIT 128 - FOR UPDATE - SKIP LOCKED") - -(defn- get-event-rows - [{:keys [::db/conn] :as cfg}] - (->> (db/exec! conn [sql:get-audit-log-chunk]) - (not-empty))) - -(defn- archive-events! - [{:keys [::uri] :as cfg}] - (db/tx-run! cfg (fn [cfg] - (when-let [rows (get-event-rows cfg)] - (let [events (into [] xf:create-event rows)] - (l/trc :hint "archive events chunk" :uri uri :events (count events)) - (when (send! cfg events) - (mark-archived! cfg rows) - (count events))))))) - -(def ^:private schema:handler-params - [:map - ::db/pool - ::setup/shared-keys - ::http/client]) - -(defmethod ig/assert-key ::handler - [_ params] - (assert (sm/valid? schema:handler-params params) "valid params expected for handler")) - -(defmethod ig/init-key ::handler - [_ cfg] - (fn [params] - ;; NOTE: this let allows overwrite default configured values from - ;; the repl, when manually invoking the task. - (let [enabled (or (contains? cf/flags :audit-log-archive) - (:enabled params false)) - - uri (cf/get :audit-log-archive-uri) - uri (or uri (:uri params)) - cfg (assoc cfg ::uri uri)] - - (when (and enabled (not uri)) - (ex/raise :type :internal - :code :task-not-configured - :hint "archive task not configured, missing uri")) - - (when enabled - (loop [total 0] - (if-let [n (archive-events! cfg)] - (do - (px/sleep 100) - (recur (+ total ^long n))) - - (when (pos? total) - (l/dbg :hint "events archived" :total total)))))))) - diff --git a/backend/src/app/loggers/audit/consumer_task.clj b/backend/src/app/loggers/audit/consumer_task.clj new file mode 100644 index 0000000000..20b6255163 --- /dev/null +++ b/backend/src/app/loggers/audit/consumer_task.clj @@ -0,0 +1,688 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.loggers.audit.consumer-task + (:require + [app.common.exceptions :as ex] + [app.common.logging :as l] + [app.common.schema :as sm] + [app.common.time :as ct] + [app.common.transit :as t] + [app.common.uri :as u] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.db.sql :as sql] + [app.http.client :as http] + [app.jobs :as jobs] + [app.metrics :as mtx] + [app.nitrate :as nitrate] + [app.setup :as-alias setup] + [integrant.core :as ig] + [promesa.exec :as px])) + +;; This task drains the audit log buffer into the long-term archives. +;; +;; Two separate jobs share this namespace and the queue helpers, one +;; per destination: `:audit-log-consumer-nexus` and +;; `:audit-log-consumer-admin-console`. Each job drains only its own queue +;; and deletes its pendings on completion; whoever deletes the last +;; pending also deletes the record ("last one out turns off the +;; light"), so no separate GC task is needed. Housekeeping (reaping +;; disabled pendings, sweeping orphans) lives in both jobs on purpose: +;; only the enabled job runs, so it must clean up for the rest. + +;; Consumer ids are fixed constants, never reuse one: retired +;; pendings are reaped by id, so a reused id would inherit the +;; previous owner's queue. +(def consumer-nexus 1) +(def consumer-admin-console 2) + +(def ^:private consumers + [{:id consumer-nexus :flag :nexus :label "nexus"} + {:id consumer-admin-console :flag :admin-console :label "admin-console"}]) + +(def ^:private xf:enabled-consumers + "Static transducer: keep the consumers whose send flag is on. + Allocated once so hot paths (every audited RPC) do not rebuild + it per call; the flag lookup itself still reads the live flags." + (comp (filter #(contains? cf/flags (:flag %))) + (map :id))) + +(defn enabled-consumers + "Consumers allowed to work right now, from the send flags. Public: + producers queue every record for exactly these consumers." + [] + (into [] xf:enabled-consumers consumers)) + +(defn queue-pendings + "Queue records for every enabled consumer, nothing when there is + nothing to queue or no consumer is on. One multi-row statement for + all ids instead of one per consumer: this runs inside every audited + RPC and the bulk frontend ingest. Runs inside the row transaction: + a rollback takes both." + [cfg ids] + (let [consumers (enabled-consumers)] + (when (and (seq ids) (seq consumers)) + (db/insert-many! cfg :audit-log-pending + [:audit-log-id :consumer] + (for [id ids + consumer consumers] + [id consumer]) + {::sql/on-conflict-do-nothing true})))) + +(def ^:private consumer-labels + (into {} (map (juxt :id :label)) consumers)) + +(defn- consumer-label + [consumer] + (get consumer-labels consumer)) + +(defn- decode-row + [{:keys [props ip-addr context] :as row}] + (cond-> row + (db/pgobject? props) + (assoc :props (db/decode-transit-pgobject props)) + + (db/pgobject? context) + (assoc :context (db/decode-transit-pgobject context)) + + (db/pgobject? ip-addr "inet") + (assoc :ip-addr (db/decode-inet ip-addr)))) + +(def ^:private event-keys + [:type + :name + :source + :created-at + :tracked-at + :profile-id + :ip-addr + :props + :context]) + +(defn- row->event + [row] + (select-keys row event-keys)) + +(defn- record-count + [metrics id labels amount] + (mtx/run! metrics :id id :labels labels :inc amount)) + +(defn- truncate-body + [body limit] + (when (some? body) + (let [s (str body)] + (if (> (count s) limit) + (subs s 0 limit) + s)))) + +;; NOTE: deliberately no SKIP LOCKED here. Batches must reach the +;; destinations in chronological order (Nexus numbers by arrival), +;; so every consumer takes the same total order and a second +;; overlapping run waits for the first to commit, then sees whether +;; it was last. The brief blocking is accepted; skipping rows could +;; reorder archive arrival. +(def ^:private sql:pending-chunk + "SELECT a.* + FROM audit_log_pending p + JOIN audit_log a ON a.id = p.audit_log_id + WHERE p.consumer = ?::smallint + ORDER BY a.created_at ASC, a.id ASC + LIMIT 128 + FOR UPDATE OF a") + +(def ^:private sql:delete-pendings + "DELETE FROM audit_log_pending + WHERE consumer = ?::smallint + AND audit_log_id = ANY(?)") + +(def ^:private sql:delete-drained + "DELETE FROM audit_log a + WHERE a.id = ANY(?) + AND NOT EXISTS ( + SELECT 1 FROM audit_log_pending p + WHERE p.audit_log_id = a.id + )") + +(defn- get-pending-rows + [{:keys [::db/conn] :as cfg} consumer] + (->> (db/exec! conn [sql:pending-chunk consumer]) + (not-empty))) + +(defn- complete-batch + "Delete our pendings for the batch, then the records nobody is + waiting for anymore. Two statements on purpose: inside a single + statement the NOT EXISTS would still see the pending just deleted + and never delete a record." + [{:keys [::db/conn]} consumer rows] + (let [ids (db/create-array conn "uuid" (map :id rows))] + (db/exec! conn [sql:delete-pendings consumer ids]) + (db/exec! conn [sql:delete-drained ids]))) + +(defn- generate-nexus-uri + "Joins relative path segments to the Nexus backend URI, same shape + as the admin-console helper in `app.nitrate` (there the ingest path + is `api/audit-log`; here it is `api/audit`). + Segments must not start with `/`. Returns nil when no base URI is + configured, so the caller keeps the `:task-not-configured` guard." + [& segments] + (when-let [base (cf/get :nexus-uri)] + (apply cf/join-uri base segments))) + +(defn- send-to-nexus + "POST events to Nexus. Returns the number of events sent; throws on + transport failure or non-204 refusal, carrying the attempted count + as :events in the error data so the caller records the failure. + No metrics or logs here: the runner reports the aborted job." + [{:keys [::uri] :as cfg} events] + (let [n (count events) + skey (-> cfg ::setup/shared-keys :nexus) + body (t/encode {:events events}) + params {:uri uri + :timeout 12000 + :method :post + :headers {"content-type" "application/transit+json" + "origin" (str (cf/get :public-uri)) + "x-shared-key" (str "nexus " skey)} + :body body} + resp (try + (http/req cfg params {:skip-ssrf-check? true}) + (catch Throwable cause + (throw (ex/raise :type :internal + :code :nexus-send-failed + :hint "unable to send events to nexus" + :cause cause + :events n))))] + (if (= (:status resp) 204) + n + (throw (ex/raise :type :internal + :code :consumer-chunk-refused + :hint "nexus refused archive chunk" + :resp-status (:status resp) + :resp-body (truncate-body (:body resp) 500) + :events n))))) + +(def ^:private event-names-for-admin-console + #{"accept-organization-invitation" + "accept-organization-invitation-from" + "accept-team-invitation" + "accept-team-invitation-from" + "add-member-to-organization" + "add-team-to-organization" + "assign-organization-attribute" + "auditlog-exported" + "auditlog-guide-downloaded" + "cancel-organization-invitation" + "create-export-binfile-job" + "create-file" + "create-organization" + "create-organization-attribute" + "create-organization-invitation" + "create-project" + "create-team" + "create-team-access-request" + "create-team-invitation" + "create-team-invitations" + "create-webhook" + "delete-file" + "delete-font" + "delete-organization" + "delete-organization-member" + "delete-project" + "delete-team" + "delete-team-invitation" + "delete-team-member" + "leave-team" + "move-project" + "move-team-to-organization" + "organization-sso-auth-failed" + "organization-sso-auth-started" + "organization-sso-auth-succeeded" + "organization-sso-token-issued" + "organization-sso-token-revoked" + "permanently-delete-team-files" + "remove-organization-team" + "remove-team-from-organization" + "rename-organization" + "rename-project" + "restore-deleted-team-files" + "sso-config-deleted" + "sso-config-tested" + "success-activate-organization-sso" + "success-apply-organization-sso-changes" + "unassign-organization-attribute" + "update-organization-attribute" + "update-organization-invitation" + "update-organization-permissions" + "update-team-invitation" + "update-team-invitation-role" + "update-team-member-role" + "update-team-photo" + "verify-token"}) + +;; Props id key -> email prop filled from profile.email (admin-console path only). +;; Some producers store ids as UUID strings (e.g. :user-who-send-invitation). +(def ^:private id->email-prop + {:profile-id :profile-email + :user-id :user-email + :member-id :member-email + :user-who-send-invitation :user-who-send-invitation-email}) + +(defn- collect-team-ids-needing-name + [events] + (into #{} + (comp (map :props) + (keep (fn [props] + (when-let [team-id (and (not (contains? props :team-name)) + (uuid/coerce (:team-id props)))] + team-id)))) + events)) + +(defn- collect-profile-ids-needing-email + [events] + (into #{} + (mapcat (fn [{:keys [props]}] + (keep (fn [[id-key email-key]] + (when-not (contains? props email-key) + (uuid/coerce (get props id-key)))) + id->email-prop))) + events)) + +(defn- collect-actor-ids-needing-profile + "Actor ids (event :profile-id) that still need top-level :profile-email + and/or :profile-name. Skips uuid/zero and non-uuids." + [events] + (into #{} + (keep (fn [event] + (when (or (not (contains? event :profile-email)) + (not (contains? event :profile-name))) + (when-let [id (uuid/coerce (:profile-id event))] + (when-not (uuid/zero? id) + id))))) + events)) + +(defn- load-team-names + [conn ids] + (if (seq ids) + (let [arr (db/create-array conn "uuid" ids) + rows (db/exec! conn ["SELECT id, name FROM team WHERE id = ANY(?)" arr])] + (into {} (map (juxt :id :name) rows))) + {})) + +(defn- load-profiles + "Batch-load profile email and fullname (including soft-deleted)." + [conn ids] + (if (seq ids) + (let [arr (db/create-array conn "uuid" ids) + rows (db/exec! conn ["SELECT id, email, fullname FROM profile WHERE id = ANY(?)" arr])] + (into {} (map (juxt :id identity) rows))) + {})) + +(defn- maybe-assoc-team-name + [event team-names] + (let [props (:props event)] + (if-let [team-name (and (not (contains? props :team-name)) + (when-let [team-id (uuid/coerce (:team-id props))] + (get team-names team-id)))] + (assoc-in event [:props :team-name] team-name) + event))) + +(defn- maybe-assoc-emails + [event profiles] + (update event :props + (fn [props] + (reduce-kv (fn [props id-key email-key] + (if-let [email (and (not (contains? props email-key)) + (when-let [id (uuid/coerce (get props id-key))] + (:email (get profiles id))))] + (assoc props email-key email) + props)) + props + id->email-prop)))) + +(defn- maybe-assoc-actor-profile + "Fill-only top-level :profile-email / :profile-name from the event + actor (:profile-id). Omits keys when the id is missing, zero, the + profile row is absent, or the corresponding column is empty + (fullname defaults to \"\" in the schema)." + [event profiles] + (let [id (uuid/coerce (:profile-id event))] + (if (or (nil? id) (uuid/zero? id)) + event + (if-let [profile (get profiles id)] + (cond-> event + (and (not (contains? event :profile-email)) + (not-empty (:email profile))) + (assoc :profile-email (:email profile)) + + (and (not (contains? event :profile-name)) + (not-empty (:fullname profile))) + (assoc :profile-name (:fullname profile))) + event)))) + +(defn- enrich-events-for-admin-console + "Enrich allowlisted events before the admin-console ingest: + 1. Fill missing :team-name from team id (including soft-deleted). + 2. Fill missing emails from matching id keys in props (including + soft-deleted profiles): :profile-email, :user-email, :member-email, + :user-who-send-invitation-email. + 3. Fill missing top-level :profile-email / :profile-name from the + event actor (:profile-id), including soft-deleted profiles; omit + when there is no usable actor id or the value is empty." + [{:keys [::db/conn]} events] + (let [team-ids (collect-team-ids-needing-name events) + profile-ids (into (collect-profile-ids-needing-email events) + (collect-actor-ids-needing-profile events)) + team-names (load-team-names conn team-ids) + profiles (load-profiles conn profile-ids)] + (mapv (fn [event] + (-> event + (maybe-assoc-team-name team-names) + (maybe-assoc-emails profiles) + (maybe-assoc-actor-profile profiles))) + events))) + +(defn- send-to-admin-console + "Ingest rows into the admin console. Returns {:sent :skipped} + (filtered-out rows count as processed for the queue); throws + carrying the attempted count as :events, like send-to-nexus. Enrich + failures propagate untouched: a local DB/decode failure is ours, not + the destination's, and must not count as a send failure." + [cfg rows] + (let [events (filterv #(contains? event-names-for-admin-console (:name %)) rows) + total (count rows) + skipped (- total (count events)) + uri-host (some-> (cf/get :admin-console-uri) u/uri u/get-domain)] + (l/dbg :hint "consumer events chunk ready" + :consumer "admin-console" + :uri-host uri-host + :total total + :skipped skipped) + (if (empty? events) + {:sent 0 :skipped skipped} + ;; Enrich first, outside the try: a local DB/decode failure is + ;; ours, not the destination's, and must not count as + ;; admin-console/failed. + (let [enriched (enrich-events-for-admin-console cfg events) + n (count events)] + (try + (nitrate/call cfg :ingest-audit-log {:events enriched}) + (l/dbg :hint "consumer events chunk sent" + :consumer "admin-console" + :uri-host uri-host + :sent n + :skipped skipped) + {:sent n :skipped skipped} + (catch Throwable cause + (l/dbg :hint "consumer events chunk failed" + :consumer "admin-console" + :uri-host uri-host + :events n + :code :admin-console-send-failed) + (throw (ex/raise :type :internal + :code :admin-console-send-failed + :hint "unable to archive events to the admin console" + :cause cause + :events n)))))))) + +(defn- consumer-nexus-chunk + "Send one pending chunk to Nexus and complete it. Returns {:sent n} + or nil when the queue is empty; throws on refusal. Takes a + transactional cfg: the caller opens one transaction per chunk. No + metrics here: a normal return means the transaction committed, so + the caller records the success." + [cfg] + (db/exec! cfg ["SET LOCAL lock_timeout = '30s'"]) + (when-let [rows (get-pending-rows cfg consumer-nexus)] + (let [events (mapv row->event (mapv decode-row rows)) + ;; send-to-nexus returns the count or throws: a refusal + ;; aborts the job, the rows stay for the next tick. + n (send-to-nexus cfg events)] + + (l/trc :hint "consumer events chunk" + :consumer "nexus" + :events n) + + (complete-batch cfg consumer-nexus rows) + {:sent n}))) + +(defn- consumer-admin-console-chunk + "Send one pending chunk to the admin console and complete it. Returns + {:sent :skipped} or nil when the queue is empty; throws on failure. + Takes a transactional cfg, no metrics here: same contract as + consumer-nexus-chunk." + [cfg] + (db/exec! cfg ["SET LOCAL lock_timeout = '30s'"]) + (when-let [rows (get-pending-rows cfg consumer-admin-console)] + (let [decoded (mapv decode-row rows) + _ (l/trc :hint "consumer events chunk" + :consumer "admin-console" + :events (count decoded)) + admin-console-res (send-to-admin-console cfg decoded)] + ;; Filtered-out rows count as processed for the + ;; admin-console queue: their pending is done, the + ;; record stays until every consumer is done. + (complete-batch cfg consumer-admin-console rows) + admin-console-res))) + +(defn- drain-consumer + "Drain one consumer queue chunk by chunk, one transaction per chunk. + Returns totals; a chunk the destination refuses (or that throws) + aborts the drain with a throw, and the committed chunks so far + stay sent." + [cfg send-chunk] + (loop [sent 0 + skipped 0 + chunks 0] + ;; One transaction per chunk: fetch, send and complete commit (or + ;; roll back) together, and the next chunk sees a fresh snapshot. + (if-let [res (db/tx-run! cfg send-chunk)] + (do + (jobs/heartbeat cfg) + (px/sleep 100) + (recur (+ sent ^long (:sent res)) + (+ skipped ^long (:skipped res 0)) + (inc chunks))) + {:sent sent :skipped skipped :chunks chunks}))) + +(defn- sql:reap-pendings + [consumer] + (str "DELETE FROM audit_log_pending" + " WHERE consumer = " consumer + " AND audit_log_id IN (SELECT audit_log_id FROM audit_log_pending" + " WHERE consumer = " consumer + " ORDER BY audit_log_id LIMIT 5000)")) + +(def ^:private sql:sweep-orphans + "DELETE FROM audit_log a + WHERE a.id IN (SELECT a2.id FROM audit_log a2 + WHERE NOT EXISTS (SELECT 1 FROM audit_log_pending p + WHERE p.audit_log_id = a2.id) + ORDER BY a2.id LIMIT 5000)") + +(defn- delete-batched + "Run a batched DELETE until it removes nothing. Returns the total." + [cfg sql] + (loop [total 0] + (let [n (db/get-update-count (db/exec-one! cfg [sql]))] + (if (pos? n) + (do + (px/sleep 100) + (recur (+ total ^long n))) + total)))) + +(defn- reap-disabled + "Drop the pendings of consumers that are off: a disabled destination + will never drain them, and without this they would pin their records + forever. This also absorbs any later deactivation, with no manual step. + Dropped pendings count as discarded: their rows may still be archived + by an enabled consumer, or swept unsent when nothing waits for them." + [cfg] + (let [total (->> consumers + (remove #(contains? cf/flags (:flag %))) + (map #(delete-batched cfg (sql:reap-pendings (:id %)))) + (reduce + 0))] + (when (pos? total) + (record-count (mtx/instance cfg) :audit-consumer-discarded ["reaped"] total)) + total)) + +(defn- sweep-orphans + "Delete records nobody waits for anymore: rows born while no + consumer was on, rows left pending-less by the reap, and leftovers + archived by the old task. Batched and normally a no-op. Deleted rows + count as discarded: on a live system they were never archived." + [cfg] + (let [total (delete-batched cfg sql:sweep-orphans)] + (when (pos? total) + (record-count (mtx/instance cfg) :audit-consumer-discarded ["swept"] total)) + total)) + +(declare execute-audit-log-consumer-nexus) +(declare execute-audit-log-consumer-admin-console) + +(def schema:audit-log-consumer-nexus-params + "Overrides for the nexus job repl invocation defaults. + Cron submits {}; system deps (pool, shared-keys, http) live on the + job-def cfg closed over by the handler, not in job params. + Closed on purpose: a stale key (like the old :enabled) must fail + validation loudly instead of skipping silently." + [:map {:closed true} + [:uri {:optional true} ::sm/uri]]) + +(def schema:audit-log-consumer-admin-console-params + "Params of the admin-console job: none. Cron submits {}; system deps + (pool, admin-console client) live on the job-def cfg closed over by + the handler, not in job params. Closed on purpose, same as the nexus + schema." + [:map {:closed true}]) + +(defmethod ig/assert-key ::nexus-job-def + [_ cfg] + (assert (db/pool? (::db/pool cfg)) "expected a valid database pool") + (assert (http/client? (::http/client cfg)) "expected a valid http client") + (assert (mtx/metrics? (::mtx/metrics cfg)) "expected a valid metrics instance")) + +(defmethod ig/init-key ::nexus-job-def + [_ cfg] + ;; Effective destination, once at boot: per-run logs carry counts, + ;; the runner says the job started, this says where it sends. + (l/dbg :hint "audit log consumer job initialized" + :consumer "nexus" + :uri-host (some-> (cf/get :nexus-uri) u/uri u/get-domain)) + {::jobs/name :audit-log-consumer-nexus + ::jobs/schema schema:audit-log-consumer-nexus-params + ::jobs/handler + (fn [_context params] + (execute-audit-log-consumer-nexus cfg params)) + ::jobs/decoder (sm/decoder schema:audit-log-consumer-nexus-params sm/json-transformer) + ::jobs/validator (sm/validator schema:audit-log-consumer-nexus-params)}) + +(defmethod ig/assert-key ::admin-console-job-def + [_ cfg] + ;; No assert on the admin-console client: it inits to nil without the + ;; :admin-console flag, and the job skips on its flag anyway. + (assert (db/pool? (::db/pool cfg)) "expected a valid database pool") + (assert (mtx/metrics? (::mtx/metrics cfg)) "expected a valid metrics instance")) + +(defmethod ig/init-key ::admin-console-job-def + [_ cfg] + ;; Same as the nexus job-def: destination host once at boot. The + ;; ingest uri is resolved by nitrate/call at send time (it raises + ;; when missing); here it is display-only. + (l/dbg :hint "audit log consumer job initialized" + :consumer "admin-console" + :uri-host (some-> (cf/get :admin-console-uri) u/uri u/get-domain)) + {::jobs/name :audit-log-consumer-admin-console + ::jobs/schema schema:audit-log-consumer-admin-console-params + ::jobs/handler + (fn [_context params] + (execute-audit-log-consumer-admin-console cfg params)) + ::jobs/decoder (sm/decoder schema:audit-log-consumer-admin-console-params sm/json-transformer) + ::jobs/validator (sm/validator schema:audit-log-consumer-admin-console-params)}) + +(defn- run-single-consumer + "Drain one consumer queue, then housekeeping (reap disabled + pendings, sweep orphans) and the per-run summary. Housekeeping lives + in both jobs: only the enabled job runs, so it must reap the queues + of the consumers that are off; both halves are idempotent deletes. + A refused chunk (or a throw) aborts the job: the committed chunks + stay sent, the rest waits for the next tick, and the cron retries. + Every effect is rows and logs (drain metrics land here, outside + every chunk transaction)." + [cfg consumer send-chunk tpoint] + (let [label (consumer-label consumer) + metrics (mtx/instance cfg) + reaped (reap-disabled cfg) + ;; All drain metrics land here, outside every chunk + ;; transaction. Success is recorded on the totals: a normal + ;; chunk return means its transaction committed (a rollback + ;; always throws, taking the failure path instead). The send + ;; fns carry the attempted count as :events; anything else (a + ;; local DB failure) is not a destination failure and stays + ;; uncounted. The throw keeps rolling: a failed chunk aborts + ;; the job, the cron retries. + drained (try + (drain-consumer cfg send-chunk) + (catch Throwable cause + (let [n (:events (ex-data cause))] + (when (and (integer? n) (pos? n)) + (record-count metrics :audit-consumer-events [label "failed"] n))) + (throw cause))) + _ (let [{:keys [sent skipped]} drained] + (when (pos? sent) + (record-count metrics :audit-consumer-events [label "archived"] sent)) + (when (pos? skipped) + (record-count metrics :audit-consumer-events [label "skipped"] skipped))) + swept (sweep-orphans cfg) + sent (:sent drained) + skipped (:skipped drained) + chunks (:chunks drained) + ;; Strict binding on purpose: log props evaluate lazily in + ;; the logger thread, so the elapsed must be frozen here. + elapsed (ct/format-duration (tpoint))] + (if (and (zero? sent) + (zero? skipped) + (zero? reaped) + (zero? swept)) + (l/dbg :hint "no pending audit events" + :consumer label) + (l/dbg :hint "events archived" + :consumer label + :elapsed elapsed + :sent sent + :skipped skipped + :chunks chunks + :reaped reaped + :swept swept)))) + +(defn execute-audit-log-consumer-nexus + "Nexus job handler: drain the nexus queue in chunks, then clean up + (heartbeat per iteration: the sent chunk batches can be long)." + [cfg params] + ;; NOTE: this let allows overwrite default configured values from + ;; the repl, when manually invoking the task. + (if-not (contains? cf/flags :nexus) + (l/dbg :hint "audit log consumer (nexus) skipped, flag off") + (let [uri (or (:uri params) (generate-nexus-uri "api/audit")) + ;; Normalize to an uri object; params may carry a plain string + ;; on direct invocations (validation only applies on submit!). + uri (u/uri uri) + cfg (assoc cfg ::uri uri)] + (when-not uri + (ex/raise :type :internal + :code :task-not-configured + :hint "consumer task not configured, missing uri")) + (run-single-consumer cfg consumer-nexus consumer-nexus-chunk (ct/tpoint))))) + +(defn execute-audit-log-consumer-admin-console + "Admin-console job handler: drain the admin-console queue in chunks, + then clean up (heartbeat per iteration: the sent chunk batches can + be long)." + [cfg _params] + (if-not (contains? cf/flags :admin-console) + (l/dbg :hint "audit log consumer (admin-console) skipped, flag off") + (run-single-consumer cfg consumer-admin-console consumer-admin-console-chunk (ct/tpoint)))) diff --git a/backend/src/app/loggers/audit/gc_task.clj b/backend/src/app/loggers/audit/gc_task.clj deleted file mode 100644 index 6fbd1710b4..0000000000 --- a/backend/src/app/loggers/audit/gc_task.clj +++ /dev/null @@ -1,31 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; Copyright (c) KALEIDOS SUBSIDIARY SL - -(ns app.loggers.audit.gc-task - (:require - [app.common.logging :as l] - [app.db :as db] - [integrant.core :as ig])) - -(def ^:private sql:clean-archived - "DELETE FROM audit_log - WHERE archived_at IS NOT NULL") - -(defn- clean-archived! - [{:keys [::db/pool]}] - (let [result (db/exec-one! pool [sql:clean-archived]) - result (db/get-update-count result)] - (l/debug :hint "delete archived audit log entries" :deleted result) - result)) - -(defmethod ig/assert-key ::handler - [_ params] - (assert (db/pool? (::db/pool params)) "valid database pool expected")) - -(defmethod ig/init-key ::handler - [_ cfg] - (fn [_] - (clean-archived! cfg))) diff --git a/backend/src/app/loggers/database.clj b/backend/src/app/loggers/database.clj index be4ad26503..4b79444604 100644 --- a/backend/src/app/loggers/database.clj +++ b/backend/src/app/loggers/database.clj @@ -96,12 +96,11 @@ "Convert the log record into a report object and persist it on the database" [{:keys [::db/pool]} {:keys [::l/id] :as record}] (try - (let [uri (cf/get :public-uri) - report (-> record log-record->report d/without-nils)] + (let [report (-> record log-record->report d/without-nils)] (l/dbg :hint "registering error on database" :id (str id) :src "logging" - :uri (str uri "/dbg/error/" id)) + :uri (cf/get-public-uri "dbg/error/" (str id))) (persist-on-database! pool id 3 report)) (catch Throwable cause (l/warn :hint "unexpected exception on database error logger" :cause cause)))) @@ -139,12 +138,11 @@ "Convert the log record into a report object and persist it on the database" [{:keys [::db/pool]} {:keys [id] :as event}] (try - (let [uri (cf/get :public-uri) - report (-> event audit-event->report d/without-nils)] + (let [report (-> event audit-event->report d/without-nils)] (l/dbg :hint "registering error on database" :id (str id) :src "audit-log" - :uri (str uri "/dbg/error/" id)) + :uri (cf/get-public-uri "dbg/error/" (str id))) (persist-on-database! pool id 4 report)) (catch Throwable cause (l/warn :hint "unexpected exception on database error logger" :cause cause)))) @@ -172,18 +170,17 @@ :value (pp/pprint-str result :length 50) :tenant (cf/get :tenant) :version (:full cf/version) - :href (str (cf/get :public-uri))})) + :href (cf/get-public-uri)})) (defn- handle-rlimit-event "Convert the log record into a report object and persist it on the database" [{:keys [::db/pool]} {:keys [::rlimit/id] :as event}] (try - (let [uri (cf/get :public-uri) - report (-> event rlimit-event->report d/without-nils)] + (let [report (-> event rlimit-event->report d/without-nils)] (l/dbg :hint "registering rate limit rejection" :id (str id) :src "rlimit" - :uri (str uri "/dbg/error/" id)) + :uri (cf/get-public-uri "dbg/error/" (str id))) (persist-on-database! pool id 5 report)) (catch Throwable cause (l/warn :hint "unexpected exception on database error logger" :cause cause)))) diff --git a/backend/src/app/loggers/mattermost.clj b/backend/src/app/loggers/mattermost.clj index 31820cb6e0..38a75b8546 100644 --- a/backend/src/app/loggers/mattermost.clj +++ b/backend/src/app/loggers/mattermost.clj @@ -11,7 +11,6 @@ [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.pprint :as pp] - [app.common.uri :as u] [app.config :as cf] [app.http.client :as http] [app.loggers.audit :as audit] @@ -28,7 +27,7 @@ (let [type (get report :type) text (str "#" type " | " (d/escape-markdown (get report :hint)) "\n" (when id - (str (u/join (cf/get :public-uri) "/dbg/error/" id) " ")) + (str (cf/get-public-uri "dbg/error/" id) " ")) (when-let [pid (:profile-id report)] (if (uuid? pid) diff --git a/backend/src/app/loggers/webhooks.clj b/backend/src/app/loggers/webhooks.clj index 83df9825cc..13e9d65d05 100644 --- a/backend/src/app/loggers/webhooks.clj +++ b/backend/src/app/loggers/webhooks.clj @@ -10,14 +10,15 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.logging :as l] + [app.common.schema :as sm] [app.common.time :as ct] [app.common.transit :as t] [app.common.uri :as uri] [app.config :as cf] [app.db :as db] [app.http.client :as http] + [app.jobs :as jobs] [app.loggers.audit :as audit] - [app.worker :as wrk] [clojure.data.json :as json] [cuerdas.core :as str] [integrant.core :as ig])) @@ -60,35 +61,54 @@ (some->> (:project-id props) (lookup-webhooks-by-project pool)) (some->> (:file-id props) (lookup-webhooks-by-file pool)))) -(defmethod ig/assert-key ::process-event-handler +(defmethod ig/assert-key ::process-webhook-event-job-def [_ params] (assert (db/pool? (::db/pool params)) "expect valid database pool") (assert (http/client? (::http/client params)) "expect valid http client")) -(defmethod ig/init-key ::process-event-handler +(defn process-event-impl + [cfg {:keys [event-blob]}] + ;; The blob carries the original typed event; decode it once for the + ;; lookups below and forward it untouched so deliveries keep the + ;; exact instant/UUID/set types of the legacy worker. + (let [props (t/decode-str event-blob) + items (lookup-webhooks cfg props) + event {:profile-id (:profile-id props) + :name "webhook" + :type "trigger" + :props {:name (get props :name) + :event-id (get props :id) + :total-affected (count items)}}] + + (audit/insert cfg event) + + (when items + (l/trc :hint "webhooks found for event" :total (count items)) + (db/tx-run! cfg (fn [cfg] + (doseq [item items] + (jobs/submit cfg + {::jobs/name :run-webhook + ::jobs/queue :webhooks + ::jobs/max-retries 3 + ::jobs/params {:event event-blob + :config (select-keys item [:id :uri :mtype])}}))))))) + + +(def schema:process-webhook-event-params + "The audited event travels as an opaque transit blob: transit + preserves the instant/UUID/set types that plain JSON props cannot + carry, so no per-key type declarations are needed." + [:map [:event-blob :string]]) + +(defmethod ig/init-key ::process-webhook-event-job-def [_ cfg] - (fn [{:keys [props] :as task}] - - (let [items (lookup-webhooks cfg props) - event {:profile-id (:profile-id props) - :name "webhook" - :type "trigger" - :props {:name (get props :name) - :event-id (get props :id) - :total-affected (count items)}}] - - (audit/insert cfg event) - - (when items - (l/trc :hint "webhooks found for event" :total (count items)) - (db/tx-run! cfg (fn [cfg] - (doseq [item items] - (wrk/submit! (-> cfg - (assoc ::wrk/task :run-webhook) - (assoc ::wrk/queue :webhooks) - (assoc ::wrk/max-retries 3) - (assoc ::wrk/params {:event props - :config item})))))))))) + {::jobs/name :process-webhook-event + ::jobs/schema schema:process-webhook-event-params + ::jobs/handler + (fn [_context params] + (process-event-impl cfg params)) + ::jobs/decoder (sm/decoder schema:process-webhook-event-params sm/json-transformer) + ::jobs/validator (sm/validator schema:process-webhook-event-params)}) ;; --- RUN (declare interpret-exception) @@ -98,17 +118,17 @@ {:key-fn str/camel :indent true}) -(defmethod ig/assert-key ::run-webhook-handler +(defmethod ig/assert-key ::run-webhook-job-def [_ params] (assert (db/pool? (::db/pool params)) "expect valid database pool") (assert (http/client? (::http/client params)) "expect valid http client")) -(defmethod ig/expand-key ::run-webhook-handler +(defmethod ig/expand-key ::run-webhook-job-def [k v] {k (merge {::max-errors 3} (d/without-nils v))}) -(defmethod ig/init-key ::run-webhook-handler - [_ {:keys [::db/pool ::max-errors] :as cfg}] +(defn run-webhook-impl + [{:keys [::db/pool ::max-errors] :as cfg} props] (letfn [(update-webhook! [whook err] (if err (let [sql [(str "update webhook " @@ -137,39 +157,68 @@ :req-data (db/tjson req) :rsp-data (db/tjson rsp)}))] - (fn [{:keys [props] :as task}] - (let [event (:event props) - whook (:config props) + (let [event (t/decode-str (:event props)) + whook (:config props) - body (case (:mtype whook) - "application/json" (json/write-str event json-write-opts) - "application/transit+json" (t/encode-str event) - "application/x-www-form-urlencoded" (uri/map->query-string event))] + body (case (:mtype whook) + "application/json" (json/write-str event json-write-opts) + "application/transit+json" (t/encode-str event) + "application/x-www-form-urlencoded" (uri/map->query-string event))] - (l/dbg :hint "run webhook" - :event-name (:name event) - :webhook-id (str (:id whook)) - :webhook-uri (:uri whook) - :webhook-mtype (:mtype whook)) + (l/dbg :hint "run webhook" + :event-name (:name event) + :webhook-id (str (:id whook)) + :webhook-uri (:uri whook) + :webhook-mtype (:mtype whook)) - (let [req {:uri (:uri whook) - :headers {"content-type" (:mtype whook) - "user-agent" (str/ffmt "penpot/%" (:main cf/version))} - :timeout (ct/duration "4s") - :method :post - :body body}] - (try - (let [rsp (http/req cfg req {:response-type :input-stream :sync? true}) - err (interpret-response rsp)] - (report-delivery! whook req rsp err) - (update-webhook! whook err)) - (catch Throwable cause - (let [err (interpret-exception cause)] - (report-delivery! whook req nil err) - (update-webhook! whook err) - (when (= err "unknown") - (l/err :hint "unknown error on webhook request" - :cause cause)))))))))) + (let [req {:uri (:uri whook) + :headers {"content-type" (:mtype whook) + "user-agent" (str/ffmt "penpot/%" (:main cf/version))} + :timeout (ct/duration "4s") + :method :post + :body body}] + (try + (let [rsp (http/req cfg req {:response-type :input-stream :sync? true}) + err (interpret-response rsp)] + (report-delivery! whook req rsp err) + (update-webhook! whook err)) + (catch Throwable cause + (let [err (interpret-exception cause)] + (report-delivery! whook req nil err) + (update-webhook! whook err) + (when (= err "unknown") + (l/err :hint "unknown error on webhook request" + :cause cause))))))))) + + +(def schema:run-webhook-config + "Subset of the webhook row consumed by the run-webhook job. The uri + stays text on purpose: configs come straight from the database and + submit! validates raw params without coercion." + [:map + [:id ::sm/uuid] + [:uri ::sm/text] + [:mtype ::sm/text]]) + +(def schema:run-webhook-params + "The event travels as the original transit blob inside the JSON job + props: process-webhook-event receives the transit-encoded audited + event and forwards it untouched, and the handler transit-decodes it + before delivery. This keeps the wire payload identical to the legacy + worker for every mtype, whatever shape the event has." + [:map + [:event :string] + [:config schema:run-webhook-config]]) + +(defmethod ig/init-key ::run-webhook-job-def + [_ cfg] + {::jobs/name :run-webhook + ::jobs/schema schema:run-webhook-params + ::jobs/handler + (fn [_context params] + (run-webhook-impl cfg params)) + ::jobs/decoder (sm/decoder schema:run-webhook-params sm/json-transformer) + ::jobs/validator (sm/validator schema:run-webhook-params)}) (defn interpret-response [{:keys [status] :as response}] diff --git a/backend/src/app/main.clj b/backend/src/app/main.clj index bdb5802053..71ddafd9bf 100644 --- a/backend/src/app/main.clj +++ b/backend/src/app/main.clj @@ -22,9 +22,12 @@ [app.http.debug :as-alias http.debug] [app.http.link-preview :as-alias http.link-preview] [app.http.management :as mgmt] + [app.http.mcp-oauth :as-alias http.mcp-oauth] [app.http.session :as session] - [app.http.session.tasks :as-alias session.tasks] [app.http.websocket :as http.ws] + [app.jobs :as-alias jobs] + [app.jobs.gc :as-alias jobs.gc] + [app.jobs.metrics :as jobs-metrics] [app.loggers.webhooks :as-alias webhooks] [app.metrics :as mtx] [app.metrics.definition :as-alias mdef] @@ -118,7 +121,12 @@ :tasks-timing {::mdef/name "penpot_tasks_timing" - ::mdef/help "Background tasks timing (milliseconds)." + ;; Deprecated: superseded by `penpot_jobs_execution_ms`, which adds a + ;; queue label. Kept only for existing dashboards and alerts. The help + ;; text is what someone reading /metrics sees, so it is where the + ;; redirect belongs: the name and the labels stay untouched, so no + ;; panel or alert breaks. + ::mdef/help "Deprecated: use penpot_jobs_execution_ms, which adds a queue label." ::mdef/labels ["name"] ::mdef/buckets mtx/default-tasks-histogram-buckets ::mdef/type :histogram} @@ -254,10 +262,160 @@ ::mdef/help "Total number of handler errors in the http listener." ::mdef/type :counter} + :jobs-submitted + {::mdef/name "penpot_jobs_submitted_total" + ::mdef/help "Total number of durable jobs submitted." + ::mdef/labels ["name" "queue"] + ::mdef/type :counter} + + :jobs-skipped + {::mdef/name "penpot_jobs_skipped_total" + ::mdef/help "Total number of durable job submissions skipped by skip-if-active." + ::mdef/labels ["name" "queue"] + ::mdef/type :counter} + + :jobs-dispatched + {::mdef/name "penpot_jobs_dispatched_total" + ::mdef/help "Total number of jobs pushed to a worker queue. Counts pushes, not claims: compare with the _count of penpot_jobs_queue_wait_ms." + ::mdef/labels ["name" "queue"] + ::mdef/type :counter} + + :jobs-retries + {::mdef/name "penpot_jobs_retries_total" + ::mdef/help "Total number of durable jobs rescheduled for retry." + ::mdef/labels ["name" "queue" "reason"] + ::mdef/type :counter} + + :jobs-events-total + {::mdef/name "penpot_jobs_events_total" + ::mdef/help "Total number of job events stored." + ::mdef/type :counter} + + :jobs-orphaned + {::mdef/name "penpot_jobs_orphaned_total" + ::mdef/help "Total number of running jobs marked as orphaned." + ::mdef/labels ["queue"] + ::mdef/type :counter} + + :jobs-rescheduled + {::mdef/name "penpot_jobs_rescheduled_total" + ::mdef/help "Total number of scheduled jobs recovered after transit loss." + ::mdef/labels ["queue"] + ::mdef/type :counter} + + :jobs-queue-wait-timing + {::mdef/name "penpot_jobs_queue_wait_ms" + ::mdef/help "Time from job scheduling to worker claim (milliseconds). Its _count counts claims, not pushes: compare with penpot_jobs_dispatched_total." + ::mdef/labels ["name" "queue"] + ::mdef/type :histogram + ::mdef/buckets [1 10 50 100 500 1000 5000 10000 30000 60000 300000 600000]} + + :jobs-execution-timing + {::mdef/name "penpot_jobs_execution_ms" + ::mdef/help "Time spent executing a job handler (milliseconds)." + ::mdef/labels ["name" "queue"] + ::mdef/type :histogram + ::mdef/buckets [1 10 50 100 500 1000 5000 10000 30000 60000 300000 600000]} + + :jobs-total-timing + {::mdef/name "penpot_jobs_total_ms" + ::mdef/help "Time from job creation to a terminal state (milliseconds). Its _count is the terminal total per outcome." + ::mdef/labels ["name" "queue" "outcome"] + ::mdef/type :histogram + ::mdef/buckets [10 50 100 500 1000 5000 10000 30000 60000 300000 600000 3600000]} + + :jobs-dispatcher-timing + {::mdef/name "penpot_jobs_dispatcher_ms" + ::mdef/help "Dispatcher batch processing time (milliseconds)." + ::mdef/labels ["stage" "outcome"] + ::mdef/type :histogram + ::mdef/buckets [1 10 50 100 500 1000 5000 10000 30000 60000]} + + :jobs-dispatcher-batch-size + {::mdef/name "penpot_jobs_dispatcher_batch_size" + ::mdef/help "Number of jobs in a dispatcher queue batch." + ::mdef/labels ["queue"] + ::mdef/type :histogram + ::mdef/buckets [1 5 10 25 50 100 250 500 1000]} + + :jobs-backlog + {::mdef/name "penpot_jobs_backlog" + ::mdef/help "Current number of jobs grouped by status." + ::mdef/labels ["status"] + ::mdef/type :gauge} + + :jobs-oldest-pending-age + {::mdef/name "penpot_jobs_oldest_pending_age_seconds" + ::mdef/help "Age in seconds of the oldest pending job." + ::mdef/type :gauge} + + :jobs-gc-rows + {::mdef/name "penpot_jobs_gc_rows_total" + ::mdef/help "Rows removed or resources touched by the jobs garbage collector." + ::mdef/labels ["kind" "action"] + ::mdef/type :counter} + + :jobs-gc-timing + {::mdef/name "penpot_jobs_gc_ms" + ::mdef/help "Time spent in each jobs garbage collection pass (milliseconds)." + ::mdef/labels ["kind"] + ::mdef/type :histogram + ::mdef/buckets [1 10 50 100 500 1000 5000 10000 30000 60000 300000]} + + :jobs-cron-total + {::mdef/name "penpot_jobs_cron_total" + ::mdef/help "Total number of cron scheduler events." + ::mdef/labels ["outcome" "reason"] + ::mdef/type :counter} + + :jobs-request-timing + {::mdef/name "penpot_jobs_request_ms" + ::mdef/help "Time spent waiting for an ephemeral job reply (milliseconds). Its _count is the request total per outcome." + ::mdef/labels ["outcome"] + ::mdef/type :histogram + ::mdef/buckets [1 10 50 100 500 1000 5000 10000 30000 60000 300000]} + :http-malformed-requests-total {::mdef/name "penpot_http_malformed_requests_total" ::mdef/help "Total number of requests whose body the parser could not read." - ::mdef/type :counter}}) + ::mdef/type :counter} + + :audit-consumer-events + {::mdef/name "penpot_audit_consumer_events_total" + ::mdef/help "Audit log events drained by the consumer task, by destination and outcome." + ::mdef/labels ["destination" "outcome"] + ::mdef/type :counter} + + :audit-consumer-discarded + {::mdef/name "penpot_audit_consumer_discarded_total" + ::mdef/help "Audit log entries discarded without being archived, by reason." + ::mdef/labels ["reason"] + ::mdef/type :counter} + + :audit-ingest-events + {::mdef/name "penpot_audit_ingest_events_total" + ::mdef/help "Audit log frontend events ingested, by kind and outcome." + ::mdef/labels ["kind" "outcome"] + ::mdef/type :counter} + + :file-data-offload-rows + {::mdef/name "penpot_file_data_offload_rows_total" + ::mdef/help "File data rows moved from hot to cold storage by the offload job." + ::mdef/labels ["type"] + ::mdef/type :counter} + + :file-data-offload-bytes + {::mdef/name "penpot_file_data_offload_bytes_total" + ::mdef/help "File data bytes moved from hot to cold storage by the offload job." + ::mdef/labels ["type"] + ::mdef/type :counter} + + :file-data-offload-timing + {::mdef/name "penpot_file_data_offload_timing_ms" + ::mdef/help "Time spent offloading one file from hot to cold storage (milliseconds)." + ::mdef/labels ["result"] + ::mdef/type :histogram + ::mdef/buckets [1 10 50 100 500 1000 5000 10000 30000 60000 300000]}}) (def system-config {::db/pool @@ -304,28 +462,12 @@ :app.storage.tmp/cleaner {::wrk/executor (ig/ref ::wrk/executor)} - ::sto.gc-deleted/handler - {::db/pool (ig/ref ::db/pool) - ::sto/storage (ig/ref ::sto/storage) - ::mtx/metrics (ig/ref ::mtx/metrics)} - - ::sto.gc-touched/handler - {::db/pool (ig/ref ::db/pool) - ::mtx/metrics (ig/ref ::mtx/metrics)} - - ::sto.pending-gc/handler - {::db/pool (ig/ref ::db/pool) - ::sto/storage (ig/ref ::sto/storage)} - ::http.client/client {::wrk/executor (ig/ref ::wrk/executor)} ::session/manager {::db/pool (ig/ref ::db/pool)} - ::session.tasks/gc - {::db/pool (ig/ref ::db/pool)} - ::http.awsns/routes {::setup/props (ig/ref ::setup/props) ::db/pool (ig/ref ::db/pool) @@ -374,6 +516,7 @@ ::oidc/routes {::http.client/client (ig/ref ::http.client/client) + ::jobs/defs (ig/ref ::jobs/defs) ::db/pool (ig/ref ::db/pool) ::setup/props (ig/ref ::setup/props) ::oidc/providers (ig/ref ::oidc/providers) @@ -398,9 +541,17 @@ ::http.debug/routes (ig/ref ::http.debug/routes) ::http.assets/routes (ig/ref ::http.assets/routes) ::http.link-preview/routes (ig/ref ::http.link-preview/routes) + ::http.mcp-oauth/routes (ig/ref ::http.mcp-oauth/routes) ::http.ws/routes (ig/ref ::http.ws/routes) ::http.awsns/routes (ig/ref ::http.awsns/routes)} + ::http.mcp-oauth/routes + {::db/pool (ig/ref ::db/pool) + ::rds/pool (ig/ref ::rds/pool) + ::setup/props (ig/ref ::setup/props) + ::session/manager (ig/ref ::session/manager) + ::http.client/client (ig/ref ::http.client/client)} + ::http.link-preview/routes {::db/pool (ig/ref ::db/pool)} @@ -447,6 +598,7 @@ :app.rpc/methods {::http.client/client (ig/ref ::http.client/client) + ::jobs/defs (ig/ref ::jobs/defs) ::db/pool (ig/ref ::db/pool) ::rds/pool (ig/ref ::rds/pool) :app.nitrate/client (ig/ref :app.nitrate/client) @@ -475,10 +627,13 @@ :app.nitrate/client {::http.client/client (ig/ref ::http.client/client) + ;; No ::jobs/defs: nitrate never submits jobs, and consumer-task's + ;; job-def needs this client — that would cycle through jobs/defs. ::setup/shared-keys (ig/ref ::setup/shared-keys)} :app.rpc/management-methods {::http.client/client (ig/ref ::http.client/client) + ::jobs/defs (ig/ref ::jobs/defs) ::db/pool (ig/ref ::db/pool) ::rds/pool (ig/ref ::rds/pool) ::wrk/executor (ig/ref ::wrk/executor) @@ -500,31 +655,129 @@ ::setup/props (ig/ref ::setup/props) ::setup/shared-keys (ig/ref ::setup/shared-keys)} - ::wrk/registry - {::mtx/metrics (ig/ref ::mtx/metrics) - ::wrk/tasks - {:sendmail (ig/ref ::email/handler) - :objects-gc (ig/ref :app.tasks.objects-gc/handler) - :file-gc (ig/ref :app.tasks.file-gc/handler) - :file-gc-scheduler (ig/ref :app.tasks.file-gc-scheduler/handler) - :offload-file-data (ig/ref :app.tasks.offload-file-data/handler) - :tasks-gc (ig/ref :app.tasks.tasks-gc/handler) - :telemetry (ig/ref :app.tasks.telemetry/handler) - :storage-gc-deleted (ig/ref ::sto.gc-deleted/handler) - :storage-gc-touched (ig/ref ::sto.gc-touched/handler) - :storage-pending-gc (ig/ref ::sto.pending-gc/handler) - :session-gc (ig/ref ::session.tasks/gc) - :audit-log-archive (ig/ref :app.loggers.audit.archive-task/handler) - :audit-log-gc (ig/ref :app.loggers.audit.gc-task/handler) + ::jobs/defs + {:sendmail (ig/ref ::email/job-def) + :send-templated-mail (ig/ref ::email/templated-job-def) + :delete-object (ig/ref :app.tasks.delete-object/job-def) + :demo-purge (ig/ref :app.tasks.demo-purge/job-def) + :run-webhook (ig/ref ::webhooks/run-webhook-job-def) + :process-webhook-event (ig/ref ::webhooks/process-webhook-event-job-def) + :file-gc (ig/ref :app.tasks.file-gc/job-def) + :offload-file-data (ig/ref :app.tasks.offload-file-data/job-def) + :objects-gc (ig/ref :app.tasks.objects-gc/job-def) + :export-binfile (ig/ref :app.tasks.export-binfile/job-def) + :import-binfile (ig/ref :app.tasks.import-binfile/job-def) + :storage-gc-deleted (ig/ref ::sto.gc-deleted/job-def) + :storage-gc-touched (ig/ref ::sto.gc-touched/job-def) + :storage-pending-gc (ig/ref ::sto.pending-gc/job-def) + :jobs-gc (ig/ref :app.jobs.gc/job-def) + :telemetry (ig/ref :app.tasks.telemetry/job-def) + :session-gc (ig/ref ::session/job-def) + :file-gc-scheduler (ig/ref :app.tasks.file-gc-scheduler/job-def) + :audit-log-consumer-nexus (ig/ref :app.loggers.audit.consumer-task/nexus-job-def) + :audit-log-consumer-admin-console (ig/ref :app.loggers.audit.consumer-task/admin-console-job-def)} - :delete-object - (ig/ref :app.tasks.delete-object/handler) - :demo-purge - (ig/ref :app.tasks.demo-purge/handler) - :process-webhook-event - (ig/ref ::webhooks/process-event-handler) - :run-webhook - (ig/ref ::webhooks/run-webhook-handler)}} + ::jobs/request-pool + {::rds/client (ig/ref ::rds/client) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.email/job-def + {::email/sendmail (ig/ref ::email/sendmail) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.email/templated-job-def + {::email/sendmail (ig/ref ::email/sendmail) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.tasks.delete-object/job-def + {::db/pool (ig/ref ::db/pool) + ::sto/storage (ig/ref ::sto/storage) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.tasks.demo-purge/job-def + {::db/pool (ig/ref ::db/pool) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.loggers.webhooks/run-webhook-job-def + {::db/pool (ig/ref ::db/pool) + ::http.client/client (ig/ref ::http.client/client) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.loggers.webhooks/process-webhook-event-job-def + {::db/pool (ig/ref ::db/pool) + ::http.client/client (ig/ref ::http.client/client) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.tasks.file-gc/job-def + {::db/pool (ig/ref ::db/pool) + ::sto/storage (ig/ref ::sto/storage) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.tasks.offload-file-data/job-def + {::db/pool (ig/ref ::db/pool) + ::sto/storage (ig/ref ::sto/storage) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.tasks.objects-gc/job-def + {::db/pool (ig/ref ::db/pool) + ::sto/storage (ig/ref ::sto/storage) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.tasks.export-binfile/job-def + {::db/pool (ig/ref ::db/pool) + ::sto/storage (ig/ref ::sto/storage) + ::mtx/metrics (ig/ref ::mtx/metrics) + ;; the job belongs to a profile, so its events are published + ::mbus/msgbus (ig/ref ::mbus/msgbus)} + + :app.tasks.import-binfile/job-def + {::db/pool (ig/ref ::db/pool) + ::sto/storage (ig/ref ::sto/storage) + ::mtx/metrics (ig/ref ::mtx/metrics) + ::mbus/msgbus (ig/ref ::mbus/msgbus)} + + :app.storage.gc-deleted/job-def + {::db/pool (ig/ref ::db/pool) + ::sto/storage (ig/ref ::sto/storage) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.storage.gc-touched/job-def + {::db/pool (ig/ref ::db/pool) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.storage.pending-gc/job-def + {::db/pool (ig/ref ::db/pool) + ::sto/storage (ig/ref ::sto/storage) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.jobs.gc/job-def + {::db/pool (ig/ref ::db/pool) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.tasks.telemetry/job-def + {::db/pool (ig/ref ::db/pool) + ::http.client/client (ig/ref ::http.client/client) + ::setup/props (ig/ref ::setup/props) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + ::session/job-def + {::db/pool (ig/ref ::db/pool) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.tasks.file-gc-scheduler/job-def + {::db/pool (ig/ref ::db/pool) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.loggers.audit.consumer-task/nexus-job-def + {::db/pool (ig/ref ::db/pool) + ::http.client/client (ig/ref ::http.client/client) + ::setup/shared-keys (ig/ref ::setup/shared-keys) + ::mtx/metrics (ig/ref ::mtx/metrics)} + + :app.loggers.audit.consumer-task/admin-console-job-def + {::db/pool (ig/ref ::db/pool) + :app.nitrate/client (ig/ref :app.nitrate/client) + ::mtx/metrics (ig/ref ::mtx/metrics)} ::email/blacklist {} @@ -542,38 +795,6 @@ ::email/default-reply-to (cf/get :smtp-default-reply-to) ::email/default-from (cf/get :smtp-default-from)} - ::email/handler - {::email/sendmail (ig/ref ::email/sendmail)} - - :app.tasks.tasks-gc/handler - {::db/pool (ig/ref ::db/pool)} - - :app.tasks.objects-gc/handler - {::db/pool (ig/ref ::db/pool) - ::sto/storage (ig/ref ::sto/storage)} - - :app.tasks.delete-object/handler - {::db/pool (ig/ref ::db/pool) - ::sto/storage (ig/ref ::sto/storage)} - - :app.tasks.demo-purge/handler - {::db/pool (ig/ref ::db/pool)} - - :app.tasks.file-gc/handler - {::db/pool (ig/ref ::db/pool) - ::sto/storage (ig/ref ::sto/storage)} - - :app.tasks.file-gc-scheduler/handler - {::db/pool (ig/ref ::db/pool)} - - :app.tasks.offload-file-data/handler - {::db/pool (ig/ref ::db/pool) - ::sto/storage (ig/ref ::sto/storage)} - - :app.tasks.telemetry/handler - {::db/pool (ig/ref ::db/pool) - ::http.client/client (ig/ref ::http.client/client) - ::setup/props (ig/ref ::setup/props)} ::srepl/urepl {:port (cf/get :urepl-port 6062) @@ -607,22 +828,6 @@ ::setup/clock {} - :app.loggers.audit.archive-task/handler - {::setup/shared-keys (ig/ref ::setup/shared-keys) - ::http.client/client (ig/ref ::http.client/client) - ::db/pool (ig/ref ::db/pool)} - - :app.loggers.audit.gc-task/handler - {::db/pool (ig/ref ::db/pool)} - - ::webhooks/process-event-handler - {::db/pool (ig/ref ::db/pool) - ::http.client/client (ig/ref ::http.client/client)} - - ::webhooks/run-webhook-handler - {::db/pool (ig/ref ::db/pool) - ::http.client/client (ig/ref ::http.client/client)} - :app.loggers.mattermost/reporter {::http.client/client (ig/ref ::http.client/client)} @@ -665,41 +870,41 @@ (def worker-config {::wrk/cron - {::wrk/registry (ig/ref ::wrk/registry) + {::jobs/defs (ig/ref ::jobs/defs) ::db/pool (ig/ref ::db/pool) + ::mtx/metrics (ig/ref ::mtx/metrics) ::wrk/entries [{:cron #penpot/cron "0 0 0 * * ?" ;; daily - :task :session-gc} + :job :session-gc} {:cron #penpot/cron "0 0 0 * * ?" ;; daily - :task :objects-gc} - - ;; FIXME: temporary commented - ;; {:cron #penpot/cron "0 0 0 * * ?" ;; daily - ;; :task :storage-gc-deleted} + :job :objects-gc} {:cron #penpot/cron "0 0 0 * * ?" ;; daily - :task :storage-gc-touched} + :job :storage-gc-deleted} {:cron #penpot/cron "0 0 0 * * ?" ;; daily - :task :storage-pending-gc} + :job :storage-gc-touched} {:cron #penpot/cron "0 0 0 * * ?" ;; daily - :task :tasks-gc} + :job :storage-pending-gc} + + {:cron #penpot/cron "0 0 0 * * ?" ;; daily + :job :jobs-gc} {:cron #penpot/cron "0 0 2 * * ?" ;; daily - :task :file-gc-scheduler} + :job :file-gc-scheduler} {:cron #penpot/cron "0 30 */3,23 * * ?" - :task :telemetry} + :job :telemetry} - (when (contains? cf/flags :audit-log-archive) + (when (contains? cf/flags :nexus) {:cron #penpot/cron "0 */5 * * * ?" ;; every 5m - :task :audit-log-archive}) + :job :audit-log-consumer-nexus}) - (when (contains? cf/flags :audit-log-gc) - {:cron #penpot/cron "30 */5 * * * ?" ;; every 5m - :task :audit-log-gc})]} + (when (contains? cf/flags :admin-console) + {:cron #penpot/cron "30 */5 * * * ?" ;; every 5m, offset 30s from nexus + :job :audit-log-consumer-admin-console})]} ::wrk/dispatcher {::rds/client (ig/ref ::rds/client) @@ -707,23 +912,50 @@ ::db/pool (ig/ref ::db/pool) ::wrk/tenant (cf/get :tenant)} + ::jobs-metrics/sampler + {::db/pool (ig/ref ::db/pool) + ::mtx/metrics (ig/ref ::mtx/metrics)} + [::default ::wrk/runner] {::wrk/parallelism (cf/get :worker-default-parallelism 1) ::wrk/queue :default ::wrk/tenant (cf/get :tenant) ::rds/client (ig/ref ::rds/client) - ::wrk/registry (ig/ref ::wrk/registry) + ::jobs/defs (ig/ref ::jobs/defs) ::mtx/metrics (ig/ref ::mtx/metrics) - ::db/pool (ig/ref ::db/pool)} + ::db/pool (ig/ref ::db/pool) + ::mbus/msgbus (ig/ref ::mbus/msgbus)} [::webhook ::wrk/runner] {::wrk/parallelism (cf/get :worker-webhook-parallelism 1) ::wrk/queue :webhooks ::wrk/tenant (cf/get :tenant) ::rds/client (ig/ref ::rds/client) - ::wrk/registry (ig/ref ::wrk/registry) + ::jobs/defs (ig/ref ::jobs/defs) ::mtx/metrics (ig/ref ::mtx/metrics) - ::db/pool (ig/ref ::db/pool)}}) + ::db/pool (ig/ref ::db/pool) + ::mbus/msgbus (ig/ref ::mbus/msgbus)} + + ;; the heavy work of a user job does not share the default runner + [::binfile ::wrk/runner] + {::wrk/parallelism (cf/get :worker-binfile-parallelism 1) + ::wrk/queue :binfile + ::wrk/tenant (cf/get :tenant) + ::rds/client (ig/ref ::rds/client) + ::jobs/defs (ig/ref ::jobs/defs) + ::mtx/metrics (ig/ref ::mtx/metrics) + ::db/pool (ig/ref ::db/pool) + ::mbus/msgbus (ig/ref ::mbus/msgbus)} + + [::cron ::wrk/runner] + {::wrk/parallelism (cf/get :worker-cron-parallelism 2) + ::wrk/queue :cron + ::wrk/tenant (cf/get :tenant) + ::rds/client (ig/ref ::rds/client) + ::jobs/defs (ig/ref ::jobs/defs) + ::mtx/metrics (ig/ref ::mtx/metrics) + ::db/pool (ig/ref ::db/pool) + ::mbus/msgbus (ig/ref ::mbus/msgbus)}}) (defn start diff --git a/backend/src/app/mcp/oauth_clients.clj b/backend/src/app/mcp/oauth_clients.clj new file mode 100644 index 0000000000..2e36fa6751 --- /dev/null +++ b/backend/src/app/mcp/oauth_clients.clj @@ -0,0 +1,220 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.mcp.oauth-clients + "Client metadata from self-contained registrations or HTTPS documents. + Registration describes an application; it does not authorize a Penpot user. + Both sources resolve to the same metadata shape without a client database." + (:require + [app.http.client :as http] + [app.mcp.oauth-state :as state] + [app.util.cache :as cache] + [app.util.json :as json] + [clojure.string :as str]) + (:import + java.io.InputStream + java.net.URI + java.util.Base64)) + +(defn invalid! + ([error] (invalid! error 400)) + ([error status] + (throw (ex-info "MCP OAuth request rejected" {:oauth-error error :status status})))) + +(defn uri + [value] + (when (and (string? value) (<= 1 (count value) 2048)) + (try (URI. value) (catch Exception _ nil)))) + +(defn- loopback? + [^URI uri] + (and (= "http" (.getScheme uri)) + (contains? #{"127.0.0.1" "[::1]" "localhost"} (.getHost uri)))) + +(defn redirect-uri? + [value] + (when-let [uri (uri value)] + (and (nil? (.getFragment uri)) + (nil? (.getUserInfo uri)) + (or (and (= "https" (.getScheme uri)) (some? (.getHost uri))) + (loopback? uri) + ;; Native applications use a reverse-domain scheme. + (and (some? (.getScheme uri)) + (str/includes? (.getScheme uri) ".") + (not (.isOpaque uri))))))) + +(defn redirect-matches? + "Exact callback match, except for the port of loopback HTTP URLs. + Native applications may allocate a different local port for each login." + [registered requested] + (and (redirect-uri? requested) + (some (fn [value] + (or (= value requested) + (let [a (uri value) + b (uri requested)] + (and (loopback? a) (loopback? b) + (= (.getHost a) (.getHost b)) + (= (.getRawPath a) (.getRawPath b)) + (= (.getRawQuery a) (.getRawQuery b)))))) + (:redirect_uris registered)))) + +(defn- logo-uri? + [value] + (when-let [uri (uri value)] + (and (= "https" (.getScheme uri)) + (some? (.getHost uri)) + (nil? (.getUserInfo uri)) + (nil? (.getFragment uri))))) + +(defn validate-metadata + "Validate untrusted metadata and retain the fields this server supports. + CIMD selects public-client authentication from the advertised methods; + dynamic registration also supports Basic and form-post client secrets." + [metadata cimd?] + (let [redirects (:redirect_uris metadata) + grants (get metadata :grant_types ["authorization_code"]) + methods (if cimd? + (or (:token_endpoint_auth_methods_supported metadata) + [(get metadata :token_endpoint_auth_method "none")]) + [(get metadata :token_endpoint_auth_method "client_secret_basic")]) + method (if cimd? "none" (first methods))] + (when-not (and (map? metadata) + (vector? redirects) (<= 1 (count redirects) 20) + (every? redirect-uri? redirects) + (or (nil? (:client_name metadata)) + (and (string? (:client_name metadata)) + (<= 1 (count (:client_name metadata)) 200))) + ;; Published metadata describes client capabilities across + ;; servers. Extra grants do not require us to issue them. + (if cimd? + (and (vector? grants) (every? string? grants) + (some #{"authorization_code"} grants)) + (= ["authorization_code"] grants)) + (= ["code"] (get metadata :response_types ["code"])) + (or (nil? (:scope metadata)) (= "mcp" (:scope metadata))) + (if cimd? + (and (vector? methods) (some #{"none"} methods)) + (contains? #{"none" "client_secret_basic" "client_secret_post"} method))) + (invalid! "invalid_client_metadata")) + (cond-> {:redirect_uris redirects + :grant_types ["authorization_code"] + :response_types ["code"] + :scope "mcp" + :token_endpoint_auth_method method} + (:client_name metadata) (assoc :client_name (:client_name metadata)) + ;; Logos are optional browser-loaded images. Ignore unusable URLs + ;; instead of preventing authorization because of cosmetic metadata. + (logo-uri? (:logo_uri metadata)) (assoc :logo_uri (:logo_uri metadata))))) + +(defn register! + "Issue a reusable client ID and, when applicable, a separate client secret. + No client record is stored. The public ID carries only the secret hash; + the random nonce distinguishes registrations with otherwise equal metadata." + [_cfg metadata] + (let [metadata (validate-metadata metadata false) + secret (when-not (= "none" (:token_endpoint_auth_method metadata)) + (state/random-secret)) + ;; Omit fixed protocol values to keep IDs short; validation restores + ;; them on resolution. The version prefix defines the payload format. + payload (cond-> (-> metadata + (dissoc :grant_types :response_types :scope) + (assoc :nonce (state/random-secret))) + secret (assoc :secret-hash (state/digest secret))) + id (str "penpot-mcp-v1." + (.encodeToString (.withoutPadding (Base64/getUrlEncoder)) + (json/encode payload)))] + ;; Enforce the resolver's size cap here rather than issue an unusable ID. + (when (> (count id) 2048) + (invalid! "invalid_client_metadata")) + (cond-> (assoc metadata :client_id id + :client_id_issued_at (quot (System/currentTimeMillis) 1000)) + secret (assoc :client_secret secret :client_secret_expires_at 0)))) + +(defn- decode-client-id + [client-id] + ;; Open registration accepts client-supplied metadata, not a trusted identity. + ;; Changing these details changes the ID; codes are bound to the exact ID + ;; approved by the user, so an edited ID cannot redeem an existing code. + (try + (let [[_ encoded] (re-matches #"penpot-mcp-v1\.([A-Za-z0-9_-]+)" client-id) + _ (when-not encoded (invalid! "invalid_client")) + payload (json/decode (.decode (Base64/getUrlDecoder) ^String encoded)) + metadata (validate-metadata payload false) + nonce (:nonce payload) + secret-hash (:secret-hash payload)] + (when-not (and (string? nonce) (re-matches #"[A-Za-z0-9_-]{43}" nonce) + (if (= "none" (:token_endpoint_auth_method metadata)) + (nil? secret-hash) + (and (string? secret-hash) + (re-matches #"[A-Za-z0-9_-]{43}" secret-hash)))) + (invalid! "invalid_client")) + (cond-> (assoc metadata :client_id client-id) + secret-hash (assoc :secret-hash secret-hash))) + (catch Exception _ + (invalid! "invalid_client")))) + +(defonce ^:private metadata-cache + (cache/create :expire "1m" :max-size 256)) + +(defn- fetch-metadata + "Fetch a client's own HTTPS metadata document with bounded size and freshness. + The shared HTTP client checks SSRF targets and does not follow redirects." + [cfg client-id] + (let [response (http/req cfg {:method :get :uri client-id :timeout 5000 + :headers {"accept" "application/json"}} + {:response-type :input-stream})] + (with-open [^InputStream body (:body response)] + (when-not (and (= 200 (:status response)) + (str/starts-with? (get-in response [:headers "content-type"] "") + "application/json")) + (invalid! "invalid_client")) + ;; Bound both bytes read and time spent receiving the response body. + (let [reader (future (.readNBytes body 65537)) + bytes (deref reader 5000 ::timeout)] + (when (= ::timeout bytes) + (future-cancel reader) + (invalid! "invalid_client")) + (when (> (alength ^bytes bytes) 65536) + (invalid! "invalid_client")) + (let [metadata (json/decode bytes) + control (get-in response [:headers "cache-control"] "") + ;; The local cache ceiling never extends the publisher's TTL. + ;; Missing cache lifetime is treated as immediately stale. + max-age (some-> (re-find #"(?i)max-age=(\d+)" control) second parse-long) + ttl (if (re-find #"(?i)no-store|no-cache" control) + 0 + (min 60 (or max-age 0)))] + (when-not (= client-id (:client_id metadata)) + (invalid! "invalid_client")) + {:client (assoc (validate-metadata metadata true) :client_id client-id) + :expires (+ (System/currentTimeMillis) (* ttl 1000))}))))) + +(defn resolve-client + "Resolve a client ID to validated metadata for authorization or exchange. + HTTPS IDs use the document cache; versioned IDs decode locally without + PostgreSQL or Redis. Neither form establishes user approval." + [cfg client-id] + (when-not (and (string? client-id) (<= 1 (count client-id) 2048)) + (invalid! "invalid_client")) + (if-let [url (when (str/starts-with? client-id "https://") (uri client-id))] + (do + (when-not (and (.getHost url) + (nil? (.getUserInfo url)) + (nil? (.getFragment url)) + (not (str/blank? (.getPath url)))) + (invalid! "invalid_client")) + (try + (let [cached (cache/get metadata-cache client-id) + entry (if (and cached (> (:expires cached) (System/currentTimeMillis))) + cached + (fetch-metadata cfg client-id))] + (when-not (identical? cached entry) + (cache/invalidate metadata-cache client-id) + (cache/get metadata-cache client-id (constantly entry))) + (:client entry)) + (catch Exception _ + (invalid! "invalid_client")))) + (decode-client-id client-id))) diff --git a/backend/src/app/mcp/oauth_rate.lua b/backend/src/app/mcp/oauth_rate.lua new file mode 100644 index 0000000000..0a8a168cfc --- /dev/null +++ b/backend/src/app/mcp/oauth_rate.lua @@ -0,0 +1,5 @@ +-- Copyright (c) KALEIDOS SUBSIDIARY SL +-- MPL-2.0 +local count = redis.call('INCR', KEYS[1]) +if count == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]) end +return {count} diff --git a/backend/src/app/mcp/oauth_state.clj b/backend/src/app/mcp/oauth_state.clj new file mode 100644 index 0000000000..6f27a3b05e --- /dev/null +++ b/backend/src/app/mcp/oauth_state.clj @@ -0,0 +1,105 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.mcp.oauth-state + "Temporary approval requests and authorization codes in Redis/Valkey. + Handles are opaque; tenant, record kind and handle hash form the key. + Mutations compare the exact record that the HTTP handler validated." + (:require + [app.config :as cf] + [app.redis :as rds] + [app.redis.script :as-alias script] + [app.util.json :as json]) + (:import + java.nio.charset.StandardCharsets + java.security.MessageDigest + java.security.SecureRandom + java.util.Base64)) + +(defn random-secret + "32 random bytes encoded as a 43-character, unpadded base64url string." + [] + (let [bytes (byte-array 32)] + (.nextBytes (SecureRandom.) bytes) + (.encodeToString (.withoutPadding (Base64/getUrlEncoder)) bytes))) + +(defn digest + "Unpadded base64url SHA-256 digest, also used for PKCE S256 challenges." + [^String value] + (-> (.digest (MessageDigest/getInstance "SHA-256") + (.getBytes value StandardCharsets/UTF_8)) + (#(.encodeToString (.withoutPadding (Base64/getUrlEncoder)) %)))) + +(defn secret= + [a b] + (and (string? a) (string? b) + (MessageDigest/isEqual (.getBytes ^String a StandardCharsets/UTF_8) + (.getBytes ^String b StandardCharsets/UTF_8)))) + +(defn- state-key + [kind handle] + (str "penpot:" (cf/get :tenant) ":mcp-oauth:" (name kind) ":" (digest handle))) + +(defn create! + "Store a new record for ttl seconds and return its random handle. + The handle is returned only after Redis confirms SET NX EX succeeded." + [cfg kind value ttl] + (let [handle (random-secret) + result (rds/run! cfg + (fn [{::rds/keys [conn]}] + (rds/set conn (state-key kind handle) + (json/encode-str value) {:ex ttl :nx true})))] + (when-not (= "OK" result) + (throw (ex-info "Unable to store OAuth state" {:oauth-error "temporarily_unavailable"}))) + handle)) + +(defn read! + "Read an unexpired record, or nil for a missing/malformed handle. + Return both decoded :value for validation and original :raw for atomic + comparison; serializing the decoded map again is not an equivalent check." + [cfg kind handle] + (when (and (string? handle) (re-matches #"[A-Za-z0-9_-]{43}" handle)) + (when-let [raw (rds/run! cfg (fn [{::rds/keys [conn]}] + (rds/get conn (state-key kind handle))))] + {:raw raw :value (json/decode raw)}))) + +(defn- compare-and-update! + "Atomically change only the exact record the caller read and validated. + Lua deletes for nil replacement, or replaces while preserving the TTL. + False means the record expired or another request changed/consumed it." + [cfg kind handle record replacement] + (let [result (rds/run! cfg + (fn [{::rds/keys [conn]}] + (rds/eval conn + {::script/name ::compare-and-update + ::script/path "app/mcp/oauth_state.lua" + ::script/keys [(state-key kind handle)] + ::script/vals [(:raw record) (or replacement "")]})))] + (= [1] result))) + +(defn consume! + "Delete a validated record once; true only for the request that consumes it." + [cfg kind handle record] + (compare-and-update! cfg kind handle record nil)) + +(defn replace! + "Replace a validated record without extending its original expiration." + [cfg kind handle record value] + (compare-and-update! cfg kind handle record (json/encode-str value))) + +(defn allow-request! + "Limit requests per endpoint kind, tenant and remote address. + The one-minute window begins with the first request; subsequent requests + increment the count without extending that window." + [cfg kind remote-address limit] + (let [[count] (rds/run! cfg + (fn [{::rds/keys [conn]}] + (rds/eval conn + {::script/name ::rate + ::script/path "app/mcp/oauth_rate.lua" + ::script/keys [(state-key kind (or remote-address "unknown"))] + ::script/vals [60]})))] + (and (number? count) (<= count limit)))) diff --git a/backend/src/app/mcp/oauth_state.lua b/backend/src/app/mcp/oauth_state.lua new file mode 100644 index 0000000000..b7bcd02330 --- /dev/null +++ b/backend/src/app/mcp/oauth_state.lua @@ -0,0 +1,11 @@ +-- Copyright (c) KALEIDOS SUBSIDIARY SL +-- MPL-2.0 +if redis.call('GET', KEYS[1]) ~= ARGV[1] then + return {0} +end +if ARGV[2] == '' then + redis.call('DEL', KEYS[1]) +else + redis.call('SET', KEYS[1], ARGV[2], 'KEEPTTL') +end +return {1} diff --git a/backend/src/app/metrics.clj b/backend/src/app/metrics.clj index fcf92b5228..673c193e38 100644 --- a/backend/src/app/metrics.clj +++ b/backend/src/app/metrics.clj @@ -7,6 +7,7 @@ (ns app.metrics (:refer-clojure :exclude [run!]) (:require + [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] [app.metrics.definition :as-alias mdef] @@ -179,6 +180,25 @@ (swap! warned-hints conj hint) (l/wrn :hint hint :cause cause)))) +(defn instance + "Resolve the metrics instance from a cfg; raises when it is absent. + + The shared resolver: a module that records metrics takes a cfg and + calls this, so no caller has to know where the instance lives or + capture it beforehand. That matters most inside an + `app.db/after-commit` callback, which closes over whatever it was + given and cannot read the cfg again later. + + Raises on purpose. A cfg without metrics is a wiring bug, and it must + surface where the metric is recorded rather than disappear: a caller + that resolved this at the top of a write and dropped the value is + precisely the pattern this replaces." + [cfg] + (or (::metrics cfg) + (ex/raise :type :assertion + :code :missing-metrics + :hint "missing ::mtx/metrics on the provided cfg"))) + (defn run! "Record a metric. diff --git a/backend/src/app/migrations.clj b/backend/src/app/migrations.clj index 4daef3a0ee..9e1a91b636 100644 --- a/backend/src/app/migrations.clj +++ b/backend/src/app/migrations.clj @@ -507,6 +507,9 @@ {:name "0154-add-upload-session-chunk-table" :fn (mg/resource "app/migrations/sql/0154-add-upload-session-chunk-table.sql")} + {:name "0154-add-job-table" + :fn (mg/resource "app/migrations/sql/0154-add-job-table.sql")} + {:name "0155-normalize-storage-object-metadata" :fn (mg/resource "app/migrations/sql/0155-normalize-storage-object-metadata.sql")} @@ -528,6 +531,9 @@ {:name "0156-reset-pg-storage-defaults" :fn (mg/resource "app/migrations/sql/0156-reset-pg-storage-defaults.sql")} + {:name "0157-add-audit-log-pending-table" + :fn (mg/resource "app/migrations/sql/0157-add-audit-log-pending-table.sql")} + {:name "0157-migrate-legacy-file-data" :fn (mg/resource "app/migrations/sql/0157-migrate-legacy-file-data.sql")} diff --git a/backend/src/app/migrations/sql/0154-add-job-table.sql b/backend/src/app/migrations/sql/0154-add-job-table.sql new file mode 100644 index 0000000000..4c2c7c6bae --- /dev/null +++ b/backend/src/app/migrations/sql/0154-add-job-table.sql @@ -0,0 +1,150 @@ +-- Migration: Add unified `job` table. +-- +-- A single row per unit of work, replacing the generic `task` table as the +-- durable substrate for backend jobs. Columns combine the dispatch/lifecycle +-- fields of `task` with optional user-facing ledger fields (borrowed from the +-- planned export_job/import_job tables, which this migration supersedes). +-- +-- Job kinds are identified by `name` (the job-def registry key); user jobs are +-- marked by `profile_id IS NOT NULL` and expirable jobs by `expires_at IS NOT +-- NULL`. There is no `modified_at` trigger: the application code updates it on +-- every UPDATE (used for the unified lease/orphan detection). +-- +-- `params` carries the business payload (plain JSON) of the job. Progress is +-- not a column: it is an append-only `job_event` row, so a job keeps its whole +-- progress history instead of only the last value. +-- +-- `tenant` is the instance the job belongs to and `queue` is its bare queue +-- name. They are separate columns because several instances can share one +-- database and the tenant is what keeps them from claiming each other's work: +-- as an equality the filter is something an index can serve, where a +-- `:` prefix could only be matched with a LIKE over text. Every +-- query that acts on one instance's work (dispatch, orphan sweep, reschedule, +-- cron no-overlap check, submit dedupe) filters on `tenant` for that reason. +-- The prefix still exists, but only in the Redis key the queue's payloads +-- travel through, composed in one place by `app.worker/queue-key`. +-- +-- The legacy `task` table stays in place (dormant) on purpose until the +-- next version, so PRE (legacy) and HOURLY (jobs) can run in parallel on +-- the same database and the deploy can roll back. During that window +-- each table is cleaned by its own version: `task` rows by the parallel +-- legacy `tasks-gc`, `job` rows by the new `:jobs-gc`. + +CREATE TABLE job ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + name text NOT NULL, + tenant text NOT NULL, + queue text NOT NULL, + label text, + priority int NOT NULL DEFAULT 100, + scheduled_at timestamptz NOT NULL DEFAULT now(), + retry_num int NOT NULL DEFAULT 0, + max_retries int NOT NULL DEFAULT 3, + status text NOT NULL DEFAULT 'new' + CHECK (status IN ('new', 'scheduled', 'running', 'retry', + 'completed', 'failed', 'cancelled', + 'aborted')), + created_at timestamptz NOT NULL DEFAULT now(), + modified_at timestamptz NOT NULL DEFAULT now(), + started_at timestamptz, + completed_at timestamptz, + params jsonb NOT NULL DEFAULT '{}', + + -- optional ledger columns (user-facing jobs) + profile_id uuid NULL REFERENCES profile(id) ON DELETE NO ACTION DEFERRABLE, + error jsonb, + result jsonb, + resource_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL DEFERRABLE, + expires_at timestamptz +); + +-- Dispatcher claim index: the worker selects due `new`/`retry` rows of +-- its own tenant, orders them by priority and scheduled time, and locks +-- the batch with SKIP LOCKED. The partial predicate keeps terminal rows +-- out and `status` needs no key column for that reason; `tenant` leads +-- because it is an equality the index can answer, and the leading +-- columns then carry the ORDER BY unchanged. `queue` does not lead: the +-- dispatcher claims every queue of its tenant and groups them when +-- pushing, so filtering on one queue would be wrong. +CREATE INDEX job__dispatcher__idx + ON job (tenant, priority DESC, scheduled_at) + WHERE status IN ('new', 'retry'); + +-- Dispatcher orphan sweep: the worker finds the `running` rows of its +-- own tenant whose modified_at is older than the lease and marks them +-- `aborted` (system-side terminal, never retried). +CREATE INDEX job__orphan__idx + ON job (tenant, modified_at) + WHERE status = 'running'; + +-- User-facing job ledger index: it supports filtering by profile and +-- ordering a user's jobs by newest first. No current production query +-- uses it yet; it is kept for the user-facing ledger path. +CREATE INDEX job__profile__idx + ON job (profile_id, created_at DESC) + WHERE profile_id IS NOT NULL; + +-- Cron no-overlap check and submit dedupe: both look for jobs of one +-- tenant with the same name and queue and label that have not started +-- yet, so one index serves the two queries. +CREATE INDEX job__name_label__idx + ON job (tenant, name, queue, label) + WHERE status IN ('new', 'scheduled', 'running', 'retry'); + +-- Storage GC reference check: it answers whether any job still points +-- to a candidate storage object. Rows without a resource are excluded. +CREATE INDEX job__resource__idx + ON job (resource_id) + WHERE resource_id IS NOT NULL; + +-- Dispatcher recovery: it finds the `scheduled` jobs of its own tenant +-- that were pushed to Redis but not claimed within the recovery window. +CREATE INDEX job__scheduled__idx + ON job (tenant, scheduled_at) + WHERE status = 'scheduled'; + +-- Jobs GC expiration: it scans jobs past expires_at while excluding +-- running and retry jobs from the expiration delete. +CREATE INDEX job__expires__idx + ON job (expires_at) + WHERE expires_at IS NOT NULL; + +-- Jobs GC retention: it finds old internal terminal jobs with no +-- profile, which are eligible for removal after the retention delay. +CREATE INDEX job__retention__idx + ON job (modified_at) + WHERE status IN ('completed', 'failed', 'cancelled', 'aborted') + AND profile_id IS NULL; + +-- Append-only job event log: `start`, `progress`, `retry` and `end` rows in +-- insertion order. This is the only source of truth for progress; there is no +-- Redis key and no mutable progress column. The foreign key cascades so +-- deleting a job removes its whole history, and it is DEFERRABLE like every +-- other new reference of the substrate. +CREATE TABLE job_event ( + id bigserial PRIMARY KEY, + job_id uuid NOT NULL REFERENCES job(id) ON DELETE CASCADE DEFERRABLE, + kind text NOT NULL + CHECK (kind IN ('start', 'progress', 'retry', 'end')), + payload jsonb NOT NULL DEFAULT '{}', + created_at timestamptz NOT NULL DEFAULT now() +); + +-- Event history read: all events of a job, and the last progress report +-- (`WHERE job_id = ? AND kind = 'progress' ORDER BY created_at DESC LIMIT 1`). +CREATE INDEX job_event__job_kind_created_idx + ON job_event (job_id, kind, created_at DESC, id DESC); + +-- Deletion protection, like the domain tables (`team`, `profile`, the +-- file thumbnail tables). The only intended deleter of `job` rows is the +-- jobs GC, which also marks the referenced storage objects as touched in +-- the same transaction; a stray `DELETE FROM job` would drop the row +-- (and cascade `job_event`) without touching the object, orphaning it +-- forever. The GC and the submit dedupe disable the guard inside their +-- transaction with `SET LOCAL rules.deletion_protection TO off`, the +-- same escape hatch used by `app.tasks.objects-gc`. +CREATE OR REPLACE TRIGGER deletion_protection__tgr +BEFORE DELETE ON job FOR EACH STATEMENT + WHEN ((current_setting('rules.deletion_protection', true) IN ('on', '')) OR + (current_setting('rules.deletion_protection', true) IS NULL)) + EXECUTE PROCEDURE raise_deletion_protection(); diff --git a/backend/src/app/migrations/sql/0157-add-audit-log-pending-table.sql b/backend/src/app/migrations/sql/0157-add-audit-log-pending-table.sql new file mode 100644 index 0000000000..ce797dd12c --- /dev/null +++ b/backend/src/app/migrations/sql/0157-add-audit-log-pending-table.sql @@ -0,0 +1,39 @@ +-- Work queue for the audit log consumers: one row per (record, +-- consumer) pair still pending. Inserting a record creates its +-- pendings; each consumer deletes its own on completion, and whoever +-- deletes the last pending also deletes the record. Consumer ids are +-- fixed in code and never reused: 1 = nexus, 2 = admin console. + +CREATE TABLE audit_log_pending ( + -- DEFERRABLE so the test-suite reset (which deletes table by table + -- with SET CONSTRAINTS ALL DEFERRED) keeps working; the guard still + -- fires when a record with pendings is deleted. Plain DEFERRABLE + -- (initially immediate) is enough: every deleter removes pendings + -- before rows, so the check passes at each statement. + audit_log_id uuid NOT NULL REFERENCES audit_log(id) DEFERRABLE, + consumer smallint NOT NULL CHECK (consumer BETWEEN 1 AND 20), + PRIMARY KEY (consumer, audit_log_id)); + +-- Lets the final delete ask "is anything still pending for this +-- record?" without scanning the whole queue. +CREATE INDEX ON audit_log_pending (audit_log_id); + +-- NOTE: no backfill on purpose. Rows predating this queue (sent or +-- not) get no pendings, so the orphan sweep deletes them unsent. +-- Losing the unarchived ones at upgrade time is accepted: this +-- table is a minutes/hours buffer, not a store. + +-- The pending queue replaces the archived_at mark: whoever deletes +-- the last pending deletes the record, so the flag column and the +-- indexes that served the old mark-and-sweep scans go away. +DROP INDEX IF EXISTS audit_log__created_at__idx; +DROP INDEX IF EXISTS audit_log__archived_at__idx; +DROP INDEX IF EXISTS audit_log_id_archived_at_idx; +ALTER TABLE audit_log DROP COLUMN IF EXISTS archived_at; + +-- Serves the consumers' chronological scan (ORDER BY created_at, id): +-- one total order for every consumer, so concurrent drains cannot +-- deadlock on row locks. Covers the whole ORDER BY (bulk ingest +-- shares request-at constantly, so ties are the norm). +CREATE INDEX IF NOT EXISTS audit_log__created_at__idx + ON audit_log (created_at ASC, id); diff --git a/backend/src/app/nitrate.clj b/backend/src/app/nitrate.clj index 3b4b41eff0..d0d8c3e219 100644 --- a/backend/src/app/nitrate.clj +++ b/backend/src/app/nitrate.clj @@ -7,6 +7,7 @@ (ns app.nitrate "Module that make calls to the external nitrate aplication" (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.json :as json] @@ -16,7 +17,6 @@ [app.common.time :as ct] [app.common.types.organization :as cto :refer [schema:nitrate-sso]] - [app.common.uri :as u] [app.config :as cf] [app.http.client :as http] [app.http.session :as session] @@ -31,31 +31,17 @@ ;; HELPERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn- join-path-segments - "Build a single relative path from Nitrate URI segments, normalizing slashes." - [segments] - (let [path (->> segments (map str) (str/join "/"))] - (->> (str/split path #"/") - (remove str/blank?) - (str/join "/")))) - -(defn- join-base-uri - "Join path segments to a base URI." - [base-uri & segments] - (u/join (u/ensure-path-slash base-uri) - (join-path-segments segments))) - (defn- generate-nitrate-uri "Joins relative path segments to the Nitrate backend URI. Segments must not start with `/`" [& segments] - (apply join-base-uri (cf/get :admin-console-uri) segments)) + (apply cf/join-uri (cf/get :admin-console-uri) segments)) (defn- generate-public-uri "Joins relative path segments to the public backend URI. Segments must not start with `/`" [& segments] - (apply join-base-uri (cf/get :public-uri) segments)) + (apply cf/get-public-uri segments)) (defn- request-builder [cfg method uri shared-key profile-id request-params] @@ -279,7 +265,7 @@ (request-to-nitrate cfg :get (generate-nitrate-uri "api/organizations/" - organization-id + (str organization-id "/") "members/" profile-id) schema:profile-organization params)) @@ -289,7 +275,7 @@ (request-to-nitrate cfg :get (generate-nitrate-uri "api/teams/" - team-id + (str team-id "/") "users/" profile-id) schema:profile-organization params)) @@ -299,7 +285,7 @@ (request-to-nitrate cfg :get (generate-nitrate-uri "api/organizations/" - organization-id + (str organization-id "/") "summary") schema:organization-summary params)) @@ -308,7 +294,7 @@ (request-to-nitrate cfg :get (generate-nitrate-uri "api/users/" - profile-id + (str profile-id "/") "owned-organizations") [:vector schema:organization-summary] params)) @@ -328,7 +314,7 @@ (let [organizations (request-to-nitrate cfg :get (generate-nitrate-uri "api/users/" - profile-id + (str profile-id "/") "owned-organizations-summary") [:vector schema:organization-summary-counts] params)] @@ -343,7 +329,7 @@ (request-to-nitrate cfg :post (generate-nitrate-uri "api/users/" - profile-id + (str profile-id "/") "cleanup-after-deletion") nil params)) @@ -354,7 +340,7 @@ team (request-to-nitrate cfg :post (generate-nitrate-uri "api/organizations/" - organization-id + (str organization-id "/") "add-team") cto/schema:team-with-organization params) custom-photo (when-let [logo-id (dm/get-in team [:organization :logo-id])] @@ -371,7 +357,7 @@ (request-to-nitrate cfg :post (generate-nitrate-uri "api/organizations/" - organization-id + (str organization-id "/") "add-user") schema:profile-organization params))) @@ -386,7 +372,7 @@ (request-to-nitrate cfg :post (generate-nitrate-uri "api/organizations/" - organization-id + (str organization-id "/") "remove-user") nil params))) @@ -396,7 +382,7 @@ (request-to-nitrate cfg :post (generate-nitrate-uri "api/organizations/" - organization-id + (str organization-id "/") "remove-team") nil params))) @@ -453,12 +439,13 @@ (request-to-nitrate cfg :get (generate-nitrate-uri "api/organizations/" - organization-id + (str organization-id "/") "permissions") [:map [:organization-id ::sm/uuid] [:owner-id ::sm/uuid] - [:permissions [:map-of :keyword :string]]] + [:permissions [:map-of :keyword :string]] + [:restrictions-met {:optional true} [:maybe [:map-of :keyword :boolean]]]] params)) (defn- get-organization-sso-api @@ -467,7 +454,7 @@ (request-to-nitrate cfg :get (generate-nitrate-uri "api/organizations/" - organization-id + (str organization-id "/") "sso") schema:nitrate-sso params)) @@ -475,7 +462,7 @@ (defn- get-organization-sso-by-team-api [cfg {:keys [team-id] :as params}] (request-to-nitrate cfg :get - (generate-nitrate-uri "api/teams/" team-id "sso") + (generate-nitrate-uri "api/teams/" (str team-id "/") "sso") schema:nitrate-sso params)) @@ -484,7 +471,7 @@ (request-to-nitrate cfg :get (generate-nitrate-uri "api/organizations/" - organization-id + (str organization-id "/") "members-list") [:vector ::sm/uuid] params)) @@ -496,6 +483,29 @@ schema:redeem-result (assoc params :throw-on-error? true))) +(def ^:private audit-event-keys + [:id :name :type :profile-id :profile-email :profile-name + :props :context :created-at :tracked-at :source :ip-addr]) + +(defn- ->audit-event + [event] + (-> (select-keys event audit-event-keys) + (d/without-nils))) + +(defn- ingest-audit-log-api + "POST a batch of audit events to Admin Console `/api/audit-log`. + Expects HTTP 204; returns nil on success. 4xx raises when + `:throw-on-error?` is set." + [cfg {:keys [events] :as params}] + (let [request-params {:events (mapv ->audit-event events)} + params (assoc params + :request-params request-params + :throw-on-error? true)] + (request-to-nitrate cfg :post + (generate-nitrate-uri "api/audit-log") + nil + params))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; INITIALIZATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -505,28 +515,29 @@ (when (contains? cf/flags :admin-console) (when (nil? (cf/get :admin-console-uri)) (l/warn :hint "admin console is not configured; nitrate calls will fail until the setup is complete")) - {:get-team-organization (partial get-team-organization-api cfg) - :get-teams-organizations (partial get-teams-organizations-api cfg) - :set-team-organization (partial set-team-organization-api cfg) - :get-organization-membership (partial get-organization-membership-api cfg) - :get-organization-membership-by-team (partial get-organization-membership-by-team-api cfg) - :get-organization-summary (partial get-organization-summary-api cfg) - :get-owned-organizations (partial get-owned-organizations-api cfg) - :get-owned-organizations-summary (partial get-owned-organizations-summary-api cfg) - :get-organization-members (partial get-organization-members-api cfg) - :cleanup-deleted-penpot-user (partial cleanup-deleted-penpot-user-api cfg) - :add-profile-to-organization (partial add-profile-to-organization-api cfg) - :remove-profile-from-organization (partial remove-profile-from-organization-api cfg) - :get-organization-permissions (partial get-organization-permissions-api cfg) - :get-organization-sso-by-team (partial get-organization-sso-by-team-api cfg) - :get-organization-sso (partial get-organization-sso-api cfg) - :delete-team (partial delete-team-api cfg) - :remove-team-from-organization (partial remove-team-from-organization-api cfg) - :get-subscription (partial get-subscription-api cfg) - :get-subscription-warning (partial get-subscription-warning-api cfg) - :connectivity (partial get-connectivity-api cfg) - :get-identity (partial get-identity-api cfg) - :redeem-activation-code (partial redeem-activation-code-api cfg)})) + {:get-team-organization (partial get-team-organization-api cfg) + :get-teams-organizations (partial get-teams-organizations-api cfg) + :set-team-organization (partial set-team-organization-api cfg) + :get-organization-membership (partial get-organization-membership-api cfg) + :get-organization-membership-by-team (partial get-organization-membership-by-team-api cfg) + :get-organization-summary (partial get-organization-summary-api cfg) + :get-owned-organizations (partial get-owned-organizations-api cfg) + :get-owned-organizations-summary (partial get-owned-organizations-summary-api cfg) + :get-organization-members (partial get-organization-members-api cfg) + :cleanup-deleted-penpot-user (partial cleanup-deleted-penpot-user-api cfg) + :add-profile-to-organization (partial add-profile-to-organization-api cfg) + :remove-profile-from-organization (partial remove-profile-from-organization-api cfg) + :get-organization-permissions (partial get-organization-permissions-api cfg) + :get-organization-sso-by-team (partial get-organization-sso-by-team-api cfg) + :get-organization-sso (partial get-organization-sso-api cfg) + :delete-team (partial delete-team-api cfg) + :remove-team-from-organization (partial remove-team-from-organization-api cfg) + :get-subscription (partial get-subscription-api cfg) + :get-subscription-warning (partial get-subscription-warning-api cfg) + :connectivity (partial get-connectivity-api cfg) + :get-identity (partial get-identity-api cfg) + :redeem-activation-code (partial redeem-activation-code-api cfg) + :ingest-audit-log (partial ingest-audit-log-api cfg)})) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; UTILS @@ -642,6 +653,35 @@ :cause cause) team))))) +(defn add-organization-info-to-team-strict + "Like `add-organization-info-to-team`, but fails closed for authorization + checks: the team is returned unchanged only when Nitrate answers 404 (the + team belongs to no organization). Any other failure, including a 4xx or an + invalid response body, raises instead of looking like a team without an + organization." + [cfg team params] + (let [params (assoc (or params {}) + :team-id (:id team) + :throw-on-error? true) + team-with-organization + (try + (call cfg :get-team-organization params) + (catch Throwable cause + (let [{:keys [type status]} (ex-data cause)] + (if (and (= :nitrate-http-error type) (= 404 status)) + ::no-organization + (throw cause)))))] + (cond + (= ::no-organization team-with-organization) + team + + (nil? team-with-organization) + (ex/raise :type :nitrate-unavailable + :hint "nitrate did not return a valid team organization response") + + :else + (apply-organization-info-to-team team team-with-organization)))) + (defn add-organization-info-to-teams "Enriches teams with organization information using one batched Nitrate request. Teams absent from the Nitrate response are returned unchanged. diff --git a/backend/src/app/redis.clj b/backend/src/app/redis.clj index 0ab41cc70d..146e8f54fe 100644 --- a/backend/src/app/redis.clj +++ b/backend/src/app/redis.clj @@ -78,6 +78,7 @@ (-hget [_ key field]) (-set [_ key val args]) (-del [_ key-or-keys]) + (-expire [_ key seconds]) (-ping [_])) (defprotocol IPubSubConnection @@ -234,6 +235,10 @@ (let [keys (into-array String keys)] (.del cmd ^String/1 keys))) + (-expire [_ key seconds] + (assert (string? key) "key expected to be string") + (.expire cmd ^String key ^long seconds)) + (-ping [_] (.ping cmd)) @@ -511,6 +516,11 @@ [cause] (instance? RedisCommandTimeoutException cause)) +(defn expire + [conn key seconds] + (assert (string? key) "key must be string instance") + (-expire conn key seconds)) + (defn exception? [cause] (instance? RedisException cause)) diff --git a/backend/src/app/rpc.clj b/backend/src/app/rpc.clj index c5e464c75e..f9c9d7eabf 100644 --- a/backend/src/app/rpc.clj +++ b/backend/src/app/rpc.clj @@ -13,7 +13,6 @@ [app.common.schema :as sm] [app.common.spec :as us] [app.common.time :as ct] - [app.common.uri :as u] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] @@ -125,7 +124,8 @@ (cond-> (and (not (uuid? session-pid)) (uuid? token-pid)) (-> (assoc ::auth-type :token) - (assoc ::token-perms (set (::actoken/perms request #{}))))) + (assoc ::token-perms (set (::actoken/perms request #{}))) + (assoc ::token-type (::actoken/type request)))) (cond-> key-id (assoc ::auth-key-id key-id)) ;; Server-side only: request bodies cannot forge @@ -172,10 +172,12 @@ [_ f mdata] (let [required-auth? (::auth mdata true) required-auth-type (::auth-type mdata) + required-token-type (::token-type mdata) required-perms (into #{} (::perms mdata))] (fn [cfg params] (let [profile-id (::profile-id params) auth-type (::auth-type params) + token-type (::token-type params) token-perms (set (::token-perms params #{}))] (cond (and required-auth? (not (uuid? profile-id))) @@ -183,8 +185,13 @@ :code :authentication-required :hint "authentication required for this endpoint") - (and (= required-auth-type :token) - (not= auth-type :token)) + (or (and (= required-auth-type :token) + (not= auth-type :token)) + (and required-token-type + (or (not= auth-type :token) + (not= required-token-type token-type))) + (and (= token-type "mcp") + (not= required-token-type "mcp"))) (ex/raise :type :authorization :code :token-auth-required :hint "access token authentication required for this endpoint") @@ -359,6 +366,28 @@ (wrap-params-validation cfg $ mdata) (wrap-authentication cfg $ mdata))) +(defn- wrap-shared-key-authorization + "Authorizes a shared-key caller against the method's `::allowed-shared-keys`. + + A management method declares the shared-key ids allowed to call it with + `::allowed-shared-keys`. When the metadata is absent any configured shared key is + accepted, preserving the previous behavior. When it is present and empty no + key is accepted." + [_ f mdata] + (if (contains? mdata ::allowed-shared-keys) + (let [allowed-keys (::allowed-shared-keys mdata)] + (fn [cfg params] + (if (contains? allowed-keys (::auth-key-id params)) + (f cfg params) + (do + (l/wrn :hint "shared key is not allowed for this method" + :handler-name (::handler-name params) + :key-id (::auth-key-id params)) + (ex/raise :type :authorization + :code :shared-key-not-allowed + :hint "shared key is not allowed for this method"))))) + f)) + (defn- wrap-management [cfg f mdata] (as-> f $ @@ -369,7 +398,10 @@ (wrap-audit cfg $ mdata) (wrap-nitrate-sso cfg $ mdata) (wrap-params-validation cfg $ mdata) - (wrap-authentication cfg $ mdata))) + (wrap-authentication cfg $ mdata) + ;; Outermost on purpose: a denied key never opens a transaction, + ;; validates params or runs the method body. + (wrap-shared-key-authorization cfg $ mdata))) (defn- process-method [cfg wrap-fn [f mdata]] @@ -401,6 +433,7 @@ 'app.rpc.commands.files-update 'app.rpc.commands.files-snapshot 'app.rpc.commands.files-thumbnails + 'app.rpc.commands.jobs 'app.rpc.commands.ldap 'app.rpc.commands.management 'app.rpc.commands.media @@ -448,6 +481,7 @@ [cfg] (let [cfg (assoc cfg ::module "management" ::type "command" ::metrics-id :rpc-management-timing) mods (cond->> (list 'app.rpc.management.exporter + 'app.rpc.management.jobs 'app.rpc.management.files) (contains? cf/flags :admin-console) (cons 'app.rpc.management.nitrate))] @@ -504,42 +538,40 @@ (defmethod ig/init-key ::routes [_ {:keys [::methods ::management-methods ::setup/shared-keys] :as cfg}] + ["/api" + ["/management" + ["/methods/:method-name" + {:middleware [[mw/shared-key-auth shared-keys] + [session/authz cfg]] + :handler (make-rpc-handler management-methods)}] - (let [public-uri (cf/get :public-uri)] - ["/api" - ["/management" - ["/methods/:method-name" - {:middleware [[mw/shared-key-auth shared-keys] - [session/authz cfg]] - :handler (make-rpc-handler management-methods)}] + (doc/routes :methods management-methods + :label "management" + :base-uri (cf/get-public-uri "api/management") + :description "MANAGEMENT API")] - (doc/routes :methods management-methods - :label "management" - :base-uri (u/join public-uri "/api/management") - :description "MANAGEMENT API")] + ["/main" + ["/methods/:method-name" + {:middleware [[mw/cors] + [sec/client-header-check] + [session/authz cfg] + [actoken/authz (assoc cfg ::actoken/allow-mcp true)]] + :handler (make-rpc-handler methods)}] - ["/main" - ["/methods/:method-name" - {:middleware [[mw/cors] - [sec/client-header-check] - [session/authz cfg] - [actoken/authz cfg]] - :handler (make-rpc-handler methods)}] + (doc/routes :methods methods + :label "main" + :base-uri (cf/get-public-uri "api/main") + :description "MAIN API")] - (doc/routes :methods methods - :label "main" - :base-uri (u/join public-uri "/api/main") - :description "MAIN API")] + ;; BACKWARD COMPATIBILITY + ["/_doc" {:handler (redirect (cf/get-public-uri "api/main/doc"))}] + ["/doc" {:handler (redirect (cf/get-public-uri "api/main/doc"))}] + ["/openapi" {:handler (redirect (cf/get-public-uri "api/main/doc/openapi"))}] + ["/openapi.join" {:handler (redirect (cf/get-public-uri "api/main/doc/openapi.json"))}] - ;; BACKWARD COMPATIBILITY - ["/_doc" {:handler (redirect (u/join public-uri "/api/main/doc"))}] - ["/doc" {:handler (redirect (u/join public-uri "/api/main/doc"))}] - ["/openapi" {:handler (redirect (u/join public-uri "/api/main/doc/openapi"))}] - ["/openapi.join" {:handler (redirect (u/join public-uri "/api/main/doc/openapi.json"))}] - - ["/rpc/command/:method-name" - {:middleware [[mw/cors] - [sec/client-header-check] - [session/authz cfg] - [actoken/authz cfg]] - :handler (make-rpc-handler methods)}]])) + ["/rpc/command/:method-name" + {:middleware [[mw/cors] + [sec/client-header-check] + [session/authz cfg] + [actoken/authz (assoc cfg ::actoken/allow-mcp true)]] + :handler (make-rpc-handler methods)}]]) diff --git a/backend/src/app/rpc/commands/access_token.clj b/backend/src/app/rpc/commands/access_token.clj index 09aa9189c2..8474d2ba80 100644 --- a/backend/src/app/rpc/commands/access_token.clj +++ b/backend/src/app/rpc/commands/access_token.clj @@ -6,12 +6,15 @@ (ns app.rpc.commands.access-token (:require + [app.common.exceptions :as ex] [app.common.schema :as sm] [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] + [app.http.access-token :as actoken] [app.main :as-alias main] [app.rpc :as-alias rpc] + [app.rpc.commands.profile :as profile] [app.rpc.doc :as-alias doc] [app.rpc.quotes :as quotes] [app.setup :as-alias setup] @@ -28,16 +31,27 @@ AND id != ? AND type = 'mcp'") +(def ^:private sql:get-valid-mcp-token + "SELECT * FROM access_token + WHERE profile_id = ? + AND type = 'mcp' + AND (expires_at IS NULL OR expires_at > now()) + ORDER BY created_at DESC + LIMIT 1") + (defn create-access-token "Create an access token with empty perms. Elevated permissions (e.g. error-reports:read) are not assignable via the public API; grant them with SQL or `repl:grant-access-token-perm`." [{:keys [::db/conn] :as cfg} profile-id name expiration type] + ;; Serialize regeneration with OAuth provisioning for the same profile. + (when (= type "mcp") + (db/get-by-id conn :profile profile-id {::db/for-update true})) (let [token-id (uuid/next) expires-at (some-> expiration (ct/in-future)) created-at (ct/now) - token-iss (if (= type "mcp") "urn:penpot:mcp-token" "access-token") + token-iss (if (= type "mcp") actoken/mcp-token-issuer "access-token") token (tokens/generate cfg {:iss token-iss :uid profile-id :iat created-at @@ -62,6 +76,27 @@ (decode-row token))) +(defn ensure-mcp-access! + "Enable MCP and return the profile's unexpired token, creating it if absent. + Must run in a transaction. Lock the profile even when no token exists, so + concurrent approvals and settings regeneration use the same lock. Enabling + MCP must not regenerate a token already held by a connected client; an + expired token is replaced because no client can use it, and so is a token + issued before MCP tokens had their own issuer, or one this instance cannot + decrypt, because the backend rejects both." + [{:keys [::db/conn] :as cfg} profile-id] + (let [profile (profile/get-profile conn profile-id ::db/for-update true) + token (db/exec-one! conn [sql:get-valid-mcp-token profile-id]) + ;; A token this instance cannot decrypt is as unusable as an expired one + token (when (= actoken/mcp-token-issuer + (ex/ignoring (:iss (tokens/decode cfg (:token token))))) + token)] + (when-not (:mcp-enabled (:props profile)) + (profile/update-profile-props cfg profile-id {:mcp-enabled true})) + (if token + (decode-row token) + (create-access-token cfg profile-id "MCP" nil "mcp")))) + (defn repl:create-access-token [cfg profile-id name expiration] (db/tx-run! cfg create-access-token profile-id name expiration)) diff --git a/backend/src/app/rpc/commands/audit.clj b/backend/src/app/rpc/commands/audit.clj index 893160a23e..4d5f26a402 100644 --- a/backend/src/app/rpc/commands/audit.clj +++ b/backend/src/app/rpc/commands/audit.clj @@ -16,8 +16,10 @@ [app.db :as db] [app.http :as-alias http] [app.loggers.audit :as audit] + [app.loggers.audit.consumer-task :as consumer] [app.loggers.database :as loggers.db] [app.loggers.mattermost :as loggers.mm] + [app.metrics :as mtx] [app.rpc :as-alias rpc] [app.rpc.climit :as-alias climit] [app.rpc.doc :as-alias doc] @@ -71,6 +73,9 @@ (def ^:private xf:map-event-row (comp (map adjust-timestamp) + ;; Each row mints its own id: the full row and its telemetry + ;; shadow are distinct records that must not share it. + (map #(assoc % :id (uuid/next))) (map event->row))) (defn- prepare-events @@ -80,8 +85,7 @@ initiator (audit/get-client-initiator request) xform (comp (map (fn [event] - {:id (uuid/next) - :type (:type event) + {:type (:type event) :name (:name event) :props (:props event) ;; The initiator always comes from the @@ -113,6 +117,19 @@ (assoc :source "telemetry:frontend")))) (map event->row))) +(defn- record-ingested + [metrics kind outcome amount] + (when (pos? amount) + (mtx/run! metrics :id :audit-ingest-events :labels [kind outcome] :inc amount))) + +(defn- insert-rows! + [tx rows] + (when (seq rows) + (db/insert-many! tx :audit-log event-columns rows) + ;; Same transaction as the rows: every ingested record is born + ;; pending for every enabled consumer (see consumer-task). + (consumer/queue-pendings tx (map first rows)))) + (defn- handle-events [{:keys [::db/pool] :as cfg} params] (let [events (prepare-events params)] @@ -124,19 +141,30 @@ (run! (partial loggers.db/emit cfg) events) (run! (partial loggers.mm/emit cfg) events)) - (when (contains? cf/flags :audit-log) - ;; Process and save full audit events when audit-log flag is active - (when-let [rows (-> (sequence xf:map-event-row events) - (not-empty))] - (db/insert-many! pool :audit-log event-columns rows))) - - (when (contains? cf/flags :telemetry) - ;; Store anonymized frontend events so the telemetry task can ship them - ;; in batches. Runs independently from the audit-log insert above so - ;; both modes can be active simultaneously. - (when-let [rows (-> (sequence xf:map-telemetry-event-row events) - (not-empty))] - (db/insert-many! pool :audit-log event-columns rows))))) + ;; Rows and their pendings commit together in one transaction: a + ;; row never outlives its queue. Both faces are best-effort, so a + ;; failure drops the whole batch (counted below as failed); the + ;; endpoint still answers without breaking the request. + (let [metrics (mtx/instance cfg) + full-rows (if (contains? cf/flags :audit-log) + (into [] xf:map-event-row events) + []) + shadow-rows (if (contains? cf/flags :telemetry) + (into [] xf:map-telemetry-event-row events) + [])] + (try + (db/tx-run! pool + (fn [tx] + (insert-rows! tx full-rows) + (insert-rows! tx shadow-rows))) + (record-ingested metrics "full" "inserted" (count full-rows)) + (record-ingested metrics "shadow" "inserted" (count shadow-rows)) + (catch Throwable cause + ;; Whatever never landed counts as failed per attempted + ;; kind; the endpoint logs the cause as before. + (record-ingested metrics "full" "failed" (count full-rows)) + (record-ingested metrics "shadow" "failed" (count shadow-rows)) + (throw cause)))))) (def ^:private valid-event-types #{"action" "identify" "trigger"}) diff --git a/backend/src/app/rpc/commands/auth.clj b/backend/src/app/rpc/commands/auth.clj index 4ab14258b7..b6cc91e70a 100644 --- a/backend/src/app/rpc/commands/auth.clj +++ b/backend/src/app/rpc/commands/auth.clj @@ -202,11 +202,11 @@ (update-password conn))] (when profile (login-lockout/clear-attempts! cfg (:id profile)) - (eml/send! {::eml/conn conn - ::eml/factory eml/password-changed - :public-uri (cf/get :public-uri) - :to (:email profile) - :name (:fullname profile)}))) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "password-changed" + :public-uri (cf/get :public-uri) + :to (:email profile) + :name (:fullname profile)}))) nil)) @@ -389,12 +389,17 @@ :cause cause) nil))) +(def ^:private default-renderer-new-profiles + "Renderer stored on new profiles. Profiles without one fall back to SVG." + :wasm) + (defn create-profile "Create the profile entry on the database with limited set of input attrs (all the other attrs are filled with default values)." [{:keys [::db/conn] :as cfg} {:keys [email] :as params}] (let [id (or (:id params) (uuid/next)) props (-> (:props params) + (update :renderer d/nilv default-renderer-new-profiles) (merge {:viewed-tutorial? false :viewed-walkthrough? false :nudge {:big 10 :small 1} @@ -467,7 +472,7 @@ (defn send-email-verification! ([cfg profile] (send-email-verification! cfg profile nil)) - ([{:keys [::db/conn] :as cfg} profile invitation-token] + ([cfg profile invitation-token] (let [vclaims (cond-> {:iss :verify-email :exp (ct/in-future "72h") :profile-id (:id profile) @@ -485,13 +490,13 @@ {:iss :profile-identity :profile-id (:id profile) :exp (ct/in-future {:days 30})})] - (eml/send! {::eml/conn conn - ::eml/factory eml/register - :public-uri (cf/get :public-uri) - :to (:email profile) - :name (:fullname profile) - :token vtoken - :extra-data ptoken})))) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "register" + :public-uri (cf/get :public-uri) + :to (:email profile) + :name (:fullname profile) + :token vtoken + :extra-data ptoken})))) (defn register-profile [{:keys [::db/conn] :as cfg} {:keys [token] :as params}] @@ -516,8 +521,9 @@ created? (-> profile meta :created true?) - props (-> (audit/profile->props profile) - (assoc :from-invitation (some? invitation)))] + props (cond-> (audit/profile->props profile) + :always (assoc :from-invitation (some? invitation)) + created? (assoc :renderer-default (-> profile :props :renderer)))] (cond ;; When profile is blocked, we just ignore it and return plain data @@ -650,18 +656,18 @@ :profile-id id})] (assoc profile :token token))) - (send-email-notification [conn profile] + (send-email-notification [profile] (let [ptoken (tokens/generate cfg {:iss :profile-identity :profile-id (:id profile) :exp (ct/in-future {:days 30})})] - (eml/send! {::eml/conn conn - ::eml/factory eml/password-recovery - :public-uri (cf/get :public-uri) - :to (:email profile) - :token (:token profile) - :name (:fullname profile) - :extra-data ptoken}) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "password-recovery" + :public-uri (cf/get :public-uri) + :to (:email profile) + :token (:token profile) + :name (:fullname profile) + :extra-data ptoken}) nil))] (let [profile (->> (profile/clean-email email) @@ -699,7 +705,7 @@ {:id (:id profile)}) (->> profile (create-recovery-token) - (send-email-notification conn))))))) + (send-email-notification))))))) (def schema:request-profile-recovery [:map {:title "request-profile-recovery"} diff --git a/backend/src/app/rpc/commands/binfile.clj b/backend/src/app/rpc/commands/binfile.clj index c0781e74c4..61fa58cbac 100644 --- a/backend/src/app/rpc/commands/binfile.clj +++ b/backend/src/app/rpc/commands/binfile.clj @@ -10,7 +10,6 @@ [app.binfile.common :as bfc] [app.binfile.v1 :as bf.v1] [app.binfile.v3 :as bf.v3] - [app.common.features :as cfeat] [app.common.schema :as sm] [app.common.time :as ct] [app.common.uri :as u] @@ -47,20 +46,14 @@ [:embed-assets {:optional true} ::sm/boolean]]) (defn- export-binfile - [{:keys [::sto/storage] :as cfg} {:keys [type file-id include-libraries embed-assets]}] - (let [output (tmp/tempfile*) - ;; Convert legacy boolean flags to unified export-type - export-type (cond - (some? type) type - (true? include-libraries) :include-libraries - (true? embed-assets) :merge-libraries - :else :detach-libraries)] + [{:keys [::sto/storage] :as cfg} {:keys [file-id] :as params}] + (let [output (tmp/tempfile*)] (try - (-> cfg - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type export-type) - (bf.v3/export-files! output)) + (-> (bfc/export-cfg cfg {:file-ids #{file-id} + :export-type (bfc/resolve-export-type params) + :output output}) + (bf.v3/export-files!)) (let [data (sto/content output) object (sto/put-object! storage @@ -98,22 +91,13 @@ :project-id project-id) cfg - (-> cfg - (assoc ::bfc/features (cfeat/get-team-enabled-features cf/flags team)) - (assoc ::bfc/project-id project-id) - (assoc ::bfc/profile-id profile-id) - (assoc ::bfc/team-id (:id team)) - (assoc ::bfc/name name) - (assoc ::bfc/import-max-binary-entry-size (cf/get :binfile-import-max-binary-entry-size)) - (assoc ::bfc/import-max-text-entry-size (cf/get :binfile-import-max-text-entry-size)) - (assoc ::bfc/import-max-text-total-size (cf/get :binfile-import-max-text-total-size)) - (assoc ::bfc/import-max-zip-entries (cf/get :binfile-import-max-zip-entries))) + (bfc/import-cfg cfg {:profile-id profile-id + :project-id project-id + :team team + :name name + :input (:path file)}) - input-path (:path file) - owned? (some? upload-id) - - cfg - (assoc cfg ::bfc/input input-path) + owned? (some? upload-id) result (try @@ -126,7 +110,7 @@ :version version}))) (finally (when owned? - (fs/delete input-path))))] + (fs/delete (:path file)))))] (db/update! pool :project {:modified-at (ct/now)} diff --git a/backend/src/app/rpc/commands/comments.clj b/backend/src/app/rpc/commands/comments.clj index 8ca0a165bc..9c5f24414a 100644 --- a/backend/src/app/rpc/commands/comments.clj +++ b/backend/src/app/rpc/commands/comments.clj @@ -29,7 +29,6 @@ [app.rpc.doc :as-alias doc] [app.rpc.quotes :as quotes] [app.rpc.retry :as rtry] - [app.util.pointer-map :as pmap] [app.util.services :as sv] [clojure.set :as set] [cuerdas.core :as str])) @@ -83,7 +82,7 @@ (not= :none (-> props :notifications :email-comments))) (defn send-comment-emails! - [conn profile comment thread file] + [{:keys [::db/conn] :as cfg} profile comment thread file] (let [team-users (get-team-users conn (:team-id file)) comment-reference (format-comment-ref thread file) comment-content (format-comment comment) @@ -113,9 +112,10 @@ (doseq [mention comment-mentions] (let [{:keys [fullname email props]} (get team-users mention)] (when (mention-email? props) - (eml/send! - {::eml/conn conn - ::eml/factory eml/comment-mention + (eml/send-template + cfg + {::eml/reuse-conn true + ::eml/template "comment-mention" :public-uri (cf/get :public-uri) :to email :name fullname @@ -128,9 +128,10 @@ (doseq [mention thread-mentions] (let [{:keys [fullname email props]} (get team-users mention)] (when (mention-email? props) - (eml/send! - {::eml/conn conn - ::eml/factory eml/comment-thread + (eml/send-template + cfg + {::eml/reuse-conn true + ::eml/template "comment-thread" :public-uri (cf/get :public-uri) :to email :name fullname @@ -143,9 +144,10 @@ (doseq [user-id notificate-users-ids] (let [{:keys [id fullname email props]} (get team-users user-id)] (when (notification-email? id (:owner-id thread) props) - (eml/send! - {::eml/conn conn - ::eml/factory eml/comment-notification + (eml/send-template + cfg + {::eml/reuse-conn true + ::eml/template "comment-notification" :public-uri (cf/get :public-uri) :to email :name fullname @@ -167,7 +169,7 @@ (defn- get-file "A specialized version of get-file for comments module." [cfg file-id page-id] - (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)] + (feat.fdata/with-pointers cfg file-id (let [file (bfc/get-file cfg file-id) data (get file :data)] (-> file @@ -538,7 +540,7 @@ (update-thread-seqn conn file-id seqn) ;; Send mentions emails - (send-comment-emails! conn profile comment thread file) + (send-comment-emails! cfg profile comment thread file) (-> thread (add-owner profile) @@ -648,7 +650,7 @@ ;; Update the current profile status in relation to the current thread (upsert-comment-thread-status! conn profile-id thread-id) - (send-comment-emails! conn profile comment thread file) + (send-comment-emails! cfg profile comment thread file) (vary-meta comment assoc ::audit/props (assoc comment :team-id team-id))))) diff --git a/backend/src/app/rpc/commands/demo.clj b/backend/src/app/rpc/commands/demo.clj index 1e9f81ea63..2386a5bea8 100644 --- a/backend/src/app/rpc/commands/demo.clj +++ b/backend/src/app/rpc/commands/demo.clj @@ -14,12 +14,12 @@ [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] + [app.jobs :as jobs] [app.loggers.audit :as audit] [app.rpc :as-alias rpc] [app.rpc.commands.auth :as auth] [app.rpc.doc :as-alias doc] [app.util.services :as sv] - [app.worker :as wrk] [buddy.core.codecs :as bc] [buddy.core.nonce :as bn])) @@ -66,7 +66,8 @@ ::doc/changes [["1.15" "This method is migrated from mutations to commands."] ["2.18" "Add optional `skip-onboarding` param. When true, the profile is created with `onboarding-viewed` and `release-notes-viewed` (current version) set, skipping the onboarding flow."] ["2.18" "Add optional `expires-in` param. When set, the demo purge is scheduled that long after creation instead of the global deletion delay. Only values between 5 minutes and the global delay are accepted."] - ["2.18.1" "Add optional `renderer` param. When set to `wasm` or `svg`, the profile is created with that `renderer` in props. When omitted, no renderer is stored and the Penpot default applies."]] + ["2.18.1" "Add optional `renderer` param. When set to `wasm` or `svg`, the profile is created with that `renderer` in props. When omitted, no renderer is stored and the Penpot default applies."] + ["2.20" "When `renderer` is omitted, the profile is created with the default renderer for new accounts (`wasm`)."]] ::sm/params schema:create-demo-profile} [cfg {:keys [skip-onboarding expires-in renderer]}] @@ -101,10 +102,10 @@ (->> (auth/create-profile cfg params) (auth/create-profile-rels cfg))))] - (wrk/submit! (-> cfg - (assoc ::wrk/task :demo-purge) - (assoc ::wrk/delay deletion-delay) - (assoc ::wrk/params {:profile-id (:id profile)}))) + (jobs/submit cfg + {::jobs/name :demo-purge + ::jobs/delay deletion-delay + ::jobs/params {:profile-id (:id profile)}}) (with-meta {:email email :password password} diff --git a/backend/src/app/rpc/commands/feedback.clj b/backend/src/app/rpc/commands/feedback.clj index 346697abbc..b51079f74b 100644 --- a/backend/src/app/rpc/commands/feedback.clj +++ b/backend/src/app/rpc/commands/feedback.clj @@ -13,11 +13,57 @@ [app.config :as cf] [app.db :as db] [app.email :as eml] + [app.loggers.audit :as-alias audit] [app.rpc :as-alias rpc] [app.rpc.climit :as-alias climit] [app.rpc.commands.profile :as profile] [app.rpc.doc :as-alias doc] - [app.util.services :as sv])) + [app.rpc.helpers :as rph] + [app.util.services :as sv] + [clojure.string :as str])) + +(def schema:send-mcp-feedback + [:map {:title "send-mcp-feedback" :closed true} + [:subject [:and [:string {:min 1 :max 120}] + [:fn #(and (not (str/blank? %)) + (not (re-find #"\p{Cc}" %)))]]] + ;; Line breaks and tabs are the only control characters allowed + [:feedback [:and [:string {:min 1 :max 5000}] + [:fn #(and (not (str/blank? %)) + (not (re-find #"[\p{Cc}&&[^\t\n\r]]" %)))]]]]) + +(sv/defmethod ::send-mcp-feedback + {::rpc/token-type "mcp" + ::db/transaction true + ::doc/added "2.20" + ::sm/params schema:send-mcp-feedback} + [cfg {:keys [::rpc/profile-id subject feedback]}] + (let [profile (profile/get-profile cfg profile-id) + destination (cf/get :mcp-feedback-email)] + ;; Blocking a profile invalidates its sessions but not its access tokens + (when (:is-blocked profile) + (ex/raise :type :authorization + :code :profile-blocked + :hint "profile is marked as blocked")) + (when-not (:is-active profile) + (ex/raise :type :authorization + :code :profile-not-active + :hint "profile is not active")) + (when (str/blank? destination) + (ex/raise :type :restriction + :code :feedback-disabled + :hint "MCP feedback is not configured")) + (eml/send cfg {::eml/reuse-conn true + :to destination + :reply-to (:email profile) + :subject (str "[MCP feedback] " subject) + :body {"text/plain" (str "Submitted by: " (:fullname profile) + " <" (:email profile) ">\n" + "Profile ID: " (:id profile) "\n\n" + "Feedback:\n\n" feedback)}}) + ;; The audit event keeps who sent feedback, not its text + (rph/with-meta {:status "queued"} + {::audit/replace-props {}}))) (declare ^:private send-user-feedback!) @@ -32,20 +78,22 @@ (sv/defmethod ::send-user-feedback {::climit/id [[:send-user-feedback/by-profile ::rpc/profile-id] [:send-user-feedback/global]] + ::db/transaction true ::doc/added "1.18" ::sm/params schema:send-user-feedback} - [{:keys [::db/pool]} {:keys [::rpc/profile-id] :as params}] + [cfg {:keys [::rpc/profile-id] :as params}] (when-not (contains? cf/flags :user-feedback) (ex/raise :type :restriction :code :feedback-disabled :hint "feedback not enabled")) - (let [profile (profile/get-profile pool profile-id)] - (send-user-feedback! pool profile params) + (let [pool (::db/pool cfg) + profile (profile/get-profile pool profile-id)] + (send-user-feedback! cfg profile params) nil)) (defn- send-user-feedback! - [pool profile params] + [cfg profile params] (let [destination (or (cf/get :user-feedback-destination) ;; LEGACY @@ -55,16 +103,16 @@ (d/without-nils {"error-report.txt" (:error-report params)})] - (eml/send! {::eml/conn pool - ::eml/factory eml/user-feedback - :to destination - :reply-to (:email profile) - :email (:email profile) - :attachments attachments + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "feedback" + :to destination + :reply-to (:email profile) + :email (:email profile) + :attachments attachments - :feedback-subject (:subject params) - :feedback-type (:type params "not-specified") - :feedback-content (:content params) - :feedback-error-href (:error-href params) - :profile profile}) + :feedback-subject (:subject params) + :feedback-type (:type params "not-specified") + :feedback-content (:content params) + :feedback-error-href (:error-href params) + :profile (select-keys profile [:id :fullname :email])}) nil)) diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj index d07a9c1280..b2b465cf78 100644 --- a/backend/src/app/rpc/commands/files.clj +++ b/backend/src/app/rpc/commands/files.clj @@ -22,6 +22,7 @@ [app.common.transit :as t] [app.common.types.components-list :as ctkl] [app.common.types.file :as ctf] + [app.common.types.plugins :as ctpg] [app.common.types.tokens-lib :as ctob] [app.common.uri :as uri] [app.config :as cf] @@ -30,6 +31,7 @@ [app.features.fdata :as feat.fdata] [app.features.logical-deletion :as ldel] [app.http.sse :as sse] + [app.jobs :as jobs] [app.loggers.audit :as-alias audit] [app.loggers.webhooks :as-alias webhooks] [app.msgbus :as mbus] @@ -45,7 +47,6 @@ [app.util.events :as events] [app.util.pointer-map :as pmap] [app.util.services :as sv] - [app.worker :as wrk] [cuerdas.core :as str])) ;; --- FEATURES @@ -430,7 +431,7 @@ (cfeat/check-client-features! (:features params)) (cfeat/check-file-features! (:features file))) - page (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)] + page (feat.fdata/with-pointers cfg file-id (let [page (dm/get-in file [:data :pages-index resolved-page-id])] (if (pmap/pointer-map? page) (deref page) @@ -560,7 +561,7 @@ calculate-from-db (fn [] (let [file (bfc/get-file cfg id) - result (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)] + result (feat.fdata/with-pointers cfg id (calculate-library-summary file))] (-> file (assoc :library-summary result) @@ -684,7 +685,7 @@ (defn- get-file-stats [{:keys [::db/conn] :as cfg} file-id] (let [file (bfc/get-file cfg file-id) - base (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)] + base (feat.fdata/with-pointers cfg file-id (cfs/calc-file-stats (:data file))) lib-cnt (get-file-stats-library-counts conn file-id)] (-> base @@ -731,6 +732,53 @@ (get-file-stats cfg id)) +;; --- COMMAND QUERY: get-file-plugin-data + +(def ^:private schema:get-file-plugin-data + [:map {:title "get-file-plugin-data"} + [:id ::sm/uuid] + [:namespace {:optional true} :keyword]]) + +(sv/defmethod ::get-file-plugin-data + "Return the file-level plugin data: the arbitrary key/value metadata + stored on the file root through the Plugin API. Optionally narrowed + to a single `namespace`. Lets external tooling read metadata such as + issue or commit references without downloading the whole file." + {::doc/added "2.20" + ::sm/params schema:get-file-plugin-data + ::sm/result ctpg/schema:plugin-data + ::db/transaction true} + [{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id namespace]}] + (check-read-permissions! conn profile-id id) + (let [plugin-data (-> (bfc/get-file cfg id) + (get-in [:data :plugin-data]))] + (if namespace + (select-keys plugin-data [namespace]) + (or plugin-data {})))) + +;; --- COMMAND QUERY: get-file-tokens + +(def ^:private schema:get-file-tokens + [:map {:title "get-file-tokens"} + [:file-id ::sm/uuid]]) + +(sv/defmethod ::get-file-tokens + "Return the design tokens of a file as a DTCG document, or nil when + the file has none. Active themes and sets come from the file's + tokens status. Cheap alternative to `get-file` when only the tokens + are needed." + {::doc/added "2.20" + ::sm/params schema:get-file-tokens + ::sm/result [:maybe [:map-of :string :any]]} + [cfg {:keys [::rpc/profile-id file-id]}] + (bfc/check-file-exists cfg file-id) + (check-read-permissions! cfg profile-id file-id) + (let [{:keys [data]} (bfc/get-file cfg file-id)] + (feat.fdata/with-pointers cfg file-id + (when-let [tokens-lib (cfo/get-tokens-lib data)] + (ctob/export-dtcg-json tokens-lib (cfo/get-tokens-status data)))))) + + ;; --- COMMAND QUERY: get-file-libraries (def ^:private schema:get-file-libraries @@ -945,8 +993,7 @@ (assert (db/connection-map? cfg) "expected cfg with valid connection") - (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id) - pmap/*tracked* (pmap/create-tracked)] + (feat.fdata/with-tracked-pointers cfg file-id (let [file (-> (bfc/get-file cfg file-id :include-deleted? true :lock-for-update? true) @@ -1077,13 +1124,13 @@ ;; --- MUTATION COMMAND: delete-file (defn- mark-file-deleted - [conn team file-id] - (let [delay (ldel/get-deletion-delay team) - file (db/update! conn :file - {:deleted-at (ct/in-future delay)} - {:id file-id} - {::db/return-keys [:id :name :is-shared :deleted-at - :project-id :created-at :modified-at]}) + [{:keys [::db/conn] :as cfg} team file-id] + (let [delay (ldel/get-deletion-delay team) + file (db/update! conn :file + {:deleted-at (ct/in-future delay)} + {:id file-id} + {::db/return-keys [:id :name :is-shared :deleted-at + :project-id :created-at :modified-at]}) ;; Files using the library, captured before their relations are removed file-ids (when (:is-shared file) (get-referenced-files conn file-id))] @@ -1092,13 +1139,13 @@ (db/delete! conn :file-library-rel {:library-file-id file-id}) - (wrk/submit! {::db/conn conn - ::wrk/task :delete-object - ::wrk/params (cond-> {:object :file - :deleted-at (:deleted-at file) - :id file-id} - (some? file-ids) - (assoc :file-ids file-ids))}) + (jobs/submit cfg + {::jobs/name :delete-object + ::jobs/params (cond-> {:object :file + :deleted-at (:deleted-at file) + :id file-id} + (some? file-ids) + (assoc :file-ids file-ids))}) file)) (def ^:private @@ -1112,7 +1159,7 @@ (let [team (teams/get-team conn :profile-id profile-id :file-id id) - file (mark-file-deleted conn team id) + file (mark-file-deleted cfg team id) msgbus (::mbus/msgbus cfg)] (mbus/pub! msgbus @@ -1122,12 +1169,12 @@ :profile-id profile-id}) (rph/with-meta (rph/wrap) - {::audit/props {:project-id (:project-id file) + {::audit/props {:team-id (:id team) + :project-id (:project-id file) :name (:name file) :created-at (:created-at file) :modified-at (:modified-at file) - :deleted-at (:deleted-at file) - :team-id (:id team)}}))) + :deleted-at (:deleted-at file)}}))) (sv/defmethod ::delete-file {::doc/added "1.17" @@ -1294,7 +1341,7 @@ [:ids [::sm/set ::sm/uuid]]]) (defn- permanently-delete-team-files - [{:keys [::db/conn]} {:keys [::rpc/request-at team-id ids]}] + [{:keys [::db/conn] :as cfg} {:keys [::rpc/request-at team-id ids]}] (let [ids (into #{} d/xf:map-id (db/exec! conn [sql:get-delete-team-files-candidates team-id @@ -1306,11 +1353,11 @@ {:deleted-at request-at} {:id id} {::db/return-keys false}) - (wrk/submit! {::db/conn conn - ::wrk/task :delete-object - ::wrk/params {:object :file - :deleted-at request-at - :id id}}) + (jobs/submit cfg + {::jobs/name :delete-object + ::jobs/params {:object :file + :deleted-at request-at + :id id}}) (conj acc id)) #{} ids))) diff --git a/backend/src/app/rpc/commands/files_create.clj b/backend/src/app/rpc/commands/files_create.clj index 23959989af..368f480ec7 100644 --- a/backend/src/app/rpc/commands/files_create.clj +++ b/backend/src/app/rpc/commands/files_create.clj @@ -14,6 +14,7 @@ [app.common.types.file :as ctf] [app.config :as cf] [app.db :as db] + [app.features.fdata :as fdata] [app.loggers.audit :as-alias audit] [app.loggers.webhooks :as-alias webhooks] [app.rpc :as-alias rpc] @@ -22,7 +23,6 @@ [app.rpc.doc :as-alias doc] [app.rpc.permissions :as perms] [app.rpc.quotes :as quotes] - [app.util.pointer-map :as pmap] [app.util.services :as sv] [clojure.set :as set])) @@ -44,32 +44,31 @@ (assert (db/connection? conn) "expected a valid connection") - (binding [pmap/*tracked* (pmap/create-tracked) - cfeat/*current* features] + (binding [cfeat/*current* features] + (fdata/with-tracked + (let [file (ctf/make-file {:id id + :project-id project-id + :name name + :revn revn + :is-shared is-shared + :features features + :migrations fmg/available-migrations + :ignore-sync-until ignore-sync-until + :created-at modified-at + :deleted-at deleted-at} + {:create-page create-page + :page-id page-id})] - (let [file (ctf/make-file {:id id - :project-id project-id - :name name - :revn revn - :is-shared is-shared - :features features - :migrations fmg/available-migrations - :ignore-sync-until ignore-sync-until - :created-at modified-at - :deleted-at deleted-at} - {:create-page create-page - :page-id page-id})] + (bfc/insert-file! cfg file) - (bfc/insert-file! cfg file) + (->> (assoc params :file-id (:id file) :role :owner) + (create-file-role! conn)) - (->> (assoc params :file-id (:id file) :role :owner) - (create-file-role! conn)) + (db/update! conn :project + {:modified-at (ct/now)} + {:id project-id}) - (db/update! conn :project - {:modified-at (ct/now)} - {:id project-id}) - - (bfc/get-file cfg (:id file))))) + (bfc/get-file cfg (:id file)))))) (def ^:private schema:create-file [:map {:title "create-file" :closed true} diff --git a/backend/src/app/rpc/commands/files_snapshot.clj b/backend/src/app/rpc/commands/files_snapshot.clj index cb3e93285f..80be2cd189 100644 --- a/backend/src/app/rpc/commands/files_snapshot.clj +++ b/backend/src/app/rpc/commands/files_snapshot.clj @@ -9,10 +9,12 @@ [app.binfile.common :as bfc] [app.common.exceptions :as ex] [app.common.features :as-alias cfeat] + [app.common.files.migrations :as fmg] [app.common.schema :as sm] [app.common.time :as ct] [app.db :as db] [app.db.sql :as-alias sql] + [app.features.fdata :as fdata] [app.features.file-snapshots :as fsnap] [app.features.logical-deletion :as ldel] [app.main :as-alias main] @@ -53,7 +55,7 @@ ::sm/params schema:get-file-snapshot ::sm/result files/schema:file-with-permissions ::db/transaction true} - [cfg {:keys [::rpc/profile-id file-id id] :as params}] + [cfg {:keys [::rpc/profile-id file-id id features] :as params}] (let [perms (perms/get-file-read-permissions cfg profile-id file-id)] (files/check-read-permissions! perms) (let [snapshot (fsnap/get-snapshot cfg file-id id)] @@ -66,12 +68,41 @@ ;; Load current file metadata only (no data decoding) then overlay ;; the snapshot data so the client receives the same shape as a ;; normal get-file response but with historical page/object content. - (let [base-file (bfc/get-file cfg file-id :load-data? false)] + ;; Snapshots may hold pointer-mapped pages shared with the live + ;; file, so materialize them here: the preview UI applies the + ;; response straight into state and cannot resolve pointers. + ;; NOTE: the loader binds the live file-id, not the snapshot id. + ;; + ;; A snapshot taken while a migration was pending stores the old + ;; format. Apply the pending migrations in memory, the same pure + ;; step a normal read runs, and never persist them: the stored + ;; version keeps its data and restoring it re-migrates on the + ;; next read. `decode-snapshot` hands an ordered set, which is + ;; what `need-migration?`/`migrate` require; a stored NULL is + ;; regenerated from the snapshot's version. + (let [base-file (bfc/get-file cfg file-id :load-data? false) + snapshot (assoc snapshot :id file-id) + file (if (fmg/need-migration? snapshot) + (let [file (fdata/realize cfg snapshot)] + (fmg/migrate-file + file + (delay (bfc/get-resolved-file-libraries cfg file)))) + (update snapshot :data + #(fdata/realize-pointers cfg file-id %))) + ;; Works like in `get-file`: a client that does not declare + ;; `fdata/objects-map` gets plain objects. The historical + ;; feature set is deliberately not checked against the + ;; enabled one: a version may predate a feature change. + file (if (and (contains? (:features file) "fdata/objects-map") + (not (contains? features "fdata/objects-map"))) + (fdata/realize-objects cfg file) + file)] (-> base-file - (assoc :data (:data snapshot)) - (assoc :version (:version snapshot)) - (assoc :features (:features snapshot)) - (assoc :revn (:revn snapshot)) + (assoc :data (:data file)) + (assoc :version (:version file)) + (assoc :features (:features file)) + (assoc :migrations (:migrations file)) + (assoc :revn (:revn file)) (assoc :vern (rand-int 100000)) (assoc :permissions perms)))))) @@ -84,10 +115,22 @@ {::doc/added "1.20" ::sm/params schema:create-file-snapshot ::climit/id [[:create-file-snapshot/by-profile ::rpc/profile-id] - [:create-file-snapshot/global]]} + [:create-file-snapshot/global]] + ::db/transaction true} [cfg {:keys [::rpc/profile-id file-id label]}] (files/check-edition-permissions! cfg profile-id file-id) - (let [file (bfc/get-file cfg file-id :realize? true) + ;; NOTE: the file is intentionally NOT realized here: the snapshot + ;; shares fragment rows with the live file (same as automatic + ;; snapshots) instead of storing a materialized copy. file-gc treats + ;; every unpurged snapshot as a liveness root, and readers + ;; (get-file-snapshot, restore!, reduce-snapshots) resolve pointers + ;; on demand. + ;; NOTE: the file is read with FOR SHARE inside this transaction so + ;; snapshot creation serializes with a concurrent :file-gc pass + ;; (which takes FOR UPDATE + SKIP LOCKED on the same row). Without + ;; the lock, a version pinned mid-pass could reference fragment rows + ;; the pass is recreating. + (let [file (bfc/get-file cfg file-id :lock-for-share? true) project (db/get-by-id cfg :project (:project-id file))] (-> cfg @@ -115,38 +158,58 @@ [:restore-file-snapshot/global]]} [{:keys [::db/pool ::mbus/msgbus] :as cfg} {:keys [::rpc/profile-id ::rpc/session-id file-id id] :as params}] - ;; Check permissions and read current file state (short-lived, outside restore transaction) + ;; Check permissions (files/check-edition-permissions! pool profile-id file-id) - (let [file (bfc/get-file cfg file-id) - team (teams/get-team pool + (let [team (teams/get-team pool :profile-id profile-id :file-id file-id) delay (ldel/get-deletion-delay team) - file-revn (:revn file)] - ;; Create backup snapshot of the current state (committed immediately - ;; independently of the restore outcome) - (fsnap/create! cfg file - {:profile-id profile-id - :deleted-at (ct/in-future delay) - :created-by "system"}) + ;; Create backup snapshot of the current state (committed immediately + ;; independent of the restore outcome). The read takes FOR SHARE so a + ;; concurrent :file-gc pass (which holds FOR UPDATE + SKIP LOCKED on the + ;; file row) either finishes first -- and we snapshot the new fragments -- + ;; or skips the file and retries later. Without the lock, the backup + ;; could reference fragments the pass is about to mark as deleted, + ;; breaking the backup permanently. + ;; + ;; The backup read also provides the revn the guard below compares + ;; against. An edit committed before this read is captured in the + ;; backup and no longer aborts the restore; only edits committed + ;; after the backup conflict. + backup-revn + (db/tx-run! cfg + (fn [cfg] + (let [file (bfc/get-file cfg file-id :lock-for-share? true)] + (fsnap/create! cfg file + {:profile-id profile-id + :deleted-at (ct/in-future delay) + :created-by "system"}) + (:revn file))))] ;; Restore snapshot inside its own transaction; the revn check ;; ensures no data is lost if the file was edited concurrently (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] (let [current (bfc/get-minimal-file conn file-id {::db/for-update true})] - (when (not= (:revn current) file-revn) + (when (not= (:revn current) backup-revn) (ex/raise :type :conflict :code :file-modified :hint "the file was modified during the restore process, please retry"))) - (let [vern (fsnap/restore! cfg file-id id)] + (let [snapshot (fsnap/get-minimal-snapshot cfg id) + vern (fsnap/restore! cfg file-id id)] + ;; Peers name the restored version by its label when a + ;; user named it, else by its date (mbus/pub! msgbus - :topic (:id file) - :message {:type :file-restored - :session-id session-id - :file-id (:id file) - :vern vern}) + :topic file-id + :message (cond-> {:type :file-restored + :session-id session-id + :profile-id profile-id + :file-id file-id + :vern vern + :created-at (:created-at snapshot)} + (= "user" (:created-by snapshot)) + (assoc :label (:label snapshot)))) nil))))) (def ^:private schema:update-file-snapshot @@ -171,7 +234,7 @@ {::doc/added "1.20" ::sm/params schema:remove-file-snapshot ::db/transaction true} - [{:keys [::db/conn]} {:keys [::rpc/profile-id id]}] + [{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id]}] (let [snapshot (fsnap/get-minimal-snapshot conn id)] (files/check-edition-permissions! conn profile-id (:file-id snapshot)) @@ -194,7 +257,7 @@ :profile-id profile-id :file-id (:file-id snapshot)) delay (ldel/get-deletion-delay team)] - (fsnap/delete! conn (assoc snapshot :deleted-at (ct/in-future delay)))))) + (fsnap/delete! cfg (assoc snapshot :deleted-at (ct/in-future delay)))))) ;;; Lock/unlock version endpoints diff --git a/backend/src/app/rpc/commands/files_thumbnails.clj b/backend/src/app/rpc/commands/files_thumbnails.clj index f837750b21..879dcee805 100644 --- a/backend/src/app/rpc/commands/files_thumbnails.clj +++ b/backend/src/app/rpc/commands/files_thumbnails.clj @@ -15,10 +15,12 @@ [app.common.schema :as sm] [app.common.thumbnails :as thc] [app.common.time :as ct] + [app.common.types.objects-map :as omap] [app.common.types.shape-tree :as ctt] [app.config :as cf] [app.db :as db] [app.db.sql :as-alias sql] + [app.features.fdata :as fdata] [app.loggers.audit :as-alias audit] [app.loggers.webhooks :as-alias webhooks] [app.media.validation :as media.v] @@ -92,26 +94,38 @@ ;; --- COMMAND QUERY: get-file-data-for-thumbnail -;; We need to improve how we set frame for thumbnail in order to avoid -;; loading all pages into memory for find the frame set for thumbnail. +(defn- get-thumbnail-frame-candidates + "Only decode the objects whose encoded form can carry the flag." + [objects] + (if (omap/objects-map? objects) + (keep (fn [[id encoded]] + (when (str/includes? encoded "use-for-thumbnail") + (get objects id))) + (omap/get-data objects)) + (vals objects))) + +(defn- get-thumbnail-frame + "Find the frame marked to be used as thumbnail, loading pages lazily. + The returned frame has the :page-id assoc'ed." + [data] + (d/seek some? + (for [page (-> data :pages-index vals) + :let [page (cond-> page (pmap/pointer-map? page) deref)]] + (some-> (d/seek #(and (cfh/frame-shape? %) + (or (:use-for-thumbnail %) + (:use-for-thumbnail? %))) ; NOTE: backward comp (remove on v1.21) + (get-thumbnail-frame-candidates (:objects page))) + (assoc :page-id (:id page)))))) (defn get-file-data-for-thumbnail [{:keys [::db/conn] :as cfg} {:keys [data id] :as file} strip-frames-with-thumbnails] - (letfn [;; function responsible on finding the frame marked to be - ;; used as thumbnail; the returned frame always have - ;; the :page-id set to the page that it belongs. - (get-thumbnail-frame [{:keys [data]}] - (d/seek #(or (:use-for-thumbnail %) - (:use-for-thumbnail? %)) ; NOTE: backward comp (remove on v1.21) - (for [page (-> data :pages-index vals) - frame (-> page :objects ctt/get-frames)] - (assoc frame :page-id (:id page))))) - - ;; function responsible to filter objects data structure of + (letfn [;; function responsible to filter objects data structure of ;; all unneeded shapes if a concrete frame is provided. If no - ;; frame, the objects is returned untouched. + ;; frame, all objects are returned as a plain map. (filter-objects [objects frame-id] - (d/index-by :id (cfh/get-children-with-self objects frame-id))) + (if (some? frame-id) + (d/index-by :id (cfh/get-children-with-self objects frame-id)) + (into {} objects))) ;; function responsible of assoc available thumbnails ;; to frames and remove all children shapes from objects if @@ -152,29 +166,28 @@ objects)))] - (let [frame (get-thumbnail-frame file) - frame-id (:id frame) - page-id (or (:page-id frame) - (-> data :pages first)) + (fdata/with-pointers cfg id + (let [frame (get-thumbnail-frame data) + frame-id (:id frame) + page-id (or (:page-id frame) + (-> data :pages first)) - page (dm/get-in data [:pages-index page-id]) - page (cond-> page (pmap/pointer-map? page) deref) - frame-ids (if (some? frame) (list frame-id) (map :id (ctt/get-frames (:objects page)))) + page (dm/get-in data [:pages-index page-id]) + page (cond-> page (pmap/pointer-map? page) deref) + frame-ids (if (some? frame) (list frame-id) (map :id (ctt/get-frames (:objects page)))) - obj-ids (map #(thc/fmt-object-id (:id file) page-id % "frame") frame-ids) - thumbs (get-object-thumbnails conn id obj-ids)] + obj-ids (map #(thc/fmt-object-id (:id file) page-id % "frame") frame-ids) + thumbs (get-object-thumbnails conn id obj-ids)] - (cond-> page - ;; If we have frame, we need to specify it on the page level - ;; and remove the all other unrelated objects. - (some? frame-id) - (-> (assoc :thumbnail-frame-id frame-id) - (update :objects filter-objects frame-id)) + (cond-> (update page :objects filter-objects frame-id) + ;; If we have frame, we need to specify it on the page level + (some? frame-id) + (assoc :thumbnail-frame-id frame-id) - ;; Assoc the available thumbnails and prune not visible shapes - ;; for avoid transfer unnecessary data. - strip-frames-with-thumbnails - (update :objects assoc-thumbnails page-id thumbs))))) + ;; Assoc the available thumbnails and prune not visible shapes + ;; for avoid transfer unnecessary data. + strip-frames-with-thumbnails + (update :objects assoc-thumbnails page-id thumbs)))))) (def ^:private schema:get-file-data-for-thumbnail @@ -204,7 +217,6 @@ :file-id file-id) file (bfc/get-file cfg file-id :include-deleted? true - :realize? true :read-only? true) strip-frames-with-thumbnails (or (nil? strip-frames-with-thumbnails) ;; if not present, default to true diff --git a/backend/src/app/rpc/commands/files_update.clj b/backend/src/app/rpc/commands/files_update.clj index 5c47dbf61d..266214ec27 100644 --- a/backend/src/app/rpc/commands/files_update.clj +++ b/backend/src/app/rpc/commands/files_update.clj @@ -38,7 +38,8 @@ [app.util.blob :as blob] [app.util.pointer-map :as pmap] [app.util.services :as sv] - [clojure.set :as set])) + [clojure.set :as set] + [cuerdas.core :as str])) (declare ^:private get-lagged-changes) (declare ^:private send-notifications!) @@ -69,19 +70,38 @@ [:changes-with-metadata {:optional true} [:vector [:map [:changes [:vector cpc/schema:change]] + [:commit-id {:optional true} ::sm/uuid] [:hint-origin {:optional true} :keyword] [:hint-events {:optional true} [:vector [:string {:max 250}]]]]]] [:skip-validate {:optional true} ::sm/boolean]]) (def ^:private schema:update-file-result - [:vector {:title "update-file-result"} - [:map - [:changes [:vector cpc/schema:change]] - [:file-id ::sm/uuid] - [:id ::sm/uuid] - [:revn {:min 0} ::sm/int] - [:session-id ::sm/uuid]]]) + [:map {:title "update-file-result"} + ;; The revision the file had before this update + [:revn {:min 0} ::sm/int] + ;; Every change stored after the incoming revision, this one included + [:lagged + [:vector + [:map + [:changes [:vector cpc/schema:change]] + [:file-id ::sm/uuid] + [:id ::sm/uuid] + [:revn {:min 0} ::sm/int] + [:session-id ::sm/uuid]]]] + ;; Commits not saved: the first one that failed and every later one + [:rejected {:optional true} + [:map + [:commit-ids [:vector ::sm/uuid]] + [:failed-commit-id ::sm/uuid] + ;; The data the failed commit touches was invalid before it + [:preexisting-damage {:optional true} :boolean] + [:error + [:map + [:type :keyword] + [:code {:optional true} [:maybe :keyword]] + [:hint {:optional true} [:maybe :string]] + [:details {:optional true} [:vector :any]]]]]]]) ;; --- HELPERS @@ -184,12 +204,18 @@ (->> changes-with-metadata (mapcat :changes) vec) (vec changes)) + ;; Saved commit by commit only when every group names its commit + commits (when (and (seq changes-with-metadata) + (every? :commit-id changes-with-metadata)) + changes-with-metadata) + params (-> params (assoc :profile-id profile-id) (assoc :features (set/difference features cfeat/frontend-only-features)) (assoc :team team) (assoc :file file) - (assoc :changes changes)) + (assoc :changes changes) + (assoc :commits commits)) cfg (assoc cfg ::timestamp (ct/now) ::validation-timings (atom {})) @@ -280,10 +306,9 @@ Only intended for internal use on this module." [{:keys [::db/conn ::timestamp] :as cfg} - {:keys [profile-id file team features changes session-id skip-validate] :as params}] + {:keys [profile-id file team features changes commits session-id skip-validate] :as params}] - (binding [pmap/*tracked* (pmap/create-tracked) - pmap/*load-fn* (partial fdata/load-pointer cfg (:id file))] + (fdata/with-tracked-pointers cfg (:id file) (let [file (assoc file :features (-> features @@ -299,7 +324,17 @@ cfeat/*previous* (:features file)] (update-file-data! cfg file process-changes-and-validate - changes skip-validate)) + changes commits skip-validate)) + + ;; Only the accepted changes are stored and broadcast + changes (::changes file) + rejected (::rejected file) + params (assoc params :changes changes) + file (dissoc file ::changes ::rejected) + + ;; A request whose first commit is rejected leaves the file as is + saved? (or (nil? rejected) (seq changes)) + snapshot (when saved? (::snapshot file)) deleted-at (ct/plus timestamp (ct/duration {:hours 1})) @@ -316,7 +351,7 @@ (ct/plus (ct/duration {:millis 1})))] (if repair - (let [snapshot-file (or (::snapshot file) + (let [snapshot-file (or snapshot (fdata/realize cfg (:original repair)))] (fsnap/create! cfg snapshot-file {:label (str "internal/repair/" revn) @@ -324,62 +359,65 @@ :deleted-at snapshot-deleted-at :profile-id profile-id :session-id repair-session-id})) - (when-let [file (::snapshot file)] - (fsnap/create! cfg file + (when snapshot + (fsnap/create! cfg snapshot {:label (str "internal/snapshot/" revn) :created-by "system" :deleted-at snapshot-deleted-at :profile-id profile-id :session-id session-id}))) - (when repair + (when saved? + (when repair + (db/insert! conn :file-change + {:id (uuid/next) + :session-id repair-session-id + :profile-id profile-id + :created-at timestamp + :updated-at timestamp + :deleted-at deleted-at + :file-id (:id file) + :revn (:revn repair) + :version (:version file) + :features (into-array (:features file)) + :changes (blob/encode (:changes repair))} + {::db/return-keys false}) + (l/wrn :hint "file auto-repaired" + :file-id (str (:id file)) + :error-codes (error-codes (:errors repair)) + :error-count (count (:errors repair)) + :change-count (count (:changes repair))) + (record-repair-outcome cfg :repaired)) + + ;; Insert change (xlog) with deleted_at in a future data for + ;; make them automatically eleggible for GC once they expires (db/insert! conn :file-change {:id (uuid/next) - :session-id repair-session-id + :session-id session-id :profile-id profile-id - :created-at timestamp - :updated-at timestamp + :created-at change-created-at + :updated-at change-created-at :deleted-at deleted-at :file-id (:id file) - :revn (:revn repair) + :revn (:revn file) :version (:version file) :features (into-array (:features file)) - :changes (blob/encode (:changes repair))} + :changes (blob/encode changes)} {::db/return-keys false}) - (l/wrn :hint "file auto-repaired" - :file-id (str (:id file)) - :error-codes (error-codes (:errors repair)) - :error-count (count (:errors repair)) - :change-count (count (:changes repair))) - (record-repair-outcome cfg :repaired)) - ;; Insert change (xlog) with deleted_at in a future data for - ;; make them automatically eleggible for GC once they expires - (db/insert! conn :file-change - {:id (uuid/next) - :session-id session-id - :profile-id profile-id - :created-at change-created-at - :updated-at change-created-at - :deleted-at deleted-at - :file-id (:id file) - :revn (:revn file) - :version (:version file) - :features (into-array (:features file)) - :changes (blob/encode changes)} - {::db/return-keys false}) + (persist-file! cfg (dissoc file ::repair)) - (persist-file! cfg (dissoc file ::repair)) + (when (contains? cf/flags :redis-cache) + (invalidate-caches! cfg file)) - (when (contains? cf/flags :redis-cache) - (invalidate-caches! cfg file)) + ;; Send asynchronous notifications + (when repair + (send-repair-notifications cfg params file repair)) + (send-notifications! cfg params file)) - ;; Send asynchronous notifications - (when repair - (send-repair-notifications cfg params file repair)) - (send-notifications! cfg params file) - - (with-meta {:revn revn :lagged (get-lagged-changes conn params)} + (with-meta (cond-> {:revn revn :lagged (get-lagged-changes conn params)} + (some? rejected) + (assoc :rejected rejected)) {::audit/replace-props {:id (:id file) :name (:name file) @@ -391,7 +429,7 @@ ;; `modified-at` mirrors `persist-file!`: same `::timestamp` ;; fallback, so both agree to the millisecond when the ;; request timestamp exists. - :revn (:revn file) + :revn (if saved? (:revn file) revn) :modified-at (or timestamp (ct/now))}})))) (defn get-file @@ -430,11 +468,13 @@ (rds/del conn key))))) (defn- attach-snapshot - "Attach snapshot data to the file. This should be called before the - upcoming file operations are applied to the file." - [cfg migrated? file] - (let [snapshot (if migrated? file (fdata/realize cfg file))] - (assoc file ::snapshot snapshot))) + "Attach snapshot data to the file. Snapshots share fragment rows with + the live file, so the snapshot is kept as-is (with pointers); it is + NOT realized/materialized here. The file-gc liveness set unions main + and snapshots, so shared fragments survive while any of them needs + them." + [file] + (assoc file ::snapshot file)) (defn- update-file-data! "Perform a file data transformation in with all update context setup. @@ -442,8 +482,9 @@ This function expected not-decoded file and transformation function. Returns an encoded file. - This function is not responsible of saving the file. It only saves - fdata/pointer-map modified fragments." + This function is not responsible of saving the file. Fragment rows are + persisted later by `persist-file!` (via `bfc/update-file!` inside the + same transaction); nothing is written to `file_data` here." [cfg {:keys [id] :as file} update-fn & args] (let [file (update file :data (fn [data] @@ -458,6 +499,11 @@ take-snapshot? (take-snapshot? file) + ;; Attach the snapshot before migrating: a migration can rebuild a + ;; page and give its PointerMap a new id with `modified?` set, and a + ;; snapshot taken after that would keep the very pointer the save + ;; then rewrites, ending up with post-save content. + ;; ;; For avoid unnecesary overhead of creating multiple ;; pointers and handly internally with objects map in their ;; worst case (when probably all shapes and all pointers @@ -468,18 +514,18 @@ ;; need-migration? ;; (->> (fdata/realize cfg)) - need-migration? - (fmg/migrate-file libs) - take-snapshot? - (->> (attach-snapshot cfg need-migration?)))] + (->> (attach-snapshot)) + + need-migration? + (fmg/migrate-file libs))] (apply update-fn cfg file args))) (defn- soft-validate-file-schema! - [file] + [file changes] (try - (val/validate-file-schema! file) + (val/validate-file-schema-affected! file changes) (catch Throwable cause (l/error :hint "file schema validation error" :cause cause)))) @@ -501,16 +547,213 @@ (l/error :hint "file validation error" :cause cause)))) +(defmacro ^:private with-validation-timing + "Runs `body` and records its time as the validation step `step-key` + (`step-label` in the metric), also when it throws." + [cfg step-key step-label & body] + `(let [t# (ct/tpoint)] + (try + ~@body + (finally + (record-validation-timing ~cfg ~step-key ~step-label (inst-ms (t#))))))) + +(defn- validate-file + "Validates the entities `changes` affect and the schema of the data they + modify." + [cfg file libs changes skip-validate] + (binding [pmap/*tracked* nil] + (when (contains? cf/flags :soft-file-validation) + (with-validation-timing cfg :soft-validate-affected-ms "soft-validate-affected" + (soft-validate-file! file libs changes))) + + (when (contains? cf/flags :soft-file-schema-validation) + (with-validation-timing cfg :soft-validate-schema-ms "soft-validate-schema" + (soft-validate-file-schema! file changes))) + + (when (and (contains? cf/flags :file-validation) + (not skip-validate)) + (with-validation-timing cfg :validate-affected-ms "validate-affected" + (val/validate-file-affected! file libs changes))) + + (when (and (contains? cf/flags :file-schema-validation) + (not skip-validate)) + (with-validation-timing cfg :validate-schema-ms "validate-schema" + (val/validate-file-schema-affected! file changes))))) + +(def ^:private debug-fault-prefix + "Start of the plugin data key that asks for a debug save fault." + "save-fault:") + +(defn- get-debug-fault + "Name of the fault a debug marker in `changes` asks for, or nil. A marker + is a `:set-plugin-data` of the file in the `:penpot-debug` namespace with + no value, so applying it changes nothing." + [changes] + (some (fn [{:keys [type namespace key]}] + (when (and (= :set-plugin-data type) + (= :penpot-debug namespace) + (str/starts-with? key debug-fault-prefix)) + (subs key (count debug-fault-prefix)))) + changes)) + +(defn- raise-debug-fault + "Throws the error named `fault`, standing in for a real failure while the + changes are applied." + [fault] + (let [message "debug save fault"] + (case fault + "validation" (ex/raise :type :validation :code :debug-fault :hint message) + "runtime" (throw (RuntimeException. message)) + "ex-info" (throw (ex-info message {})) + "assertion" (throw (AssertionError. message)) + "sql" (throw (org.postgresql.util.PSQLException. + message org.postgresql.util.PSQLState/CONNECTION_FAILURE)) + "io" (throw (java.net.SocketTimeoutException. message)) + "eof" (throw (java.io.EOFException. message)) + "interrupted" (throw (InterruptedException. message)) + "not-found" (ex/raise :type :not-found :code :object-not-found :hint message) + "stack-overflow" (throw (StackOverflowError. message)) + (ex/raise :type :validation :code :unknown-debug-fault :hint fault)))) + (defn- apply-changes - [cfg file changes] - (let [state (atom {}) - file (binding [cpc/*state* state] - (-> file - (update :data cpc/process-changes changes) - (update :data d/without-nils)))] - (if-let [media-refs (-> @state :media-refs not-empty)] - (bfc/update-media-references! cfg file media-refs) - file))) + "Applies and validates `changes`. With the `debug-save-faults` flag, a + debug marker in them makes this fail with the error it names." + [cfg file libs changes skip-validate] + (when (contains? cf/flags :debug-save-faults) + (some-> (get-debug-fault changes) (raise-debug-fault))) + (let [file (with-validation-timing cfg :process-changes-ms "process-changes" + (update file :data (fn [data] + (-> data + (cpc/process-changes changes) + (d/without-nils)))))] + (validate-file cfg file libs changes skip-validate) + file)) + +(defn- decode-objects + "Decodes the objects of the pages `changes` touch. Every file derived from + `file` shares them, so each attempt does not decode them again." + [file changes] + (doseq [page-id (into #{} (keep :page-id) changes)] + (let [objects (get-in file [:data :pages-index page-id :objects])] + (doseq [id (keys objects)] + (get objects id))))) + +(defn- infrastructure-error? + [cause] + (some (fn [cause] + (or (instance? java.sql.SQLException cause) + (instance? java.io.IOException cause) + (instance? InterruptedException cause) + ;; A file fragment that cannot be loaded + (= :not-found (:type (ex-data cause))))) + (take-while some? (iterate ex-cause cause)))) + +(defn- change-failure? + "True when `cause` comes from applying or validating changes. Only these + reject a commit; any other error fails the whole request." + [cause] + (and (or (instance? Exception cause) + (instance? AssertionError cause)) + (not (infrastructure-error? cause)))) + +(defn- preexisting-damage? + "True when `file` already fails validation on the pages and components + `changes` touch, before they are applied." + [file libs changes skip-validate] + (try + (binding [pmap/*tracked* nil] + (when (and (contains? cf/flags :file-validation) + (not skip-validate)) + (val/validate-file-affected! file libs changes)) + (when (and (contains? cf/flags :file-schema-validation) + (not skip-validate)) + (val/validate-file-schema-affected! file changes))) + false + (catch Throwable cause + (if (change-failure? cause) + true + (throw cause))))) + +(defn- rejection + "Describes `commits` as rejected, the first one failing with `cause`. + With `damaged`, the data that commit touches was invalid before it." + [commits cause damaged] + (let [data (ex-data cause)] + (cond-> {:commit-ids (mapv :commit-id commits) + :failed-commit-id (:commit-id (first commits)) + ;; An ex-info without a type reports like any unexpected exception + :error (if (:type data) + (cond-> {:type (:type data) + :code (:code data) + :hint (or (:hint data) (ex-message cause))} + (:details data) + (assoc :details (mapv #(dissoc % :shape) (:details data)))) + {:type :internal + :code :unexpected-error + :hint (or (ex-message cause) (.getName (class cause)))})} + damaged + (assoc :preexisting-damage true)))) + +(defn- apply-prefix + "Applies and validates the first `n` commits of `attempt`, starting from + its `:file` and its `:tracked` pointers. Returns the resulting `:file`, + with the pointers to persist in `:tracked` and the media refs in `:refs`, + or the cause of the failure." + [{:keys [cfg file libs commits skip-validate state tracked]} n] + (reset! pmap/*tracked* tracked) + (reset! state {}) + (let [changes (into [] (comp (take n) (mapcat :changes)) commits)] + (try + (let [file (apply-changes cfg file libs changes skip-validate)] + {:file (assoc file ::changes changes) + :tracked @pmap/*tracked* + :refs @state}) + (catch Throwable cause + (when-not (change-failure? cause) + (throw cause)) + cause)))) + +(defn- apply-commits + "Keeps the longest prefix of the `:commits` of `attempt` that applies and + validates, found by bisection, and rejects the rest. `cause` is the + failure of all of them together. Takes about log2 of the commits + attempts; a commit that repairs an earlier one can keep both. Leaves the + pointers and media refs of the kept prefix in place and returns its file, + with the accepted changes in `::changes` and the rejection in + `::rejected`, which tells whether the data the first rejected commit + touches was already invalid. The whole search is timed as the `retry` + step of `cfg`." + [cfg {:keys [file libs commits skip-validate state tracked] :as attempt} cause] + (let [tpoint (ct/tpoint)] + (with-validation-timing cfg :retry-ms "retry" + (loop [lo 0 + hi (count commits) + cause cause + attempts 0 + accepted nil] + (if (> (- hi lo) 1) + (let [mid (quot (+ lo hi) 2) + result (apply-prefix attempt mid)] + (if (instance? Throwable result) + (recur lo mid result (inc attempts) accepted) + (recur mid hi cause (inc attempts) result))) + (let [rejected (subvec commits lo) + base (:file accepted file) + damaged (preexisting-damage? base libs (:changes (first rejected)) + skip-validate)] + (reset! pmap/*tracked* (:tracked accepted tracked)) + (reset! state (:refs accepted {})) + (l/error :hint "update-file commit rejected" + :file-id (str (:id file)) + :commit-id (str (:commit-id (first rejected))) + :rejected (count rejected) + :commits (count commits) + :attempts attempts + :preexisting-damage damaged + :elapsed (ct/format-duration (tpoint)) + :cause cause) + (assoc (:file accepted (assoc file ::changes [])) + ::rejected (rejection rejected cause damaged)))))))) (defn- fail-repair [cfg file errors cause] @@ -523,10 +766,11 @@ (defn- repair-preexisting-damage "Repair damage that was already in `original` on the pages/components - the client's `changes` touch, then re-apply those changes. Throws - `cause` when the client's changes are at fault or the repair cannot - fix the file." - [cfg original libs changes tracked tracked-0 cause] + the client's `changes` touch, then re-apply those changes. Starts from + the `tracked` pointers and leaves the media refs of the changes in + `cpc/*state*`. Throws `cause` when the client's changes are at fault + or the repair cannot fix the file." + [cfg original libs changes tracked cause] (let [libs (or libs {})] (try (let [pre-errors @@ -536,49 +780,42 @@ (when (empty? pre-errors) (throw cause)) - (when tracked - (reset! tracked tracked-0)) + (reset! pmap/*tracked* tracked) - (let [trepair (ct/tpoint) - - result - (try - (binding [pmap/*tracked* tracked] - (cfr/repair-file-until-valid - original libs - (fn [file applied] - (binding [pmap/*tracked* nil] - (val/validate-file-affected file libs (into applied changes)))) - :initial-errors pre-errors)) - (finally - (record-validation-timing cfg :repair-ms "repair" (inst-ms (trepair))))) + (let [result + (with-validation-timing cfg :repair-ms "repair" + (cfr/repair-file-until-valid + original libs + (fn [file applied] + (binding [pmap/*tracked* nil] + (val/validate-file-affected file libs (into applied changes)))) + :initial-errors pre-errors)) repair-changes (:changes result)] (when (seq (:errors result)) (fail-repair cfg original (:errors result) cause)) - (let [treprocess (ct/tpoint) - repaired (try - (binding [pmap/*tracked* tracked] - (apply-changes cfg (:file result) changes)) - (finally - (record-validation-timing cfg :reprocess-changes-ms "reprocess-changes" - (inst-ms (treprocess))))) - scope (into repair-changes changes)] + (reset! cpc/*state* {}) + + (let [repaired (with-validation-timing cfg :reprocess-changes-ms "reprocess-changes" + (update (:file result) :data + (fn [data] + (-> data + (cpc/process-changes changes) + (d/without-nils))))) + scope (into repair-changes changes)] (binding [pmap/*tracked* nil] (val/validate-file-affected! repaired libs scope) (when (contains? cf/flags :file-schema-validation) - (let [t (ct/tpoint)] - (try - (val/validate-file-schema! repaired) - (finally - (record-validation-timing cfg :validate-schema-ms "validate-schema" (inst-ms (t)))))))) + (with-validation-timing cfg :validate-schema-ms "validate-schema" + (val/validate-file-schema! repaired)))) (-> repaired (assoc :revn (+ (:revn original) 2)) + (assoc ::changes changes) (assoc ::repair {:changes repair-changes :errors pre-errors :revn (inc (:revn original)) @@ -593,81 +830,94 @@ (record-repair-outcome cfg :unrepaired) (throw cause))))))) +(defn- repair-or-reject + "Handles the failure `cause` of applying all `changes` at once. Repairs + damage `original` already had when `cause` is a referential integrity + error. If that does not save every change and there are `commits`, + keeps the longest prefix of them that validates." + [cfg original file libs changes commits skip-validate tracked cause] + (let [repaired + (when (and (referential-integrity-error? cause) + (contains? cf/flags :file-validation) + (not skip-validate)) + (try + (repair-preexisting-damage cfg original libs changes tracked cause) + (catch Throwable inner + (when-not (identical? inner cause) + (throw inner)))))] + (cond + (some? repaired) + repaired + + (and (some? commits) + (change-failure? cause)) + (apply-commits cfg + {;; The retries record no step timings + :cfg (dissoc cfg ::mtx/metrics ::validation-timings) + :file file + :libs libs + :commits (vec commits) + :skip-validate skip-validate + :state cpc/*state* + :tracked tracked} + cause) + + :else + (throw cause)))) + (defn- process-changes-and-validate - [cfg file changes skip-validate] - (let [original file - tracked pmap/*tracked* - tracked-0 (some-> tracked deref) + "Applies and validates all `changes` at once. If that fails, repairs + damage the file already had or keeps the longest prefix of the + `commits` that validates." + [cfg file changes commits skip-validate] + (let [original file should-resolve-libs? (and (or (contains? cf/flags :file-validation) (contains? cf/flags :soft-file-validation)) (not skip-validate)) - tlibs - (ct/tpoint) - libs - (try - (when should-resolve-libs? - (bfc/get-resolved-file-libraries cfg file)) - (finally - (when should-resolve-libs? - (record-validation-timing cfg :resolve-libs-ms "resolve-libs" (inst-ms (tlibs)))))) + (when should-resolve-libs? + (with-validation-timing cfg :resolve-libs-ms "resolve-libs" + (bfc/get-resolved-file-libraries cfg file))) - tproc - (ct/tpoint) + ;; The main purpose of this atom is provide a contextual state + ;; for the changes subsystem where optionally some hints can + ;; be provided for the changes processing. Right now we are + ;; using it for notify about the existence of media refs when + ;; a new shape is added. + state + (atom {}) + + ;; True when validation reads every object of the touched pages + schema-check? + (or (contains? cf/flags :soft-file-schema-validation) + (and (contains? cf/flags :file-schema-validation) + (not skip-validate))) file - (try - (apply-changes cfg - (-> (files/check-version! file) - (update :revn inc)) - changes) - (finally - (record-validation-timing cfg :process-changes-ms "process-changes" (inst-ms (tproc))))) + (-> (files/check-version! file) + (update :revn inc)) - pending-repair - (atom nil)] + tracked + @pmap/*tracked* - (binding [pmap/*tracked* nil] - (when (contains? cf/flags :soft-file-validation) - (let [t (ct/tpoint)] + file + (binding [cpc/*state* state] + ;; Decoded on `file`, the objects the schema check reads are + ;; shared by every retry + (when (and (next commits) schema-check?) + (decode-objects file changes)) (try - (soft-validate-file! file libs changes) - (finally - (record-validation-timing cfg :soft-validate-affected-ms "soft-validate-affected" (inst-ms (t))))))) - - (when (contains? cf/flags :soft-file-schema-validation) - (let [t (ct/tpoint)] - (try - (soft-validate-file-schema! file) - (finally - (record-validation-timing cfg :soft-validate-schema-ms "soft-validate-schema" (inst-ms (t))))))) - - (when (and (contains? cf/flags :file-validation) - (not skip-validate)) - (let [t (ct/tpoint)] - (try - (val/validate-file-affected! file libs changes) + (-> (apply-changes cfg file libs changes skip-validate) + (assoc ::changes changes)) (catch Throwable cause - (reset! pending-repair cause) - (when-not (referential-integrity-error? cause) - (throw cause))) - (finally - (record-validation-timing cfg :validate-affected-ms "validate-affected" (inst-ms (t))))))) + (repair-or-reject cfg original file libs changes commits + skip-validate tracked cause))))] - (when (and (contains? cf/flags :file-schema-validation) - (not skip-validate) - (nil? @pending-repair)) - (let [t (ct/tpoint)] - (try - (val/validate-file-schema! file) - (finally - (record-validation-timing cfg :validate-schema-ms "validate-schema" (inst-ms (t)))))))) - - (if-let [cause @pending-repair] - (repair-preexisting-damage cfg original libs changes tracked tracked-0 cause) + (if-let [media-refs (-> @state :media-refs not-empty)] + (bfc/update-media-references! cfg file media-refs) file))) (defn- take-snapshot? diff --git a/backend/src/app/rpc/commands/jobs.clj b/backend/src/app/rpc/commands/jobs.clj new file mode 100644 index 0000000000..c3f590315b --- /dev/null +++ b/backend/src/app/rpc/commands/jobs.clj @@ -0,0 +1,380 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.rpc.commands.jobs + "Creation of durable, user-facing jobs. + + One command per job type: the body carries the business params its + job-def declares and nothing else, and the job-def schema is the single + place that says what those params are. Creating a job never runs it: it + freezes what the job will do, and the work belongs to the runner." + (:require + [app.binfile.common :as bfc] + [app.binfile.v3 :as bf.v3] + [app.common.data :as d] + [app.common.exceptions :as ex] + [app.common.schema :as sm] + [app.common.time :as ct] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.jobs.storage :as js] + [app.loggers.audit :as-alias audit] + [app.loggers.webhooks :as-alias webhooks] + [app.rpc :as-alias rpc] + [app.rpc.climit :as-alias climit] + [app.rpc.commands.files :as files] + [app.rpc.commands.media :as media] + [app.rpc.commands.projects :as projects] + [app.rpc.doc :as-alias doc] + [app.rpc.quotes :as quotes] + [app.storage :as sto] + [app.tasks.export-binfile :as export-binfile] + [app.tasks.import-binfile :as import-binfile] + [app.util.services :as sv] + [datoteka.fs :as fs])) + +(def ^:private schema:create-export-binfile-job + "The `.penpot` package of the files frozen in the params, produced by + the backend runner; the business params are what the `:export-binfile` + job-def declares." + [:map {:title "create-export-binfile-job" :closed true} + [:params export-binfile/schema:params]]) + +(def ^:private schema:create-import-binfile-job + "The `.penpot` package the caller uploaded, imported into a project by + the backend runner; the business params are what the `:import-binfile` + job-def declares for creation." + [:map {:title "create-import-binfile-job" :closed true} + [:params import-binfile/schema:create-params] + ;; the package arrives as a chunked upload and never as a path the + ;; caller names: see `assemble-upload` + [:upload-id ::sm/uuid]]) + +(def ^:private schema:job-summary + "What the caller gets when a job is created: enough to follow it, and + nothing about the work it will do." + [:map {:title "job-summary"} + [:id ::sm/uuid] + [:status ::sm/text] + [:name ::sm/text] + [:created-at ::ct/inst] + [:expires-at [:maybe ::ct/inst]]]) + +(defn- resolve-job-def + "The job the caller named, refused when it is not a job of `family`: the + registry is the source of truth, and a name that is not one of its jobs + is rejected before anything is read or stored. + + The job-def is returned because the caller needs it right away: it is + what decodes and validates the business params." + [cfg family name] + (let [job-def (get (jobs/get-defs cfg) (keyword name))] + (when-not (= family (::jobs/family job-def)) + (ex/raise :type :validation + :code :not-a-job-of-the-family + :hint "the job name is not a registered job of that family" + :name (d/name name) + :family (d/name family))) + job-def)) + +(defn- get-job-summary + [cfg job-id] + (let [job (jobs/get-job cfg job-id)] + {:id (:id job) + :status (jobs/get-user-status (:status job)) + :name (:name job) + :created-at (:created-at job) + :expires-at (:expires-at job)})) + +(defn- submit-job + "Create the job row with what every user job shares: the queue its + job-def routes the heavy work to (`::jobs/queue-name`, `:binfile` when + the def says nothing), no automatic retry and the expiry of the ledger." + [cfg job-def name params profile-id & {:keys [resource-id]}] + (jobs/submit cfg (cond-> {::jobs/name name + ::jobs/params params + ::jobs/queue (or (::jobs/queue-name job-def) + :binfile) + ::jobs/profile-id profile-id + ::jobs/max-retries 0 + ::jobs/expires-at (ct/plus (ct/now) + (cf/get-jobs-user-ttl))} + (some? resource-id) + (assoc ::jobs/resource-id resource-id)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; EXPORT +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- single-team-id + "The team id when every `file-ids` entry resolves and they share one + team. Otherwise nil." + [cfg file-ids] + (when (seq file-ids) + (db/run! cfg + (fn [{:keys [::db/conn]}] + (let [arr (db/create-array conn "uuid" file-ids) + rows (db/exec! conn + ["SELECT f.id AS file_id, t.id AS team_id + FROM file AS f + JOIN project AS p ON (p.id = f.project_id) + JOIN team AS t ON (t.id = p.team_id) + WHERE f.id = ANY(?)" arr]) + teams (into #{} (map :team-id) rows)] + (when (and (= (count file-ids) (count rows)) + (= 1 (count teams))) + (first teams))))))) + +(sv/defmethod ::create-export-binfile-job + "Create a durable job that exports a set of files as a `.penpot` + package. + + Its params are `:file-ids`, the set of files to export, and + `:export-type`, how the libraries of those files are handled: what + the job-def declares, and nothing more. There is no default for the + type, because a caller that does not say it would get a package that + is not the one it asked for. + + The job is created, not run: the caller follows it by its id. The + read permission of every file is checked here so a refusal arrives + synchronously, and the handler checks it again before reading." + {::doc/added "2.20" + ::webhooks/event? true + ::sm/params schema:create-export-binfile-job + ::sm/result schema:job-summary} + [cfg {:keys [::rpc/profile-id] :as envelope}] + (let [job-def (resolve-job-def cfg :export :export-binfile) + ;; the RPC layer decoded the params against the job-def schema + ;; before the command ran + params (jobs/validate-params job-def (:params envelope)) + file-ids (:file-ids params)] + + (when (empty? file-ids) + (ex/raise :type :validation + :code :no-files-to-export + :hint "expected at least one file to export")) + + (doseq [file-id file-ids] + (files/check-read-permissions! cfg profile-id file-id)) + + (quotes/check! cfg {::quotes/id ::quotes/export-jobs-per-profile + ::quotes/profile-id profile-id}) + + (let [summary (get-job-summary cfg (submit-job cfg job-def :export-binfile + params profile-id)) + team-id (single-team-id cfg file-ids)] + (with-meta summary + {::audit/props (cond-> {:job-id (:id summary) + :files (count file-ids) + :export-type (d/name (:export-type params))} + team-id (assoc :team-id team-id))})))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IMPORT +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- assemble-upload + "The package the caller uploaded, assembled into a local file that + belongs to this call. + + The command only takes a chunked upload: a plain file upload would mean + reading a path the caller named, and the server must never read a file + it did not write itself." + [cfg profile-id upload-id] + (db/tx-run! cfg media/assemble-chunks profile-id upload-id)) + +(defn- resolve-version + "The version of the package: what the caller says, or what the header + of the file says." + [path version] + (or version + (case (bfc/parse-file-format path) + :binfile-v3 3 + 1))) + +(sv/defmethod ::create-import-binfile-job + "Create a durable job that imports a `.penpot` package into a project. + + The package arrives as a chunked upload, which the server assembles + itself: the command never reads a path the caller names. The package is + then checked and stored as the resource of the job, so any worker can + consume it by `resource_id`. The job is created, not run, and the caller + follows it by its id." + {::doc/added "2.20" + ::webhooks/event? true + ::sm/params schema:create-import-binfile-job + ::sm/result schema:job-summary + ;; assembling the upload and storing the package happens here, in the + ;; request: the same limit the legacy import command declares + ::climit/id [[:create-import-binfile-job/by-profile ::rpc/profile-id] + [:create-import-binfile-job/global]]} + [cfg {:keys [::rpc/profile-id] :as envelope}] + (let [job-def (resolve-job-def cfg :import :import-binfile) + ;; the RPC layer decoded the params against the create schema + ;; before the command ran + given (:params envelope) + project-id (:project-id given)] + + ;; the edition permission is checked here so a refusal arrives + ;; synchronously and before the upload is assembled; the handler + ;; checks it again before writing + (projects/check-edition-permissions! cfg profile-id project-id) + + (quotes/check! cfg {::quotes/id ::quotes/import-jobs-per-profile + ::quotes/profile-id profile-id}) + + ;; the shape of what the caller sent is checked before the upload + ;; is assembled: the version is known good here (the create schema + ;; rejects one out of range, and the caller may omit it), so the + ;; merged map below only adds what the package says + (jobs/validate-params job-def (assoc given :version (or (:version given) 1))) + + (let [file (assemble-upload cfg profile-id (:upload-id envelope)) + path (:path file)] + + ;; everything that happens once the upload is on disk belongs to + ;; this try: a failure anywhere in it must not leave the temporary + ;; file behind + ;; + ;; the version and the manifest metadata are resolved here, in the + ;; command, and not in the job: only the creation path reads the + ;; package, and the job keeps what the audit will show + (try + (let [version (resolve-version path (:version given)) + manifest (when (= 3 version) + (bf.v3/get-manifest cfg path)) + ;; the manifest may name the tool that produced the package + ;; with either spelling: resolve it once, for the params and + ;; for the audit + referer (or (:referer manifest) (:refer manifest)) + params (cond-> (assoc given :version version) + manifest + (assoc :generated-by (:generated-by manifest) + :referer referer))] + + ;; the params are complete here: the version came from the + ;; caller or from the package, and the manifest filled the audit + (jobs/validate-params job-def params) + + (let [staged (js/put-resource cfg profile-id + {:content (sto/content path) + :filename "package.penpot" + :mtype "application/zip"})] + (try + (let [summary (get-job-summary cfg (submit-job cfg job-def :import-binfile params profile-id + :resource-id (:resource-id staged)))] + (with-meta summary + {::audit/props {:job-id (:id summary) + :generated-by (:generated-by manifest) + :referer referer}})) + + (catch Throwable cause + ;; the job never came to own the package, so it must not be + ;; left for the storage GC to find late + (js/release-resource cfg (:resource-id staged)) + (throw cause))))) + + (finally + ;; the assembled file belongs to this call: the package is + ;; already in storage by the time we get here, or the creation + ;; failed and it is of no use to anyone + (fs/delete path)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; READ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private schema:get-job + [:map {:title "get-job" :closed true} + [:id ::sm/uuid]]) + +(def ^:private schema:job-detail + "The summary of a job plus what only exists once it is over: the result + it produced or, when it failed, its public error." + [:map {:title "job-detail"} + [:id ::sm/uuid] + [:status ::sm/text] + [:name ::sm/text] + [:created-at ::ct/inst] + [:expires-at [:maybe ::ct/inst]] + [:result {:optional true} :any] + [:error {:optional true} :any]]) + +(defn- get-job-detail + "The job the caller owns, as the answer of `get-job`. A job that does + not exist and a job of another profile get the same answer: the caller + cannot tell them apart." + [cfg profile-id job-id] + (let [job (jobs/get-job cfg job-id)] + (when-not (and (some? job) (= profile-id (:profile-id job))) + (ex/raise :type :not-found + :code :job-not-found + :hint "the job does not exist or belongs to another profile" + :job-id job-id)) + (cond-> {:id (:id job) + :status (jobs/get-user-status (:status job)) + :name (:name job) + :created-at (:created-at job) + :expires-at (:expires-at job)} + (some? (:result job)) + (assoc :result (:result job)) + + (some? (:error job)) + (assoc :error (jobs/decode-job-error (:error job)))))) + +(sv/defmethod ::get-job + "Read a job the caller created: its state and, once it is over, what it + produced or why it failed. + + The result of a finished job is the JSON the row holds, decoded with the + result schema its job-def declares: a job that keys its result by uuid + (the import resolution) answers with the types it stored, and a job whose + job-def declares no schema answers with the JSON as it is on disk, where a + uuid is text and a uuid used as a map key is a keyword. A result that does + not conform to the declared schema never reaches the column: the substrate + validates it when the job is completed. The progress and the history of + the job are not part of the answer: a client that follows a job takes its + progress from the websocket, and the event log still has no read surface." + {::doc/added "2.20" + ::sm/params schema:get-job + ::sm/result schema:job-detail} + [cfg {:keys [::rpc/profile-id] :as params}] + (get-job-detail cfg profile-id (:id params))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; CANCEL +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private schema:cancel-job + [:map {:title "cancel-job" :closed true} + [:id ::sm/uuid]]) + +(sv/defmethod ::cancel-job + "Cancel a job the caller created, whether it is still waiting or + already running. + + A running job stops at its next beat and rolls its work back; a job + that already finished between the request and the update is answered + with the state it is in, never an error. Like every other command + here, a job of another profile is indistinguishable from one that does + not exist." + {::doc/added "2.20" + ::sm/params schema:cancel-job + ::sm/result schema:job-summary} + [cfg {:keys [::rpc/profile-id] :as params}] + (let [job-id (:id params) + job (jobs/get-job cfg job-id)] + (when-not (and (some? job) (= profile-id (:profile-id job))) + (ex/raise :type :not-found + :code :job-not-found + :hint "the job does not exist or belongs to another profile" + :job-id job-id)) + (jobs/cancel cfg job-id) + ;; the answer names the outcome of this call, not the collapsed + ;; user status: a job the caller just cancelled reads "cancelled" + (assoc (get-job-summary cfg job-id) + :status (:status (jobs/get-job cfg job-id))))) diff --git a/backend/src/app/rpc/commands/nitrate.clj b/backend/src/app/rpc/commands/nitrate.clj index 0a51654c3a..af52bede6f 100644 --- a/backend/src/app/rpc/commands/nitrate.clj +++ b/backend/src/app/rpc/commands/nitrate.clj @@ -445,7 +445,8 @@ ;; Check moveTeams permission on the source organization (when (contains? cf/flags :admin-console) (let [organization-perms (nitrate/call cfg :get-organization-permissions - {:organization-id organization-id})] + {:organization-id organization-id + ::rpc/profile-id profile-id})] (if (nil? organization-perms) (ex/raise :type :validation :code :not-allowed @@ -480,8 +481,10 @@ "Returns info about external (non-organization-member) invitations pending for a team. External invitations are those sent to users who are not members of the given organization. Returns {:allows-anybody bool :external-emails [...]}" - [{:keys [::db/conn] :as cfg} team-id organization-id] - (let [organization-perms (nitrate/call cfg :get-organization-permissions {:organization-id organization-id}) + [{:keys [::db/conn] :as cfg} team-id organization-id profile-id] + (let [organization-perms (nitrate/call cfg :get-organization-permissions + {:organization-id organization-id + ::rpc/profile-id profile-id}) allows-anybody (cto/allowed? :add-anybody-to-team {:organization-perms organization-perms})] (if allows-anybody {:allows-anybody true :external-emails []} @@ -516,13 +519,16 @@ (when (contains? cf/flags :admin-console) (let [organization-member-ids-before (into #{} (nitrate/call cfg :get-organization-members {:organization-id organization-id})) - team-with-organization (nitrate/call cfg :get-team-organization {:team-id team-id}) + team-with-organization (nitrate/call cfg :get-team-organization {:team-id team-id + ::rpc/profile-id profile-id}) source-organization-id (get-in team-with-organization [:organization :id]) source-organization-perms (when source-organization-id (nitrate/call cfg :get-organization-permissions - {:organization-id source-organization-id})) + {:organization-id source-organization-id + ::rpc/profile-id profile-id})) target-organization-perms (nitrate/call cfg :get-organization-permissions - {:organization-id organization-id}) + {:organization-id organization-id + ::rpc/profile-id profile-id}) target-organization-same-owner? (and (some? source-organization-perms) (some? target-organization-perms) (= (:owner-id source-organization-perms) @@ -571,7 +577,7 @@ (notifications/notify-team-change cfg team "dashboard.team-belong-organization")) ;; Delete pending invitations for users who are not members of the target organization - (let [{:keys [allows-anybody external-emails]} (get-external-invitation-info cfg team-id organization-id)] + (let [{:keys [allows-anybody external-emails]} (get-external-invitation-info cfg team-id organization-id profile-id)] (when (and (not allows-anybody) (seq external-emails)) (let [conn (::db/conn cfg) emails-array (db/create-array conn "text" external-emails)] @@ -686,7 +692,7 @@ (ex/raise :type :validation :code :insufficient-permissions)) (nitrate/assert-membership cfg profile-id organization-id) - (let [{:keys [allows-anybody external-emails]} (get-external-invitation-info cfg team-id organization-id)] + (let [{:keys [allows-anybody external-emails]} (get-external-invitation-info cfg team-id organization-id profile-id)] {:has-external-invitations (boolean (seq external-emails)) :allows-anybody allows-anybody})) {:has-external-invitations false diff --git a/backend/src/app/rpc/commands/profile.clj b/backend/src/app/rpc/commands/profile.clj index d0e85a8624..3bd86b459b 100644 --- a/backend/src/app/rpc/commands/profile.clj +++ b/backend/src/app/rpc/commands/profile.clj @@ -20,6 +20,7 @@ [app.db.sql :as-alias sql] [app.email :as eml] [app.http.session :as session] + [app.jobs :as jobs] [app.loggers.audit :as audit] [app.main :as-alias main] [app.media :as media] @@ -33,7 +34,6 @@ [app.storage :as sto] [app.tokens :as tokens] [app.util.services :as sv] - [app.worker :as wrk] [cuerdas.core :as str])) (declare check-profile-existence!) @@ -225,11 +225,11 @@ (update-profile-password! cfg (assoc profile :password password)) - (eml/send! {::eml/conn (::db/conn cfg) - ::eml/factory eml/password-changed - :public-uri (cf/get :public-uri) - :to (:email profile) - :name (:fullname profile)}) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "password-changed" + :public-uri (cf/get :public-uri) + :to (:email profile) + :name (:fullname profile)}) (->> (rph/get-request params) (session/get-session) @@ -463,14 +463,14 @@ :email (:email profile) :hint "looks like the email has spam complaint reports")) - (eml/send! {::eml/conn conn - ::eml/factory eml/change-email - :public-uri (cf/get :public-uri) - :to (:email profile) - :name (:fullname profile) - :pending-email email - :token token - :extra-data ptoken}) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "change-email" + :public-uri (cf/get :public-uri) + :to (:email profile) + :name (:fullname profile) + :pending-email email + :token token + :extra-data ptoken}) nil)) ;; --- MUTATION: Update Profile Props @@ -574,11 +574,11 @@ {:profile-id profile-id})) ;; Schedule cascade deletion to a worker - (wrk/submit! {::db/conn conn - ::wrk/task :delete-object - ::wrk/params {:object :profile - :deleted-at deleted-at - :id profile-id}}) + (jobs/submit cfg + {::jobs/name :delete-object + ::jobs/params {:object :profile + :deleted-at deleted-at + :id profile-id}}) ;; Invalidate all sessions for this profile to ensure immediate ;; access revocation across all devices diff --git a/backend/src/app/rpc/commands/projects.clj b/backend/src/app/rpc/commands/projects.clj index 5c809cfd96..18e5c282f0 100644 --- a/backend/src/app/rpc/commands/projects.clj +++ b/backend/src/app/rpc/commands/projects.clj @@ -15,6 +15,7 @@ [app.db :as db] [app.db.sql :as-alias sql] [app.features.logical-deletion :as ldel] + [app.jobs :as jobs] [app.loggers.audit :as-alias audit] [app.loggers.webhooks :as webhooks] [app.rpc :as-alias rpc] @@ -23,8 +24,7 @@ [app.rpc.helpers :as rph] [app.rpc.permissions :as perms] [app.rpc.quotes :as quotes] - [app.util.services :as sv] - [app.worker :as wrk])) + [app.util.services :as sv])) ;; --- Check Project Permissions @@ -283,7 +283,7 @@ ;; --- MUTATION: Delete Project (defn- delete-project - [conn team project-id] + [{:keys [::db/conn] :as cfg} team project-id] (let [delay (ldel/get-deletion-delay team) project (db/update! conn :project {:deleted-at (ct/in-future delay)} @@ -295,11 +295,11 @@ :code :non-deletable-project :hint "impossible to delete default project")) - (wrk/submit! {::db/conn conn - ::wrk/task :delete-object - ::wrk/params {:object :project - :deleted-at (:deleted-at project) - :id project-id}}) + (jobs/submit cfg + {::jobs/name :delete-object + ::jobs/params {:object :project + :deleted-at (:deleted-at project) + :id project-id}}) project)) @@ -313,12 +313,12 @@ ::sm/params schema:delete-project ::webhooks/event? true ::db/transaction true} - [{:keys [::db/conn]} {:keys [::rpc/profile-id id] :as params}] + [{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id] :as params}] (check-edition-permissions! conn profile-id id) (let [team (teams/get-team conn :profile-id profile-id :project-id id) - project (delete-project conn team id)] + project (delete-project cfg team id)] (rph/with-meta (rph/wrap) {::audit/props {:team-id (:team-id project) :name (:name project) diff --git a/backend/src/app/rpc/commands/teams.clj b/backend/src/app/rpc/commands/teams.clj index 255111c6f9..d96c9a9cf1 100644 --- a/backend/src/app/rpc/commands/teams.clj +++ b/backend/src/app/rpc/commands/teams.clj @@ -20,6 +20,7 @@ [app.db.sql :as sql] [app.email :as eml] [app.features.logical-deletion :as ldel] + [app.jobs :as jobs] [app.loggers.audit :as audit] [app.main :as-alias main] [app.media.validation :as media.v] @@ -35,7 +36,6 @@ [app.setup :as-alias setup] [app.storage :as sto] [app.util.services :as sv] - [app.worker :as wrk] [clojure.set :as set])) ;; --- Helpers & Specs @@ -544,7 +544,8 @@ (nitrate/assert-membership cfg profile-id organization-id) (let [organization-perms (nitrate/call cfg :get-organization-permissions - {:organization-id organization-id})] + {:organization-id organization-id + ::rpc/profile-id profile-id})] (if (nil? organization-perms) (ex/raise :type :validation :code :not-allowed @@ -892,8 +893,7 @@ can-delete? (if in-organization? (cto/allowed? :delete-team - {:organization-perms {:owner-id (dm/get-in team [:organization :owner-id]) - :permissions (dm/get-in team [:organization :permissions])} + {:organization-perms organization :profile-id profile-id :team-perms perms}) (boolean (:is-owner perms)))] @@ -909,22 +909,27 @@ :code :non-deletable-team :hint "impossible to delete default team")) - (let [delay (ldel/get-deletion-delay team) - team (db/update! conn :team - {:deleted-at (ct/in-future delay)} - {:id team-id} - {::db/return-keys true})] + (let [delay (ldel/get-deletion-delay team) + organization-id (dm/get-in team [:organization :id]) + team-name (:name team) + team (db/update! conn :team + {:deleted-at (ct/in-future delay)} + {:id team-id} + {::db/return-keys true})] ;; Api call to nitrate (when (contains? cf/flags :admin-console) (nitrate/call cfg :delete-team {:profile-id profile-id :team-id team-id})) - (wrk/submit! {::db/conn conn - ::wrk/task :delete-object - ::wrk/params {:object :team - :deleted-at (:deleted-at team) - :id team-id}}) - team))) + (jobs/submit cfg + {::jobs/name :delete-object + ::jobs/params {:object :team + :deleted-at (:deleted-at team) + :id team-id}}) + (rph/with-meta (rph/wrap team) + {::audit/props {:organization-id organization-id + :team-id team-id + :team-name team-name}})))) (def ^:private schema:delete-team [:map {:title "delete-team"} @@ -936,14 +941,19 @@ ::sm/params schema:delete-team ::db/transaction true} [cfg {:keys [::rpc/profile-id id] :as params}] - (let [team (delete-team cfg {:team-id id :profile-id profile-id})] + (let [result (delete-team cfg {:team-id id :profile-id profile-id}) + team (rph/unwrap result)] ;; The client keeps receiving nil; the audit event carries the deleted row. + ;; Both prop sets are kept: the organization ones (develop) and the entity + ;; identity and timestamps (staging); `with-meta` merges shallowly, so the + ;; maps are merged explicitly. (rph/with-meta (rph/wrap) - {::audit/props {:id (:id team) - :name (:name team) - :created-at (:created-at team) - :modified-at (:modified-at team) - :deleted-at (:deleted-at team)}}))) + {::audit/props (merge (::audit/props (meta result)) + {:id (:id team) + :name (:name team) + :created-at (:created-at team) + :modified-at (:modified-at team) + :deleted-at (:deleted-at team)})}))) ;; --- Mutation: Team Update Role @@ -1098,7 +1108,7 @@ (update-team-photo cfg (assoc params :profile-id profile-id))) (defn update-team-photo - [{:keys [::db/pool ::sto/storage] :as cfg} {:keys [profile-id team-id] :as params}] + [{:keys [::db/pool ::sto/storage] :as cfg} {:keys [profile-id team-id file] :as params}] (let [team (get-team pool :profile-id profile-id :team-id team-id) photo (profile/upload-photo cfg params)] @@ -1114,4 +1124,10 @@ {:photo-id (:id photo)} {:id team-id}) - (assoc team :photo-id (:id photo)))) + (rph/with-meta (assoc team :photo-id (:id photo)) + {::audit/replace-props + {:team-id team-id + :file-name (:filename file) + :file-size (:size file) + :file-path (str (:path file)) + :file-mtype (:mtype file)}}))) diff --git a/backend/src/app/rpc/commands/teams_invitations.clj b/backend/src/app/rpc/commands/teams_invitations.clj index 66ac06493d..d151f28287 100644 --- a/backend/src/app/rpc/commands/teams_invitations.clj +++ b/backend/src/app/rpc/commands/teams_invitations.clj @@ -291,24 +291,24 @@ (not recent?)) (if organization (when (contains? cf/flags :admin-console) - (eml/send! {::eml/conn conn - ::eml/factory eml/invite-to-organization - :public-uri (cf/get :public-uri) - :to email - :invited-by (:fullname profile) - :user-name (:fullname member) - :organization organization - :token itoken - :extra-data ptoken})) - (eml/send! {::eml/conn conn - ::eml/factory eml/invite-to-team - :public-uri (cf/get :public-uri) - :to email - :invited-by (:fullname profile) - :team (:name team) - :organization (:organization team) - :token itoken - :extra-data ptoken}))) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "invite-to-organization" + :public-uri (cf/get :public-uri) + :to email + :invited-by (:fullname profile) + :user-name (:fullname member) + :organization organization + :token itoken + :extra-data ptoken})) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "invite-to-team" + :public-uri (cf/get :public-uri) + :to email + :invited-by (:fullname profile) + :team (:name team) + :organization (:organization team) + :token itoken + :extra-data ptoken}))) itoken))))) @@ -355,13 +355,13 @@ (db/delete! conn :team-invitation {:team-id team-id :email-to (:email member)}) - (eml/send! {::eml/conn conn - ::eml/factory eml/join-team - :public-uri (cf/get :public-uri) - :to (:email member) - :invited-by (:fullname profile) - :team (:name team) - :team-id (:id team)}))) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "join-team" + :public-uri (cf/get :public-uri) + :to (:email member) + :invited-by (:fullname profile) + :team (:name team) + :team-id (:id team)}))) (def ^:private sql:valid-access-request-profiles "SELECT p.id, p.email, p.is_blocked @@ -382,13 +382,12 @@ "Unified function to handle both create and resend team invitations. Accepts either: - emails (set) + role (single role for all emails) - - invitations (vector of {:email :role} maps)" + - invitations (vector of {:email :role} maps) + + `team` must already carry its organization info when nitrate is + active (see `nitrate/add-organization-info-to-team-strict`)." [{:keys [::db/conn] :as cfg} {:keys [profile team role emails invitations] :as params}] - (let [;; Enrich team with organization info once for all invitations when nitrate is active - team (if (contains? cf/flags :admin-console) - (nitrate/add-organization-info-to-team cfg team {}) - team) - organization (:organization team) + (let [organization (:organization team) organization-id (:id organization) restricted? (and organization-id (not (cto/allowed? :add-anybody-to-team {:organization-perms organization}))) all-organization-member-ids @@ -488,8 +487,10 @@ (let [perms (teams/get-permissions cfg profile-id team-id) profile (db/get-by-id cfg :profile profile-id) team (db/get-by-id cfg :team team-id) + ;; Strict lookup: a failed Nitrate call must not skip the + ;; organization policy below by looking like a team without one. team-with-org (when (contains? cf/flags :admin-console) - (nitrate/add-organization-info-to-team cfg team {})) + (nitrate/add-organization-info-to-team-strict cfg team {::rpc/profile-id profile-id})) organization (:organization team-with-org) ;; Determine which format is being used using-emails-format? (and emails role) @@ -509,8 +510,7 @@ (when (and (contains? cf/flags :admin-console) organization (not (cto/allowed? :send-invitations - {:organization-perms {:owner-id (:owner-id organization) - :permissions (:permissions organization)} + {:organization-perms organization :profile-id profile-id :team-perms perms}))) (ex/raise :type :validation @@ -540,7 +540,9 @@ ;; Check if the current profile is allowed to send emails (teams/check-profile-muted cfg profile) - (let [team (db/get-by-id cfg :team team-id) + (let [;; Reuse the strict organization lookup made for the policy + ;; check above, so every rule sees the same Nitrate answer. + team (or team-with-org team) ;; NOTE: Is important pass RPC method params down to the ;; `create-team-invitations` because it uses the implicit ;; RPC properties from params for fill necessary data on @@ -809,30 +811,30 @@ (teams/check-email-spam conn (:email team-owner) true) (let [request (upsert-team-access-request cfg team-id profile-id) - factory (cond - (and (some? file) (:is-default team) is-viewer) - eml/request-file-access-yourpenpot-view + template (cond + (and (some? file) (:is-default team) is-viewer) + "request-file-access-yourpenpot-view" - (and (some? file) (:is-default team)) - eml/request-file-access-yourpenpot + (and (some? file) (:is-default team)) + "request-file-access-yourpenpot" - (some? file) - eml/request-file-access + (some? file) + "request-file-access" - :else - eml/request-team-access)] + :else + "request-team-access")] - (eml/send! {::eml/conn conn - ::eml/factory factory - :public-uri (cf/get :public-uri) - :to (:email team-owner) - :requested-by (:fullname requester) - :requested-by-email (:email requester) - :team-name (:name team) - :team-id team-id - :file-name (:name file) - :file-id file-id - :page-id (:page-id file)}) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template template + :public-uri (cf/get :public-uri) + :to (:email team-owner) + :requested-by (:fullname requester) + :requested-by-email (:email requester) + :team-name (:name team) + :team-id team-id + :file-name (:name file) + :file-id file-id + :page-id (:page-id file)}) (with-meta {:request request} {::audit/props {:request 1}})))) diff --git a/backend/src/app/rpc/commands/verify_token.clj b/backend/src/app/rpc/commands/verify_token.clj index 26746382c8..4a0b84981f 100644 --- a/backend/src/app/rpc/commands/verify_token.clj +++ b/backend/src/app/rpc/commands/verify_token.clj @@ -302,21 +302,26 @@ ;; for the same name, and tied the record to the browser finishing ;; the flow. Everything the copy carried is computed above. (when organization-id-on-add - (audit/submit cfg (-> (audit/event-from-rpc-params params) - (assoc :name "accept-organization-invitation") - (assoc :props - (-> props - (assoc :organization-id organization-id-on-add) - (assoc :invited-by (:created-by invitation)) - (assoc :profile-id (:id profile)) - (assoc :profile-email (:email profile)) - (assoc :organization-member-add-source - organization-add-source) - (assoc :belongs-to-team-on-add - (boolean team-id)) - (assoc :organization-member-count-before - organization-member-count-before) - (audit/clean-props)))))) + (let [org-props (-> props + (assoc :organization-id organization-id-on-add + :invited-by (:created-by invitation) + :profile-id (:id profile) + :profile-email (:email profile) + :organization-member-add-source + organization-add-source + :belongs-to-team-on-add + (boolean team-id) + :organization-member-count-before + organization-member-count-before) + (audit/clean-props))] + (audit/submit cfg (-> (audit/event-from-rpc-params params) + (assoc :name "accept-organization-invitation" + :props org-props))) + (when-let [created-by (:created-by invitation)] + (audit/submit cfg (-> (audit/event-from-rpc-params params) + (assoc :profile-id created-by + :name "accept-organization-invitation-from" + :props org-props)))))) (cond-> (assoc claims :state :created diff --git a/backend/src/app/rpc/management/exporter.clj b/backend/src/app/rpc/management/exporter.clj index 007aa39fb9..0b41f3d5b3 100644 --- a/backend/src/app/rpc/management/exporter.clj +++ b/backend/src/app/rpc/management/exporter.clj @@ -28,11 +28,16 @@ schema:upload-tempfile-result [:map {:title "upload-templfile-result"}]) +(def ^:private allowed-shared-key-ids + "The exporter uploads its artifacts; the media worker uploads its results." + #{:exporter :media-processor}) + (sv/defmethod ::upload-tempfile {::doc/added "2.12" ::sm/params schema:upload-tempfile-params ::rpc/multipart-only-params [:content] - ::sm/result schema:upload-tempfile-result} + ::sm/result schema:upload-tempfile-result + ::rpc/allowed-shared-keys allowed-shared-key-ids} [cfg {:keys [::rpc/profile-id content]}] (media.v/check-media-type content cm/tempfile-types) (media.v/check-upload-path content) diff --git a/backend/src/app/rpc/management/files.clj b/backend/src/app/rpc/management/files.clj index d947ec7549..377247b56c 100644 --- a/backend/src/app/rpc/management/files.clj +++ b/backend/src/app/rpc/management/files.clj @@ -28,6 +28,10 @@ (def projection-version 1) +(def ^:private allowed-shared-key-ids + "Only the nexus file-security pipeline reads file projections." + #{:nexus}) + ;; Bounds for the data that crosses to nexus on every sweep. The ;; projection stays small by construction; anything past the caps sets ;; `:truncated?` instead of growing the payload. @@ -220,7 +224,8 @@ {::doc/added "2.20" ::sm/params schema:get-file-for-analysis-params ::sm/result schema:get-file-for-analysis-result - ::rpc/auth false} + ::rpc/auth false + ::rpc/allowed-shared-keys allowed-shared-key-ids} [cfg {:keys [file-id]}] ;; `realize?` loads the whole object tree eagerly: the projection ;; walks every shape, so lazy pointers (no `load-fn` bound here) diff --git a/backend/src/app/rpc/management/jobs.clj b/backend/src/app/rpc/management/jobs.clj new file mode 100644 index 0000000000..3fd79ff3d0 --- /dev/null +++ b/backend/src/app/rpc/management/jobs.clj @@ -0,0 +1,172 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.rpc.management.jobs + "Generic job management API for external workers (media and future + subsystems). External workers never touch the database: this API is + the ledger. The methods are family-agnostic (they operate on any row + of the `job` table) and use the plain JSON schemas of `app.jobs`. + + Auth: these endpoints take no profile, so every method declares + ::rpc/auth false. The route applies mw/shared-key-auth + (app.http.middleware) to authenticate the ` ` header, and + wrap-management applies wrap-shared-key-authorization (app.rpc) to + check the key id against ::rpc/allowed-shared-keys. If the route + middleware or wrap-management changes, revisit these methods." + (:require + [app.common.exceptions :as ex] + [app.common.schema :as sm] + [app.common.time :as ct] + [app.jobs :as jobs] + [app.rpc :as-alias rpc] + [app.rpc.doc :as doc] + [app.util.services :as sv])) + +(def ^:private allowed-shared-key-ids + "Only the exporter worker claims and settles durable jobs today." + #{:exporter}) + +;; ---- RPC METHOD: CLAIM-JOB + +(def ^:private schema:claim-job-params + [:map {:title "claim-job-params"} + [:job-id ::sm/uuid] + [:scheduled-at ::ct/inst]]) + +(def ^:private schema:claim-job-result + [:map {:title "claim-job-result"} + [:action [:enum :run :skip]] + [:name {:optional true} ::sm/text] + ;; Opaque per-job params blob; intentionally untyped. + [:params {:optional true} :any]]) + +(sv/defmethod ::claim-job + {::doc/added "2.20" + ::sm/params schema:claim-job-params + ::sm/result schema:claim-job-result + ::rpc/auth false + ::rpc/allowed-shared-keys allowed-shared-key-ids} + [cfg {:keys [job-id scheduled-at]}] + (let [row (jobs/get-job cfg job-id)] + ;; Race condition: if the job is claimed between get-job and claim! by + ;; another worker, claim! returns 0 and we return :skip. The conditional + ;; claim (UPDATE ... WHERE status IN ('new','scheduled','retry') AND + ;; scheduled_at=?) handles this correctly: only a pending row with an + ;; exact scheduled-at match is claimed, a stale payload is skipped. + (if (and row (pos? (jobs/claim cfg job-id scheduled-at))) + {:action :run + :name (:name row) + :params (:params row)} + {:action :skip}))) + +;; ---- RPC METHOD: REPORT-JOB-PROGRESS + +(def ^:private schema:report-job-progress-params + [:map {:title "report-job-progress-params"} + ;; The progress schema is the same one the jobs substrate validates: + ;; `:stage` is mandatory (a keyword of the worker vocabulary) and + ;; `:counters` is an optional map of `scope -> {:current :total?}`. + [:job-id ::sm/uuid] + [:progress jobs/schema:progress]]) + +(def ^:private schema:report-job-progress-result + [:map {:title "report-job-progress-result"} + [:action [:enum :run :skip]]]) + +(sv/defmethod ::report-job-progress + {::doc/added "2.20" + ::sm/params schema:report-job-progress-params + ::sm/result schema:report-job-progress-result + ::rpc/auth false + ::rpc/allowed-shared-keys allowed-shared-key-ids} + [cfg {:keys [job-id progress]}] + ;; A lost race (row already terminal) reports :skip so the worker + ;; stops retrying a report that can never land, mirroring claim-job. + ;; The forced route propagates database errors: the external worker + ;; must be able to retry a report it was not sure about. The interrupt + ;; the beat raises when the job is no longer active is caught here for + ;; the same reason, and it never reaches the HTTP error mapping: it is + ;; an internal signal, not a failure of the request. + (let [result (ex/try! (jobs/heartbeat cfg + :job-id job-id + :progress progress + ::jobs/force? true))] + (cond + (ex/exception? result) + (if (= :interrupt (:type (ex-data result))) + {:action :skip} + (throw result)) + + (pos? result) + {:action :run} + + :else + {:action :skip}))) + +;; ---- RPC METHOD: COMPLETE-JOB + +(def ^:private schema:complete-job-params + [:map {:title "complete-job-params"} + [:job-id ::sm/uuid] + ;; Per-job result: any JSON-encodable value, nil when the job returns + ;; nothing. Nested maps and vectors are allowed; unserializable + ;; values are dropped to nil by jobs/encode-result with a warning. A + ;; job whose job-def declares a result schema has its result validated + ;; against it: one that does not conform is rejected and the job is + ;; not completed. + [:result [:maybe :any]] + ;; Storage object the worker produced for this job. A resource is only + ;; set, never replaced: a job that already has one is rejected. + [:resource-id {:optional true} ::sm/uuid]]) + +(def ^:private schema:complete-job-result + [:map {:title "complete-job-result"} + [:action [:enum :run :skip]]]) + +(sv/defmethod ::complete-job + "Complete a job on behalf of the worker that ran it, with the result it + produced and, when it wrote one, the resource it stores. + + The result of a job whose job-def declares a result schema is validated + against it before it is stored: one that does not conform is rejected and + the job is not completed. A job that is no longer completable is skipped, + and nothing is validated for it." + {::doc/added "2.20" + ::sm/params schema:complete-job-params + ::sm/result schema:complete-job-result + ::rpc/auth false + ::rpc/allowed-shared-keys allowed-shared-key-ids} + [cfg {:keys [job-id result resource-id]}] + (if (pos? (jobs/complete cfg + :job-id job-id + :result result + :resource-id resource-id)) + {:action :run} + {:action :skip})) + +;; ---- RPC METHOD: FAIL-JOB + +(def ^:private schema:fail-job-params + [:map {:title "fail-job-params"} + [:job-id ::sm/uuid] + ;; Rich error report: type/code/hint are required, the map stays + ;; open to worker-defined details (see jobs/fail). + [:error jobs/schema:job-error]]) + +(def ^:private schema:fail-job-result + [:map {:title "fail-job-result"} + [:action [:enum :run :skip]]]) + +(sv/defmethod ::fail-job + {::doc/added "2.20" + ::sm/params schema:fail-job-params + ::sm/result schema:fail-job-result + ::rpc/auth false + ::rpc/allowed-shared-keys allowed-shared-key-ids} + [cfg {:keys [job-id error]}] + (if (pos? (jobs/fail cfg job-id error)) + {:action :run} + {:action :skip})) diff --git a/backend/src/app/rpc/management/nitrate.clj b/backend/src/app/rpc/management/nitrate.clj index 8c5d1bb658..55a409a0a5 100644 --- a/backend/src/app/rpc/management/nitrate.clj +++ b/backend/src/app/rpc/management/nitrate.clj @@ -17,7 +17,7 @@ [app.common.time :as ct] [app.common.types.organization :as cto] [app.common.types.profile :refer [schema:profile, schema:basic-profile]] - [app.common.types.team :refer [schema:team]] + [app.common.types.team :as ctt :refer [schema:team]] [app.common.uri :as u] [app.common.uuid :as uuid] [app.config :as cf] @@ -25,6 +25,7 @@ [app.email :as eml] [app.http :as-alias http] [app.http.session :as session] + [app.jobs :as jobs] [app.loggers.audit :as audit] [app.media.validation :as media.v] [app.nitrate :as nitrate] @@ -42,10 +43,13 @@ [app.storage :as sto] [app.util.services :as sv] [app.util.ssrf :as ssrf] - [app.worker :as wrk] [cuerdas.core :as str])) +(def ^:private allowed-shared-key-ids + "The Nitrate admin-console is the only caller of this API." + #{:admin-console}) + (defn- profile-to-map [profile] {:id (:id profile) :name (:fullname profile) @@ -58,6 +62,7 @@ (sv/defmethod ::authenticate "Authenticate the current user" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map] ::sm/result schema:profile ::nitrate/sso false} @@ -78,6 +83,13 @@ AND t.is_default IS FALSE AND t.deleted_at IS NULL;") +(def ^:private sql:get-member-teams + "SELECT t.id, t.name, t.is_default + FROM team AS t + JOIN team_profile_rel AS tpr ON t.id = tpr.team_id + WHERE tpr.profile_id = ? + AND t.deleted_at IS NULL;") + ;; ---- API: get-penpot-version (def ^:private schema:get-penpot-version-result @@ -98,6 +110,7 @@ (sv/defmethod ::get-penpot-version "Get the current Penpot version" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map] ::sm/result schema:get-penpot-version-result ::rpc/auth false} @@ -107,9 +120,17 @@ (def ^:private schema:get-teams-result [:vector schema:team]) +(def ^:private schema:get-member-teams-result + [:vector + [:map + [:id ::sm/uuid] + [:name ::sm/text] + [:is-default ::sm/boolean]]]) + (sv/defmethod ::get-teams "List teams for which current user is owner" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map] ::sm/result schema:get-teams-result ::nitrate/sso false} @@ -118,6 +139,54 @@ (->> (db/exec! cfg [sql:get-teams current-user-id]) (map #(select-keys % [:id :name]))))) +(sv/defmethod ::get-member-teams + "List all active teams where the current user is a member" + {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids + ::sm/params [:map] + ::sm/result schema:get-member-teams-result + ::nitrate/sso false} + [cfg {:keys [::rpc/profile-id]}] + (let [current-user-id (-> (profile/get-profile cfg profile-id) :id)] + (db/exec! cfg [sql:get-member-teams current-user-id]))) + +;; ---- API: update-profile-theme + +(def ^:private schema:update-profile-theme + [:map {:title "update-profile-theme"} + [:theme [:enum "light" "dark" "system"]]]) + +(sv/defmethod ::update-profile-theme + "Update the current user's theme" + {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids + ::sm/params schema:update-profile-theme + ::db/transaction true + ::nitrate/sso false} + [{:keys [::db/conn]} {:keys [::rpc/profile-id theme]}] + (profile/get-profile conn profile-id ::db/for-update true) + (db/update! conn :profile + {:theme theme} + {:id profile-id} + {::db/return-keys false}) + nil) + +;; ---- API: update-profile-props + +(def ^:private schema:update-profile-props + [:map {:title "update-profile-props"} + [:props profile/schema:props-writeable]]) + +(sv/defmethod ::update-profile-props + "Merge writable properties into the current user's profile" + {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids + ::sm/params schema:update-profile-props + ::db/transaction true + ::nitrate/sso false} + [cfg {:keys [::rpc/profile-id props]}] + (profile/update-profile-props cfg profile-id props)) + ;; ---- API: upload-organization-logo (def ^:private schema:upload-organization-logo @@ -134,6 +203,7 @@ Accepts an optional previous-id to mark the old logo for garbage collection when replacing an existing one." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:upload-organization-logo ::rpc/multipart-only-params [:content] ::sm/result schema:upload-organization-logo-result @@ -167,6 +237,7 @@ (sv/defmethod ::notify-team-change "Notify to Penpot a team change from nitrate" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:notify-team-change ::rpc/auth false} [cfg team] @@ -187,6 +258,7 @@ (sv/defmethod ::notify-user-added-to-organization "Notify to Penpot that an user has joined an organization from nitrate" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:notify-user-added-to-organization ::rpc/auth false} [cfg {:keys [profile-id organization-id]}] @@ -218,6 +290,7 @@ (sv/defmethod ::get-managed-profiles "List profiles that belong to teams for which current user is owner" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map] ::sm/result schema:managed-profile-result ::nitrate/sso false} @@ -258,6 +331,7 @@ (sv/defmethod ::get-teams-summary "Get summary information for a list of teams" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:get-teams-summary-params ::sm/result schema:get-teams-summary-result ::nitrate/sso false} @@ -324,11 +398,11 @@ RETURNING id, deleted_at;") deleted-at (db/create-array conn "uuid" team-ids)])] (doseq [{:keys [id deleted-at]} updated] - (wrk/submit! {::db/conn conn - ::wrk/task :delete-object - ::wrk/params {:object :team - :deleted-at deleted-at - :id id}})))) + (jobs/submit cfg + {::jobs/name :delete-object + ::jobs/params {:object :team + :deleted-at deleted-at + :id id}})))) nil) (defn manage-deleted-organization-teams @@ -379,6 +453,7 @@ RETURNING id, deleted_at;") "For a deleted organization, preserve organization teams and only prefix or delete imported Personal Projects before notifying connected users." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:notify-organization-deletion ::rpc/auth false} [cfg {:keys [organization-id]}] @@ -399,6 +474,7 @@ RETURNING id, deleted_at;") "For a given user, find all owned organizations and apply the deleted-organization transfer rules to their imported Personal Projects teams." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:notify-user-organizations-deletion ::nitrate/sso false} [cfg {:keys [profile-id]}] @@ -425,6 +501,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::get-profile-by-email "Get profile by email" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map [:email ::sm/email]] ::sm/result schema:profile ::nitrate/sso false} @@ -449,6 +526,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::get-profile-by-id "Get profile by email" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map [:id ::sm/uuid]] ::sm/result schema:profile ::nitrate/sso false} @@ -462,6 +540,33 @@ RETURNING id, deleted_at;") (profile-to-map profile))) +;; ---- API: get-profiles-by-ids + +(def ^:private sql:get-profiles-by-ids + "SELECT DISTINCT id, fullname, email, photo_id + FROM profile + WHERE id = ANY(?) + AND deleted_at IS NULL;") + +(def ^:private schema:get-profiles-by-ids-params + [:map [:ids [:vector ::sm/uuid]]]) + +(sv/defmethod ::get-profiles-by-ids + "Get multiple profiles by id. Unknown ids are simply absent from the result." + {::doc/added "2.20" + ::rpc/allowed-shared-keys allowed-shared-key-ids + ::sm/params schema:get-profiles-by-ids-params + ::sm/result [:vector schema:profile] + ::nitrate/sso false} + [cfg {:keys [ids]}] + (if (empty? ids) + [] + (db/run! cfg (fn [{:keys [::db/conn]}] + (let [ids-array (db/create-array conn "uuid" ids)] + (->> (db/exec! conn [sql:get-profiles-by-ids ids-array]) + (mapv profile-to-map))))))) + + ;; ---- API: get-organization-member-team-counts (def ^:private sql:get-organization-member-team-counts @@ -484,6 +589,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::get-organization-member-team-counts "Get the number of non-default teams each profile belongs to within a set of teams." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:get-organization-member-team-counts-params ::sm/result schema:get-organization-member-team-counts-result ::rpc/auth false} @@ -504,6 +610,97 @@ RETURNING id, deleted_at;") (db/exec! conn [sql:get-organization-member-team-counts ids-array]))))))) +;; ---- API: get-organization-team-roles + +(def ^:private sql:get-organization-team-roles + "SELECT t.id AS team_id, t.name AS team_name, tpr.profile_id, + CASE WHEN tpr.is_owner THEN 'owner' ELSE 'admin' END AS role + FROM team_profile_rel AS tpr + JOIN team AS t ON t.id = tpr.team_id + JOIN profile AS p ON p.id = tpr.profile_id + WHERE t.id = ANY(?) + AND t.deleted_at IS NULL + AND t.is_default IS FALSE + AND p.deleted_at IS NULL + AND (tpr.is_owner OR tpr.is_admin);") + +(def ^:private schema:get-organization-team-roles-params + [:map [:team-ids [:vector ::sm/uuid]]]) + +(def ^:private schema:get-organization-team-roles-result + [:vector [:map + [:team-id ::sm/uuid] + [:team-name ::sm/text] + [:profile-id ::sm/uuid] + [:role [:enum :owner :admin]]]]) + +(sv/defmethod ::get-organization-team-roles + "Get the owner and admin profiles of each non-default team within a set of teams." + {::doc/added "2.20" + ::rpc/allowed-shared-keys allowed-shared-key-ids + ::sm/params schema:get-organization-team-roles-params + ::sm/result schema:get-organization-team-roles-result + ::rpc/auth false} + [cfg {:keys [team-ids]}] + (if (empty? team-ids) + [] + (db/run! cfg (fn [{:keys [::db/conn]}] + (let [ids-array (db/create-array conn "uuid" team-ids)] + (->> (db/exec! conn [sql:get-organization-team-roles ids-array]) + (mapv (fn [{:keys [role] :as row}] + (assoc row :role (keyword role)))))))))) + + +;; ---- API: get-organization-member-teams + +(def ^:private sql:get-organization-member-teams + "SELECT t.id, t.name, t.photo_id, + CASE WHEN tpr.is_owner THEN 'owner' + WHEN tpr.is_admin THEN 'admin' + WHEN tpr.can_edit THEN 'editor' + ELSE 'viewer' + END AS role + FROM team_profile_rel AS tpr + JOIN team AS t ON t.id = tpr.team_id + WHERE tpr.profile_id = ? + AND t.id = ANY(?) + AND t.deleted_at IS NULL + AND t.is_default IS FALSE;") + +(def ^:private schema:get-organization-member-teams-params + [:map + [:profile-id ::sm/uuid] + [:team-ids [:or ::sm/uuid [:vector ::sm/uuid]]]]) + +(def ^:private schema:get-organization-member-teams-result + [:vector [:map + [:id ::sm/uuid] + [:name ::sm/text] + [:photo-url {:optional true} ::sm/uri] + [:role ctt/schema:role]]]) + +(sv/defmethod ::get-organization-member-teams + "Get the non-default teams a profile belongs to within a set of teams, + with the profile's role in each one." + {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids + ::sm/params schema:get-organization-member-teams-params + ::sm/result schema:get-organization-member-teams-result + ::nitrate/sso false} + [cfg {:keys [profile-id team-ids]}] + (let [team-ids (if (uuid? team-ids) [team-ids] team-ids)] + (if (empty? team-ids) + [] + (db/run! cfg (fn [{:keys [::db/conn]}] + (let [ids-array (db/create-array conn "uuid" team-ids)] + (->> (db/exec! conn [sql:get-organization-member-teams profile-id ids-array]) + (mapv (fn [{:keys [photo-id role] :as row}] + (cond-> (-> row + (dissoc :photo-id) + (assoc :role (keyword role))) + photo-id (assoc :photo-url (files/resolve-public-uri photo-id)))))))))))) + + ;; API: invite-to-organization (defn- get-invitation-organization @@ -524,6 +721,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::invite-to-organization "Invite to organization" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map [:email ::sm/email] [:organization cto/schema:organization-with-avatar]] @@ -556,6 +754,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::get-organization-invitations "Get valid invitations for an organization, returning at most one invitation per email." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:get-organization-invitations-params ::sm/result schema:get-organization-invitations-result ::nitrate/sso false} @@ -584,6 +783,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::delete-organization-invitations "Delete all invitations for one email in an organization scope (organization + organization teams)." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:delete-organization-invitations-params ::nitrate/sso false} [cfg {:keys [organization-id email]}] @@ -611,6 +811,7 @@ RETURNING id, deleted_at;") Called from Nitrate when an organization is about to be deleted, so users that click their invitation token hit the existing invalid-token landing page." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:delete-all-organization-invitations-params ::rpc/auth false} [cfg {:keys [organization-id]}] @@ -649,6 +850,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::remove-from-organization "Remove an user from an organization" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map [:profile-id ::sm/uuid] [:organization-id ::sm/uuid] @@ -694,6 +896,7 @@ RETURNING id, deleted_at;") "Get a summary of the teams that would be deleted, transferred, or exited if the user were removed from the organization" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map [:profile-id ::sm/uuid] [:organization-id ::sm/uuid] @@ -729,6 +932,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::send-renewal-email "Send an Enterprise subscription renewal notice email to a user." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:send-renewal-email-params ::rpc/auth false} [cfg {:keys [profile-id user-email user-name renewal-date estimated-amount organizations]}] @@ -741,15 +945,15 @@ RETURNING id, deleted_at;") user-name (if (nil? user-name) (:fullname (profile/get-profile cfg profile-id)) (str/trim user-name))] - (db/tx-run! cfg (fn [{:keys [::db/conn]}] - (eml/send! {::eml/conn conn - ::eml/factory eml/renewal-notice - :public-uri (cf/get :public-uri) - :to user-email - :user-name user-name - :renewal-date renewal-date - :estimated-amount amount-str - :organizations organizations})))) + (db/tx-run! cfg (fn [tx-cfg] + (eml/send-template tx-cfg {::eml/reuse-conn true + ::eml/template "renewal-notice" + :public-uri (cf/get :public-uri) + :to user-email + :user-name user-name + :renewal-date renewal-date + :estimated-amount amount-str + :organizations organizations})))) nil) ;; API: exists-organization-team-invitations-for-non-members / @@ -805,6 +1009,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::exists-organization-team-invitations-for-non-members "Return if there are any team invitations for emails that are not organization members." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:organization-team-invitations-for-non-members-params ::sm/result schema:exists-organization-team-invitations-for-non-members-result ::nitrate/sso false} @@ -815,6 +1020,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::delete-organization-team-invitations-for-non-members "Delete team invitations for emails that are not organization members." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:organization-team-invitations-for-non-members-params ::db/transaction true ::nitrate/sso false} @@ -847,6 +1053,7 @@ RETURNING id, deleted_at;") events)" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::audit/skip true ::sm/params schema:push-audit-events-params ::rpc/auth false} @@ -954,6 +1161,7 @@ RETURNING id, deleted_at;") "Get detailed information for all non-deleted teams in an organization, including owner info and project/file/member counts." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:get-teams-detail-params ::sm/result schema:get-teams-detail-result ::nitrate/sso false} @@ -984,6 +1192,7 @@ RETURNING id, deleted_at;") (customer-configured), so it is checked against the SSRF blocklist before any outbound request is attempted." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params cto/schema:nitrate-sso ::sm/result schema:check-organization-sso-result ::rpc/auth false} @@ -997,15 +1206,26 @@ RETURNING id, deleted_at;") (sv/defmethod ::notify-organization-sso-change "Nitrate notifies that an organization sso values have changed" {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map [:organization-id ::sm/uuid] [:updated-props ::sm/boolean] [:announce-activation ::sm/boolean]] ::rpc/auth false} - [{:keys [::db/pool] :as cfg} {:keys [organization-id updated-props announce-activation]}] + [{:keys [::db/pool] :as cfg} + {:keys [organization-id updated-props announce-activation] :as params}] (when updated-props (rpc/invalidate-organization-sso-cache-by-organization! organization-id) - (session/clear-organization-sso-sessions! pool organization-id)) + (session/clear-organization-sso-sessions! pool organization-id) + (let [request (-> params meta ::http/request) + context (-> (audit/prepare-context-from-request request) + (assoc :request-id (::rpc/request-id params)))] + (audit/submit cfg {:type "action" + :name "organization-sso-token-revoked" + :profile-id uuid/zero + :ip-addr (::rpc/ip-addr params) + :props {:organization-id organization-id} + :context (d/without-nils context)}))) (notifications/notify-organization-change-sso cfg organization-id) (when announce-activation (neh/send-organization-setup-sso-emails! cfg organization-id)) @@ -1046,6 +1266,7 @@ RETURNING id, deleted_at;") email allow-list. Requires the `admin-console-bulk-create-profiles` flag, disabled by default so it is only available on test environments." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params schema:bulk-create-profiles-params ::sm/result schema:bulk-create-profiles-result ::rpc/auth false} @@ -1080,6 +1301,7 @@ RETURNING id, deleted_at;") (sv/defmethod ::get-air-gapped "Returns whether this Penpot instance runs in air-gapped mode." {::doc/added "2.18" + ::rpc/allowed-shared-keys allowed-shared-key-ids ::sm/params [:map] ::sm/result schema:get-air-gapped-result ::rpc/auth false} diff --git a/backend/src/app/rpc/nitrate/emails_helper.clj b/backend/src/app/rpc/nitrate/emails_helper.clj index 5a9379e358..6f5c703d42 100644 --- a/backend/src/app/rpc/nitrate/emails_helper.clj +++ b/backend/src/app/rpc/nitrate/emails_helper.clj @@ -52,14 +52,14 @@ (defn- send-organization-setup-sso-email! "Send the organization SSO setup email to a single recipient, when allowed." - [conn organization-name {:keys [email profile]}] + [{:keys [::db/conn] :as cfg} organization-name {:keys [email profile]}] (when (or (nil? profile) (eml/allow-send-emails? conn profile)) - (eml/send! {::eml/conn conn - ::eml/factory eml/organization-setup-sso - :public-uri (cf/get :public-uri) - :to email - :organization-name organization-name}))) + (eml/send-template cfg {::eml/reuse-conn true + ::eml/template "organization-setup-sso" + :public-uri (cf/get :public-uri) + :to email + :organization-name organization-name}))) (defn- get-organization-sso-notify-recipients "Unique organization members and pending organization/team invitees for SSO activation emails." @@ -89,9 +89,9 @@ [cfg organization-id] (let [organization-summary (nitrate/call cfg :get-organization-summary {:organization-id organization-id})] (db/tx-run! cfg - (fn [{:keys [::db/conn]}] + (fn [{:keys [::db/conn] :as cfg}] (doseq [recipient (get-organization-sso-notify-recipients conn cfg organization-id organization-summary)] - (send-organization-setup-sso-email! conn (:name organization-summary) recipient)))))) + (send-organization-setup-sso-email! cfg (:name organization-summary) recipient)))))) (defn send-organization-setup-sso-emails-for-team! "Notify team members who are not in `organization-member-ids-before` and pending team invitees." @@ -99,6 +99,6 @@ (when (organization-sso-active? cfg organization-id) (let [organization-summary (nitrate/call cfg :get-organization-summary {:organization-id organization-id})] (db/tx-run! cfg - (fn [{:keys [::db/conn]}] + (fn [{:keys [::db/conn] :as cfg}] (doseq [recipient (get-team-sso-notify-recipients conn team-id organization-member-ids-before)] - (send-organization-setup-sso-email! conn (:name organization-summary) recipient))))))) + (send-organization-setup-sso-email! cfg (:name organization-summary) recipient))))))) diff --git a/backend/src/app/rpc/quotes.clj b/backend/src/app/rpc/quotes.clj index 96309afd49..ac79505d16 100644 --- a/backend/src/app/rpc/quotes.clj +++ b/backend/src/app/rpc/quotes.clj @@ -7,13 +7,14 @@ (ns app.rpc.quotes "Penpot resource usage quotes." (:require + [app.common.data :as d] [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] [app.common.time :as ct] [app.config :as cf] [app.db :as db] - [app.worker :as wrk] + [app.jobs :as jobs] [cuerdas.core :as str])) (defmulti check-quote ::id) @@ -73,39 +74,39 @@ (run! (partial check cfg) others))))))) (defn- send-notification! - [{:keys [::db/conn] :as params}] + [cfg] (l/warn :hint "max quote reached" - :target (::target params) - :profile-id (some-> params ::profile-id str) - :team-id (some-> params ::team-id str) - :project-id (some-> params ::project-id str) - :file-id (some-> params ::file-id str) - :quote (::quote params) - :total (::total params) - :incr (::inc params 1)) + :target (::target cfg) + :profile-id (some-> cfg ::profile-id str) + :team-id (some-> cfg ::team-id str) + :project-id (some-> cfg ::project-id str) + :file-id (some-> cfg ::file-id str) + :quote (::quote cfg) + :total (::total cfg) + :incr (::inc cfg 1)) (when-let [admins (seq (cf/get :admins))] - (let [subject (str/istr "[quotes:notification]: max quote reached ~(::target params)") - content (str/istr "- Param: profile-id '~(::profile-id params)}'\n" - "- Param: team-id '~(::team-id params)'\n" - "- Param: project-id '~(::project-id params)'\n" - "- Param: file-id '~(::file-id params)'\n" - "- Quote ID: '~(::target params)'\n" - "- Max: ~(::quote params)\n" - "- Total: ~(::total params) (INCR ~(::incr params 1))\n")] - (wrk/submit! {::db/conn conn - ::wrk/task :sendmail - ::wrk/delay (ct/duration "30s") - ::wrk/max-retries 4 - ::wrk/priority 200 - ::wrk/dedupe true - ::wrk/label "quotes-notification" - ::wrk/params {:to (vec admins) - :subject subject - :body content}})))) + (let [subject (str/istr "[quotes:notification]: max quote reached ~(::target cfg)") + content (str/istr "- Param: profile-id '~(::profile-id cfg)}'\n" + "- Param: team-id '~(::team-id cfg)'\n" + "- Param: project-id '~(::project-id cfg)'\n" + "- Param: file-id '~(::file-id cfg)'\n" + "- Quote ID: '~(::target cfg)'\n" + "- Max: ~(::quote cfg)\n" + "- Total: ~(::total cfg) (INCR ~(::incr cfg 1))\n")] + (jobs/submit cfg + {::jobs/name :sendmail + ::jobs/delay (ct/duration "30s") + ::jobs/max-retries 4 + ::jobs/priority 200 + ::jobs/dedupe true + ::jobs/label "quotes-notification" + ::jobs/params {:to (vec admins) + :subject subject + :body content}})))) (defn- generic-check! - [{:keys [::db/conn ::incr ::quote-sql ::count-sql ::default ::target] :or {incr 1} :as params}] + [{:keys [::db/conn ::incr ::quote-sql ::count-sql ::default ::target] :or {incr 1} :as cfg}] (let [quote (->> (db/exec! conn quote-sql) (map :quote) (reduce max (- Integer/MAX_VALUE))) @@ -114,7 +115,7 @@ (when (> (+ total incr) quote) (if (contains? cf/flags :soft-quotes) - (send-notification! (assoc params ::quote quote ::total total)) + (send-notification! (assoc cfg ::quote quote ::total total)) (ex/raise :type :restriction :code :max-quote-reached :target target @@ -170,9 +171,9 @@ WHERE profile_id = ?") (defmethod check-quote ::teams-per-profile - [{:keys [::profile-id ::target] :as quote}] - (assert (valid-teams-per-profile-quote? quote) "invalid quote parameters") - (-> quote + [{:keys [::profile-id ::target] :as cfg}] + (assert (valid-teams-per-profile-quote? cfg) "invalid quote parameters") + (-> cfg (assoc ::default (cf/get :quotes-teams-per-profile Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-1 target profile-id]) (assoc ::count-sql [sql:get-teams-per-profile profile-id]) @@ -194,10 +195,10 @@ WHERE profile_id = ?") (defmethod check-quote ::access-tokens-per-profile - [{:keys [::profile-id ::target] :as quote}] - (assert (valid-access-tokens-per-profile-quote? quote) "invalid quote parameters") + [{:keys [::profile-id ::target] :as cfg}] + (assert (valid-access-tokens-per-profile-quote? cfg) "invalid quote parameters") - (-> quote + (-> cfg (assoc ::default (cf/get :quotes-access-tokens-per-profile Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-1 target profile-id]) (assoc ::count-sql [sql:get-access-tokens-per-profile profile-id]) @@ -222,10 +223,10 @@ AND p.deleted_at IS NULL") (defmethod check-quote ::projects-per-team - [{:keys [::profile-id ::team-id ::target] :as quote}] - (assert (valid-projects-per-team-quote? quote) "invalid quote parameters") + [{:keys [::profile-id ::team-id ::target] :as cfg}] + (assert (valid-projects-per-team-quote? cfg) "invalid quote parameters") - (-> quote + (-> cfg (assoc ::default (cf/get :quotes-projects-per-team Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-2 target team-id profile-id profile-id]) (assoc ::count-sql [sql:get-projects-per-team team-id]) @@ -249,10 +250,10 @@ WHERE v.team_id = ?") (defmethod check-quote ::font-variants-per-team - [{:keys [::profile-id ::team-id ::target] :as quote}] - (assert (valid-font-variant-per-team-quote? quote) "invalid quote parameters") + [{:keys [::profile-id ::team-id ::target] :as cfg}] + (assert (valid-font-variant-per-team-quote? cfg) "invalid quote parameters") - (-> quote + (-> cfg (assoc ::default (cf/get :quotes-font-variants-per-team Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-2 target team-id profile-id profile-id]) (assoc ::count-sql [sql:get-font-variants-per-team team-id]) @@ -277,10 +278,10 @@ WHERE team_id = ?") (defmethod check-quote ::invitations-per-team - [{:keys [::profile-id ::team-id ::target] :as quote}] - (assert (valid-invitations-per-team-quote? quote) "invalid quote parameters") + [{:keys [::profile-id ::team-id ::target] :as cfg}] + (assert (valid-invitations-per-team-quote? cfg) "invalid quote parameters") - (-> quote + (-> cfg (assoc ::default (cf/get :quotes-invitations-per-team Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-2 target team-id profile-id profile-id]) (assoc ::count-sql [sql:get-invitations-per-team team-id]) @@ -311,10 +312,10 @@ ;; effective members plus ongoing valid invitations. (defmethod check-quote ::profiles-per-team - [{:keys [::profile-id ::team-id ::target] :as quote}] - (assert (valid-profiles-per-team-quote? quote) "invalid quote parameters") + [{:keys [::profile-id ::team-id ::target] :as cfg}] + (assert (valid-profiles-per-team-quote? cfg) "invalid quote parameters") - (-> quote + (-> cfg (assoc ::default (cf/get :quotes-profiles-per-team Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-2 target team-id profile-id profile-id]) (assoc ::count-sql [sql:get-profiles-per-team team-id team-id]) @@ -340,10 +341,10 @@ AND f.deleted_at IS NULL") (defmethod check-quote ::files-per-project - [{:keys [::profile-id ::project-id ::team-id ::target] :as quote}] - (assert (valid-files-per-project-quote? quote) "invalid quote parameters") + [{:keys [::profile-id ::project-id ::team-id ::target] :as cfg}] + (assert (valid-files-per-project-quote? cfg) "invalid quote parameters") - (-> quote + (-> cfg (assoc ::default (cf/get :quotes-files-per-project Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-3 target project-id profile-id team-id profile-id profile-id]) (assoc ::count-sql [sql:get-files-per-project project-id]) @@ -368,10 +369,10 @@ WHERE ct.file_id = ?") (defmethod check-quote ::comment-threads-per-file - [{:keys [::profile-id ::file-id ::team-id ::project-id ::target] :as quote}] - (assert (valid-comment-threads-per-file-quote? quote) "invalid quote parameters") + [{:keys [::profile-id ::file-id ::team-id ::project-id ::target] :as cfg}] + (assert (valid-comment-threads-per-file-quote? cfg) "invalid quote parameters") - (-> quote + (-> cfg (assoc ::default (cf/get :quotes-comment-threads-per-file Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-4 target file-id profile-id project-id profile-id team-id profile-id profile-id]) @@ -398,9 +399,9 @@ WHERE ct.file_id = ?") (defmethod check-quote ::comments-per-file - [{:keys [::profile-id ::file-id ::team-id ::project-id ::target] :as quote}] - (assert (valid-comments-per-file-quote? quote) "invalid quote parameters") - (-> quote + [{:keys [::profile-id ::file-id ::team-id ::project-id ::target] :as cfg}] + (assert (valid-comments-per-file-quote? cfg) "invalid quote parameters") + (-> cfg (assoc ::default (cf/get :quotes-comments-per-file Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-4 target file-id profile-id project-id profile-id team-id profile-id profile-id]) @@ -430,9 +431,9 @@ AND fc.label IS NOT NULL") (defmethod check-quote ::snapshots-per-file - [{:keys [::profile-id ::file-id ::team-id ::project-id ::target] :as quote}] - (assert (valid-snapshots-per-file-quote? quote) "invalid quote parameters") - (-> quote + [{:keys [::profile-id ::file-id ::team-id ::project-id ::target] :as cfg}] + (assert (valid-snapshots-per-file-quote? cfg) "invalid quote parameters") + (-> cfg (assoc ::default (cf/get :quotes-snapshots-per-file Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-4 target file-id profile-id project-id profile-id team-id profile-id profile-id]) @@ -463,9 +464,9 @@ AND fc.label IS NOT NULL") (defmethod check-quote ::snapshots-per-team - [{:keys [::profile-id ::team-id ::target] :as quote}] - (assert (valid-snapshots-per-team-quote? quote) "invalid quote parameters") - (-> quote + [{:keys [::profile-id ::team-id ::target] :as cfg}] + (assert (valid-snapshots-per-team-quote? cfg) "invalid quote parameters") + (-> cfg (assoc ::default (cf/get :quotes-snapshots-per-team Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-2 target team-id profile-id profile-id]) (assoc ::count-sql [sql:get-snapshots-per-team team-id]) @@ -489,9 +490,9 @@ WHERE tar.team_id = ?") (defmethod check-quote ::team-access-requests-per-team - [{:keys [::profile-id ::team-id ::target] :as quote}] - (assert (valid-team-access-requests-per-team-quote? quote) "invalid quote parameters") - (-> quote + [{:keys [::profile-id ::team-id ::target] :as cfg}] + (assert (valid-team-access-requests-per-team-quote? cfg) "invalid quote parameters") + (-> cfg (assoc ::default (cf/get :quotes-team-access-requests-per-team Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-2 target team-id profile-id profile-id]) (assoc ::count-sql [sql:get-team-access-requests-per-team team-id]) @@ -514,9 +515,9 @@ WHERE tar.requester_id = ?") (defmethod check-quote ::team-access-requests-per-requester - [{:keys [::profile-id ::target] :as quote}] - (assert (valid-team-access-requests-per-requester-quote? quote) "invalid quote parameters") - (-> quote + [{:keys [::profile-id ::target] :as cfg}] + (assert (valid-team-access-requests-per-requester-quote? cfg) "invalid quote parameters") + (-> cfg (assoc ::default (cf/get :quotes-team-access-requests-per-requester Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-1 target profile-id]) (assoc ::count-sql [sql:get-team-access-requests-per-requester profile-id]) @@ -539,9 +540,9 @@ AND deleted_at IS NULL") (defmethod check-quote ::upload-sessions-per-profile - [{:keys [::profile-id ::target] :as quote}] - (assert (valid-upload-sessions-per-profile-quote? quote) "invalid quote parameters") - (-> quote + [{:keys [::profile-id ::target] :as cfg}] + (assert (valid-upload-sessions-per-profile-quote? cfg) "invalid quote parameters") + (-> cfg (assoc ::default (cf/get :quotes-upload-sessions-per-profile Integer/MAX_VALUE)) (assoc ::quote-sql [sql:get-quotes-1 target profile-id]) (assoc ::count-sql [sql:get-upload-sessions-per-profile profile-id]) @@ -607,9 +608,9 @@ WHERE so.deleted_at IS NULL") (defmethod check-quote ::media-storage-bytes-per-team - [{:keys [::profile-id ::team-id ::target] :as quote}] - (assert (valid-media-storage-bytes-per-team-quote? quote) "invalid quote parameters") - (-> quote + [{:keys [::profile-id ::team-id ::target] :as cfg}] + (assert (valid-media-storage-bytes-per-team-quote? cfg) "invalid quote parameters") + (-> cfg (assoc ::default (cf/get :quotes-media-storage-bytes-per-team (* 20 1024 1024 1024))) (assoc ::quote-sql [sql:get-quotes-2 target team-id profile-id profile-id]) @@ -617,6 +618,61 @@ team-id team-id team-id team-id team-id team-id]) (generic-check!))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; QUOTE: BINFILES JOBS PER PROFILE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def ^:private schema:jobs-per-profile + [:map [::profile-id ::sm/uuid]]) + +(def ^:private valid-jobs-per-profile-quote? + (sm/lazy-validator schema:jobs-per-profile)) + +;; Only the jobs that are still on their way count: a terminal job is not +;; pending work anymore, and the jobs GC removes it. The tenant filters +;; like every other query of the substrate: instances share one database +;; and one profile can have work in more than one of them. +(def ^:private + sql:get-jobs-per-profile + "SELECT count(*) AS total + FROM job + WHERE tenant = ? + AND profile_id = ? + AND status IN ('new', 'scheduled', 'running', 'retry') + AND name = ANY(?)") + +(defn- names-of-family + "The registered names of a family of jobs. The quote counts what the + registry knows, so a new job of the family is covered without touching + this file." + [cfg family] + (->> (jobs/get-defs cfg) + (vals) + (filter #(= family (::jobs/family %))) + (map #(d/name (::jobs/name %))) + (vec))) + +(defn- check-jobs-quote + [cfg family config-key] + (let [{:keys [::db/conn ::profile-id ::target]} cfg] + (assert (valid-jobs-per-profile-quote? cfg) "invalid quote parameters") + (-> cfg + (assoc ::default (cf/get config-key Integer/MAX_VALUE)) + (assoc ::quote-sql [sql:get-quotes-1 target profile-id]) + (assoc ::count-sql [sql:get-jobs-per-profile + (cf/get :tenant) + profile-id + (db/create-array conn "text" (names-of-family cfg family))]) + (generic-check!)))) + +(defmethod check-quote ::export-jobs-per-profile + [cfg] + (check-jobs-quote cfg :export :quotes-export-jobs-per-profile)) + +(defmethod check-quote ::import-jobs-per-profile + [cfg] + (check-jobs-quote cfg :import :quotes-import-jobs-per-profile)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; QUOTE: DEFAULT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/backend/src/app/srepl/main.clj b/backend/src/app/srepl/main.clj index 10656d0c28..a0bab89055 100644 --- a/backend/src/app/srepl/main.clj +++ b/backend/src/app/srepl/main.clj @@ -27,6 +27,7 @@ [app.features.file-snapshots :as fsnap] [app.health :as health] [app.http.session :as session] + [app.jobs :as jobs] [app.loggers.audit :as audit] [app.msgbus :as mbus] [app.rpc.commands.auth :as auth] @@ -40,7 +41,6 @@ [app.system :as sys] [app.util.blob :as blob] [app.util.pointer-map :as pmap] - [app.worker :as wrk] [clojure.datafy :refer [datafy]] [clojure.java.io :as io] [clojure.pprint :refer [print-table]] @@ -57,38 +57,39 @@ ;; TASKS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn print-tasks +(defn print-jobs [] - (let [tasks (:app.worker/registry sys/system)] - (pp/pprint (keys tasks) :level 200))) + (let [jobs (:app.jobs/defs sys/system)] + (pp/pprint (into (sorted-set) (keys jobs))))) -(defn run-task! +(defn run-job + "Execute the job handler in-process (no job row)." ([tname] - (run-task! tname {})) + (run-job tname {})) ([tname params] - (wrk/invoke! (-> sys/system - (assoc ::wrk/task tname) - (assoc ::wrk/params params))))) + (jobs/invoke (-> sys/system + (assoc ::jobs/name tname) + (assoc ::jobs/params params))))) -(defn schedule-task! +(defn submit-job + "Submit a durable job to the queue." ([name] - (schedule-task! name {})) + (submit-job name {})) ([name params] - (wrk/submit! (-> sys/system - (assoc ::wrk/task name) - (assoc ::wrk/params params))))) + (jobs/submit sys/system + {::jobs/name name + ::jobs/params params}))) -(defn send-test-email! +(defn send-test-email [destination] (assert (string? destination) "destination should be provided") - (-> sys/system - (assoc ::wrk/task :sendmail) - (assoc ::wrk/params {:body "test email" - :subject "test email" - :to [destination]}) - (wrk/invoke!))) + (jobs/submit sys/system + {::jobs/name :sendmail + ::jobs/params {:body "test email" + :subject "test email" + :to [destination]}})) -(defn resend-email-verification-email! +(defn resend-email-verification-email [email] (db/tx-run! sys/system (fn [{:keys [::db/conn] :as cfg}] @@ -285,11 +286,11 @@ :context {:triggered-by "srepl" :cause cause}}) - (wrk/invoke! (-> system - (assoc ::wrk/task :delete-object) - (assoc ::wrk/params {:object :profile - :deleted-at deleted-at - :id (:id profile)}))) + (jobs/invoke (-> system + (assoc ::jobs/name :delete-object) + (assoc ::jobs/params {:object :profile + :deleted-at deleted-at + :id (:id profile)}))) (:id profile))) (defn delete-profiles-by-email! @@ -775,11 +776,11 @@ :context {:triggered-by "srepl" :cause "explicit call to delete-file!"} :tracked-at tnow}) - (wrk/invoke! (-> sys/system - (assoc ::wrk/task :delete-object) - (assoc ::wrk/params {:object :file - :deleted-at tnow - :id file-id}))) + (jobs/invoke (-> sys/system + (assoc ::jobs/name :delete-object) + (assoc ::jobs/params {:object :file + :deleted-at tnow + :id file-id}))) :deleted)) (defn restore-file! @@ -816,11 +817,11 @@ :cause "explicit call to delete-project!"} :tracked-at tnow}) - (wrk/invoke! (-> sys/system - (assoc ::wrk/task :delete-object) - (assoc ::wrk/params {:object :project - :deleted-at tnow - :id project-id}))) + (jobs/invoke (-> sys/system + (assoc ::jobs/name :delete-object) + (assoc ::jobs/params {:object :project + :deleted-at tnow + :id project-id}))) :deleted)) (defn- restore-project* @@ -868,11 +869,11 @@ :cause "explicit call to delete-team!"} :tracked-at tnow}) - (wrk/invoke! (-> sys/system - (assoc ::wrk/task :delete-object) - (assoc ::wrk/params {:object :team - :deleted-at tnow - :id team-id}))) + (jobs/invoke (-> sys/system + (assoc ::jobs/name :delete-object) + (assoc ::jobs/params {:object :team + :deleted-at tnow + :id team-id}))) :deleted)) (def ^:private sql:restore-font-file-objects @@ -939,11 +940,11 @@ :cause "explicit call to delete-profile!"} :tracked-at tnow}) - (wrk/invoke! (-> sys/system - (assoc ::wrk/task :delete-object) - (assoc ::wrk/params {:object :profile - :deleted-at tnow - :id profile-id}))) + (jobs/invoke (-> sys/system + (assoc ::jobs/name :delete-object) + (assoc ::jobs/params {:object :profile + :deleted-at tnow + :id profile-id}))) :deleted)) (defn restore-profile! @@ -1000,11 +1001,11 @@ (defn- submit-deleted-cascade "Replay the deletion cascade of every soft-deleted row of `table`, by - submitting one `:delete-object` task per row. + submitting one `:delete-object` job per row. Reads the rows with a server-side cursor (`db/plan`) instead of materialising - the whole table, and enqueues the task instead of running it inline, so it - can be used against production in bounded batches. The task is idempotent (it + the whole table, and enqueues the job instead of running it inline, so it + can be used against production in bounded batches. The job is idempotent (it only marks the children with the row's own date), `label` is the row id and `dedupe` drops not-yet-due duplicates. @@ -1029,13 +1030,13 @@ (seq ids) (conj (db/create-array conn "uuid" ids))) found (reduce (fn [total {:keys [id deleted-at]}] (when-not dry-run? - (wrk/submit! (-> system - (assoc ::wrk/task :delete-object) - (assoc ::wrk/params {:object object - :deleted-at deleted-at - :id id}) - (assoc ::wrk/label (str id)) - (assoc ::wrk/dedupe true)))) + (jobs/submit system + {::jobs/name :delete-object + ::jobs/params {:object object + :deleted-at deleted-at + :id id} + ::jobs/label (str id) + ::jobs/dedupe true})) (inc total)) 0 (db/plan conn query))] diff --git a/backend/src/app/storage.clj b/backend/src/app/storage.clj index 25c2d3482a..f73fd3557e 100644 --- a/backend/src/app/storage.clj +++ b/backend/src/app/storage.clj @@ -48,6 +48,13 @@ "Bucket name for chunked-upload chunks." stsch/upload-session-bucket) +(def job-resource-bucket + "Bucket for storage objects referenced by job.resource_id (the + unified jobs substrate): transient artifacts owned by their job row, + reclaimed by storage-gc-touched (via the jobs GC touch) once no job + row references them anymore." + stsch/job-resource-bucket) + (def valid-buckets stsch/metadata-buckets) @@ -241,11 +248,15 @@ backend' (impl/resolve-backend storage backend) bucket (:bucket mdata) + ;; `job-resource` objects belong to one profile, so they are + ;; never shared: a dedup hit would hand the artifact of one + ;; profile to the job of another. dedupable? (and (::deduplicate? params) (:hash mdata) (some? bucket) (not= tempfile-bucket bucket) - (not= upload-session-bucket bucket)) + (not= upload-session-bucket bucket) + (not= job-resource-bucket bucket)) hit (when dedupable? (get-database-object-by-hash pool backend bucket (:hash mdata)))] diff --git a/backend/src/app/storage/gc_deleted.clj b/backend/src/app/storage/gc_deleted.clj index 342cfed123..aacc2d7ac3 100644 --- a/backend/src/app/storage/gc_deleted.clj +++ b/backend/src/app/storage/gc_deleted.clj @@ -15,8 +15,10 @@ (:require [app.common.data :as d] [app.common.logging :as l] + [app.common.schema :as sm] [app.common.time :as ct] [app.db :as db] + [app.jobs :as jobs] [app.metrics :as mtx] [app.storage :as sto] [app.storage.impl :as impl] @@ -166,6 +168,11 @@ :inc n :labels [(name backend-id)])) + ;; Beat the job after every chunk: each chunk commits on its own, so a + ;; long sweep neither loses work on a late failure nor outruns the + ;; orphan lease. + (jobs/heartbeat cfg) + (if-let [cursor (peek rows)] (let [new-total (+ total (reduce + 0 (vals counts)))] (throttle total new-total) @@ -174,15 +181,36 @@ (:id cursor))) total)))) -(defmethod ig/assert-key ::handler +(declare execute-storage-gc-deleted) + +(defmethod ig/assert-key ::job-def [_ params] (assert (sto/valid-storage? (::sto/storage params)) "expect valid storage") - (assert (db/pool? (::db/pool params)) "expect valid storage") + (assert (db/pool? (::db/pool params)) "expect valid db pool") (assert (mtx/metrics? (::mtx/metrics params)) "expect valid metrics instance")) -(defmethod ig/init-key ::handler +(def schema:storage-gc-deleted-params + "Params map (no params needed; cfg-provided config only)." + [:map {:closed true}]) + +(defmethod ig/init-key ::job-def [_ cfg] - (fn [_] - (let [total (clean-deleted cfg)] - (l/inf :hint "task finished" :total total) - {:deleted total}))) + {::jobs/name :storage-gc-deleted + ::jobs/schema schema:storage-gc-deleted-params + ::jobs/handler + (fn [_context params] + (execute-storage-gc-deleted cfg params)) + ::jobs/decoder (sm/decoder schema:storage-gc-deleted-params sm/json-transformer) + ::jobs/validator (sm/validator schema:storage-gc-deleted-params)}) + +(defn execute-storage-gc-deleted + "Plain job handler: clean the marked-deleted storage objects." + ([cfg] (execute-storage-gc-deleted cfg {})) + ([cfg _params] + ;; NOTE: no outer transaction here on purpose — clean-deleted + ;; commits each chunk in its own transaction, so a late failure + ;; only loses the in-flight chunk. + (jobs/heartbeat cfg) + (let [total (clean-deleted cfg)] + (l/inf :hint "task finished" :total total) + {:deleted total}))) diff --git a/backend/src/app/storage/gc_touched.clj b/backend/src/app/storage/gc_touched.clj index 104f9b7f17..80e411d9da 100644 --- a/backend/src/app/storage/gc_touched.clj +++ b/backend/src/app/storage/gc_touched.clj @@ -22,8 +22,10 @@ [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.logging :as l] + [app.common.schema :as sm] [app.common.time :as ct] [app.db :as db] + [app.jobs :as jobs] [app.metrics :as mtx] [app.storage :as sto] [app.storage.impl :as impl] @@ -68,8 +70,7 @@ (-> (db/exec-one! conn [sql:has-file-object-thumbnail-refs id]) (get :has-refs))) -(def ^:private - sql:has-file-thumbnail-refs +(def ^:private sql:has-file-thumbnail-refs "SELECT EXISTS (SELECT 1 FROM file_thumbnail WHERE media_id = ?) AS has_refs") (defn- has-file-thumbnails-refs? @@ -77,6 +78,24 @@ (-> (db/exec-one! conn [sql:has-file-thumbnail-refs id]) (get :has-refs))) +;; Objects in the job-resource bucket are referenced by the resource_id +;; column of job rows (the unified jobs substrate). A live job row of +;; any status keeps the object frozen; once no row references it (the +;; jobs GC deletes expiring/retained rows and marks the object as +;; touched before/at the same time) the object becomes deletable. + +(def ^:private sql:has-job-resource-refs + "SELECT EXISTS (SELECT 1 FROM job WHERE resource_id = ?) AS has_refs") + +(defn- has-job-resource-refs? + "Checks if ANY job row (any status) references the object. Terminal states + (completed, failed, cancelled, aborted) also freeze the object because the jobs GC + hasn't run yet to clean them up. Once the jobs GC deletes the row, the + object becomes eligible for storage GC." + [conn {:keys [id]}] + (-> (db/exec-one! conn [sql:has-job-resource-refs id]) + (get :has-refs))) + (def ^:private sql:has-upload-session-chunk-refs "SELECT EXISTS (SELECT 1 FROM upload_session_chunk WHERE object_id = ?) AS has_refs") @@ -220,6 +239,7 @@ (= bucket "file-data") (process-objects conn has-file-data-refs? bucket objects) (= bucket sto/tempfile-bucket) (process-objects conn (constantly false) sto/tempfile-bucket objects) (= bucket sto/upload-session-bucket) (process-objects conn has-upload-session-chunk-refs? sto/upload-session-bucket objects) + (= bucket sto/job-resource-bucket) (process-objects conn has-job-resource-refs? bucket objects) (= bucket "organization") (process-objects conn (constantly false) bucket objects) :else (ex/raise :type :internal @@ -288,6 +308,7 @@ (let [[nfo ndo] (if (seq chunk) (db/tx-run! cfg process-chunk chunk) [0 0])] + (jobs/heartbeat cfg) (recur (long (+ freezed nfo)) (long (+ deleted ndo)))) {:freeze freezed :delete deleted})))) @@ -296,20 +317,40 @@ ;; HANDLER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defmethod ig/assert-key ::handler +(declare execute-storage-gc-touched) + +(defmethod ig/assert-key ::job-def [_ params] (assert (db/pool? (::db/pool params)) "expect valid storage") (assert (mtx/metrics? (::mtx/metrics params)) "expect valid metrics")) -(defmethod ig/expand-key ::handler +(defmethod ig/expand-key ::job-def [k v] {k (merge {::min-age (ct/duration {:hours 2})} v)}) -(defmethod ig/init-key ::handler - [_ {:keys [::min-age] :as cfg}] - (fn [{:keys [props]}] - (let [threshold (if (:skip-delay props) - (ct/now) - (ct/minus (ct/now) min-age))] - (process-touched (assoc cfg ::timestamp threshold))))) +(def schema:storage-gc-touched-params + "Optional :skip-delay processes all touched objects immediately, + bypassing the min-age threshold (repl-driven deletion cascades)." + [:map {:closed true} + [:skip-delay {:optional true} :boolean]]) + +(defmethod ig/init-key ::job-def + [_ cfg] + {::jobs/name :storage-gc-touched + ::jobs/schema schema:storage-gc-touched-params + ::jobs/handler + (fn [_context params] + (execute-storage-gc-touched cfg params)) + ::jobs/decoder (sm/decoder schema:storage-gc-touched-params sm/json-transformer) + ::jobs/validator (sm/validator schema:storage-gc-touched-params)}) + +(defn execute-storage-gc-touched + "Plain job handler: analyze the touched storage objects and freeze or + delete them depending on their references." + ([cfg] (execute-storage-gc-touched cfg {})) + ([cfg params] + (let [threshold (if (:skip-delay params) + (ct/now) + (ct/minus (ct/now) (::min-age cfg)))] + (process-touched (assoc cfg ::timestamp threshold))))) diff --git a/backend/src/app/storage/pending_gc.clj b/backend/src/app/storage/pending_gc.clj index df161f40c9..ff90e34f9a 100644 --- a/backend/src/app/storage/pending_gc.clj +++ b/backend/src/app/storage/pending_gc.clj @@ -14,7 +14,9 @@ ever iterating the whole physical store." (:require [app.common.logging :as l] + [app.common.schema :as sm] [app.db :as db] + [app.jobs :as jobs] [app.storage :as sto] [app.storage.impl :as impl] [integrant.core :as ig])) @@ -75,14 +77,33 @@ (recur (long (+ total (count rows))))) total))) -(defmethod ig/assert-key ::handler +(declare execute-storage-pending-gc) + +(defmethod ig/assert-key ::job-def [_ params] (assert (db/pool? (::db/pool params)) "expected valid db pool") (assert (sto/valid-storage? (::sto/storage params)) "expect valid storage")) -(defmethod ig/init-key ::handler +(def schema:storage-pending-gc-params + "Params map (no params needed; cfg-provided config only)." + [:map {:closed true}]) + +(defmethod ig/init-key ::job-def [_ cfg] - (fn [_] - (let [total (process! cfg)] - (l/inf :hint "task finished" :total total) - {:processed total}))) + {::jobs/name :storage-pending-gc + ::jobs/schema schema:storage-pending-gc-params + ::jobs/handler + (fn [_context params] + (execute-storage-pending-gc cfg params)) + ::jobs/decoder (sm/decoder schema:storage-pending-gc-params sm/json-transformer) + ::jobs/validator (sm/validator schema:storage-pending-gc-params)}) + +(defn execute-storage-pending-gc + "Plain job handler: reclaim storage objects created in 'pending' state + that were never promoted to 'valid'." + ([cfg] (execute-storage-pending-gc cfg {})) + ([cfg _params] + (jobs/heartbeat cfg) + (let [total (process! cfg)] + (l/inf :hint "task finished" :total total) + {:processed total}))) diff --git a/backend/src/app/storage/schema.clj b/backend/src/app/storage/schema.clj index 2ded471780..50a354d12f 100644 --- a/backend/src/app/storage/schema.clj +++ b/backend/src/app/storage/schema.clj @@ -33,6 +33,11 @@ "Bucket name for chunked-upload chunks." "upload-session") +(def job-resource-bucket + "Bucket name for storage objects owned by a job row + (`job.resource_id`)." + "job-resource") + (def bucket-requirements "Canonical buckets and the extra keys each one must carry, so a new bucket and its contract live in one place. Listed keys are declared as @@ -48,6 +53,8 @@ "organization" #{:organization-id} tempfile-bucket #{} upload-session-bucket #{} + ;; read back only by the profile that owns them + job-resource-bucket #{:profile-id} "file-data" #{:file-id :id} "file-data-fragment" #{} "file-change" #{}}) diff --git a/backend/src/app/tasks/delete_object.clj b/backend/src/app/tasks/delete_object.clj index 2545fe97e4..87cacd7a53 100644 --- a/backend/src/app/tasks/delete_object.clj +++ b/backend/src/app/tasks/delete_object.clj @@ -8,9 +8,11 @@ "A generic task for object deletion cascade handling" (:require [app.common.logging :as l] + [app.common.schema :as sm] [app.common.time :as ct] [app.db :as db] [app.db.sql :as-alias sql] + [app.jobs :as jobs] [app.loggers.audit :as audit] [app.rpc.commands.files :as files] [app.rpc.commands.profile :as profile] @@ -174,6 +176,7 @@ {::db/return-keys false}) (doseq [team (profile/get-owned-teams conn id)] + (jobs/heartbeat cfg) (delete-object cfg (assoc team :object :team :deleted-at deleted-at)))) @@ -182,27 +185,47 @@ [_cfg props] (l/wrn :obj (:object props) :hint "not implementation found")) -(defmethod ig/assert-key ::handler +(def schema:delete-object-params + [:map + [:object [:enum :snapshot :team :project :profile :file]] + [:deleted-at ::ct/inst] + [:id ::sm/uuid] + [:file-id {:optional true} ::sm/uuid] + [:file-ids {:optional true} [:vector ::sm/uuid]]]) + +(defn execute-delete-object + "Plain job handler: run the delete-object multimethod on the provided + params inside a single transaction, tracking the cascade and reporting + it as an audit event when anything was marked." + [cfg params] + (db/tx-run! cfg + (fn [cfg] + (when (top-exists? cfg params) + (binding [*cascade* (atom {:teams [] :projects [] :files []})] + (delete-object cfg params) + (let [{:keys [teams projects files]} @*cascade*] + (when (or (seq teams) (seq projects) (seq files)) + (audit/submit cfg {:name "delete-object-with-cascade" + :type "action" + :props {:object (:object params) + :id (:id params) + :deleted-at (:deleted-at params) + :cascade {:team-ids teams + :project-ids projects + :file-ids files}} + :context {:triggered-by "delete-object"}})))))))) + +(defmethod ig/assert-key ::job-def [_ params] (assert (db/pool? (::db/pool params)) "expected a valid database pool") (assert (sto/valid-storage? (::sto/storage params)) "expected valid storage to be provided")) -(defmethod ig/init-key ::handler +(defmethod ig/init-key ::job-def [_ cfg] - (fn [{:keys [props] :as task}] - (db/tx-run! cfg - (fn [cfg] - (when (top-exists? cfg props) - (binding [*cascade* (atom {:teams [] :projects [] :files []})] - (delete-object cfg props) - (let [{:keys [teams projects files]} @*cascade*] - (when (or (seq teams) (seq projects) (seq files)) - (audit/submit cfg {:name "delete-object-with-cascade" - :type "action" - :props {:object (:object props) - :id (:id props) - :deleted-at (:deleted-at props) - :cascade {:team-ids teams - :project-ids projects - :file-ids files}} - :context {:triggered-by "delete-object"}}))))))))) + {::jobs/name :delete-object + ::jobs/schema schema:delete-object-params + ::jobs/handler + (fn [_context params] + (execute-delete-object cfg params)) + ::jobs/decoder (sm/decoder schema:delete-object-params sm/json-transformer) + ::jobs/validator (sm/validator schema:delete-object-params)}) diff --git a/backend/src/app/tasks/demo_purge.clj b/backend/src/app/tasks/demo_purge.clj index a77fb36e09..4427f7b83e 100644 --- a/backend/src/app/tasks/demo_purge.clj +++ b/backend/src/app/tasks/demo_purge.clj @@ -9,33 +9,47 @@ creation time with a delay matching the configured deletion-delay." (:require [app.common.logging :as l] + [app.common.schema :as sm] [app.common.time :as ct] [app.db :as db] - [app.worker :as wrk] + [app.jobs :as jobs] [integrant.core :as ig])) -(defmethod ig/assert-key ::handler +(def schema:demo-purge-params + [:map + [:profile-id ::sm/uuid]]) +(declare execute-demo-purge) + +(defmethod ig/assert-key ::job-def [_ params] (assert (db/pool? (::db/pool params)) "expected a valid database pool")) -(defmethod ig/init-key ::handler +(defmethod ig/init-key ::job-def [_ cfg] - (fn [{:keys [props]}] - (let [profile-id (get props :profile-id) - now (ct/now)] + {::jobs/name :demo-purge + ::jobs/schema schema:demo-purge-params + ::jobs/handler + (fn [_context params] + (execute-demo-purge cfg params)) + ::jobs/decoder (sm/decoder schema:demo-purge-params sm/json-transformer) + ::jobs/validator (sm/validator schema:demo-purge-params)}) - (l/trc :hint "demo-purge" :profile-id (str profile-id)) +(defn execute-demo-purge + "Plain job handler: mark the demo profile as deleted and submit the + corresponding delete-object job." + [cfg params] + (let [profile-id (:profile-id params) + now (ct/now)] + (l/trc :hint "demo-purge" :profile-id (str profile-id)) - ;; Mark the profile for immediate deletion - (db/tx-run! cfg - (fn [{:keys [::db/conn] :as cfg}] - (db/update! conn :profile - {:deleted-at now} - {:id profile-id} - {::db/return-keys false}) - (wrk/submit! - (-> cfg - (assoc ::wrk/task :delete-object) - (assoc ::wrk/params {:object :profile - :deleted-at now - :id profile-id})))))))) + (db/tx-run! cfg + (fn [{:keys [::db/conn] :as cfg}] + (db/update! conn :profile + {:deleted-at now} + {:id profile-id} + {::db/return-keys false}) + (jobs/submit cfg + {::jobs/name :delete-object + ::jobs/params {:object :profile + :deleted-at now + :id profile-id}}))))) diff --git a/backend/src/app/tasks/export_binfile.clj b/backend/src/app/tasks/export_binfile.clj new file mode 100644 index 0000000000..5ac105a087 --- /dev/null +++ b/backend/src/app/tasks/export_binfile.clj @@ -0,0 +1,99 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.tasks.export-binfile + "The `:export-binfile` job: it produces the `.penpot` artifact of the + set of files frozen in its params." + (:require + [app.binfile.common :as bfc] + [app.binfile.jobs :as bfj] + [app.binfile.v3 :as bf.v3] + [app.common.schema :as sm] + [app.db :as db] + [app.jobs :as jobs] + [app.jobs.storage :as js] + [app.metrics :as mtx] + [app.rpc.commands.files :as files] + [app.storage :as sto] + [app.storage.tmp :as tmp] + [datoteka.fs :as fs] + [integrant.core :as ig])) + +(def schema:params + "Business params of an export: the files to export, frozen when the job + was created, and how their libraries are handled." + [:map {:title "export-binfile-params" :closed true} + [:file-ids [::sm/set ::sm/uuid]] + [:export-type [::sm/one-of #{:include-libraries :merge-libraries + :detach-libraries :link-later}]]]) + +(def ^:private artifact + "What the user sees of the package an export produces. A single name + describes a set of files, so it does not come from any of them." + {:filename "export.penpot" + :mtype "application/zip"}) + +(def schema:result + "What the job stores when the export succeeds: where the artifact can be + downloaded from and what it is." + [:map {:title "export-binfile-result" :closed true} + [:resource-uri ::sm/text] + [:filename ::sm/text] + [:mtype ::sm/text] + [:size ::sm/int]]) + +(def ^:private check-result + (sm/check-fn schema:result + :hint "invalid result of an export job" + :type :validation + :code :invalid-result)) + +(defn execute-export + "Plain handler, importable and testable without integrant. + + The read permission is checked again here, and not only when the job was + created: it can be revoked while the job waits in its queue. The artifact + is stored as the resource of the job, and the completion envelope carries + its descriptor as the business result and its id for the runner to + associate." + [cfg context params] + (let [profile-id (:profile-id context) + file-ids (:file-ids params)] + + (doseq [file-id file-ids] + (files/check-read-permissions! cfg profile-id file-id)) + + (let [output (tmp/tempfile* :suffix ".penpot") + cfg (bfc/export-cfg cfg {:file-ids file-ids + :export-type (:export-type params) + :output output})] + (try + (bfj/check-active cfg context) + (bfj/run cfg context bf.v3/export-files!) + (let [resource (->> (assoc artifact :content (sto/content output)) + (js/put-resource cfg profile-id)) + result (check-result (dissoc resource :resource-id))] + {:result result + :resource-id (:resource-id resource)}) + (finally + (fs/delete output)))))) + +(defmethod ig/assert-key ::job-def + [_ params] + (assert (db/pool? (::db/pool params)) "expected a valid database pool") + (assert (sto/valid-storage? (::sto/storage params)) "expected valid storage to be provided") + (assert (mtx/metrics? (::mtx/metrics params)) "expected valid metrics")) + +(defmethod ig/init-key ::job-def + [_ cfg] + {::jobs/name :export-binfile + ::jobs/family :export + ::jobs/resource-role :output + ::jobs/schema schema:params + ::jobs/handler (fn [context params] + (execute-export cfg context params)) + ::jobs/decoder (sm/decoder schema:params sm/json-transformer) + ::jobs/validator (sm/validator schema:params)}) diff --git a/backend/src/app/tasks/file_gc.clj b/backend/src/app/tasks/file_gc.clj index 78461e9b99..c8b2e41870 100644 --- a/backend/src/app/tasks/file_gc.clj +++ b/backend/src/app/tasks/file_gc.clj @@ -15,6 +15,7 @@ [app.common.files.helpers :as cfh] [app.common.files.validate :as cfv] [app.common.logging :as l] + [app.common.schema :as sm] [app.common.thumbnails :as thc] [app.common.time :as ct] [app.common.types.components-list :as ctkl] @@ -24,8 +25,8 @@ [app.db :as db] [app.features.fdata :as feat.fdata] [app.features.file-snapshots :as fsnap] + [app.jobs :as jobs] [app.storage :as sto] - [app.worker :as wrk] [integrant.core :as ig])) (declare get-file) @@ -41,14 +42,27 @@ (map :data) (mapcat cfh/collect-used-media))) +(defn- collect-snapshot-liveness + "Single snapshot scan collecting both fragment pointer ids and media + ids referenced by unpurged snapshots. Runs once per file-gc pass and + feeds clean-file-media! and clean-fragments!, so snapshot blobs are + read and decoded only once instead of once per cleaner." + [cfg file-id] + (let [xf (comp (filter :data) + (map (fn [{:keys [data]}] + {:pointers (feat.fdata/get-used-pointer-ids data) + :media (cfh/collect-used-media data)}))) + rf (completing (fn [acc {:keys [pointers media]}] + (-> acc + (update :pointers into pointers) + (update :media into media))))] + (fsnap/reduce-snapshots cfg file-id xf rf {:pointers #{} :media #{}}))) + (defn- clean-file-media! "Performs the garbage collection of file media objects." - [{:keys [::db/conn ::timestamp] :as cfg} {:keys [id] :as file}] + [{:keys [::db/conn ::timestamp] :as cfg} snap-media {:keys [id] :as file}] (let [used-media - (fsnap/reduce-snapshots cfg id xf:collect-used-media conj #{}) - - used-media - (into used-media xf:collect-used-media [file]) + (into snap-media xf:collect-used-media [file]) used-media (db/create-array conn "uuid" used-media) @@ -57,6 +71,7 @@ (->> (db/exec! conn [sql:mark-file-media-object-deleted timestamp id used-media]) (into #{} (map :id)))] + (jobs/heartbeat cfg) (doseq [id unused-media] (l/trc :obj "media-object" :file-id (str id) @@ -71,7 +86,7 @@ RETURNING object_id") (defn- clean-file-object-thumbnails! - [{:keys [::db/conn ::timestamp]} {:keys [data] :as file}] + [{:keys [::db/conn ::timestamp] :as cfg} {:keys [data] :as file}] (let [file-id (:id file) using (->> (vals (:pages-index data)) (into #{} (comp @@ -87,6 +102,7 @@ unused (->> (db/exec! conn [sql:mark-file-object-thumbnails-deleted timestamp file-id ids]) (into #{} (map :object-id)))] + (jobs/heartbeat cfg) (doseq [object-id unused] (l/trc :obj "object-thumbnail" :file-id (str file-id) @@ -101,10 +117,11 @@ RETURNING revn") (defn- clean-file-thumbnails! - [{:keys [::db/conn ::timestamp]} {:keys [id revn] :as file}] + [{:keys [::db/conn ::timestamp] :as cfg} {:keys [id revn] :as file}] (let [unused (->> (db/exec! conn [sql:mark-file-thumbnails-deleted timestamp id revn]) (into #{} (map :revn)))] + (jobs/heartbeat cfg) (doseq [revn unused] (l/trc :obj "thumbnail" :file-id (str id) @@ -182,12 +199,20 @@ (mapcat feat.fdata/get-used-pointer-ids))) (defn- clean-fragments! - [{:keys [::db/conn ::timestamp]} {:keys [id] :as file}] - (let [used (into #{} xf:collect-pointers [file]) + "Mark fragment rows referenced neither by the current main file data + nor by any unpurged snapshot. The file arrives unrealized (see + `get-file`), so `main-used` holds its stored fragment ids; combined + with `reattach-unchanged`, unchanged pages keep their ids and are + never marked. Runs inside the caller transaction, which already + holds FOR UPDATE on the file row." + [{:keys [::db/conn ::timestamp] :as cfg} snap-pointers {:keys [id] :as file}] + (let [main-used (into #{} xf:collect-pointers [file]) + used (into main-used snap-pointers) unused (->> (db/exec! conn [sql:mark-deleted-data-fragments timestamp id (db/create-array conn "uuid" used)]) (into #{} bfc/xf-map-id))] + (jobs/heartbeat cfg) (doseq [id unused] (l/trc :obj "fragment" :file-id (str id) @@ -196,10 +221,10 @@ file)) (defn- clean-media! - [cfg file] + [cfg {:keys [media]} file] (let [file (->> file (clean-deleted-components! cfg) - (clean-file-media! cfg) + (clean-file-media! cfg media) (clean-file-thumbnails! cfg) (clean-file-object-thumbnails! cfg))] (cfv/validate-file-schema! file) @@ -207,8 +232,12 @@ (defn get-file [cfg {:keys [file-id revn]}] + ;; NOTE: deliberately NOT realized: `process-file!` binds the fragment + ;; loader and works on PointerMaps directly, so unchanged pages keep + ;; their fragment ids (see `reattach-unchanged`). Realizing here would + ;; force every page to be re-persisted under a new id on every pass. (let [file (bfc/get-file cfg file-id - :realize? true + :realize? false :skip-locked? true :lock-for-update? true)] @@ -224,13 +253,21 @@ (defn- process-file! [cfg {:keys [file-id] :as props}] (if-let [file (get-file cfg props)] - (let [file (->> file - (bfl/clean-file) - (clean-media! cfg) - (clean-fragments! cfg)) - file (assoc file :has-media-trimmed true)] - (bfc/update-file! cfg file) - true) + ;; Every fragment load goes through `with-pointers`. Each pointer + ;; instance loads its content once (`loaded?`), and the replacement + ;; instances `clean-file` creates already carry their content, so + ;; the pass reads each fragment once without a separate cache. + (feat.fdata/with-pointers cfg file-id + (let [liveness (collect-snapshot-liveness cfg file-id) + original (feat.fdata/original-pointers (:data file)) + file (->> file + (bfl/clean-file) + (clean-media! cfg liveness) + (feat.fdata/reattach-unchanged original) + (clean-fragments! cfg (:pointers liveness))) + file (assoc file :has-media-trimmed true)] + (bfc/update-file! cfg file) + true)) (do (l/dbg :hint "skip cleaning, criteria does not match" :file-id (str file-id)) @@ -240,28 +277,44 @@ ;; HANDLER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defmethod ig/assert-key ::handler - [_ params] - (assert (db/pool? (::db/pool params)) "expected a valid database pool") - (assert (sto/valid-storage? (::sto/storage params)) "expected valid storage to be provided")) +(defn execute-file-gc + "Plain job handler: clean the media/thumbnails/fdata of one file." + [cfg params] + (try + (-> cfg + (assoc ::db/rollback (:rollback? params)) + (db/tx-run! (fn [{:keys [::db/conn] :as cfg}] + (let [cfg (-> cfg + (update ::sto/storage sto/configure conn) + (assoc ::timestamp (ct/now))) + processed? (process-file! cfg params)] -(defmethod ig/init-key ::handler + (when (and processed? (contains? cf/flags :tiered-file-data-storage)) + ;; Pass only the file id: the offload job ignores + ;; any other file-gc param, and forwarding the whole + ;; map would silently couple the two jobs. + (jobs/submit cfg + {::jobs/name :offload-file-data + ::jobs/params {:file-id (:file-id params)} + ::jobs/priority 10 + ::jobs/delay 1000})) + processed?)))) + (catch Throwable cause + (l/err :hint "error on cleaning file" + :file-id (str (:file-id params)) + :cause cause)))) + +(def schema:file-gc-params + [:map + [:file-id ::sm/uuid] + [:revn ::sm/int]]) + +(defmethod ig/init-key ::job-def [_ cfg] - (fn [{:keys [props] :as task}] - (try - (-> cfg - (assoc ::db/rollback (:rollback? props)) - (assoc ::timestamp (ct/now)) - (db/tx-run! (fn [cfg] - (let [processed? (process-file! cfg props)] - (when (and processed? (contains? cf/flags :tiered-file-data-storage)) - (wrk/submit! (-> cfg - (assoc ::wrk/task :offload-file-data) - (assoc ::wrk/params props) - (assoc ::wrk/priority 10) - (assoc ::wrk/delay 1000)))) - processed?)))) - (catch Throwable cause - (l/err :hint "error on cleaning file" - :file-id (str (:file-id props)) - :cause cause))))) + {::jobs/name :file-gc + ::jobs/schema schema:file-gc-params + ::jobs/handler + (fn [_context params] + (execute-file-gc cfg params)) + ::jobs/decoder (sm/decoder schema:file-gc-params sm/json-transformer) + ::jobs/validator (sm/validator schema:file-gc-params)}) diff --git a/backend/src/app/tasks/file_gc_scheduler.clj b/backend/src/app/tasks/file_gc_scheduler.clj index e70703afaa..4ac26acf6e 100644 --- a/backend/src/app/tasks/file_gc_scheduler.clj +++ b/backend/src/app/tasks/file_gc_scheduler.clj @@ -9,10 +9,11 @@ file-gc task for all files that matches the eligibility threshold." (:require [app.common.logging :as l] + [app.common.schema :as sm] [app.common.time :as ct] [app.config :as cf] [app.db :as db] - [app.worker :as wrk] + [app.jobs :as jobs] [integrant.core :as ig])) (def ^:private @@ -28,7 +29,7 @@ FOR UPDATE OF f SKIP LOCKED") -(defn- schedule! +(defn- schedule-files [{:keys [::db/conn] :as cfg} threshold] (let [total (reduce (fn [total {:keys [id modified-at revn]}] (let [params {:file-id id :revn revn}] @@ -36,29 +37,50 @@ :file-id (str id) :revn revn :modified-at (ct/format-inst modified-at)) - (wrk/submit! (assoc cfg ::wrk/params params)) + (jobs/submit cfg + {::jobs/name :file-gc + ::jobs/params params + ::jobs/priority 10 + ::jobs/delay 10000}) + (jobs/heartbeat cfg) (inc total))) 0 (db/plan conn [sql:get-candidates threshold] {:fetch-size 10}))] {:processed total})) -(defmethod ig/assert-key ::handler +(declare execute-file-gc-scheduler) + +(defmethod ig/assert-key ::job-def [_ params] (assert (db/pool? (::db/pool params)) "expected a valid database pool")) -(defmethod ig/expand-key ::handler +(defmethod ig/expand-key ::job-def [k v] {k (assoc v ::min-age (cf/get-file-clean-delay))}) -(defmethod ig/init-key ::handler +(def schema:file-gc-scheduler-params + "min-age: int millis or text over the job pipeline, duration object + in-process (repl runs)." + [:map + [:min-age {:optional true} [:or :int :string ::ct/duration]]]) + +(defmethod ig/init-key ::job-def [_ cfg] - (fn [{:keys [props] :as task}] - (let [threshold (-> (ct/duration (or (:min-age props) (::min-age cfg))) - (ct/in-past))] - (-> cfg - (assoc ::db/rollback (:rollback? props)) - (assoc ::wrk/task :file-gc) - (assoc ::wrk/priority 10) - (assoc ::wrk/mark-retries 0) - (assoc ::wrk/delay 10000) - (db/tx-run! schedule! threshold))))) + {::jobs/name :file-gc-scheduler + ::jobs/schema schema:file-gc-scheduler-params + ::jobs/handler + (fn [_context params] + (execute-file-gc-scheduler cfg params)) + ::jobs/decoder (sm/decoder schema:file-gc-scheduler-params sm/json-transformer) + ::jobs/validator (sm/validator schema:file-gc-scheduler-params)}) + +(defn execute-file-gc-scheduler + "Plain job handler: select the file gc candidates (media-trim pending + files before threshold) and schedule one file-gc job per file; no props + needed (min-age default from config; overridable for the repl runs)." + [cfg params] + (let [threshold (-> (ct/duration (or (:min-age params) (::min-age cfg))) + (ct/in-past))] + (-> cfg + (assoc ::db/rollback (:rollback? params)) + (db/tx-run! schedule-files threshold)))) diff --git a/backend/src/app/tasks/import_binfile.clj b/backend/src/app/tasks/import_binfile.clj new file mode 100644 index 0000000000..27949b0074 --- /dev/null +++ b/backend/src/app/tasks/import_binfile.clj @@ -0,0 +1,199 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.tasks.import-binfile + "The `:import-binfile` job: it imports the package the job owns as a + resource into the project frozen in its params." + (:require + [app.binfile.common :as bfc] + [app.binfile.jobs :as bfj] + [app.binfile.v1 :as bf.v1] + [app.binfile.v3 :as bf.v3] + [app.common.exceptions :as ex] + [app.common.schema :as sm] + [app.common.time :as ct] + [app.db :as db] + [app.jobs :as jobs] + [app.jobs.storage :as js] + [app.metrics :as mtx] + [app.rpc.commands.projects :as projects] + [app.rpc.commands.teams :as teams] + [app.storage :as sto] + [datoteka.fs :as fs] + [integrant.core :as ig])) + +(def schema:params + "Business params of an import: where the package goes, its name, the + format version and the manifest metadata kept for audit." + [:map {:title "import-binfile-params" :closed true} + [:project-id ::sm/uuid] + [:name [:or [:string {:max 250}] + [:map-of ::sm/uuid [:string {:max 250}]]]] + [:version [:enum 1 3]] + [:generated-by {:optional true} [:maybe ::sm/text]] + [:referer {:optional true} [:maybe ::sm/text]]]) + +(def schema:create-params + "The business params of an import as the creation command receives + them: where the package goes and its name. The version is optional + because the command reads it from the package header when the caller + does not say it, and the manifest metadata kept for audit is filled by + the command, never sent by the caller." + [:map {:title "import-binfile-create-params" :closed true} + [:project-id ::sm/uuid] + [:name [:or [:string {:max 250}] + [:map-of ::sm/uuid [:string {:max 250}]]]] + [:version {:optional true} [:enum 1 3]]]) + +(def schema:candidate + "A library of the instance whose slugified name matches the one the package + used and is therefore offered to the user as a replacement." + [:map + [:id ::sm/uuid] + [:name :string] + [:project-id ::sm/uuid] + [:project-name :string]]) + +(def schema:done-entry + "A library of the package that was linked while it was read." + [:map + [:id ::sm/uuid] + [:name :string] + [:linked-to ::sm/uuid]]) + +(def schema:pending-entry + "A library of the package that needs the user to choose a replacement: + candidates are the libraries of the instance with the same slugified name + (`bfc/find-shared-files-by-slug`)." + [:map + [:id ::sm/uuid] + [:name :string] + [:candidates [:vector schema:candidate]]]) + +(def schema:resolution-entry + "The libraries one imported file uses, split in the ones already linked + and the ones pending resolution." + [:map + [:id ::sm/uuid] + [:name :string] + [:done [:vector schema:done-entry]] + [:pending [:vector schema:pending-entry]]]) + +(def schema:resolution + "The resolution of every imported file, keyed by its new file id. The key + is a uuid, so the map needs this schema to come back typed: the result + column is plain JSON, and without a decoder every key of the map would be + read back as a keyword." + [:map-of ::sm/uuid schema:resolution-entry]) + +(def schema:result + "What the job stores when the import succeeds: the files it created and + how the libraries of the package were resolved." + [:map {:title "import-binfile-result" :closed true} + [:file-ids [:vector ::sm/uuid]] + [:resolution {:optional true} schema:resolution] + [:name [:or [:string {:max 250}] + [:map-of ::sm/uuid [:string {:max 250}]]]] + [:version [:enum 1 3]]]) + +(def ^:private check-result + (sm/check-fn schema:result + :hint "invalid result of an import job" + :type :validation + :code :invalid-result)) + +(defn- touch-project + "Mark the destination project as modified. + + An import adds files, and the triggers of the schema only touch the + project on an update, so a project that just received files keeps its + old date and stays where it was in the list." + [cfg project-id] + (db/update! cfg :project + {:modified-at (ct/now)} + {:id project-id} + {::db/return-keys false})) + +(defn execute-import + "Plain handler, importable and testable without integrant. + + The edition permission is checked again here, and not only when the job + was created: it can be revoked while the job waits in its queue. The + core runs inside a transaction, so a cancellation detected between units + rolls the whole import back, and the result is checked against its schema + inside that same transaction: a result the contract does not accept rolls + the import back instead of leaving half of a file imported. The substrate + checks the result again when the job is completed, which is the only net + for a job that does not validate its own result. + + The package is the input of the job: it is released and its temporary + copy deleted once the job is terminal, whatever the outcome. A crash is + covered by the storage GC." + [cfg context params] + (let [profile-id (:profile-id context) + project-id (:project-id params) + input (js/load-input cfg context)] + + (try + (projects/check-edition-permissions! cfg profile-id project-id) + + (let [team (or (teams/get-team cfg + :profile-id profile-id + :project-id project-id) + (ex/raise :type :not-found + :code :project-not-found + :hint "the destination project is not available" + :profile-id profile-id + :project-id project-id))] + (db/tx-run! cfg (fn [tx-cfg] + ;; the core joins this transaction: it + ;; is the one that decides whether the + ;; whole import is kept or rolled back + (-> (bfc/import-cfg tx-cfg {:profile-id profile-id + :project-id project-id + :team team + :name (:name params) + :input input}) + (bfj/check-active context) + (bfj/run context + (fn [cfg] + (let [result (case (int (:version params)) + 1 (bf.v1/import-files! cfg) + 3 (bf.v3/import-files! cfg) + (ex/raise :type :validation + :code :unsupported-version + :hint "unsupported binfile version" + :version (:version params)))] + (touch-project cfg project-id) + result))) + (assoc :name (:name params)) + (assoc :version (:version params)) + (check-result))))) + (finally + (fs/delete input) + (js/release-input cfg context))))) + +(defmethod ig/assert-key ::job-def + [_ params] + (assert (db/pool? (::db/pool params)) "expected a valid database pool") + (assert (sto/valid-storage? (::sto/storage params)) "expected valid storage to be provided") + (assert (mtx/metrics? (::mtx/metrics params)) "expected valid metrics")) + +(defmethod ig/init-key ::job-def + [_ cfg] + {::jobs/name :import-binfile + ::jobs/family :import + ::jobs/resource-role :input + ::jobs/schema schema:params + ::jobs/handler (fn [context params] + (execute-import cfg context params)) + ::jobs/decoder (sm/decoder schema:params sm/json-transformer) + ::jobs/validator (sm/validator schema:params) + ;; The resolution is keyed by file id: without the result schema the + ;; keys come back as keywords and the dashboard wizard can no longer + ;; match them with the file ids it holds. The substrate compiles the + ;; decoder from it and checks the result against it before storing it. + ::jobs/result-schema schema:result}) diff --git a/backend/src/app/tasks/objects_gc.clj b/backend/src/app/tasks/objects_gc.clj index 1d6f842e30..1fca7bf321 100644 --- a/backend/src/app/tasks/objects_gc.clj +++ b/backend/src/app/tasks/objects_gc.clj @@ -9,10 +9,12 @@ of deleted or unreachable objects." (:require [app.common.logging :as l] + [app.common.schema :as sm] [app.common.time :as ct] [app.config :as cf] [app.db :as db] [app.features.fdata :as fdata] + [app.jobs :as jobs] [app.loggers.audit :as audit] [app.storage :as sto] [app.tasks.delete-object :as dobj] @@ -28,6 +30,10 @@ ;; A plan row is a view over the open result set, valid only while the ;; reduction is running, so the key is read inside the reducing function: ;; buffering the row itself yields nil columns. +;; +;; Every proc beats its job (`jobs/heartbeat`) after each processed chunk, +;; through the pool and never through the chunk transaction, so a long backlog +;; in a single proc cannot outrun the orphan lease. (defn- submit-hard-delete-event "Emits one hard-delete audit event for the batch of rows just purged @@ -89,10 +95,10 @@ [cfg ids] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp ::sto/storage]}] - (let [stalled-threshold (ct/minus timestamp {:hours 1}) + (fn [{:keys [::db/conn ::threshold ::sto/storage]}] + (let [stalled-threshold (ct/minus threshold {:hours 1}) ids (db/create-array conn "uuid" ids)] - (->> (db/exec! conn [sql:lock-upload-sessions ids timestamp stalled-threshold timestamp]) + (->> (db/exec! conn [sql:lock-upload-sessions ids threshold stalled-threshold threshold]) (reduce (fn [total {:keys [id]}] (l/trc :obj "upload-session" :id (str id)) @@ -113,14 +119,17 @@ 0))) (defn- delete-upload-sessions - [{:keys [::timestamp ::chunk-size] :as cfg}] - (let [stalled-threshold (ct/minus timestamp {:hours 1}) + [{:keys [::threshold ::chunk-size] :as cfg}] + (let [stalled-threshold (ct/minus threshold {:hours 1}) rf (fn ([total] total) - ([total chunk] (+ total (delete-upload-sessions-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-upload-sessions-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map :id) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-upload-sessions timestamp stalled-threshold timestamp] + (db/plan conn [sql:get-upload-sessions threshold stalled-threshold threshold] {:fetch-size 5})))))) (def ^:private sql:get-profiles @@ -141,10 +150,10 @@ [cfg ids] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp ::sto/storage] :as cfg}] + (fn [{:keys [::db/conn ::threshold ::sto/storage] :as cfg}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [ids (db/create-array conn "uuid" ids)] - (->> (db/exec! conn [sql:lock-profiles ids timestamp]) + (->> (db/exec! conn [sql:lock-profiles ids threshold]) (reduce (fn [total {:keys [id photo-id deleted-at]}] (l/trc :obj "profile" :id (str id)) @@ -153,7 +162,7 @@ ;; Cascade soft-delete to owned teams, projects, files, etc. ;; with the deletion date of the profile, not the - ;; timestamp of the run. + ;; date of the run. (dobj/delete-object cfg {:object :profile :id id :deleted-at deleted-at}) @@ -170,13 +179,16 @@ 0))) (defn- delete-profiles - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-profiles-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-profiles-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map :id) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-profiles timestamp] + (db/plan conn [sql:get-profiles threshold] {:fetch-size 5})))))) (def ^:private sql:get-teams @@ -197,11 +209,11 @@ [cfg ids] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp ::sto/storage] :as cfg}] + (fn [{:keys [::db/conn ::threshold ::sto/storage] :as cfg}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [ids (db/create-array conn "uuid" ids) [total deleted-ids] - (->> (db/exec! conn [sql:lock-teams ids timestamp]) + (->> (db/exec! conn [sql:lock-teams ids threshold]) (reduce (fn [[total deleted-ids] {:keys [id photo-id deleted-at]}] (l/trc :obj "team" :id (str id) @@ -227,13 +239,16 @@ 0))) (defn- delete-teams - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-teams-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-teams-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map :id) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-teams timestamp] + (db/plan conn [sql:get-teams threshold] {:fetch-size 5})))))) (def ^:private sql:get-fonts @@ -255,10 +270,10 @@ [cfg ids] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp ::sto/storage]}] + (fn [{:keys [::db/conn ::threshold ::sto/storage]}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [ids (db/create-array conn "uuid" ids)] - (->> (db/exec! conn [sql:lock-fonts ids timestamp]) + (->> (db/exec! conn [sql:lock-fonts ids threshold]) (reduce (fn [total {:keys [id team-id deleted-at] :as font}] (l/trc :obj "font-variant" :id (str id) @@ -283,13 +298,16 @@ 0))) (defn- delete-fonts - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-fonts-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-fonts-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map :id) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-fonts timestamp] + (db/plan conn [sql:get-fonts threshold] {:fetch-size 5})))))) (def ^:private sql:get-projects @@ -310,11 +328,11 @@ [cfg ids] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp] :as cfg}] + (fn [{:keys [::db/conn ::threshold] :as cfg}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [ids (db/create-array conn "uuid" ids) [total deleted-ids] - (->> (db/exec! conn [sql:lock-projects ids timestamp]) + (->> (db/exec! conn [sql:lock-projects ids threshold]) (reduce (fn [[total deleted-ids] {:keys [id team-id deleted-at]}] (l/trc :obj "project" :id (str id) @@ -337,13 +355,16 @@ 0))) (defn- delete-projects - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-projects-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-projects-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map :id) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-projects timestamp] + (db/plan conn [sql:get-projects threshold] {:fetch-size 5})))))) (def ^:private sql:get-files @@ -364,11 +385,11 @@ [cfg ids] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp] :as cfg}] + (fn [{:keys [::db/conn ::threshold] :as cfg}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [ids (db/create-array conn "uuid" ids) [total deleted-ids] - (->> (db/exec! conn [sql:lock-files ids timestamp]) + (->> (db/exec! conn [sql:lock-files ids threshold]) (reduce (fn [[total deleted-ids] {:keys [id deleted-at project-id]}] (l/trc :obj "file" :id (str id) @@ -397,13 +418,16 @@ unmarked and its `NO ACTION` foreign key still points at it) aborts the transaction of the whole chunk: the chunk is reported and skipped, and the next run retries its files." - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-files-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-files-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map :id) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-files timestamp] + (db/plan conn [sql:get-files threshold] {:fetch-size 5})))))) (def ^:private sql:get-file-thumbnails @@ -425,11 +449,11 @@ [cfg rows] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp ::sto/storage]}] + (fn [{:keys [::db/conn ::threshold ::sto/storage]}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [file-ids (db/create-array conn "uuid" (map :file-id rows)) revns (db/create-array conn "int8" (map :revn rows))] - (->> (db/exec! conn [sql:lock-file-thumbnails file-ids revns timestamp]) + (->> (db/exec! conn [sql:lock-file-thumbnails file-ids revns threshold]) (reduce (fn [total {:keys [file-id revn media-id deleted-at]}] (l/trc :obj "file-thumbnail" :file-id (str file-id) @@ -451,15 +475,18 @@ 0))) (defn- delete-file-thumbnails - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-file-thumbnails-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-file-thumbnails-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map (fn [row] {:file-id (:file-id row) :revn (:revn row)})) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-file-thumbnails timestamp] + (db/plan conn [sql:get-file-thumbnails threshold] {:fetch-size 5})))))) (def ^:private sql:get-file-object-thumbnails @@ -481,11 +508,11 @@ [cfg rows] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp ::sto/storage]}] + (fn [{:keys [::db/conn ::threshold ::sto/storage]}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [file-ids (db/create-array conn "uuid" (map :file-id rows)) object-ids (db/create-array conn "text" (map :object-id rows))] - (->> (db/exec! conn [sql:lock-file-object-thumbnails file-ids object-ids timestamp]) + (->> (db/exec! conn [sql:lock-file-object-thumbnails file-ids object-ids threshold]) (reduce (fn [total {:keys [file-id object-id media-id deleted-at]}] (l/trc :obj "file-object-thumbnail" :file-id (str file-id) @@ -508,15 +535,18 @@ 0))) (defn- delete-file-object-thumbnails - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-file-object-thumbnails-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-file-object-thumbnails-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map (fn [row] {:file-id (:file-id row) :object-id (:object-id row)})) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-file-object-thumbnails timestamp] + (db/plan conn [sql:get-file-object-thumbnails threshold] {:fetch-size 5})))))) (def ^:private sql:get-file-media-objects @@ -538,10 +568,10 @@ [cfg ids] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp ::sto/storage]}] + (fn [{:keys [::db/conn ::threshold ::sto/storage]}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [ids (db/create-array conn "uuid" ids)] - (->> (db/exec! conn [sql:lock-file-media-objects ids timestamp]) + (->> (db/exec! conn [sql:lock-file-media-objects ids threshold]) (reduce (fn [total {:keys [id file-id deleted-at] :as fmo}] (l/trc :obj "file-media-object" :id (str id) @@ -564,13 +594,16 @@ 0))) (defn- delete-file-media-objects - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-file-media-objects-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-file-media-objects-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map :id) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-file-media-objects timestamp] + (db/plan conn [sql:get-file-media-objects threshold] {:fetch-size 5})))))) (def ^:private sql:get-file-data @@ -592,11 +625,11 @@ [cfg rows] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp] :as cfg}] + (fn [{:keys [::db/conn ::threshold] :as cfg}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [file-ids (db/create-array conn "uuid" (map :file-id rows)) ids (db/create-array conn "uuid" (map :id rows))] - (->> (db/exec! conn [sql:lock-file-data file-ids ids timestamp]) + (->> (db/exec! conn [sql:lock-file-data file-ids ids threshold]) (reduce (fn [total {:keys [file-id id type deleted-at metadata backend]}] (some->> metadata @@ -625,15 +658,18 @@ 0))) (defn- delete-file-data - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-file-data-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-file-data-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map (fn [row] {:file-id (:file-id row) :id (:id row)})) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-file-data timestamp] + (db/plan conn [sql:get-file-data threshold] {:fetch-size 5})))))) (def ^:private sql:get-file-change @@ -654,10 +690,10 @@ [cfg ids] (try (db/tx-run! cfg - (fn [{:keys [::db/conn ::timestamp]}] + (fn [{:keys [::db/conn ::threshold]}] (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) (let [ids (db/create-array conn "uuid" ids)] - (->> (db/exec! conn [sql:lock-file-change ids timestamp]) + (->> (db/exec! conn [sql:lock-file-change ids threshold]) (reduce (fn [total {:keys [id file-id deleted-at]}] (l/trc :obj "file-change" :id (str id) @@ -676,46 +712,72 @@ 0))) (defn- delete-file-changes - [{:keys [::timestamp ::chunk-size] :as cfg}] + [{:keys [::threshold ::chunk-size] :as cfg}] (let [rf (fn ([total] total) - ([total chunk] (+ total (delete-file-changes-chunk cfg chunk))))] + ([total chunk] + (let [deleted (delete-file-changes-chunk cfg chunk)] + (jobs/heartbeat cfg) + (+ total deleted))))] (db/tx-run! (assoc cfg ::db/read-only true) (fn [{:keys [::db/conn]}] (transduce (comp (map :id) (partition-all chunk-size)) rf 0 - (db/plan conn [sql:get-file-change timestamp] + (db/plan conn [sql:get-file-change threshold] {:fetch-size 5})))))) -(defmethod ig/assert-key ::handler +(defmethod ig/assert-key ::job-def [_ params] (assert (db/pool? (::db/pool params)) "expected a valid database pool") (assert (sto/valid-storage? (::sto/storage params)) "expected valid storage to be provided")) -(defmethod ig/expand-key ::handler +(defmethod ig/expand-key ::job-def [k v] {k (assoc v ::chunk-size 100)}) -(defmethod ig/init-key ::handler +(declare execute-objects-gc) + +(def schema:objects-gc-params + "Optional :skip-delay processes everything immediately, ignoring + deleted-at schedules (operator/repl escape hatch, mirrors + storage-gc-touched). Optional :chunk-size overrides the per-proc batch + size from config." + [:map {:closed true} + [:skip-delay {:optional true} :boolean] + [:chunk-size {:optional true} :int]]) + +(defmethod ig/init-key ::job-def [_ cfg] - (fn [{:keys [props]}] - (let [skip-delay (:skip-delay props) - chunk-size (or (:chunk-size props) (::chunk-size cfg)) - cfg (-> cfg - (assoc ::chunk-size chunk-size) - (assoc ::timestamp (if skip-delay - (ct/in-future {:days 3650}) - (ct/now)))) - ;; NOTE: upload sessions go first: deleting a profile cascades to its - ;; sessions, which would hit the upload_session_chunk NO ACTION foreign key - ;; while mappings still exist. - total (+ (delete-upload-sessions cfg) - (delete-profiles cfg) - (delete-file-media-objects cfg) - (delete-file-object-thumbnails cfg) - (delete-file-thumbnails cfg) - (delete-file-data cfg) - (delete-file-changes cfg) - (delete-files cfg) - (delete-projects cfg) - (delete-fonts cfg) - (delete-teams cfg))] - {:processed total}))) + {::jobs/name :objects-gc + ::jobs/schema schema:objects-gc-params + ::jobs/handler + (fn [_context params] + (execute-objects-gc cfg params)) + ::jobs/decoder (sm/decoder schema:objects-gc-params sm/json-transformer) + ::jobs/validator (sm/validator schema:objects-gc-params)}) + +(defn execute-objects-gc + "Plain job handler: garbage collect orphan storage objects." + ([cfg] (execute-objects-gc cfg {})) + ([cfg params] + (let [threshold (if (:skip-delay params) + (ct/in-future {:days 3650}) + (ct/now)) + cfg (assoc cfg + ::threshold threshold + ::chunk-size (or (:chunk-size params) + (::chunk-size cfg))) + + ;; NOTE: upload sessions go first: deleting a profile cascades to its + ;; sessions, which would hit the upload_session_chunk NO ACTION foreign + ;; key while mappings still exist. + total (+ (delete-upload-sessions cfg) + (delete-profiles cfg) + (delete-file-media-objects cfg) + (delete-file-object-thumbnails cfg) + (delete-file-thumbnails cfg) + (delete-file-data cfg) + (delete-file-changes cfg) + (delete-files cfg) + (delete-projects cfg) + (delete-fonts cfg) + (delete-teams cfg))] + {:processed total}))) diff --git a/backend/src/app/tasks/offload_file_data.clj b/backend/src/app/tasks/offload_file_data.clj index 72dfd44be7..e8e3eb12ff 100644 --- a/backend/src/app/tasks/offload_file_data.clj +++ b/backend/src/app/tasks/offload_file_data.clj @@ -9,8 +9,13 @@ storage (the database row) to a cold storage (fs or s3)." (:require [app.common.logging :as l] + [app.common.schema :as sm] + [app.common.time :as ct] [app.db :as db] [app.features.fdata :as fdata] + [app.jobs :as jobs] + [app.jobs.metrics :as metrics] + [app.metrics :as mtx] [app.storage :as sto] [integrant.core :as ig])) @@ -21,28 +26,100 @@ AND fd.backend = 'db' AND fd.deleted_at IS NULL") +(def ^:private sql:lock-file + ;; NOTE: SKIP LOCKED only serializes against writers that also lock + ;; the file row. Every file_data writer must therefore hold at least + ;; FOR SHARE on it (the update RPC via lock-for-share?, file-gc via + ;; FOR UPDATE); a writer skipping that lock would reopen the + ;; lost-update race this lock closes. + "SELECT id FROM file WHERE id = ? AND deleted_at IS NULL FOR UPDATE SKIP LOCKED") + (defn- offload-file-data - [cfg {:keys [id file-id type] :as fdata}] + "Offload one hot-storage row to cold storage. Returns the row type + and the migrated bytes for the caller to aggregate." + [cfg {:keys [id file-id type data] :as fdata}] + (jobs/heartbeat cfg) (fdata/upsert! cfg (assoc fdata :backend "storage")) - (l/trc :file-id (str file-id) - :id (str id) - :type type)) + (let [size (if (bytes? data) (alength ^bytes data) 0)] + (l/trc :hint "file data row offloaded" + :file-id (str file-id) + :id (str id) + :type type + :bytes size) + {:type type + :size size})) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; HANDLER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defmethod ig/assert-key ::handler +(defn- offload-file-rows + "Offload every live hot-storage row of one file inside the caller + transaction. Takes the file row lock first and skips when the row + is locked or gone. Returns a summary map with the offloaded rows, + bytes, per-type stats and the skipped flag." + [{:keys [::db/conn] :as cfg} file-id] + (if (db/exec-one! conn [sql:lock-file file-id]) + (let [{:keys [rows bytes by-type]} + (reduce (fn [stats fdata] + (let [{:keys [type size]} (offload-file-data cfg fdata)] + (-> stats + (update :rows inc) + (update :bytes + size) + (update-in [:by-type type] + (fn [{r :rows b :bytes :or {r 0 b 0}}] + {:rows (inc r) :bytes (+ b size)}))))) + {:rows 0 :bytes 0 :by-type {}} + (db/plan conn [sql:get-file-data file-id]))] + (l/trc :hint "file data offloaded" + :file-id (str file-id) + :offloaded rows + :bytes bytes) + {:offloaded rows :bytes bytes :by-type by-type :skipped 0}) + (do + (l/dbg :hint "skip offload, file is locked or gone" + :file-id (str file-id)) + {:offloaded 0 :bytes 0 :by-type {} :skipped 1}))) + +(defn execute-offload-file-data + "Plain job handler: offload the file data rows of one file." + [cfg params] + (let [file-id (:file-id params) + tpoint (ct/tpoint)] + (try + (let [summary (-> cfg + (assoc ::db/rollback (:rollback? params)) + (db/tx-run! offload-file-rows file-id))] + (if (pos? (:skipped summary)) + (do + (metrics/record-offload-duration cfg :skip (inst-ms (tpoint))) + summary) + (do + (doseq [[type {:keys [rows bytes]}] (:by-type summary)] + (metrics/record-offload-rows cfg type rows) + (metrics/record-offload-bytes cfg type bytes)) + (metrics/record-offload-duration cfg :ok (inst-ms (tpoint))) + summary))) + (catch Throwable cause + (metrics/record-offload-duration cfg :error (inst-ms (tpoint))) + (throw cause))))) + +(def schema:offload-file-data-params + [:map + [:file-id ::sm/uuid]]) + +(defmethod ig/assert-key ::job-def [_ params] (assert (db/pool? (::db/pool params)) "expected a valid database pool") - (assert (sto/valid-storage? (::sto/storage params)) "expected valid storage to be provided")) + (assert (sto/valid-storage? (::sto/storage params)) "expected valid storage to be provided") + (assert (some? (::mtx/metrics params)) "expected metrics to be provided")) -(defmethod ig/init-key ::handler +(defmethod ig/init-key ::job-def [_ cfg] - (fn [{:keys [props] :as task}] - (let [file-id (:file-id props)] - (-> cfg - (assoc ::db/rollback (:rollback? props)) - (db/tx-run! (fn [{:keys [::db/conn] :as cfg}] - (run! (partial offload-file-data cfg) - (db/plan conn [sql:get-file-data file-id])))))))) + {::jobs/name :offload-file-data + ::jobs/schema schema:offload-file-data-params + ::jobs/handler + (fn [_context params] + (execute-offload-file-data cfg params)) + ::jobs/decoder (sm/decoder schema:offload-file-data-params sm/json-transformer) + ::jobs/validator (sm/validator schema:offload-file-data-params)}) diff --git a/backend/src/app/tasks/tasks_gc.clj b/backend/src/app/tasks/tasks_gc.clj deleted file mode 100644 index ee84e37714..0000000000 --- a/backend/src/app/tasks/tasks_gc.clj +++ /dev/null @@ -1,39 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; Copyright (c) KALEIDOS SUBSIDIARY SL - -(ns app.tasks.tasks-gc - "A maintenance task that performs a cleanup of already executed tasks - from the database table." - (:require - [app.common.logging :as l] - [app.config :as cf] - [app.db :as db] - [integrant.core :as ig])) - -(def ^:private - sql:delete-completed-tasks - "DELETE FROM task WHERE scheduled_at < now() - ?::interval") - -(defmethod ig/assert-key ::handler - [_ params] - (assert (db/pool? (::db/pool params)) "expected a valid database pool")) - -(defmethod ig/expand-key ::handler - [k v] - {k (assoc v ::min-age (cf/get-deletion-delay))}) - -(defmethod ig/init-key ::handler - [_ {:keys [::min-age] :as cfg}] - (fn [{:keys [props] :as task}] - (let [min-age (or (:min-age props) min-age)] - (-> cfg - (assoc ::db/rollback (:rollback? props)) - (db/tx-run! (fn [{:keys [::db/conn]}] - (let [interval (db/interval min-age) - result (db/exec-one! conn [sql:delete-completed-tasks interval]) - result (db/get-update-count result)] - (l/debug :hint "task finished" :total result) - result))))))) diff --git a/backend/src/app/tasks/telemetry.clj b/backend/src/app/tasks/telemetry.clj index 254ca5bf35..05cd056d92 100644 --- a/backend/src/app/tasks/telemetry.clj +++ b/backend/src/app/tasks/telemetry.clj @@ -12,9 +12,11 @@ [app.common.data :as d] [app.common.exceptions :as ex] [app.common.logging :as l] + [app.common.schema :as sm] [app.config :as cf] [app.db :as db] [app.http.client :as http] + [app.jobs :as jobs] [app.main :as-alias main] [app.setup :as-alias setup] [app.util.json :as json] @@ -216,19 +218,32 @@ ;; buffer stays bounded. (def ^:private batch-size 10000) +(def ^:private sql:aged-telemetry + "source LIKE 'telemetry:%' AND created_at < now() - interval '7 days'") + +(def ^:private sql:gc-pendings + (str "DELETE FROM audit_log_pending WHERE audit_log_id IN" + " (SELECT id FROM audit_log WHERE " sql:aged-telemetry ")")) + (def ^:private sql:gc-events - "DELETE FROM audit_log - WHERE source LIKE 'telemetry:%' - AND created_at < now() - interval '7 days'") + (str "DELETE FROM audit_log WHERE " sql:aged-telemetry)) (defn- gc-events "Delete telemetry-mode events older than `telemetry-retention-days` so that the buffer stays bounded." [{:keys [::db/conn]}] - (let [result (db/exec-one! conn [sql:gc-events])] - (when (pos? (:next.jdbc/update-count result)) - (l/warn :hint "purged stale telemetry events" - :count (:next.jdbc/update-count result))))) + ;; One transaction for both deletes: a crash between them would + ;; leave pending-less rows for the consumer sweep to delete unsent. + ;; Pendings first: the queue holds a row per consumer for every + ;; record, and the FK would reject deleting a row that still has + ;; pendings. + (db/tx-run! conn + (fn [{:keys [::db/conn]}] + (db/exec-one! conn [sql:gc-pendings]) + (let [result (db/exec-one! conn [sql:gc-events])] + (when (pos? (:next.jdbc/update-count result)) + (l/warn :hint "purged stale telemetry events" + :count (:next.jdbc/update-count result))))))) (def ^:private sql:fetch-telemetry-events "SELECT id, name, type, source, tracked_at, profile_id, props, context @@ -272,10 +287,17 @@ false)))) (defn- delete-sent-events - "Delete rows by their ids after a successful send." + "Delete rows by their ids after a successful send. One transaction + for both deletes: a crash between them would leave pending-less + rows for the consumer sweep to delete unsent." [conn ids] - (let [arr (db/create-array conn "uuid" ids)] - (db/exec-one! conn ["DELETE FROM audit_log WHERE id = ANY(?)" arr]))) + (db/tx-run! conn + (fn [tx] + (let [tx-conn (::db/conn tx) + arr (db/create-array tx-conn "uuid" ids)] + ;; Pendings first: same FK reason as in `gc-events`. + (db/exec-one! tx-conn ["DELETE FROM audit_log_pending WHERE audit_log_id = ANY(?)" arr]) + (db/exec-one! tx-conn ["DELETE FROM audit_log WHERE id = ANY(?)" arr]))))) (defn- collect-and-send-audit-events "Collect anonymous telemetry-mode audit events and ship them to the @@ -300,58 +322,76 @@ ;; TASK ENTRY POINT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defmethod ig/assert-key ::handler +(declare execute-telemetry) + +(defmethod ig/assert-key ::job-def [_ params] (assert (http/client? (::http/client params)) "expected a valid http client") (assert (db/pool? (::db/pool params)) "expected a valid database pool") (assert (some? (::setup/props params)) "expected setup props to be available")) -(defmethod ig/init-key ::handler +(def schema:telemetry-params + [:map + [:send? {:optional true} :boolean] + [:enabled? {:optional true} :boolean]]) + +(defmethod ig/init-key ::job-def [_ cfg] - (fn [task] - (let [params (:props task) - send? (get params :send? true) - enabled? (or (get params :enabled? false) - (contains? cf/flags :telemetry)) - ;; Deferred so the query runs only when a report is - ;; actually going to be sent. - subs (delay (get-subscriptions cfg))] + {::jobs/name :telemetry + ::jobs/schema schema:telemetry-params + ::jobs/handler + (fn [_context params] + (execute-telemetry cfg params)) + ::jobs/decoder (sm/decoder schema:telemetry-params sm/json-transformer) + ::jobs/validator (sm/validator schema:telemetry-params)}) - ;; If we have telemetry enabled, then proceed the normal - ;; operation sending legacy report +(defn execute-telemetry + "Plain job handler: send periodic telemetry data." + [cfg params] + (let [send? (get params :send? true) + enabled? (or (get params :enabled? false) + (contains? cf/flags :telemetry)) + ;; Deferred so the query runs only when a report is + ;; actually going to be sent. + subs (delay (get-subscriptions cfg))] - (if enabled? - (when send? - (db/run! cfg gc-events) - ;; Randomize start time to avoid thundering herd when multiple - ;; instances restart at the same time. - (px/sleep (rand-int 10000)) + (jobs/heartbeat cfg) + ;; If we have telemetry enabled, then proceed the normal + ;; operation sending legacy report + + (if enabled? + (when send? + (db/run! cfg gc-events) + ;; Randomize start time to avoid thundering herd when multiple + ;; instances restart at the same time. + (px/sleep (rand-int 10000)) + + (try + (let [stats (db/run! cfg get-legacy-stats)] + (send-legacy-data cfg stats @subs)) + (catch Exception cause + (l/wrn :hint "unable to send legacy report" + :cause cause))) + + ;; Ship any anonymous audit-log events accumulated in + ;; telemetry mode (only when audit-log feature is off). + (when-not (contains? cf/flags :audit-log) (try - (let [stats (db/run! cfg get-legacy-stats)] - (send-legacy-data cfg stats @subs)) + (db/run! cfg collect-and-send-audit-events) (catch Exception cause - (l/wrn :hint "unable to send legacy report" - :cause cause))) + (l/wrn :hint "unable to send events" + :cause cause))))) - ;; Ship any anonymous audit-log events accumulated in - ;; telemetry mode (only when audit-log feature is off). - (when-not (contains? cf/flags :audit-log) - (try - (db/run! cfg collect-and-send-audit-events) - (catch Exception cause - (l/wrn :hint "unable to send events" - :cause cause))))) - - ;; If we have telemetry disabled, but there are users that are - ;; explicitly checked the newsletter subscription on the - ;; onboarding dialog or the profile section, then proceed to - ;; send a limited telemetry data, that consists in the list of - ;; subscribed emails and the running penpot version. - ;; Official instances (penpot.dev / penpot.app) are excluded: - ;; they must never send subscriber emails to the telemetry - ;; endpoint (which is ourselves). - (when (and (not (cf/telemetry-excluded?)) send? @subs) - (px/sleep (rand-int 10000)) - (ex/ignoring - (send-legacy-data cfg nil @subs))))))) + ;; If we have telemetry disabled, but there are users that are + ;; explicitly checked the newsletter subscription on the + ;; onboarding dialog or the profile section, then proceed to + ;; send a limited telemetry data, that consists in the list of + ;; subscribed emails and the running penpot version. + ;; Official instances (penpot.dev / penpot.app) are excluded: + ;; they must never send subscriber emails to the telemetry + ;; endpoint (which is ourselves). + (when (and (not (cf/telemetry-excluded?)) send? @subs) + (px/sleep (rand-int 10000)) + (ex/ignoring + (send-legacy-data cfg nil @subs)))))) diff --git a/backend/src/app/util/events.clj b/backend/src/app/util/events.clj index ffbd96a47c..1c82380943 100644 --- a/backend/src/app/util/events.clj +++ b/backend/src/app/util/events.clj @@ -5,9 +5,15 @@ ;; Copyright (c) KALEIDOS SUBSIDIARY SL (ns app.util.events - "A generic asynchronous events notifications subsystem; used mainly - for mark event points in functions and be able to attach listeners - to them. Mainly used in http.sse for progress reporting." + "Event points of a run: the core marks what it does with `tap`, and + whoever runs it decides where those marks go. + + The sink bound for the run decides how each event travels: a function + is called inline, on the same thread, while a channel receives the + event for a listener of its own. The jobs adapter binds a function + that turns taps into heartbeats, so an `:interrupt` it raises aborts + the run right there; the SSE stream binds a channel instead, and its + listener writes what arrives to the response." (:refer-clojure :exclude [run!]) (:require [app.common.exceptions :as ex] @@ -15,19 +21,29 @@ [promesa.exec :as px] [promesa.exec.csp :as sp])) -(def ^:dynamic *channel* nil) +(def ^:dynamic *sink* nil) (defn tap - ([type data] - (when-let [channel *channel*] - (sp/put! channel [type data]) - nil)) - ([channel type data] - (when channel - (sp/put! channel [type data]) - nil))) + "Mark an event point of the run in course with `[type data]`. + + With no sink bound nothing happens, so untracked runs pay a single + nil check per event point. A function sink is called inline and any + exception it raises propagates to the producer: that is how an + `:interrupt` from a heartbeat stops the run at its next event point. + A channel sink receives the event for its listener instead." + [type data] + (when-let [sink *sink*] + (if (fn? sink) + (sink [type data]) + (sp/put! sink [type data])) + nil)) (defn spawn-listener + "Consume the events of `channel` on a thread of its own, until it + closes. + + `on-close` runs once the listener stops. A failure of `on-event` is + logged and stops the listener." [channel on-event on-close] (assert (sp/chan? channel) "expected active events channel") @@ -39,22 +55,14 @@ (let [result (ex/try! (on-event event))] (if (ex/exception? result) (do - (l/wrn :hint "unexpected exception" :cause result) + (l/err :hint "unexpected exception on calling-on-event of spawn-listener" + :cause result) (sp/close! channel)) (recur))))) (finally - (on-close))))) - -(defn run-with! - "A high-level facility for to run a function in context of event - emiter." - [f on-event] - - (binding [*channel* (sp/chan :buf 32)] - (let [listener (spawn-listener *channel* on-event (constantly nil))] - (try - (f) - (finally - (sp/close! *channel*) - (px/await! listener)))))) + (try + (on-close) + (catch Exception cause + (l/err :hint "unexpected exception on calling on-close of spawn-listener" + :cause cause))))))) diff --git a/backend/src/app/util/pointer_map.clj b/backend/src/app/util/pointer_map.clj index c8384480ce..75effbee5a 100644 --- a/backend/src/app/util/pointer_map.clj +++ b/backend/src/app/util/pointer_map.clj @@ -133,8 +133,13 @@ (or mdata {})) (withMeta [_ mdata'] - (let [pmap (PointerMap. id mdata' odata (not= mdata mdata') loaded?)] - (some-> *tracked* (swap! assoc id pmap)) + (let [changed? (not= mdata mdata') + ;; A fragment row never changes after it is written, so a + ;; metadata change is a new pointer (fresh id + tracked), like + ;; `assoc` does; reusing the id would rewrite the same row. + id' (if changed? (uuid/next) id) + pmap (PointerMap. id' mdata' odata changed? loaded?)] + (some-> *tracked* (swap! assoc id' pmap)) pmap)) Seqable diff --git a/backend/src/app/worker.clj b/backend/src/app/worker.clj index 09fc741ea0..26c314c7e7 100644 --- a/backend/src/app/worker.clj +++ b/backend/src/app/worker.clj @@ -5,145 +5,25 @@ ;; Copyright (c) KALEIDOS SUBSIDIARY SL (ns app.worker - "Async tasks abstraction (impl)." + "Conventions shared by the job execution machinery (impl). + + Handlers that fail with a transient error can throw an exception with + `{:type ::retry}` in ex-data (optionally `:delay` with a duration or + milliseconds, and `:strategy ::noop` to retry without counting the + attempt); the runner will schedule the job for retry (respecting + max-retries) with exponential backoff. The public submit/invoke API + lives in `app.jobs`." (:require - [app.common.data :as d] - [app.common.logging :as l] - [app.common.schema :as sm] - [app.common.time :as ct] - [app.common.uuid :as uuid] - [app.config :as cf] - [app.db :as db] - [app.metrics :as mtx] - [cuerdas.core :as str] - [integrant.core :as ig])) + [app.common.data :as d])) -(set! *warn-on-reflection* true) +(defn queue-key + "The Redis list the payloads of a queue travel through: + `penpot.worker.queue::`. -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; TASKS REGISTRY -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defprotocol IRegistry - (get-task [_ name])) - -(defn- wrap-with-metrics - [f metrics tname] - (let [labels (into-array String [tname])] - (fn [params] - (let [tp (ct/tpoint)] - (try - (f params) - (finally - (mtx/run! metrics - {:id :tasks-timing - :val (inst-ms (tp)) - :labels labels}))))))) - -(def ^:private schema:tasks - [:map-of :keyword ::sm/fn]) - -(def ^:private valid-tasks? - (sm/validator schema:tasks)) - -(defmethod ig/assert-key ::registry - [_ params] - (assert (mtx/metrics? (::mtx/metrics params)) "expected valid metrics instance") - (assert (valid-tasks? (::tasks params)) "expected a valid map of tasks")) - -(defmethod ig/init-key ::registry - [_ {:keys [::mtx/metrics ::tasks]}] - (l/inf :hint "registry initialized" :tasks (count tasks)) - (let [tasks (reduce-kv (fn [registry k f] - (let [tname (name k)] - (l/trc :hint "register task" :name tname) - (assoc registry tname (wrap-with-metrics f metrics tname)))) - {} - tasks)] - (reify - clojure.lang.Counted - (count [_] (count tasks)) - - IRegistry - (get-task [_ name] - (get tasks (d/name name)))))) - -(sm/register! - {:type ::registry - :pred #(satisfies? IRegistry %)}) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; SUBMIT API -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(def ^:private sql:insert-new-task - "insert into task (id, name, props, queue, label, priority, max_retries, created_at, modified_at, scheduled_at) - values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - returning id") - -(def ^:private - sql:remove-not-started-tasks - "DELETE FROM task - WHERE name=? - AND queue=? - AND label=? - AND status = 'new' - AND scheduled_at > ?") - -(def ^:private schema:options - [:map {:title "submit-options"} - [::task [:or ::sm/text :keyword]] - [::label {:optional true} ::sm/text] - [::delay {:optional true} - [:or ::sm/int ::ct/duration]] - [::queue {:optional true} [:or ::sm/text :keyword]] - [::priority {:optional true} ::sm/int] - [::max-retries {:optional true} ::sm/int] - [::dedupe {:optional true} ::sm/boolean]]) - -(def check-options! - (sm/check-fn schema:options)) - -(defn submit! - [& {:keys [::params ::task ::delay ::queue ::priority ::max-retries ::dedupe ::label] - :or {delay 0 queue :default priority 100 max-retries 3 label ""} - :as options}] - - (check-options! options) - - (let [delay (ct/duration delay) - now (ct/now) - scheduled-at (-> (ct/plus now delay) - (ct/truncate :millisecond)) - props (db/tjson params) - id (uuid/next) - tenant (cf/get :tenant) - task (d/name task) - queue (str/ffmt "%:%" tenant (d/name queue)) - conn (db/get-connectable options) - deleted (when dedupe - (-> (db/exec-one! conn [sql:remove-not-started-tasks task queue label now]) - (db/get-update-count)))] - - (l/trc :hint "submit task" - :name task - :task-id (str id) - :queue queue - :label label - :dedupe (boolean dedupe) - :delay (ct/format-duration delay) - :replace (or deleted 0)) - - (db/exec-one! conn [sql:insert-new-task id task props queue - label priority max-retries - now now scheduled-at]) - - id)) - -(defn invoke! - [{:keys [::task ::params] :as cfg}] - (assert (contains? cfg :app.worker/registry) - "missing worker registry on `cfg`") - (let [registry (get cfg ::registry) - task-fn (get-task registry task)] - (task-fn {:props params}))) + The tenant prefix is what keeps two instances sharing one Redis from + handing each other jobs, and `job.queue` stores the bare queue name, so + the prefix has to be composed here. Three callers push or pop on it (the + dispatcher, the runner and `app.jobs/request`), so the shape lives in + one place." + [tenant queue] + (str "penpot.worker.queue:" tenant ":" (d/name queue))) diff --git a/backend/src/app/worker/cron.clj b/backend/src/app/worker/cron.clj index f61f607bff..0105d9d57e 100644 --- a/backend/src/app/worker/cron.clj +++ b/backend/src/app/worker/cron.clj @@ -5,17 +5,28 @@ ;; Copyright (c) KALEIDOS SUBSIDIARY SL (ns app.worker.cron + "Cron pure scheduler: each tick claims the scheduled_task entry and + submits a system job (no profile_id, no user ledger) for it to the + dedicated `:cron` queue. Execution happens via the standard + dispatcher -> runner path, and so with the same lease, orphan + detection, retries and telemetry as for every other job. To keep the + no-overlap semantics (one entry is not re-fired while the previous + instance lives), the scheduler only submits when there is no active + instance (`new`/`scheduled`/`running`/`retry` with the same `name`, + `queue` and `label` of the entry). The `FOR UPDATE SKIP LOCKED` claim on the + scheduled_task row already serializes the tick between nodes." (:require [app.common.data :as d] - [app.common.exceptions :as ex] [app.common.logging :as l] [app.common.schema :as sm] [app.common.time :as ct] + [app.config :as cf] [app.db :as db] + [app.jobs :as jobs] + [app.jobs.metrics :as metrics] + [app.metrics :as-alias mtx] [app.util.cron :as cron] - [app.worker :as wrk] - [app.worker.runner :refer [get-error-context]] - [cuerdas.core :as str] + [app.worker :as-alias wrk] [integrant.core :as ig] [promesa.core :as p] [promesa.exec :as px]) @@ -30,7 +41,7 @@ on conflict (id) do nothing") -(defn- synchronize-cron-entries! +(defn- synchronize-cron-entries [{:keys [::db/conn ::entries]}] (doseq [{:keys [id cron]} entries] (let [result (db/exec-one! conn [sql:upsert-cron-task id (str cron)]) @@ -38,48 +49,87 @@ (l/dbg :hint "register task" :id id :cron (str cron) :status (if updated? "created" "exists"))))) -(defn- lock-scheduled-task! +(defn- lock-scheduled-task [conn id] (let [sql (str "SELECT id FROM scheduled_task " " WHERE id=? FOR UPDATE SKIP LOCKED")] (some? (db/exec-one! conn [sql (d/name id)])))) +(defn submit-cron-job + "Submit the system job for the entry to the `:cron` queue. Uses the + entry id as the job label (stable per entry) and returns the created + job-id, or nil when an active instance (`new`/`scheduled`/`running`/ + `retry` with the same name, queue and label) already exists." + [cfg {:keys [job props id]}] + (jobs/submit + cfg + {::jobs/name job + ::jobs/params (or props {}) ;; entries don't carry props; default to empty map + ::jobs/queue :cron + ::jobs/label (name id) + ::jobs/skip-if-active true})) + (declare ^:private schedule-cron-task) (defn- execute-cron-task - [cfg {:keys [id cron] :as task}] + "Tick thread for one entry: claim the scheduled_task row and submit + the system job; no in-process execution of the handler here." + [cfg {:keys [id cron job] :as entry}] (px/thread {:name (str "penpot/cron-task/" id)} (let [tpoint (ct/tpoint)] (try - (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}] (db/exec-one! conn ["SET LOCAL statement_timeout=0;"]) (db/exec-one! conn ["SET LOCAL idle_in_transaction_session_timeout=0;"]) - (when (lock-scheduled-task! conn id) + + (when (lock-scheduled-task conn id) (db/update! conn :scheduled-task {:cron-expr (str cron) :modified-at (ct/now)} {:id id} {::db/return-keys false}) - (l/dbg :hint "start" :id id) - ((:fn task) task) - (let [elapsed (ct/format-duration (tpoint))] - (l/dbg :hint "end" :id id :elapsed elapsed))))) + + ;; The no-overlap check lives in the submit itself + ;; (`::jobs/skip-if-active`): the check and the + ;; insert share this transaction, so a job could + ;; still transition from running to completed + ;; between the check and the insert. This is + ;; acceptable: the next tick will submit if + ;; needed. The FOR UPDATE SKIP LOCKED on the + ;; scheduled_task row prevents race conditions + ;; between nodes. + (if-let [job-id (submit-cron-job cfg entry)] + (let [elapsed (ct/format-duration (tpoint))] + (db/after-commit #(metrics/record-cron cfg :submitted :none)) + (l/dbg :hint "submit" + :id id + :job (d/name job) + :job-id (str job-id) + :elapsed elapsed)) + (do + (db/after-commit #(metrics/record-cron cfg :skipped :active)) + (l/dbg :hint "skip" + :reason "scheduling, active instance exists" + :id id :job (d/name job))))))) (catch InterruptedException _ + (metrics/record-cron cfg :error :interrupted) (let [elapsed (ct/format-duration (tpoint))] (l/debug :hint "task interrupted" :id id :elapsed elapsed))) (catch Throwable cause - (let [elapsed (ct/format-duration (tpoint))] - (binding [l/*context* (get-error-context cause task)] + (metrics/record-cron cfg :error :failure) + (let [elapsed (ct/format-duration (tpoint)) + context (assoc (cf/logging-context) :params entry)] + (binding [l/*context* context] (l/err :hint "unhandled exception on running task" :id id :elapsed elapsed :cause cause)))) (finally (when-not (px/interrupted? :current) - (schedule-cron-task cfg task))))))) + (schedule-cron-task cfg entry))))))) (defn- ms-until-valid [cron] @@ -89,9 +139,9 @@ (ct/diff now next))) (defn- schedule-cron-task - [{:keys [::running] :as cfg} {:keys [cron id] :as task}] + [{:keys [::running] :as cfg} {:keys [cron id] :as entry}] (let [ts (ms-until-valid cron) - ft (px/schedule! ts (partial execute-cron-task cfg task))] + ft (px/schedule! ts (partial execute-cron-task cfg entry))] (l/dbg :hint "schedule" :id id :ts (ct/format-duration ts) @@ -106,48 +156,51 @@ [:maybe [:map [:cron [:fn cron/cron-expr?]] - [:task :keyword] + [:job :keyword] [:props {:optional true} :map] [:id {:optional true} :keyword]]]]] - ::wrk/registry - ::db/pool]) + ::jobs/defs + ::db/pool + ::mtx/metrics]) (defmethod ig/assert-key ::wrk/cron [_ params] (assert (sm/check schema:params params))) (defmethod ig/init-key ::wrk/cron - [_ {:keys [::wrk/entries ::wrk/registry ::db/pool] :as cfg}] + [_ {:keys [::wrk/entries ::jobs/defs ::db/pool] :as cfg}] (if (db/read-only? pool) (l/wrn :hint "service not started (db is read-only)") (let [running (atom #{}) - entries (->> entries - (filter some?) - ;; If id is not defined, use the task as id. - (map (fn [{:keys [id task] :as item}] - (if (some? id) - (assoc item :id (d/name id)) - (assoc item :id (d/name task))))) - (map (fn [item] - (update item :task d/name))) - (map (fn [{:keys [task] :as item}] - (let [f (wrk/get-task registry task)] - (when-not f - (ex/raise :type :internal - :code :task-not-found - :hint (str/fmt "task %s not configured" task))) - (-> item - (dissoc :task) - (assoc :fn f)))))) - - cfg (assoc cfg ::entries entries ::running running)] + ;; doall sits outside the threading: it forces the lazy + ;; validation above (unknown job names fail fast here). + ;; NOTE: it must not go inside ->> — the threaded value + ;; would land as doall's count arg and explode. + entries (doall + (->> entries + (filter some?) + ;; If id is not defined, use the job as id. + ;; The job stays a keyword (the canonical job + ;; name): only the id is stringified, because it + ;; becomes the job label and the scheduled_task + ;; row id. + (map (fn [{:keys [id job] :as item}] + (if (some? id) + (assoc item :id (d/name id)) + (assoc item :id (d/name job))))) + (map (fn [item] + ;; fail fast when the entry references + ;; an unknown job name + (jobs/get-job-def defs (:job item)) + item))))] (l/inf :hint "started" :tasks (count entries)) - (db/tx-run! cfg synchronize-cron-entries!) + (let [cfg (assoc cfg ::entries entries ::running running)] + (db/tx-run! cfg synchronize-cron-entries) - (->> (filter some? entries) - (run! (partial schedule-cron-task cfg))) + (->> (filter some? entries) + (run! (partial schedule-cron-task cfg)))) (reify clojure.lang.IDeref @@ -163,4 +216,3 @@ (defmethod ig/halt-key! ::wrk/cron [_ instance] (some-> instance d/close!)) - diff --git a/backend/src/app/worker/dispatcher.clj b/backend/src/app/worker/dispatcher.clj index 8c128e177a..b084966714 100644 --- a/backend/src/app/worker/dispatcher.clj +++ b/backend/src/app/worker/dispatcher.clj @@ -7,15 +7,17 @@ (ns app.worker.dispatcher (:require [app.common.data :as d] + [app.common.json :as json] [app.common.logging :as l] [app.common.schema :as sm] [app.common.time :as ct] - [app.common.transit :as t] + [app.config :as cf] [app.db :as db] + [app.jobs :as jobs] + [app.jobs.metrics :as metrics] [app.metrics :as mtx] [app.redis :as rds] - [app.worker :as-alias wrk] - [cuerdas.core :as str] + [app.worker :as wrk] [integrant.core :as ig] [promesa.exec :as px]) (:import @@ -26,6 +28,7 @@ (def ^:private schema:dispatcher [:map [::wrk/tenant ::sm/text] + [::lease {:optional true} ::ct/duration] ::mtx/metrics ::db/pool ::rds/client]) @@ -35,154 +38,231 @@ {k (-> (d/without-nils v) (assoc ::timeout (ct/duration "10s")) (assoc ::batch-size 100) - (assoc ::wait-duration (ct/duration "5s")))}) + (assoc ::wait-duration (ct/duration "5s")) + (assoc ::lease (cf/get-jobs-lease)))}) (defmethod ig/assert-key ::wrk/dispatcher [_ cfg] (assert (sm/check schema:dispatcher cfg))) -(def ^:private sql:select-next-tasks - "SELECT id, queue, scheduled_at from task AS t +(def ^:private sql:select-next-jobs + "SELECT id, name, queue, scheduled_at from job AS t WHERE t.scheduled_at <= ?::timestamptz AND (t.status = 'new' OR t.status = 'retry') - AND queue ~~* ?::text + AND t.tenant = ? ORDER BY t.priority DESC, t.scheduled_at LIMIT ? FOR UPDATE SKIP LOCKED") -(def ^:private sql:mark-task-scheduled - "UPDATE task SET status = 'scheduled' +(def ^:private sql:mark-job-scheduled + "UPDATE job SET status = 'scheduled', modified_at = ? WHERE id = ANY(?)") +;; Both sweeps are bulk UPDATEs that run before every dispatch batch, so +;; they are the two queries where a missing tenant filter does the most +;; damage: without it any instance with a worker requeues another +;; instance's scheduled rows and, worse, aborts its running jobs once +;; their lease expires. (def ^:private sql:reschedule-lost - "UPDATE task + "UPDATE job SET status='new', scheduled_at=?::timestamptz FROM (SELECT t.id - FROM task AS t + FROM job AS t WHERE status = 'scheduled' - AND (?::timestamptz - t.scheduled_at) > '5 min'::interval) AS subquery - WHERE task.id=subquery.id -RETURNING task.id, task.queue") + AND t.tenant = ? + AND t.scheduled_at < ?::timestamptz - '5 min'::interval) AS subquery + WHERE job.id=subquery.id +RETURNING job.id, job.queue") -(def ^:private sql:clean-orphan - "UPDATE task - SET status='failed', modified_at=?::timestamptz, - error='orphan with running status' +(def ^:private sql:mark-orphan + "UPDATE job + SET status='aborted', modified_at=?::timestamptz, error=?::jsonb FROM (SELECT t.id - FROM task AS t + FROM job AS t WHERE status = 'running' - AND (?::timestamptz - t.modified_at) > '24 hour'::interval) AS subquery - WHERE task.id=subquery.id -RETURNING task.id, task.queue") + AND t.tenant = ? + AND t.modified_at < ?::timestamptz) AS subquery + WHERE job.id=subquery.id +RETURNING job.id, job.queue, job.name, job.created_at") + +;; the error travels as text and PostgreSQL casts it: a PGobject parameter +;; on a `::jsonb` placeholder is not what the driver expects here. Same +;; for the orphan `end` event payload below. +(def ^:private orphan-error-json + (json/encode jobs/orphan-error)) + +(def ^:private aborted-outcome-json + (json/encode {:outcome "aborted"})) + +(def ^:private sql:insert-orphan-events + "INSERT INTO job_event (job_id, kind, payload) + SELECT unnest(?::uuid[]), 'end', ?::jsonb") + +(defn- encode-payload + [{:keys [id scheduled-at]}] + (json/encode [(str id) (ct/format-inst scheduled-at)])) + +(defn- reschedule-lost-jobs + [{:keys [::db/conn ::timestamp ::wrk/tenant] :as cfg}] + (let [rows (db/exec! conn [sql:reschedule-lost timestamp tenant timestamp] + {:return-keys true})] + (db/after-commit + (fn [] + (doseq [{:keys [id queue]} rows] + (metrics/record-rescheduled cfg queue) + (l/wrn :hint "reschedule" + :id (str id) + :tenant tenant + :queue queue)))))) + +(defn- mark-orphan-jobs + [{:keys [::db/conn ::timestamp ::lease ::wrk/tenant] :as cfg}] + (let [cutoff (ct/minus timestamp (or lease (cf/get-jobs-lease))) + rows (db/exec! conn [sql:mark-orphan timestamp orphan-error-json + tenant cutoff] + {:return-keys true})] + ;; The sweep is the only status change outside `app.jobs`, so it owns + ;; its `end` events too: one bulk insert in the same transaction (never + ;; one insert per orphan) keeps the history consistent with the rows. + ;; No msgbus notification: orphans alert through the error log and the + ;; orphaned counter, like before. + (when (seq rows) + (db/exec-one! conn [sql:insert-orphan-events + (db/create-array conn "uuid" (map :id rows)) + aborted-outcome-json])) + (db/after-commit + (fn [] + (doseq [{:keys [id queue name created-at]} rows] + (metrics/record-orphan cfg queue) + (when (ct/inst? created-at) + (metrics/record-total cfg name queue :aborted + (- (inst-ms timestamp) (inst-ms created-at)))) + (l/err :hint "marked job as aborted (orphan lease expired)" + :id (str id) + :tenant tenant + :queue queue)))))) + +(defn- get-jobs + [{:keys [::db/conn ::timestamp ::batch-size ::wrk/tenant]}] + (let [result (db/exec! conn [sql:select-next-jobs timestamp tenant batch-size])] + (not-empty result))) + +(defn- mark-as-scheduled + [{:keys [::db/conn]} items] + (let [ids (map :id items) + sql [sql:mark-job-scheduled + (ct/now) + (db/create-array conn "uuid" ids)]] + (db/exec-one! conn sql))) + +(defn- push-jobs + [{:keys [::rds/conn ::wrk/tenant] :as cfg} [queue jobs]] + ;; Mark first, push last: a crash between mark and push leaves a + ;; `scheduled` row that reschedule-lost-jobs re-queues, while the + ;; reverse order leaves a duplicate payload nothing deduplicates. + (mark-as-scheduled cfg jobs) + (let [items (mapv encode-payload jobs) + key (wrk/queue-key tenant queue)] + + (rds/rpush conn key items) + (metrics/record-dispatcher-size cfg queue (count jobs)) + (doseq [{:keys [id name queue]} jobs] + (metrics/record-dispatched cfg name queue 1) + (l/trc :hist "schedule" + :tenant tenant + :queue queue + :job-id (str id))))) + +(defn- run-batch' + [cfg] + (let [cfg (assoc cfg ::timestamp (ct/now)) + tpoint (ct/tpoint)] + ;; Reschedule lost in transit jobs (can happen when + ;; redis server is restarted just after job is pushed) + (reschedule-lost-jobs cfg) + + ;; Mark as aborted all jobs that are still marked as running but + ;; their last modification (heartbeat or progress) is older than + ;; the configured lease + (mark-orphan-jobs cfg) + + ;; Then, schedule the next jobs in queue + (let [result (if-let [jobs (get-jobs cfg)] + (do + (->> (group-by :queue jobs) + (run! (partial push-jobs cfg))) + nil) + + ;; If no jobs found on this batch run, we signal the + ;; run-loop to wait for some time before start running + ;; the next batch iteration + ::wait)] + (db/after-commit + #(metrics/record-dispatcher-batch cfg :dispatch :completed (inst-ms (tpoint)))) + result))) + +(defn- sleep-after-error + [cfg] + (px/sleep (or (::timeout cfg) (ct/duration "10s")))) + +(defn run-batch + "Execute a single dispatch batch: reschedule lost jobs, mark orphans + (lease-based) and claim pending jobs into their Redis queues. Exposed + as a function for testability; the dispatcher thread loops on it." + [cfg] + (let [tpoint (ct/tpoint) + tenant (::wrk/tenant cfg)] + (try + (let [rconn (rds/connect cfg)] + (try + (-> cfg + (assoc ::rds/conn rconn) + (db/tx-run! run-batch')) + (finally + (.close ^AutoCloseable rconn)))) + (catch InterruptedException cause + (throw cause)) + + (catch Exception cause + (cond + (rds/exception? cause) + (do + (metrics/record-dispatcher-batch cfg :redis :failed (inst-ms (tpoint))) + (l/wrn :hint "redis exception (will retry in an instant)" :tenant tenant :cause cause) + (sleep-after-error cfg)) + + (db/sql-exception? cause) + (do + (metrics/record-dispatcher-batch cfg :database :failed (inst-ms (tpoint))) + (l/wrn :hint "database exception (will retry in an instant)" :tenant tenant :cause cause) + (sleep-after-error cfg)) + + :else + (do + (metrics/record-dispatcher-batch cfg :execution :failed (inst-ms (tpoint))) + (l/err :hint "unhandled exception (will retry in an instant)" :tenant tenant :cause cause) + (sleep-after-error cfg))))))) (defmethod ig/init-key ::wrk/dispatcher - [_ {:keys [::db/pool ::wrk/tenant ::batch-size ::timeout] :as cfg}] - (letfn [(reschedule-lost-tasks [{:keys [::db/conn ::timestamp]}] - (doseq [{:keys [id queue]} (db/exec! conn [sql:reschedule-lost timestamp timestamp] - {:return-keys true})] - (l/wrn :hint "reschedule" - :id (str id) - :queue queue))) - - (clean-orphan [{:keys [::db/conn ::timestamp]}] - (doseq [{:keys [id queue]} (db/exec! conn [sql:clean-orphan timestamp timestamp] - {:return-keys true})] - (l/wrn :hint "mark as orphan failed" - :id (str id) - :queue queue))) - - (get-tasks [{:keys [::db/conn ::timestamp] :as cfg}] - (let [prefix (str tenant ":%") - result (db/exec! conn [sql:select-next-tasks timestamp prefix batch-size])] - (not-empty result))) - - (mark-as-scheduled [{:keys [::db/conn]} items] - (let [ids (map :id items) - sql [sql:mark-task-scheduled - (db/create-array conn "uuid" ids)]] - (db/exec-one! conn sql))) - - (push-tasks [{:keys [::rds/conn] :as cfg} [queue tasks]] - (let [items (mapv (juxt :id :scheduled-at) tasks) - key (str/ffmt "penpot.worker.queue:%" queue)] - - (rds/rpush conn key (mapv t/encode-str items)) - (mark-as-scheduled cfg tasks) - - (doseq [{:keys [id queue]} tasks] - (l/trc :hist "schedule" - :id (str id) - :queue queue)))) - - (run-batch' [cfg] - (let [cfg (assoc cfg ::timestamp (ct/now))] - ;; Reschedule lost in transit tasks (can happen when - ;; redis server is restarted just after task is pushed) - (reschedule-lost-tasks cfg) - - ;; Mark as failed all tasks that are still marked as - ;; running but it's been more than 24 hours since its - ;; last modification - (clean-orphan cfg) - - ;; Then, schedule the next tasks in queue - (if-let [tasks (get-tasks cfg)] - (->> (group-by :queue tasks) - (run! (partial push-tasks cfg))) - - ;; If no tasks found on this batch run, we signal the - ;; run-loop to wait for some time before start running - ;; the next batch interation - ::wait))) - - (run-batch [] - (try - (let [rconn (rds/connect cfg)] - (try - (-> cfg - (assoc ::rds/conn rconn) - (db/tx-run! run-batch')) - (finally - (.close ^AutoCloseable rconn)))) - - (catch InterruptedException cause - (throw cause)) - - (catch Exception cause - (cond - (rds/exception? cause) - (do - (l/wrn :hint "redis exception (will retry in an instant)" :cause cause) - (px/sleep timeout)) - - (db/sql-exception? cause) - (do - (l/wrn :hint "database exception (will retry in an instant)" :cause cause) - (px/sleep timeout)) - - :else - (do - (l/err :hint "unhandled exception (will retry in an instant)" :cause cause) - (px/sleep timeout)))))) - - (dispatcher [] - (l/inf :hint "started") + [_ {:keys [::db/pool ::wait-duration ::wrk/tenant] :as cfg}] + (letfn [(dispatcher [] + (l/inf :hint "started" :tenant tenant) (try (loop [] - (let [result (run-batch)] + (let [result (run-batch cfg)] (when (= result ::wait) - (px/sleep (::wait-duration cfg))) + (px/sleep wait-duration)) (recur))) (catch InterruptedException _ - (l/trc :hint "interrupted")) + (l/trc :hint "interrupted" :tenant tenant)) (catch Throwable cause - (l/err :hint "unexpected exception" :cause cause)) + (l/err :hint "unexpected exception" :tenant tenant :cause cause)) (finally - (l/inf :hint "terminated"))))] + (l/inf :hint "terminated" :tenant tenant))))] (if (db/read-only? pool) - (l/wrn :hint "not started (db is read-only)") + (l/wrn :hint "not started (db is read-only)" :tenant tenant) (px/fn->thread dispatcher :name "penpot/worker-dispatcher")))) (defmethod ig/halt-key! ::wrk/dispatcher diff --git a/backend/src/app/worker/runner.clj b/backend/src/app/worker/runner.clj index d6211ca770..2202722c0d 100644 --- a/backend/src/app/worker/runner.clj +++ b/backend/src/app/worker/runner.clj @@ -5,17 +5,21 @@ ;; Copyright (c) KALEIDOS SUBSIDIARY SL (ns app.worker.runner - "Async tasks abstraction (impl)." + "Job execution over the unified `job` table (impl)." (:require [app.common.data :as d] [app.common.exceptions :as ex] + [app.common.json :as json] [app.common.logging :as l] [app.common.schema :as sm] [app.common.time :as ct] - [app.common.transit :as t] + [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] + [app.jobs :as jobs] + [app.jobs.metrics :as jobs-metrics] [app.metrics :as mtx] + [app.msgbus :as-alias mbus] [app.redis :as rds] [app.worker :as wrk] [cuerdas.core :as str] @@ -26,232 +30,310 @@ (set! *warn-on-reflection* true) -(def schema:task - [:map {:title "Task"} - [:id ::sm/uuid] - [:queue :string] - [:name :string] - [:created-at ::ct/inst] - [:modified-at ::ct/inst] - [:scheduled-at {:optional true} ::ct/inst] - [:completed-at {:optional true} ::ct/inst] - [:error {:optional true} :string] - [:max-retries :int] - [:retry-num :int] - [:priority :int] - [:status [:enum "scheduled" "running" "completed" "new" "retry" "failed"]] - [:label {:optional true} :string] - [:props :map]]) +(defn- claim-job + "Conditional claim: only transition pending jobs (new/scheduled/retry) + to running. Delegates to the shared jobs/claim so internal and + external workers can never drift. A cancelled, aborted or otherwise + terminal job produces + 0 affected rows and is skipped without touching its state + (first-terminal-wins companion). Also predicates the payload + scheduled_at: a row rescheduled after the payload was pushed + (dispatcher re-pushes on reschedule) is never claimed with the stale + payload." + [cfg job-id scheduled-at] + (jobs/claim cfg job-id scheduled-at)) -(def schema:result - [:map {:title "TaskResult"} - [:status [:enum "retry" "failed" "completed"]] - [:error {:optional true} [:fn ex/exception?]] - [:inc-by {:optional true} :int] - [:delay {:optional true} :int]]) +(defn- get-exception-type + "Extract a human-readable exception type for observability." + [error] + (cond + (and (ex/exception? error) (instance? clojure.lang.ExceptionInfo error)) + (or (:type (ex-data error)) :ex-info) -(def valid-task-result? - (sm/validator schema:result)) + (instance? java.util.concurrent.TimeoutException error) + :timeout -(defn- decode-task-row - [{:keys [props] :as row}] - (cond-> row - (db/pgobject? props) - (assoc :props (db/decode-transit-pgobject props)))) + (instance? java.sql.SQLTimeoutException error) + :timeout -(defn get-error-context - [_ item] - (-> (cf/logging-context) - (assoc :params item))) + :else + (keyword (str/lower (.getSimpleName (class error)))))) -(defn- get-task - [{:keys [::db/pool]} task-id] +(defn- error-report + "Build the error stored in `job.error` out of an exception, conforming to + `app.jobs/schema:job-error`. The exception type and its own type/code are + kept as extra details for triage, on top of the three contract keys." + [error] + (let [data (ex-data error) + as-keyword (fn [v] + (cond (keyword? v) v + (string? v) (keyword v)))] + (jobs/check-job-error + {:type (or (as-keyword (:type data)) :internal) + :code (or (as-keyword (:code data)) :failed) + :hint (or (:hint data) + (when (ex/exception? error) (ex-message error)) + (str error)) + :ex-type (get-exception-type error)}))) + +;; A handler result may be the reserved completion envelope, which carries +;; the storage object the job produced. The key is reserved: a handler that +;; wants to return a business map with :resource-id in it cannot. +(defn- completion-envelope? + [result] + (and (map? result) (contains? result :resource-id))) + +(defn- get-job + "Fetch the job row (params kept as raw pgobject; decoded later with the + job-def decoder)." + [cfg job-id] (ex/try! - (some-> (db/get* pool :task {:id task-id}) - (decode-task-row)))) + (some-> (db/get* cfg :job {:id job-id})))) -(defn- run-task - [{:keys [::db/pool ::wrk/registry ::id ::queue] :as cfg} task] +(defn- execute-job + [{:keys [::jobs/defs ::id ::queue ::wrk/tenant] :as cfg} job] (try (l/dbg :hint "start" - :name (:name task) - :task-id (str (:id task)) + :name (:name job) + :job-id (str (:id job)) + :tenant tenant :queue queue :runner-id id - :retry (:retry-num task)) + :retry (:retry-num job)) - ;; Mark task as running - (db/update! pool :task - {:status "running" - :modified-at (ct/now)} - {:id (:id task)} - {::db/return-keys false}) - - (let [tpoint (ct/tpoint) - task-fn (wrk/get-task registry (:name task)) - result (when task-fn (task-fn task)) - elapsed (ct/format-duration (tpoint)) - result (if (valid-task-result? result) - result - {:status "completed"})] - - (when-not task-fn - (l/wrn :hint "no task handler found" :name (:name task))) - - (l/dbg :hint "end" - :name (:name task) - :task-id (str (:id task)) + (if (zero? (claim-job cfg (:id job) (:scheduled-at job))) + (l/wrn :hint "skipping job, not claimable" + :id (str (:id job)) + :name (:name job) + :tenant tenant :queue queue - :runner-id id - :retry (:retry-num task) - :elapsed elapsed) + :status (:status job)) - result) + (do + (let [job-def (jobs/get-job-def defs (:name job)) + _ (jobs-metrics/record-queue-wait + cfg + (::jobs/name job-def) + queue + (- (inst-ms (ct/now)) (inst-ms (:scheduled-at job)))) + params (try + (->> (:params job) + (jobs/decode-params job-def) + (jobs/validate-params job-def)) + (catch Throwable cause + ;; Decode/validation of stored params is pure: any + ;; failure here is permanent (e.g. schema tightened + ;; after submit), never transient. Tag it so the + ;; generic catch below fails fast instead of + ;; burning max-retries. + (throw (ex-info "job params failed validation" + {:type :assertion + :code :data-validation} + cause)))) + handler (::jobs/handler job-def) + context (jobs/make-context job) + tpoint (ct/tpoint) + result (binding [jobs/*job-id* (:id job)] + (try + (handler context params) + (finally + (jobs-metrics/record-execution + cfg + (:name job) + queue + (inst-ms (tpoint))) + (jobs-metrics/record-legacy-execution + cfg + (:name job) + (inst-ms (tpoint))))))] + + (l/dbg :hint "end" + :name (:name job) + :job-id (str (:id job)) + :tenant tenant + :queue queue + :runner-id id + :retry (:retry-num job) + :elapsed (ct/format-duration (tpoint))) + + {:status "completed" + :result result}))) (catch InterruptedException cause (throw cause)) (catch Throwable cause (let [edata (ex-data cause)] - (if (and (< (:retry-num task) - (:max-retries task)) - (= ::retry (:type edata))) + (cond + ;; the interrupt is the signal that the job is no longer active: + ;; a normal procedure, so it is not reported as an error and it + ;; never fails or retries a row that is already terminal + (= :interrupt (:type edata)) + (binding [l/*context* (-> (cf/logging-context) + (assoc :params job))] + (l/inf :hint "interrupted" + :tenant tenant + :job-id (str (:job-id edata)) + :status (:status edata)) + {:status "interrupted"}) + + (and (< (:retry-num job) + (:max-retries job)) + (= ::wrk/retry (:type edata))) (cond-> {:status "retry" :error cause} (ct/duration? (:delay edata)) - (assoc :delay (:delay edata)) + (assoc :delay-ms (inst-ms (:delay edata))) + (int? (:delay edata)) + (assoc :delay-ms (:delay edata)) - (= ::noop (:strategy edata)) + (= ::wrk/noop (:strategy edata)) (assoc :inc-by 0)) + + :else (do - (l/err :hint "unhandled exception on task" - ::l/context (get-error-context cause task) + (l/err :hint "unhandled exception on job" + ::l/context (assoc (cf/logging-context) :params job) + :tenant tenant :cause cause) - (if (>= (:retry-num task) (:max-retries task)) + ;; Unknown job names and invalid params never heal by retrying + ;; (no rolling deploy will register them on this backend), so + ;; they fail fast without burning max-retries or churning + ;; modified_at. + (if (or (>= (:retry-num job) (:max-retries job)) + (contains? #{:no-job-definition :data-validation} + (:code edata))) {:status "failed" :error cause} - {:status "retry" :error cause}))))))) + {:status "retry" :error cause}))))) + (finally + (jobs/cleanup-throttle (:id job))))) -(defn- run-task! - [{:keys [::id ::timeout] :as cfg} task-id scheduled-at] - (loop [task (get-task cfg task-id)] +(defn- run-job + [{:keys [::id ::timeout ::wrk/tenant] :as cfg} job-id scheduled-at] + (loop [job (get-job cfg job-id)] (cond - (nil? task) - (l/wrn :hint "no task found on the database" + (nil? job) + (l/wrn :hint "no job found on the database" :runner-id id - :task-id task-id) + :tenant tenant + :job-id (str job-id)) - (ex/exception? task) - (if (or (db/connection-error? task) - (db/serialization-error? task)) + (ex/exception? job) + (if (or (db/connection-error? job) + (db/serialization-error? job)) (do - (l/wrn :hint "connection error on retrieving task from database (retrying in some instants)" + (l/wrn :hint "connection error on retrieving job from database (retrying in some instants)" :runner-id id - :cause task) + :tenant tenant + :cause job) (px/sleep timeout) - (recur (get-task cfg task-id))) + (recur (get-job cfg job-id))) (do - (l/err :hint "unhandled exception on retrieving task from database (retrying in some instants)" + (l/err :hint "unhandled exception on retrieving job from database (retrying in some instants)" :runner-id id - :cause task) + :tenant tenant + :cause job) (px/sleep timeout) - (recur (get-task cfg task-id)))) + (recur (get-job cfg job-id)))) (not= (inst-ms scheduled-at) - (inst-ms (:scheduled-at task))) - (l/wrn :hint "skiping task, rescheduled" - :task-id task-id + (inst-ms (:scheduled-at job))) + (l/wrn :hint "skipping job, rescheduled" + :job-id (str job-id) :runner-id id - :scheduled-at (ct/format-inst (:scheduled-at task)) + :tenant tenant + :scheduled-at (ct/format-inst (:scheduled-at job)) :expected-scheduled-at (ct/format-inst scheduled-at)) :else - (let [result (run-task cfg task)] + (let [result (execute-job cfg job)] (with-meta result - {::task task}))))) + {::job job}))))) -(defn- run-worker-loop! - [{:keys [::db/pool ::rds/conn ::timeout ::queue] :as cfg}] - (letfn [(handle-task-retry [{:keys [error inc-by delay] :or {inc-by 1 delay 1000} :as result}] - (let [explain (if (ex/exception? error) - (ex-message error) - (str error)) - task (-> result meta ::task) - nretry (+ (:retry-num task) inc-by) - now (ct/now) - delay (->> (iterate #(* % 2) delay) (take nretry) (last))] - (db/update! pool :task - {:error explain - :status "retry" - :modified-at now - :scheduled-at (-> (ct/plus now delay) - (ct/truncate :millisecond)) - :retry-num nretry} - {:id (:id task)}) +(defn- run-worker-loop + [{:keys [::rds/conn ::timeout ::queue ::wrk/tenant] :as cfg}] + (letfn [(handle-job-retry [{:keys [error delay-ms inc-by] :or {inc-by 1 delay-ms 1000} :as result}] + (let [job (-> result meta ::job) + nretry (+ (:retry-num job) inc-by) + now (ct/now) + delay (->> (iterate #(* 2 %) delay-ms) (take (max 1 nretry)) (last))] + (jobs/retry-job cfg + (:id job) + nretry + (-> (ct/plus now (ct/duration {:millis delay})) + (ct/truncate :millisecond)) + (error-report error) + (if (zero? inc-by) :noop :backoff)) nil)) - (handle-task-failure [{:keys [error] :as result}] - (let [task (-> result meta ::task) - explain (ex-message error)] - (db/update! pool :task - {:error explain - :modified-at (ct/now) - :status "failed"} - {:id (:id task)}) + (handle-job-failure [{:keys [error] :as result}] + (let [job (-> result meta ::job)] + (jobs/fail cfg (:id job) (error-report error)) nil)) - (handle-task-completion [result] - (let [task (-> result meta ::task) - now (ct/now)] - (db/update! pool :task - {:completed-at now - :modified-at now - :error nil - :status "completed"} - {:id (:id task)}) + (handle-job-completion [result] + (let [job (-> result meta ::job) + returned (:result result) + envelope? (completion-envelope? returned)] + (jobs/complete cfg + :job-id (:id job) + :result (if envelope? (:result returned) returned) + :resource-id (when envelope? (:resource-id returned))) nil)) (decode-payload [payload] (try - (let [[task-id scheduled-at :as payload] (t/decode-str payload)] - (if (and (uuid? task-id) - (ct/inst? scheduled-at)) - payload - (l/err :hint "received unexpected payload" - :payload payload))) + (let [payload (json/decode payload) + job-id (uuid/parse (first payload)) + sched-at (ct/inst (second payload))] + (if (and (uuid? job-id) + (ct/inst? sched-at)) + [job-id sched-at] + (do + (l/err :hint "received unexpected payload" + :tenant tenant + :payload payload) + nil))) (catch Throwable cause (l/err :hint "unable to decode payload" ::l/context (cf/logging-context) + :tenant tenant :payload payload - :length (alength ^String/1 payload) + :length (count payload) :cause cause)))) (process-result [{:keys [status] :as result}] (ex/try! (case status - "retry" (handle-task-retry result) - "failed" (handle-task-failure result) - "completed" (handle-task-completion result) + "retry" (handle-job-retry result) + "failed" (handle-job-failure result) + "completed" (handle-job-completion result) + ;; the job is no longer active: there is nothing to write, + ;; the row is already terminal and whoever stopped it did + "interrupted" nil (throw (IllegalArgumentException. (str "invalid status received: '" status "'")))))) - (run-task-loop [[task-id scheduled-at]] - (loop [result (run-task! cfg task-id scheduled-at)] + (run-job-loop [[job-id scheduled-at]] + (loop [result (run-job cfg job-id scheduled-at)] (when-let [cause (some-> result process-result)] (if (or (db/connection-error? cause) (db/serialization-error? cause)) (do - (l/wrn :hint "database exeption on processing task result (retrying in some instants)" + (l/wrn :hint "database exeption on processing job result (retrying in some instants)" + :tenant tenant :cause cause) (px/sleep timeout) (recur result)) - (l/err :hint "unhandled exception on processing task result" + (l/err :hint "unhandled exception on processing job result" ::l/context (cf/logging-context) + :tenant tenant :cause cause)))))] (try - (let [key (str/ffmt "penpot.worker.queue:%" queue) + (let [key (wrk/queue-key tenant queue) [_ payload] (rds/blpop conn [key] timeout)] (some-> payload decode-payload - run-task-loop)) + run-job-loop)) (catch InterruptedException cause (throw cause)) @@ -261,46 +343,50 @@ (do (l/err :hint "redis pop operation timeout, consider increasing redis timeout (will retry in some instants)" ::l/context (cf/logging-context) + :tenant tenant :timeout timeout :cause cause) (px/sleep timeout)) (l/err :hint "unhandled exception" ::l/context (cf/logging-context) + :tenant tenant :cause cause)))))) -(defn- start-thread! +(defn- start-thread [{:keys [::id ::queue ::wrk/tenant] :as cfg}] (px/thread {:name (str "penpot/job-runner/" id)} - (l/inf :hint "started" :id id :queue queue) + (l/inf :hint "started" :id id :tenant tenant :queue queue) (let [rconn (rds/connect cfg)] (try (loop [cfg (-> cfg (assoc ::rds/conn rconn) - (assoc ::queue (str/ffmt "%:%" tenant queue)) (assoc ::timeout (ct/duration "5s")))] (when (px/interrupted?) (throw (InterruptedException. "interrupted"))) - (run-worker-loop! cfg) + (run-worker-loop cfg) (recur cfg)) (catch InterruptedException _ (l/dbg :hint "interrupted" :id id + :tenant tenant :queue queue)) (catch Throwable cause (l/err :hint "unexpected exception" ::l/context (cf/logging-context) :id id + :tenant tenant :queue queue :cause cause)) (finally (.close ^AutoCloseable rconn) (l/inf :hint "terminated" :id id + :tenant tenant :queue queue)))))) (def ^:private schema:params @@ -308,10 +394,12 @@ [::wrk/parallelism {:optional true} ::sm/int] [::wrk/queue :keyword] [::wrk/tenant ::sm/text] - ::wrk/registry + ::jobs/defs ::mtx/metrics ::db/pool - ::rds/client]) + ::rds/client + ;; job events of a profile job are published on the profile topic + ::mbus/msgbus]) (defmethod ig/assert-key ::wrk/runner [_ params] @@ -322,15 +410,15 @@ {k (merge {::wrk/parallelism 1} (d/without-nils v))}) (defmethod ig/init-key ::wrk/runner - [_ {:keys [::db/pool ::wrk/queue ::wrk/parallelism] :as cfg}] + [_ {:keys [::db/pool ::wrk/queue ::wrk/parallelism ::wrk/tenant] :as cfg}] (let [queue (d/name queue) cfg (assoc cfg ::queue queue)] (if (db/read-only? pool) - (l/wrn :hint "not started (db is read-only)" :queue queue :parallelism parallelism) + (l/wrn :hint "not started (db is read-only)" :tenant tenant :queue queue :parallelism parallelism) (doall (->> (range parallelism) (map #(assoc cfg ::id (str queue "/" %))) - (map start-thread!)))))) + (map start-thread)))))) (defmethod ig/halt-key! ::wrk/runner [_ threads] diff --git a/backend/test/backend_tests/auth_oidc_test.clj b/backend/test/backend_tests/auth_oidc_test.clj index 5f86bbfe50..ea249d3144 100644 --- a/backend/test/backend_tests/auth_oidc_test.clj +++ b/backend/test/backend_tests/auth_oidc_test.clj @@ -10,6 +10,7 @@ [app.common.data :as d] [app.common.exceptions :as ex] [app.common.time :as ct] + [app.common.uuid :as uuid] [app.config :as cf] [app.http.session :as session] [app.setup :as-alias setup] @@ -575,6 +576,7 @@ (t/is (.contains loc "error=unable-to-auth"))))))) (t/deftest organization-sso-callback-success-emits-succeeded + ;; No session on the request → auth-succeeded only (no token-issued). (let [cfg (dissoc base-cfg :app.email/blacklist :app.email/whitelist) state (make-state-token cfg {:dest-url "https://penpot.example.com/#/workspace" :organization-id test-organization-id}) @@ -589,6 +591,36 @@ (t/is (= ["organization-sso-auth-succeeded"] (mapv :name @events))) (t/is (= test-organization-id (get-in (first @events) [:props :organization-id]))))))) +(t/deftest organization-sso-callback-success-with-session-emits-token-issued + (let [manager (session/inmemory-manager) + cfg (assoc (dissoc base-cfg :app.email/blacklist :app.email/whitelist) + ::session/manager manager) + state (make-state-token cfg {:dest-url "https://penpot.example.com/#/workspace" + :organization-id test-organization-id}) + request (default-request cfg :state state) + events (atom []) + sess {:id (uuid/next) + :profile-id test-profile-id + :props {}}] + (with-redefs [app.nitrate/call (constantly {:active true}) + app.auth.oidc/prepare-organization-sso-provider (constantly {:type "oidc"}) + app.auth.oidc/get-info (constantly {}) + app.http.session/get-session (constantly sess) + app.loggers.audit/submit (fn [_cfg event] (swap! events conj event))] + (let [result (#'oidc/callback-handler cfg request) + names (mapv :name @events) + stored (session/read-session manager (:id sess))] + (t/is (= "https://penpot.example.com/#/workspace" (redirect-location result))) + (t/is (= ["organization-sso-token-issued" + "organization-sso-auth-succeeded"] + names)) + (t/is (= test-organization-id + (get-in (first @events) [:props :organization-id]))) + (t/is (= test-profile-id (:profile-id (first @events)))) + (t/is (= #{:organization-id} + (set (keys (:props (first @events)))))) + (t/is (contains? (get-in stored [:props :sso]) test-organization-id)))))) + (t/deftest organization-sso-callback-error-emits-failed (let [cfg (dissoc base-cfg :app.email/blacklist :app.email/whitelist) state (make-state-token cfg {:dest-url "https://penpot.example.com/#/workspace" diff --git a/backend/test/backend_tests/binfile_jobs_test.clj b/backend/test/backend_tests/binfile_jobs_test.clj new file mode 100644 index 0000000000..d5f33dbdd9 --- /dev/null +++ b/backend/test/backend_tests/binfile_jobs_test.clj @@ -0,0 +1,569 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.binfile-jobs-test + "The binfile work of a job: the shared adapter that runs it with its + progress reported as job events, and the two job-defs that expose it." + (:require + [app.binfile.common :as bfc] + [app.binfile.jobs :as bfj] + [app.binfile.v1 :as v1] + [app.binfile.v3 :as v3] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.db :as db] + [app.jobs :as jobs] + [app.jobs.storage :as js] + [app.rpc.commands.teams :as teams] + [app.storage :as sto] + [app.storage.tmp :as tmp] + [app.util.events :as events] + [backend-tests.helpers :as th] + [clojure.java.io :as jio] + [clojure.test :as t] + [datoteka.fs :as fs] + [datoteka.io :as io])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + +(def ^:private fixture + "A real penpot export, the same fixture the binfile tests read." + "backend_tests/test_files/svg-attrs-camel-case.penpot") + +(defn- fixture-path + [] + (-> fixture io/resource jio/file)) + +(defn- import-fixture! + "A file in the default project of the profile, imported from the + fixture through the core." + [profile] + (-> th/*system* + (assoc ::bfc/project-id (:default-project-id profile)) + (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) + (assoc ::bfc/input (fixture-path)) + (v3/import-files!))) + +(defn- job-cfg + "The system cfg as the runner hands it to a handler. The job id is NOT + bound here: the adapter takes it from the context, because the progress + bridge runs on a thread of its own." + [] + th/*system*) + +(defn- job-context + [job-id] + (jobs/make-context (jobs/get-job th/*system* job-id))) + +(defn- run-export! + "Run an export through the adapter, as the job handler does: the + frozen cfg in, the progress out as job events." + [cfg context file-ids export-type output] + (let [cfg (bfc/export-cfg cfg {:file-ids file-ids + :export-type export-type + :output output})] + (bfj/check-active cfg context) + (bfj/run cfg context v3/export-files!))) + +(defn- run-import! + "Run an import through the adapter, as the job handler does: the + frozen cfg in, the progress out as job events. The project touch + stays in the handler, covered by its own tests." + [cfg context {:keys [profile-id project-id team name input version]}] + (bfj/check-active cfg context) + (let [cfg (bfc/import-cfg cfg {:profile-id profile-id + :project-id project-id + :team team + :name name + :input input})] + (bfj/run cfg context + (fn [cfg] + (case (int version) + 1 (v1/import-files! cfg) + 3 (v3/import-files! cfg) + (throw (ex-info "unsupported binfile version" + {:type :validation + :code :unsupported-version + :version version}))))))) + +(defn- progress-events + "The progress events of a job, oldest first, with decoded payloads." + [job-id] + (->> (th/db-exec! ["SELECT payload FROM job_event + WHERE job_id = ? AND kind = 'progress' + ORDER BY created_at ASC, id ASC" + job-id]) + (mapv #(jobs/decode-progress (:payload %))))) + +(defn- export-link-later-package + "A package of one file that uses a shared library, exported so the + library is resolved while importing. The library of the package is gone + afterwards and two replacements share its name, so the import cannot + link it by itself and leaves the file pending resolution." + [profile] + (let [library (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared true + :name "Icons Library"}) + file (th/create-file* 2 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + output (tmp/tempfile :suffix ".zip")] + + (db/insert! th/*system* :file-library-rel + {:file-id (:id file) + :library-file-id (:id library)}) + + (v3/export-files! (-> th/*system* + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) + + (db/update! th/*system* :file + {:deleted-at (ct/now)} + {:id (:id library)}) + + (th/create-file* 3 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared true + :name "Icons Library"}) + (th/create-file* 4 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared true + :name "Icons Library"}) + + {:library-id (:id library) + :output output})) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ADAPTER +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest export-through-the-adapter-produces-the-artifact + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (th/create-job* :export-binfile {:profile-id (:id profile)}) + output (tmp/tempfile* :suffix ".penpot")] + + (run-export! (job-cfg) (job-context job-id) + #{file-id} :detach-libraries output) + + (t/testing "the artifact is a penpot file with its manifest" + (t/is (fs/exists? output)) + (t/is (= [file-id] (mapv :id (:files (v3/get-manifest th/*system* output)))))) + + (t/testing "the progress of the core became milestones of the job" + (let [events (progress-events job-id)] + (t/is (pos? (count events))) + (t/is (every? keyword? (map :stage events))) + (t/testing "the first one is the file in course, with its position" + (let [event (first events)] + (t/is (= :files (:stage event))) + (t/is (= {:current 1 :total 1} (get-in event [:counters :files]))))) + (t/testing "and nothing else: the event is the stage and the counters" + (t/is (every? #(<= (count (keys %)) 2) events))))))) + +(t/deftest import-through-the-adapter-imports-the-package + (let [profile (th/create-profile* 1) + team (teams/get-team th/*system* + :profile-id (:id profile) + :project-id (:default-project-id profile)) + staged (js/put-resource th/*system* (:id profile) + {:content (sto/content (fixture-path)) + :filename "package.penpot" + :mtype "application/zip"}) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + context (job-context job-id) + result (run-import! (job-cfg) context + {:profile-id (:id profile) + :project-id (:default-project-id profile) + :team team + :name "imported" + :input (js/load-input th/*system* context) + :version 3})] + + (t/testing "the package is imported in the destination project" + (t/is (= 1 (count (:file-ids result))))) + + (t/testing "the job reported its progress" + (t/is (pos? (count (progress-events job-id))))))) + +(t/deftest the-adapter-composes-a-milestone-per-tap + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (th/create-job* :export-binfile {:profile-id (:id profile)}) + reports (atom [])] + + ;; the substrate throttles the durable events, so the whole stream of + ;; milestones is only visible with the writes intercepted + (with-redefs [jobs/heartbeat (fn [_cfg & {:as options}] + (swap! reports conj (:progress options)) + 1)] + (run-export! (job-cfg) (job-context job-id) + #{file-id} :detach-libraries + (tmp/tempfile* :suffix ".penpot"))) + + (t/testing "the file opens the run with its position" + (let [event (first @reports)] + (t/is (= :files (:stage event))) + (t/is (= {:current 1 :total 1} (get-in event [:counters :files]))))) + + (t/testing "the stages inside the file carry it as context, with their own counter" + (let [event (first (filter #(= :pages (:stage %)) @reports))] + (t/is (= {:current 1 :total 1} (get-in event [:counters :files]))) + (t/is (pos? (get-in event [:counters :pages :current]))) + (t/is (pos? (get-in event [:counters :pages :total]))))) + + (t/testing "a stage outside a file carries no file context" + (let [events (filter #(= :storage-objects (:stage %)) @reports)] + (t/is (pos? (count events))) + (t/is (every? #(nil? (get-in % [:counters :files])) events)))) + + (t/testing "and every milestone is a stage and its counters, and nothing else" + (t/is (every? #(<= (count (keys %)) 2) @reports))))) + +(t/deftest a-tap-carries-the-outer-counters-in-course + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (th/create-job* :export-binfile {:profile-id (:id profile)}) + taps (atom [])] + + ;; the taps themselves, before the adapter turns them into events + (with-redefs [events/tap (fn [_type data] (swap! taps conj data))] + (run-export! (job-cfg) (job-context job-id) + #{file-id} :detach-libraries + (tmp/tempfile* :suffix ".penpot"))) + + (t/testing "the tap that opens the file carries the outer counters" + (let [tap (first (filter #(= :file (:section %)) @taps))] + (t/is (= {:files {:current 1 :total 1}} (:outer-counters tap))))) + + (t/testing "every tap inside the file carries them as context" + (let [inner (filter #(some? (:file-id %)) @taps)] + (t/is (pos? (count inner))) + (t/is (every? #(= {:files {:current 1 :total 1}} (:outer-counters %)) inner)))) + + (t/testing "a tap outside a file carries no outer counters" + (let [outside (filter #(= :storage-object (:section %)) @taps)] + (t/is (pos? (count outside))) + (t/is (every? #(nil? (:outer-counters %)) outside)))))) + +(t/deftest a-milestone-merges-outer-counters-with-its-own + (let [milestone @#'bfj/milestone] + (t/testing "outer scopes survive next to the counter of the stage" + (t/is (= {:stage :pages + :counters {:teams {:current 1 :total 1} + :projects {:current 2 :total 2} + :files {:current 1 :total 4} + :pages {:current 2 :total 5}}} + (milestone {:section :page + :current 2 :total 5 + :outer-counters {:teams {:current 1 :total 1} + :projects {:current 2 :total 2} + :files {:current 1 :total 4}}})))) + (t/testing "a file tap needs no own counter, the outer one is enough" + (t/is (= {:stage :files + :counters {:files {:current 1 :total 2}}} + (milestone {:section :file + :outer-counters {:files {:current 1 :total 2}}})))))) + +(t/deftest entering-a-file-keeps-the-counters-of-the-outer-scopes + (let [cfg {::v3/outer-counters {:teams {:current 1 :total 1}}} + cfg' (@#'v3/with-outer-counter cfg :files {:current 2 :total 4})] + (t/is (= {:teams {:current 1 :total 1} + :files {:current 2 :total 4}} + (::v3/outer-counters cfg'))))) + +(t/deftest the-import-adapter-composes-the-same-milestones + (let [profile (th/create-profile* 1) + team (teams/get-team th/*system* + :profile-id (:id profile) + :project-id (:default-project-id profile)) + staged (js/put-resource th/*system* (:id profile) + {:content (sto/content (fixture-path)) + :filename "package.penpot" + :mtype "application/zip"}) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + context (job-context job-id) + reports (atom [])] + + (with-redefs [jobs/heartbeat (fn [_cfg & {:as options}] + (swap! reports conj (:progress options)) + 1)] + (run-import! (job-cfg) context + {:profile-id (:id profile) + :project-id (:default-project-id profile) + :team team + :name "imported" + :input (js/load-input th/*system* context) + :version 3})) + + (t/testing "the run opens with the manifest, which has no units to count" + (let [event (first @reports)] + (t/is (= :manifest (:stage event))) + (t/is (nil? (:counters event))))) + + (t/testing "the file carries its position" + (let [event (first (filter #(= :files (:stage %)) @reports))] + (t/is (= {:current 1 :total 1} (get-in event [:counters :files]))))) + + (t/testing "a stage inside the file carries it as context, with its own counter" + (let [event (first (filter #(= :pages (:stage %)) @reports))] + (t/is (= {:current 1 :total 1} (get-in event [:counters :files]))) + (t/is (pos? (get-in event [:counters :pages :current]))) + (t/is (pos? (get-in event [:counters :pages :total]))))) + + (t/testing "and every milestone is a stage and its counters, and nothing else" + (t/is (every? #(<= (count (keys %)) 2) @reports))))) + +(t/deftest a-job-that-is-no-longer-active-stops-the-work + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (th/create-job* :export-binfile {:profile-id (:id profile)}) + context (job-context job-id)] + + (t/testing "a running job is active, and answers with the cfg to thread it" + (t/is (identical? th/*system* (bfj/check-active th/*system* context)))) + + (t/testing "a cancelled job is not" + (th/db-update! :job {:status "cancelled"} {:id job-id}) + (let [cause (try (bfj/check-active th/*system* context) nil + (catch Throwable raised raised))] + (t/is (= :interrupt (th/ex-type cause))) + (t/is (= "cancelled" (:status (ex-data cause)))))) + + (t/testing "so the export stops at its next progress point" + (let [output (tmp/tempfile* :suffix ".penpot") + cause (try + (run-export! (job-cfg) context + #{file-id} :detach-libraries output) + nil + (catch Throwable raised raised))] + (t/is (= :interrupt (th/ex-type cause))) + (t/is (zero? (count (progress-events job-id)))))) + + (t/testing "a job that already completed is not active either" + (th/db-update! :job {:status "completed"} {:id job-id}) + (let [cause (try (bfj/check-active th/*system* context) nil + (catch Throwable raised raised))] + (t/is (= :interrupt (th/ex-type cause))))) + + (t/testing "and neither is a job row that is gone" + (th/db-force-delete :job {:id job-id}) + (let [cause (try (bfj/check-active th/*system* context) nil + (catch Throwable raised raised))] + (t/is (= :interrupt (th/ex-type cause))) + (t/is (= "gone" (:status (ex-data cause)))))))) + +(t/deftest an-interrupted-beat-stops-the-core-at-its-tap + (let [steps (atom []) + context {:id (uuid/next)} + cause (ex-info "the job is no longer active" + {:type :interrupt :code :job-interrupted})] + (with-redefs [jobs/heartbeat (fn [& _] (throw cause))] + (let [raised (try (bfj/run th/*system* context + (fn [_cfg] + (swap! steps conj :before) + ;; the sink calls the beat + ;; inline, so its interrupt + ;; aborts the run here + (events/tap :progress {:section :file}) + (swap! steps conj :after))) + nil + (catch Throwable stopped stopped))] + (t/is (= :interrupt (:type (ex-data raised)))) + (t/is (= :job-interrupted (:code (ex-data raised)))) + (t/is (= [:before] @steps) + "the core stopped at its next tap, the step after never ran"))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; JOB-DEFS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest the-binfile-jobs-follow-the-substrate-contract + (let [defs (::jobs/defs th/*system*)] + + (t/testing "the export job produces the resource of a user job" + (let [def (jobs/get-job-def defs :export-binfile)] + (t/is (= :export (::jobs/family def))) + (t/is (= :output (::jobs/resource-role def))) + (t/is (fn? (::jobs/handler def))))) + + (t/testing "the import job consumes the resource of the job" + (let [def (jobs/get-job-def defs :import-binfile)] + (t/is (= :import (::jobs/family def))) + (t/is (= :input (::jobs/resource-role def))) + (t/is (fn? (::jobs/handler def))))) + + (t/testing "the params are a closed contract" + (let [def (jobs/get-job-def defs :export-binfile) + file-id (uuid/next)] + (t/is (true? ((::jobs/validator def) + {:file-ids #{file-id} :export-type :detach-libraries}))) + (t/testing "a job id never travels in params" + (t/is (false? ((::jobs/validator def) + {:file-ids #{file-id} :export-type :detach-libraries + :job-id (uuid/next)})))))))) + +(t/deftest the-export-handler-runs-through-the-registry + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (th/create-job* :export-binfile {:profile-id (:id profile)}) + result (jobs/invoke (-> th/*system* + (assoc ::jobs/name :export-binfile) + (assoc ::jobs/params {:file-ids #{file-id} + :export-type :detach-libraries}) + (assoc ::jobs/context (job-context job-id))))] + + (t/testing "the handler answers with the completion envelope of the job" + (t/is (uuid? (:resource-id result))) + (t/is (some? (get-in result [:result :resource-uri])))) + + (t/testing "the handler reported progress without knowing the job id" + (t/is (pos? (count (progress-events job-id))))))) + +(t/deftest the-import-handler-runs-through-the-registry + (let [profile (th/create-profile* 1) + staged (js/put-resource th/*system* (:id profile) + {:content (sto/content (fixture-path)) + :filename "package.penpot" + :mtype "application/zip"}) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + result (jobs/invoke (-> th/*system* + (assoc ::jobs/name :import-binfile) + (assoc ::jobs/params {:project-id (:default-project-id profile) + :name "imported" + :version 3}) + (assoc ::jobs/context (job-context job-id))))] + + (t/testing "the package of the job was imported" + (t/is (pos? (count (:file-ids result))))) + (t/testing "the handler reported progress without knowing the job id" + (t/is (pos? (count (progress-events job-id))))))) + +(t/deftest the-import-result-keeps-the-type-of-its-ids + "The resolution of an import is a map keyed by the new file id, and it + crosses a plain JSON column: the result schema the job-def declares is + what keeps the keys and the values as uuids, which is what the dashboard + wizard matches against the ids it holds. Without the decoder the substrate + compiles from that schema, the key comes back as a keyword and the entry as + text, and this test fails." + (let [profile (th/create-profile* 1) + {:keys [library-id output]} (export-link-later-package profile) + + staged (js/put-resource th/*system* (:id profile) + {:content (sto/content output) + :filename "package.penpot" + :mtype "application/zip"}) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + result (jobs/invoke (-> th/*system* + (assoc ::jobs/name :import-binfile) + (assoc ::jobs/params {:project-id (:default-project-id profile) + :name "imported" + :version 3}) + (assoc ::jobs/context (job-context job-id))))] + + (t/testing "the result of the job is stored as the substrate does" + (t/is (= 1 (jobs/complete th/*system* :job-id job-id :result result)))) + + (let [stored (:result (jobs/get-job th/*system* job-id)) + [file-id file-res] (first (:resolution stored))] + + (t/testing "the resolution comes back keyed by the new file id" + (t/is (= 1 (count (:resolution stored)))) + (t/is (uuid? file-id)) + (t/is (= file-id (:id file-res)))) + + (t/testing "the ids of the entries keep their type" + (t/is (= [file-id] (:file-ids stored))) + (t/is (= [] (:done file-res))) + (t/is (= 1 (count (:pending file-res)))) + + (let [pending (first (:pending file-res))] + (t/is (= library-id (:id pending))) + (t/is (= 2 (count (:candidates pending)))) + (t/is (every? (comp uuid? :id) (:candidates pending))) + (t/is (every? (comp uuid? :project-id) (:candidates pending)))))))) + +(t/deftest a-package-without-external-libraries-reads-with-an-empty-resolution + (let [profile (th/create-profile* 1) + staged (js/put-resource th/*system* (:id profile) + {:content (sto/content (fixture-path)) + :filename "package.penpot" + :mtype "application/zip"}) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + result (jobs/invoke (-> th/*system* + (assoc ::jobs/name :import-binfile) + (assoc ::jobs/params {:project-id (:default-project-id profile) + :name "imported" + :version 3}) + (assoc ::jobs/context (job-context job-id))))] + + (t/is (= 1 (jobs/complete th/*system* :job-id job-id :result result))) + + (let [stored (:result (jobs/get-job th/*system* job-id))] + + (t/testing "a package without external libraries resolves nothing" + (t/is (= {} (:resolution stored)))) + + (t/testing "and the file ids come back typed" + (t/is (= 1 (count (:file-ids stored)))) + (t/is (every? uuid? (:file-ids stored))))))) + +(t/deftest a-version-1-result-reads-without-a-resolution + "A version 1 import answers with the file ids and no resolution: the read + path decodes it with the schema of the job-def, which declares the + resolution as optional. Without that schema the file ids come back as text." + (let [profile (th/create-profile* 1) + file-id (uuid/next) + job-id (th/create-job* :import-binfile {:profile-id (:id profile)})] + + (t/is (= 1 (jobs/complete th/*system* :job-id job-id + :result {:file-ids [file-id] + :name "imported-v1" + :version 1}))) + + (let [stored (:result (jobs/get-job th/*system* job-id))] + + (t/testing "there is no resolution to decode" + (t/is (nil? (:resolution stored)))) + + (t/testing "and the file ids come back typed" + (t/is (= [file-id] (:file-ids stored))) + (t/is (= 1 (:version stored))))))) + +(t/deftest a-result-of-a-retired-job-def-reads-as-plain-json + "A registry that no longer knows the kind of the row answers with the JSON + the column holds, keywordized keys and uuid as text, instead of raising." + (let [profile (th/create-profile* 1) + file-id (uuid/next) + job-id (th/create-job* :import-binfile {:profile-id (:id profile)}) + key (keyword (str file-id))] + + (t/is (= 1 (jobs/complete th/*system* :job-id job-id + :result {:file-ids [file-id] + :resolution {file-id {:id file-id + :name "card.penpot" + :done [] + :pending []}} + :name "imported" + :version 3}))) + + (let [stored (:result (jobs/get-job (assoc th/*system* ::jobs/defs {}) job-id))] + + (t/testing "the resolution comes back as it is on disk" + (t/is (= [key] (keys (:resolution stored)))) + (t/is (= (str file-id) (get-in stored [:resolution key :id])))) + + (t/testing "and so do the file ids" + (t/is (= [(str file-id)] (:file-ids stored))))))) diff --git a/backend/test/backend_tests/binfile_svg_test.clj b/backend/test/backend_tests/binfile_svg_test.clj index 9d967c827d..1c6f503920 100644 --- a/backend/test/backend_tests/binfile_svg_test.clj +++ b/backend/test/backend_tests/binfile_svg_test.clj @@ -246,15 +246,16 @@ (let [bundle (tmp/tempfile :prefix "penpot-export-" :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) + (assoc ::bfc/file-ids #{(:id file)}) (assoc ::bfc/embed-assets false) - (assoc ::bfc/include-libraries false)) - (io/output-stream bundle)) + (assoc ::bfc/include-libraries false) + (assoc ::bfc/output (io/output-stream bundle)))) (let [evil (tmp/tempfile :prefix "penpot-evil-" :suffix ".zip") _ (tamper-svg-entry! bundle evil media-id (utf8bytes evil-text) :content-type content-type) result (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) (assoc ::bfc/profile-id (:id profile)) (assoc ::bfc/team-id (:default-team-id profile)) (assoc ::bfc/input evil) @@ -363,7 +364,7 @@ (let [bundle (tmp/tempfile :prefix "penpot-export-v1-" :suffix ".bin")] (v1/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) + (assoc ::bfc/file-ids #{(:id file)}) (assoc ::bfc/embed-assets false) (assoc ::bfc/include-libraries false)) (io/output-stream bundle)) diff --git a/backend/test/backend_tests/binfile_test.clj b/backend/test/backend_tests/binfile_test.clj index 78124f0ad7..9e525af2aa 100644 --- a/backend/test/backend_tests/binfile_test.clj +++ b/backend/test/backend_tests/binfile_test.clj @@ -15,6 +15,7 @@ [app.common.files.changes-builder :as pcb] [app.common.files.tokens :as cfo] [app.common.files.validate :as cfv] + [app.common.geom.matrix :as gmt] [app.common.thumbnails :as thc] [app.common.time :as ct] [app.common.types.shape :as cts] @@ -31,7 +32,6 @@ [backend-tests.helpers :as th] [backend-tests.storage-test :as stt] [clojure.data.json :as json] - [clojure.java.io :as jio] [clojure.test :as t] [cuerdas.core :as str] [datoteka.fs :as fs] @@ -225,10 +225,10 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (cond-> (-> th/*system* - (assoc ::bfc/ids #{(:id file)})) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/output (io/output-stream output))) include-libraries - (assoc ::bfc/export-type :include-libraries)) - (io/output-stream output)) + (assoc ::bfc/export-type :include-libraries))) output)) (defn- import-bundle @@ -345,9 +345,9 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :detach-libraries)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :detach-libraries) + (assoc ::bfc/output (io/output-stream output)))) (let [result (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) @@ -377,12 +377,12 @@ (defn- import-svg-attrs-asset "Imports the `svg-attrs-camel-case.penpot` asset, a real penpot export - whose shapes carry `:svg-attrs` keys in camelCase (the format the - binary export writes), and returns the imported file." + whose shapes carry `:svg-attrs` keys in camelCase (the spelling the + binary export wrote at the time), and returns the imported file. The + v3 reader kebab-izes every key, so the shapes come back with + kebab-case keys." [profile] - (let [input (-> "backend_tests/test_files/svg-attrs-camel-case.penpot" - io/resource - jio/file) + (let [input (th/tempfile "backend_tests/test_files/svg-attrs-camel-case.penpot") result (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) (assoc ::bfc/profile-id (:id profile)) @@ -391,13 +391,11 @@ (v3/import-files!))] (bfc/get-file th/*system* (first (:file-ids result))))) -(t/deftest import-binfile-v3-preserves-camel-case-svg-attrs +(t/deftest import-binfile-v3-normalizes-svg-attrs-to-kebab ;; The json reader used by the v3 import rewrites every key of every - ;; zip entry to kebab-case, and `:svg-attrs` is the one shape map - ;; whose keys are camelCase react prop names. A shape exported with - ;; `fillRule: "evenodd"` must not come back as `:fill-rule`, or the - ;; renderer falls back to the default fill rule and the shape is - ;; painted without its hole. + ;; zip entry to kebab-case, and birth stores `:svg-attrs` in kebab-case + ;; too, so a shape exported with `fillRule: "evenodd"` stays kebab and + ;; the renderer finds the fill rule it expects. (let [profile (th/create-profile* 1) file (import-svg-attrs-asset profile) shape (get-in file [:data :pages-index @@ -405,10 +403,11 @@ :objects (uuid/uuid "ce3641bd-48c8-804c-8008-b54d35cc6f80")])] - (t/is (= {:fillRule "evenodd"} + (t/is (some? shape) "the fixture shape was found") + (t/is (= {:fill-rule "evenodd"} (:svg-attrs shape))))) -(t/deftest import-binfile-v3-preserves-camel-case-svg-attrs-on-components +(t/deftest import-binfile-v3-normalizes-svg-attrs-to-kebab-on-components ;; Same guarantee for shapes stored inside a component: the v3 import ;; cleans those in a different code path than page shapes. (let [profile (th/create-profile* 1) @@ -419,14 +418,132 @@ (get (uuid/uuid shape-id)) :svg-attrs))] - (t/is (= {:fillRule "evenodd"} + (t/is (= {:fill-rule "evenodd"} (shape "fae4bc76-0cc2-8057-8008-b540912cdf78" "fae4bc76-0cc2-8057-8008-b540912774cb"))) - (t/is (= {:fillRule "nonzero"} + (t/is (= {:fill-rule "nonzero"} (shape "fae4bc76-0cc2-8057-8008-b540912c917b" "fae4bc76-0cc2-8057-8008-b540912774c8"))))) +(def ^:private svg-defs-page-id (uuid/custom 4 1)) +(def ^:private svg-defs-shape-id (uuid/custom 4 2)) + +(defn- prepare-svg-defs-file + "A file with an svg-raw shape carrying a filter def, the stored + kebab form birth produces (what an SVG import leaves behind)." + ([profile] (prepare-svg-defs-file profile 1)) + ([profile idx] + (let [page-id svg-defs-page-id + shape-id svg-defs-shape-id + + file (th/create-file* idx {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false})] + (update-file! + :file-id (:id file) + :profile-id (:id profile) + :revn 0 + :vern 0 + :changes + [{:type :add-page + :name "page 1" + :id page-id}]) + + (update-file! + :file-id (:id file) + :profile-id (:id profile) + :revn 0 + :vern 0 + :changes + [{:type :add-obj + :page-id page-id + :id shape-id + :parent-id uuid/zero + :frame-id uuid/zero + :components-v2 true + :obj (cts/setup-shape + {:id shape-id + :name "svg-filter" + :frame-id uuid/zero + :parent-id uuid/zero + :type :svg-raw + :content {:tag :rect + :attrs {:x "10" :fill "red"} + :content []} + :svg-attrs {:fill-rule "evenodd"} + :svg-transform (gmt/matrix 1 0 0 1 10 20) + :svg-defs {"f1" {:tag :filter + :attrs {:id "f1"} + :content [{:tag :feGaussianBlur + :attrs {:std-deviation "2"} + :content []}]}}})}]) + + (dissoc file :data)))) + +(defn- export-import-file + "Exports `file` to a zip and imports it back, returning a map with + the export output and the imported file." + [profile file] + (let [output (tmp/tempfile :suffix ".zip")] + (v3/export-files! + (-> th/*system* + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :detach-libraries) + (assoc ::bfc/output (io/output-stream output)))) + (let [result (-> th/*system* + (assoc ::bfc/project-id (:default-project-id profile)) + (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) + (assoc ::bfc/input output) + (v3/import-files!))] + {:output output + :imported (bfc/get-file th/*system* (first (:file-ids result)))}))) + +(defn- svg-defs-shape + [file] + (get-in file [:data :pages-index svg-defs-page-id :objects svg-defs-shape-id])) + +(t/deftest import-binfile-v3-normalizes-svg-defs-to-kebab + ;; Def node attrs converge to the stored kebab convention through an + ;; export/import cycle: tags come back as keywords (the schema decoder + ;; restores them from the stored strings), attr keys stay kebab, outer + ;; def keys come back as strings (the decoder prefers the string + ;; branch), and the string ids inside survive byte-identical. + (let [profile (th/create-profile* 1) + file (prepare-svg-defs-file profile) + {:keys [imported]} (export-import-file profile file) + shape (svg-defs-shape imported) + node (get (:svg-defs shape) "f1")] + (t/is (some? shape) "the svg shape was found") + (t/is (= {:fill-rule "evenodd"} (:svg-attrs shape))) + (t/is (some? node) "the filter def was found") + (t/is (= :filter (:tag node)) "tags decode back to keywords") + (t/is (= "f1" (get-in node [:attrs :id])) "def ids survive byte-identical") + (t/is (= "2" (get-in node [:content 0 :attrs :std-deviation]))) + (t/is (gmt/matrix? (:svg-transform shape)) + "svg-transform comes back as a matrix, not a plain map") + (t/is (= (gmt/matrix 1 0 0 1 10 20) (:svg-transform shape))))) + +(t/deftest export-import-svg-roundtrip-converges + ;; The determinism proof: export, import, export, import. The first + ;; import lands on the kebab end state and the second trip leaves it + ;; exactly there. + (let [profile (th/create-profile* 1) + file (prepare-svg-defs-file profile) + trip-1 (:imported (export-import-file profile file)) + shape-1 (svg-defs-shape trip-1) + trip-2 (:imported (export-import-file profile trip-1)) + shape-2 (svg-defs-shape trip-2)] + (t/is (some? shape-1) "the svg shape survived the first trip") + (t/is (some? shape-2) "the svg shape survived the second trip") + (t/is (= {:fill-rule "evenodd"} (:svg-attrs shape-1)) + "first trip lands on the kebab end state") + (t/is (= (:svg-attrs shape-1) (:svg-attrs shape-2)) + "svg-attrs are a fixed point") + (t/is (= (:svg-defs shape-1) (:svg-defs shape-2)) + "svg-defs are a fixed point"))) + (t/deftest export-binfile-v3 (let [profile (th/create-profile* 1) file (prepare-simple-file profile) @@ -434,9 +551,9 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :detach-libraries)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :detach-libraries) + (assoc ::bfc/output (io/output-stream output)))) (let [result (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) @@ -468,9 +585,9 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :detach-libraries)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :detach-libraries) + (assoc ::bfc/output (io/output-stream output)))) (let [result (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) @@ -534,9 +651,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Read the manifest and check external-libraries (let [manifest (v3/get-manifest th/*system* output)] @@ -569,9 +686,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :detach-libraries)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :detach-libraries) + (assoc ::bfc/output (io/output-stream output)))) ;; Only the file is exported, not the library (let [manifest (v3/get-manifest th/*system* output) @@ -630,9 +747,9 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :merge-libraries)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :merge-libraries) + (assoc ::bfc/output (io/output-stream output)))) ;; Only the file is exported, not the library (let [manifest (v3/get-manifest th/*system* output) @@ -706,9 +823,9 @@ (v3/export-files! (assoc th/*system* - ::bfc/ids #{(:id file)} - ::bfc/export-type :detach-libraries) - (io/output-stream output)) + ::bfc/file-ids #{(:id file)} + ::bfc/export-type :detach-libraries + ::bfc/output (io/output-stream output))) (let [names (zip-entry-names output)] (t/is (contains? names (str "files/" (:id file) "/tokens.json"))) @@ -757,9 +874,9 @@ (v3/export-files! (assoc th/*system* - ::bfc/ids #{(:id file)} - ::bfc/export-type :include-libraries) - (io/output-stream output)) + ::bfc/file-ids #{(:id file)} + ::bfc/export-type :include-libraries + ::bfc/output (io/output-stream output))) (let [files (import-output profile output) library' (get files (:name library)) @@ -794,9 +911,9 @@ (v3/export-files! (assoc th/*system* - ::bfc/ids #{(:id file)} - ::bfc/export-type :include-libraries) - (io/output-stream output)) + ::bfc/file-ids #{(:id file)} + ::bfc/export-type :include-libraries + ::bfc/output (io/output-stream output))) (t/is (contains? (zip-entry-names output) (str "files/" (:id file) "/tokens.json"))) @@ -830,9 +947,9 @@ (v3/export-files! (assoc th/*system* - ::bfc/ids #{(:id file)} - ::bfc/export-type :include-libraries) - (io/output-stream output)) + ::bfc/file-ids #{(:id file)} + ::bfc/export-type :include-libraries + ::bfc/output (io/output-stream output))) (let [files (import-output profile output) file-data (:data (get files (:name file)))] @@ -862,9 +979,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Remove the source library to simulate a cross-environment import ;; where the original library does not exist in the target team. @@ -926,9 +1043,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :detach-libraries)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :detach-libraries) + (assoc ::bfc/output (io/output-stream output)))) ;; Remove the source library to simulate a cross-environment import ;; where no matching library exists in the target team. @@ -968,9 +1085,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Remove the source library to simulate a cross-environment import ;; where the original library does not exist in the target team. @@ -1046,9 +1163,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Remove the source library and recreate a matching one owned by owner (db/update! th/*system* :file @@ -1128,9 +1245,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file1) (:id file2)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file1) (:id file2)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Remove the source library and recreate a matching one (db/update! th/*system* :file @@ -1229,9 +1346,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Verify manifest has external-libraries (let [manifest (v3/get-manifest th/*system* output) @@ -1266,9 +1383,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; First import (let [result1 (-> th/*system* @@ -1299,9 +1416,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Import with overwrite (file-id set) (let [result (-> th/*system* @@ -1328,9 +1445,9 @@ ;; Export file with link-later from team 1 (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Import in team 2 (where library with same name exists) (let [result (-> th/*system* @@ -1361,9 +1478,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Import in team 2 (no library exists) (let [result (-> th/*system* @@ -1386,9 +1503,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Import in team 2 (library with different name) (let [result (-> th/*system* @@ -1417,9 +1534,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Import in team 2 (library exists but not shared) (let [result (-> th/*system* @@ -1442,9 +1559,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete the library in team 2 (db/update! th/*system* :file @@ -1494,9 +1611,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete original libraries (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib1)}) @@ -1555,9 +1672,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete both libraries (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib1)}) @@ -1607,9 +1724,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete original libraries (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib1)}) @@ -1675,9 +1792,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete original libraries (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib1)}) @@ -1741,9 +1858,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file1) (:id file2)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file1) (:id file2)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete original library and recreate (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib)}) @@ -1787,9 +1904,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file1) (:id file2)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file1) (:id file2)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete original library and recreate (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib)}) @@ -1839,9 +1956,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file1) (:id file2)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file1) (:id file2)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete original libraries and recreate (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib-icons)}) @@ -1898,9 +2015,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Import as viewer (no edit permission on library) (let [result (-> th/*system* @@ -1940,9 +2057,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Import as editor (has edit permission) (let [result (-> th/*system* @@ -1980,9 +2097,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Verify slug in manifest (let [manifest (v3/get-manifest th/*system* output) @@ -2007,9 +2124,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Library with empty slug should be dropped from external-libraries (let [manifest (v3/get-manifest th/*system* output) @@ -2027,9 +2144,9 @@ ;; Export file with no libraries (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Manifest should have no external-libraries (let [manifest (v3/get-manifest th/*system* output)] @@ -2061,9 +2178,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete original and create with different case (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib)}) @@ -2098,9 +2215,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Re-import in same team (let [result (-> th/*system* @@ -2136,9 +2253,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Import in team 2 (no library exists) (let [result (-> th/*system* @@ -2188,9 +2305,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete and recreate library (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib)}) @@ -2243,9 +2360,9 @@ (let [output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Delete and create two libraries with same name (db/update! th/*system* :file {:deleted-at (ct/now)} {:id (:id lib)}) @@ -2301,9 +2418,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Re-import in same team (let [result (-> th/*system* @@ -2353,9 +2470,9 @@ ;; Export file with link-later (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) - (assoc ::bfc/export-type :link-later)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/export-type :link-later) + (assoc ::bfc/output (io/output-stream output)))) ;; Create a viewer profile (no edit permission on the library) (let [viewer (th/create-profile* th/*system* 2 {}) @@ -2403,10 +2520,10 @@ ;; type should win (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{file-id}) + (assoc ::bfc/file-ids #{file-id}) (assoc ::bfc/export-type :link-later) - (assoc ::bfc/include-libraries true)) - (io/output-stream output)) + (assoc ::bfc/include-libraries true) + (assoc ::bfc/output (io/output-stream output)))) ;; Verify manifest has external-libraries (only produced by link-later) (let [manifest (v3/get-manifest th/*system* output) @@ -2423,14 +2540,15 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :detach-libraries)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :detach-libraries) + (assoc ::bfc/output (io/output-stream output)))) ;; Import with max-zip-entries=1 — the exported ZIP has more entries (let [cfg (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) (assoc ::bfc/input output) (assoc ::bfc/import-max-zip-entries 1)) out (try @@ -2479,14 +2597,15 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) - (assoc ::bfc/export-type :detach-libraries)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :detach-libraries) + (assoc ::bfc/output (io/output-stream output)))) ;; Import with max-binary-entry-size=1 — the media object will exceed this (let [cfg (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) (assoc ::bfc/input output) (assoc ::bfc/import-max-binary-entry-size 1)) out (try @@ -2563,10 +2682,10 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) + (assoc ::bfc/file-ids #{(:id file)}) (assoc ::bfc/embed-assets false) - (assoc ::bfc/include-libraries false)) - (io/output-stream exported)) + (assoc ::bfc/include-libraries false) + (assoc ::bfc/output (io/output-stream exported)))) (let [bombed (tmp/tempfile :suffix ".zip")] (replace-zip-entry! exported bombed @@ -2575,6 +2694,7 @@ (let [cfg (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) (assoc ::bfc/input bombed)) out (try-import-files! cfg)] (t/is (= :validation (:type out))) @@ -2608,14 +2728,15 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) + (assoc ::bfc/file-ids #{(:id file)}) (assoc ::bfc/embed-assets false) - (assoc ::bfc/include-libraries false)) - (io/output-stream output)) + (assoc ::bfc/include-libraries false) + (assoc ::bfc/output (io/output-stream output)))) (let [cfg (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) (assoc ::bfc/input output) (assoc ::bfc/import-max-text-total-size 100)) out (try-import-files! cfg)] @@ -2654,10 +2775,10 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) + (assoc ::bfc/file-ids #{(:id file)}) (assoc ::bfc/embed-assets false) - (assoc ::bfc/include-libraries false)) - (io/output-stream exported)) + (assoc ::bfc/include-libraries false) + (assoc ::bfc/output (io/output-stream exported)))) (let [sizes (text-entries-sizes exported) max-single (apply max 0 sizes) @@ -2670,6 +2791,7 @@ (let [cfg (-> th/*system* (assoc ::bfc/project-id (:default-project-id profile)) (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) (assoc ::bfc/input exported) (assoc ::bfc/import-max-text-total-size budget)) out (try-import-files! cfg)] @@ -2851,10 +2973,10 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id simple) (:id svg)}) + (assoc ::bfc/file-ids #{(:id simple) (:id svg)}) (assoc ::bfc/embed-assets false) - (assoc ::bfc/include-libraries false)) - (io/output-stream output)) + (assoc ::bfc/include-libraries false) + (assoc ::bfc/output (io/output-stream output)))) ;; import returns the imported file ids plus the library link ;; resolution; the test only needs the ids @@ -2930,10 +3052,10 @@ (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) + (assoc ::bfc/file-ids #{(:id file)}) (assoc ::bfc/embed-assets false) - (assoc ::bfc/include-libraries false)) - (io/output-stream output)) + (assoc ::bfc/include-libraries false) + (assoc ::bfc/output (io/output-stream output)))) ;; import returns the imported file ids plus the library link ;; resolution; the test only needs the ids @@ -2960,6 +3082,18 @@ (:object-id thumb))) (t/is (some? (:media-id thumb))))))) +(t/deftest get-file-without-data-rejects-locks + ;; Locking is incompatible with `load-data? false`: the guard fires + ;; before any query, so no DB row is needed. Regression test for the + ;; `lock-for-share?`/`lock-for-share?` typo that left `lock-for-update?` + ;; unchecked. NOTE: the guard is an unconditional `when`/`throw` in + ;; `get-file*` (not an `assert`), so this test is not flag-dependent: + ;; it behaves the same with or without `enable-backend-asserts`. + (doseq [opt [:lock-for-share? :lock-for-update? :skip-locked?]] + (t/is (thrown? IllegalArgumentException + (bfc/get-file th/*system* (uuid/random) + :load-data? false opt true))))) + ;; --- Library relations security tests (t/deftest import-v3-ignores-relations-outside-package @@ -3055,9 +3189,9 @@ output (tmp/tempfile :suffix ".zip")] (v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file) (:id other-file)}) - (assoc ::bfc/export-type :include-libraries)) - (io/output-stream output)) + (assoc ::bfc/file-ids #{(:id file) (:id other-file)}) + (assoc ::bfc/export-type :include-libraries) + (assoc ::bfc/output (io/output-stream output)))) (let [rels (set (map vec (get (read-manifest output) "relations")))] (t/is (contains? rels [(str (:id file)) (str (:id library))])) (t/is (contains? rels [(str (:id other-file)) (str (:id other-library))])) diff --git a/backend/test/backend_tests/climit_test.clj b/backend/test/backend_tests/climit_test.clj index 76aff9252a..090ae4d6b8 100644 --- a/backend/test/backend_tests/climit_test.clj +++ b/backend/test/backend_tests/climit_test.clj @@ -445,9 +445,12 @@ (t/is (thrown? clojure.lang.ExceptionInfo (invoke! cfg (fn [_ _] :never) {})))) - ;; the count came back down instead of staying up for good + ;; the count came back down instead of staying up for good. Only the + ;; last publication is asserted, not the exact list: the sampler + ;; republishes the same count when its first tick lands inside the + ;; window. (t/is (= 0 (waiting-count climit name))) - (t/is (= [0] (waiting-value-of name))) + (t/is (= 0 (waiting-last name))) ;; and the thread dropped the mark: with it left behind, this call would ;; run inline instead of waiting for the permit another thread holds. diff --git a/backend/test/backend_tests/config_test.clj b/backend/test/backend_tests/config_test.clj index 131370f603..74a159a703 100644 --- a/backend/test/backend_tests/config_test.clj +++ b/backend/test/backend_tests/config_test.clj @@ -6,9 +6,21 @@ (ns backend-tests.config-test (:require + [app.common.schema :as sm] + [app.common.time :as ct] [app.config :as cf] [clojure.test :as t])) +(t/deftest tenant-is-normalized-to-lowercase + (t/testing "an uppercase tenant names the same tenant" + (t/is (= "acme" (:tenant (#'cf/normalize-tenant {:tenant "ACME"})))) + (t/is (= "acme" (:tenant (#'cf/normalize-tenant {:tenant "acme"}))))) + (t/testing "a config without a tenant is left untouched" + (t/is (= {:x 1} (#'cf/normalize-tenant {:x 1})))) + (t/testing "the validated form is lowercase-only" + (t/is (not (sm/validate cf/schema:tenant "ACME"))) + (t/is (sm/validate cf/schema:tenant "acme")))) + (t/deftest get-public-uri-normalizes-base (t/testing "trailing slash is ensured with and without subpath" (doseq [[base expected] [["http://localhost:3449" "http://localhost:3449/"] @@ -38,3 +50,39 @@ (t/testing "a leading slash would drop the subpath, so it fails fast" (t/is (thrown? AssertionError (cf/join-uri "https://example.com/penpot" "/assets/by-id/123"))))) + +(t/deftest tenant-allows-only-alphanumeric + (t/testing "default and plain alphanumeric tenants validate" + (doseq [tenant ["default" "acme42" "my-company"]] + (t/is (cf/validate-config (assoc cf/config :tenant tenant))))) + (t/testing "LIKE wildcards and separators are rejected" + (doseq [tenant ["my_tenant" "a%b" "a:b" "a b" "" "acme.example.com"]] + (t/is (not (cf/validate-config (assoc cf/config :tenant tenant))))))) + +(t/deftest worker-parallelism-keys + (t/testing "schema decodes all runner parallelism keys (env strings to int)" + (let [decoded (cf/decode-config {:worker-default-parallelism "4" + :worker-webhook-parallelism "2" + :worker-cron-parallelism "3" + :worker-binfile-parallelism "5"})] + (t/is (= 4 (:worker-default-parallelism decoded))) + (t/is (= 2 (:worker-webhook-parallelism decoded))) + (t/is (= 3 (:worker-cron-parallelism decoded))) + (t/is (= 5 (:worker-binfile-parallelism decoded))))) + (t/testing "lookups resolve configured values (unnamespaced keys, as main uses)" + (with-redefs [cf/config (assoc cf/config + :worker-default-parallelism 4)] + (t/is (= 4 (cf/get :worker-default-parallelism 1)))))) + +(t/deftest jobs-user-ttl + (t/testing "the env value is decoded as a duration" + (let [decoded (cf/decode-config {:jobs-user-ttl "72h"})] + (t/is (= (ct/duration {:days 3}) (:jobs-user-ttl decoded))))) + + (t/testing "the getter resolves the configured value" + (with-redefs [cf/config (assoc cf/config :jobs-user-ttl (ct/duration {:days 1}))] + (t/is (= (ct/duration {:days 1}) (cf/get-jobs-user-ttl))))) + + (t/testing "and falls back to a week" + (with-redefs [cf/config (dissoc cf/config :jobs-user-ttl)] + (t/is (= (ct/duration {:days 7}) (cf/get-jobs-user-ttl)))))) diff --git a/backend/test/backend_tests/db_test.clj b/backend/test/backend_tests/db_test.clj index 29d3095f32..aa6732ead5 100644 --- a/backend/test/backend_tests/db_test.clj +++ b/backend/test/backend_tests/db_test.clj @@ -18,6 +18,24 @@ (t/use-fixtures :once th/state-init) +(defn- get-consistent-pool-stats + "Read pool stats until total equals active + idle, or attempts run out. + Hikari exposes each counter through a separate MXBean call, so a borrow + or return between the reads can leave a single snapshot inconsistent + (total off by one). Retrying until the pool is quiescent keeps the test + deterministic without weakening the invariant." + [pool] + (loop [attempt 20] + (let [stats (db/pool-stats pool) + total (:total-connections stats) + active (:active-connections stats) + idle (:idle-connections stats)] + (if (or (= total (+ active idle)) + (zero? attempt)) + stats + (do (Thread/sleep 10) + (recur (dec attempt))))))) + (t/deftest pool-requires-metrics ;; Metrics is required by the pool schema: a pool wired without it ;; fails at the boundary (AssertionError when asserts are enabled) or @@ -28,7 +46,7 @@ {::db/uri (java.net.URI. "postgresql://localhost/test")})))) (t/deftest pool-stats-returns-expected-keys - (let [stats (db/pool-stats th/*pool*)] + (let [stats (get-consistent-pool-stats th/*pool*)] (t/testing "all expected keys are present" (t/is (contains? stats :active-connections)) (t/is (contains? stats :idle-connections)) diff --git a/backend/test/backend_tests/db_transaction_test.clj b/backend/test/backend_tests/db_transaction_test.clj new file mode 100644 index 0000000000..b89c84be81 --- /dev/null +++ b/backend/test/backend_tests/db_transaction_test.clj @@ -0,0 +1,80 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.db-transaction-test + (:require + [app.common.uuid :as uuid] + [app.db :as db] + [backend-tests.helpers :as th] + [clojure.test :as t])) + +(t/use-fixtures :once th/state-init) + +(t/deftest after-commit-runs-after-a-successful-transaction + (let [events (atom [])] + (db/tx-run! th/*pool* + (fn [_] + (db/after-commit #(swap! events conj :committed)))) + (t/is (= [:committed] @events)))) + +(t/deftest after-commit-is-discarded-on-rollback + (let [events (atom [])] + (t/is (thrown? Exception + (db/tx-run! th/*pool* + (fn [_] + (db/after-commit #(swap! events conj :committed)) + (throw (ex-info "rollback" {})))))) + (t/is (empty? @events)))) + +(t/deftest nested-transactions-share-the-outer-commit + (let [events (atom [])] + (db/tx-run! th/*pool* + (fn [cfg] + (db/after-commit #(swap! events conj :outer)) + (db/tx-run! cfg + (fn [_] + (db/after-commit #(swap! events conj :inner)))))) + (t/is (= [:outer :inner] @events)))) + +(t/deftest after-commit-runs-immediately-outside-a-transaction + (let [events (atom [])] + (db/after-commit #(swap! events conj :immediate)) + (t/is (= [:immediate] @events)))) + +(t/deftest a-rollback-only-transaction-runs-no-callbacks + (let [events (atom [])] + (db/tx-run! {::db/pool th/*pool* + ::db/rollback true} + (fn [_] + (db/after-commit #(swap! events conj :committed)))) + (t/is (= [] @events) + "the transaction rolled back, so there is no commit to follow"))) + +(t/deftest an-independent-transaction-owns-its-callbacks + (let [events (atom []) + job-id (uuid/next)] + (try + (try + (db/tx-run! th/*pool* + (fn [_] + (db/after-commit #(swap! events conj :outer)) + ;; a different connection: its own unit of work, so + ;; it commits on its own and owns its own callbacks + (db/tx-run! th/*pool* + (fn [cfg] + (db/after-commit #(swap! events conj :inner)) + (db/exec-one! cfg ["INSERT INTO job (id, name, tenant, queue) + VALUES (?, ?, ?, ?)" + job-id "test" "acme" "default"]))) + (throw (ex-info "rollback" {})))) + (catch Exception _ nil)) + (t/testing "the row written by the inner transaction survives the caller rollback" + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job WHERE id = ?" + job-id]))))) + (t/testing "its callback ran with its own commit, the caller one was dropped" + (t/is (= [:inner] @events))) + (finally + (th/db-force-delete :job {:id job-id}))))) diff --git a/backend/test/backend_tests/demo_test.clj b/backend/test/backend_tests/demo_test.clj index 66b8c04ddf..5e9ba555b1 100644 --- a/backend/test/backend_tests/demo_test.clj +++ b/backend/test/backend_tests/demo_test.clj @@ -8,9 +8,9 @@ (:require [app.common.time :as ct] [app.db :as db] + [app.jobs :as jobs] [app.rpc.commands.profile :as profile] [app.tasks.demo-purge :as demo-purge] - [app.worker :as wrk] [backend-tests.helpers :as th] [clojure.test :as t] [integrant.core :as ig])) @@ -34,14 +34,44 @@ (t/deftest demo-purge-handler-submits-delete-object (let [profile (th/create-profile* 996 {:is-demo true}) - handler (ig/init-key :app.tasks.demo-purge/handler - {::db/pool th/*pool*}) + handler (ig/init-key :app.tasks.demo-purge/job-def + {::db/pool th/*pool* + ::jobs/defs {:delete-object {::jobs/name :delete-object + ::jobs/schema [:map] + ::jobs/handler (fn [_context params] params) + ::jobs/decoder identity + ::jobs/validator (fn [_] true)}}}) submitted (atom nil)] - (with-redefs [wrk/submit! (fn [& {:keys [::wrk/task ::wrk/params]}] - (reset! submitted {:task task :params params}))] - (handler {:props {:profile-id (:id profile) - :deleted-at (ct/now)}})) - (t/is (= :delete-object (:task @submitted))) + (with-redefs [jobs/submit (fn [cfg options] + (reset! submitted + {:name (get options ::jobs/name) + :params (get options ::jobs/params)}))] + ((::jobs/handler handler) nil {:profile-id (:id profile)})) + (t/is (= :delete-object (:name @submitted))) (t/is (= :profile (:object (:params @submitted)))) (t/is (= (:id profile) (:id (:params @submitted)))) (t/is (some? (:deleted-at (:params @submitted)))))) + +(t/deftest job-handler-uses-its-closed-cfg + "The handler is [context params]: the job-def keeps the dependencies it + closed over at init time, so it still reaches the database through its + own pool and needs nothing from the caller." + (let [profile (th/create-profile* 997 {:is-demo true}) + handler (ig/init-key :app.tasks.demo-purge/job-def + {::db/pool th/*pool* + ::jobs/defs {:delete-object {::jobs/name :delete-object + ::jobs/schema [:map] + ::jobs/handler (fn [_context params] params) + ::jobs/decoder identity + ::jobs/validator (fn [_] true)}}}) + submitted (atom nil)] + (with-redefs [jobs/submit (fn [cfg options] + (reset! submitted + {:pool (::db/pool cfg) + :params (get options ::jobs/params)}))] + ((::jobs/handler handler) nil {:profile-id (:id profile)}) + + (t/testing "the closed pool is the one the handler submitted with" + (t/is (db/pool? (:pool @submitted))) + (t/is (identical? th/*pool* (:pool @submitted))) + (t/is (= (:id profile) (:id (:params @submitted)))))))) diff --git a/backend/test/backend_tests/email_sending_test.clj b/backend/test/backend_tests/email_sending_test.clj index dace75231e..2eaff545c8 100644 --- a/backend/test/backend_tests/email_sending_test.clj +++ b/backend/test/backend_tests/email_sending_test.clj @@ -9,9 +9,13 @@ [app.config :as cf] [app.db :as db] [app.email :as emails] + [app.jobs :as jobs] + [app.metrics :as-alias mtx] [backend-tests.helpers :as th] [clojure.test :as t] [cuerdas.core :as str] + [integrant.core :as ig] + [lambdaisland.uri :as u] [promesa.core :as p])) (t/use-fixtures :once th/state-init) @@ -118,3 +122,190 @@ (t/is (not (str/includes? (email-text-body result) "Hi ,"))) (t/is (str/includes? (get-in result [:body "text/html"]) "Hi,")) (t/is (not (str/includes? (get-in result [:body "text/html"]) "Hi ,"))))) + +(t/deftest send-with-reuse-conn-and-no-conn-raises + (t/testing "reuse-conn without a caller connection fails fast" + (t/is (thrown-with-msg? clojure.lang.ExceptionInfo #"reuse-conn" + (emails/send {} + {::emails/reuse-conn true + ::emails/factory (fn [_] {:to "a@example.com"})}))))) + +(t/deftest send-with-reuse-conn-joins-caller-transaction + (let [job-def (ig/init-key ::emails/job-def + {::emails/sendmail (fn [_] nil)}) + defs {:sendmail job-def} + email {:to "a@example.com" :subject "hi" :body "hello"} + job-id (db/tx-run! {::db/pool th/*pool*} + (fn [cfg] + (emails/send (assoc cfg + ::jobs/defs defs + ::mtx/metrics (get th/*system* :app.metrics/metrics)) + (assoc email ::emails/reuse-conn true))))] + (t/testing "the email job is submitted through the caller conn" + (t/is (= "sendmail" (:name (th/db-get :job {:id job-id}))))))) + +(defn- templated-job-def + [sendmail] + (ig/init-key ::emails/templated-job-def + {::emails/sendmail sendmail})) + +(defn- submit-templated + [job-def params] + (db/tx-run! {::db/pool th/*pool*} + (fn [cfg] + (emails/send-template (assoc cfg + ::jobs/defs {:send-templated-mail job-def} + ::mtx/metrics (get th/*system* :app.metrics/metrics)) + (assoc params ::emails/reuse-conn true))))) + +(def ^:private invite-to-team-recipe + {:to "invitee@example.com" + :public-uri (cf/get :public-uri) + :invited-by "Owner User" + :team "Design Team" + :token "test-token" + :organization {:name "Acme Inc" + :sso-active false}}) + +(t/deftest templated-registry-covers-all-factories + (t/is (= #{"feedback" "register" "password-recovery" "password-changed" + "change-email" "invite-to-team" "invite-to-organization" + "organization-setup-sso" "renewal-notice" "join-team" + "request-file-access" "request-file-access-yourpenpot" + "request-file-access-yourpenpot-view" "request-team-access" + "comment-mention" "comment-thread" "comment-notification"} + (set (keys emails/templates))))) + +(t/deftest send-template-validation + (t/testing "missing template fails fast" + (t/is (thrown-with-msg? clojure.lang.ExceptionInfo #"template" + (emails/send-template {} {:to "a@example.com"})))) + (t/testing "unknown template fails fast" + (t/is (thrown-with-msg? clojure.lang.ExceptionInfo #"unknown email template" + (emails/send-template {} {:to "a@example.com" + ::emails/template "nope"})))) + (t/testing "template ids are strings, never keywords" + (t/is (thrown-with-msg? clojure.lang.ExceptionInfo #"unknown email template" + (emails/send-template {} {:to "a@example.com" + ::emails/template :app.email/register})))) + (t/testing "reuse-conn without a caller connection fails fast" + (t/is (thrown-with-msg? clojure.lang.ExceptionInfo #"reuse-conn" + (emails/send-template {} {:to "a@example.com" + ::emails/reuse-conn true + ::emails/template "register"}))))) + +(t/deftest send-template-stores-recipe-not-rendered-mail + (let [job-def (templated-job-def (fn [_] nil)) + job-id (submit-templated job-def (assoc invite-to-team-recipe + ::emails/template "invite-to-team")) + row (th/db-get :job {:id job-id}) + params (jobs/decode-params job-def (:params row)) + rendered (emails/render emails/invite-to-team invite-to-team-recipe)] + (t/testing "the job lands on the new queue" + (t/is (= "send-templated-mail" (:name row)))) + (t/testing "stored params carry the template id and no rendered content" + (t/is (= "invite-to-team" (:template params))) + (t/is (not (contains? params :subject))) + (t/is (not (contains? params :body)))) + (t/testing "stored params are smaller than the rendered html alone" + (let [stored-size (count (str (:params row))) + rendered-html (count (get-in rendered [:body "text/html"]))] + (t/is (< stored-size rendered-html)))) + (t/testing "the worker renders the same mail from the stored recipe" + (t/is (= rendered (emails/render-template (:template params) + (dissoc params :template))))))) + +(defn- decode-template-data + [params] + (let [decode (get-in emails/templates [(:template params) ::emails/decode])] + (decode (dissoc params :template)))) + +(t/deftest send-template-roundtrips-uuids-and-profile + (let [sent (atom nil) + job-def (templated-job-def (fn [_ email] (reset! sent email))) + team-id (random-uuid) + recipe {:to "member@example.com" + :public-uri (cf/get :public-uri) + :invited-by "Owner User" + :team "Design Team" + :team-id team-id} + job-id (submit-templated job-def (assoc recipe + ::emails/template "join-team")) + params (jobs/decode-params job-def (:params (th/db-get :job {:id job-id})))] + (t/testing "uuids survive the plain-JSON queue roundtrip typed" + (let [data (decode-template-data params)] + (t/is (uuid? (:team-id data))) + (t/is (= team-id (:team-id data))))) + (t/testing "the handler renders and delivers the mail" + ((::jobs/handler job-def) nil params) + (t/is (= (:subject (emails/render emails/join-team recipe)) + (:subject @sent))) + (t/is (= "member@example.com" (:to @sent))))) + (let [sent (atom nil) + job-def (templated-job-def (fn [_ email] (reset! sent email))) + recipe {:to "dest@example.com" + :reply-to "user@example.com" + :email "user@example.com" + :attachments {"error-report.txt" "trace"} + :feedback-subject "subj" + :feedback-type "bug" + :feedback-content "content" + :profile {:id (random-uuid) :fullname "User" :email "user@example.com"}} + job-id (submit-templated job-def (assoc recipe + ::emails/template "feedback")) + params (jobs/decode-params job-def (:params (th/db-get :job {:id job-id})))] + (t/testing "nested profile id survives the roundtrip typed" + (t/is (uuid? (get-in (decode-template-data params) [:profile :id])))) + (t/testing "the handler renders the feedback mail" + ((::jobs/handler job-def) nil params) + (t/is (= (:subject (emails/render emails/user-feedback recipe)) + (:subject @sent)))))) + +(def ^:private organization-with-uris + {:id (random-uuid) + :name "Acme Inc" + :initials "AI" + :logo (u/uri "https://example.com/assets/by-id/logo") + :avatar-bg-url (u/uri "https://example.com/images/avatar-bg.jpg") + :sso-active false}) + +(t/deftest send-template-roundtrips-organization-uris + (doseq [[template factory recipe] + [["invite-to-team" emails/invite-to-team + {:to "invitee@example.com" + :public-uri (cf/get :public-uri) + :invited-by "Owner User" + :team "Design Team" + :organization organization-with-uris + :token "test-token"}] + ["invite-to-organization" emails/invite-to-organization + {:to "invitee@example.com" + :public-uri (cf/get :public-uri) + :invited-by "Owner User" + :user-name "Invitee" + :organization organization-with-uris + :token "test-token"}] + ["renewal-notice" emails/renewal-notice + {:to "billing@example.com" + :public-uri (cf/get :public-uri) + :user-name "Billing" + :renewal-date "2026-12-01" + :estimated-amount "$10.00" + :organizations [organization-with-uris]}]]] + (t/testing template + (let [sent (atom nil) + job-def (templated-job-def (fn [_ email] (reset! sent email))) + job-id (submit-templated job-def (assoc recipe ::emails/template template)) + params (jobs/decode-params job-def (:params (th/db-get :job {:id job-id})))] + ((::jobs/handler job-def) nil params) + (t/is (= (emails/render factory recipe) @sent)))))) + +(t/deftest send-templated-mail-handler-rejects-unknown-template + (let [job-def (templated-job-def (fn [_] nil)) + code (try + ((::jobs/handler job-def) nil {:template "nope" + :to "a@example.com"}) + nil + (catch clojure.lang.ExceptionInfo e + (-> e ex-data :code)))] + (t/is (= :unknown-email-template code)))) diff --git a/backend/test/backend_tests/file_gc_scheduler_test.clj b/backend/test/backend_tests/file_gc_scheduler_test.clj new file mode 100644 index 0000000000..a8702d44ef --- /dev/null +++ b/backend/test/backend_tests/file_gc_scheduler_test.clj @@ -0,0 +1,43 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.file-gc-scheduler-test + (:require + [app.common.time :as ct] + [app.jobs :as jobs] + [backend-tests.helpers :as th] + [clojure.test :as t])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + +(t/deftest min-age-validation + (let [cfg th/*system*] + (t/testing "int millis, text and absence validate" + (t/is (uuid? (jobs/submit cfg {::jobs/name :file-gc-scheduler + ::jobs/params {:min-age 3600000}}))) + (t/is (uuid? (jobs/submit cfg {::jobs/name :file-gc-scheduler + ::jobs/params {:min-age "1h"}}))) + (t/is (uuid? (jobs/submit cfg {::jobs/name :file-gc-scheduler + ::jobs/params {}})))) + (t/testing "garbage fails at submit instead of burning retries" + (t/is (thrown-with-msg? Exception #"check error" + (jobs/submit cfg {::jobs/name :file-gc-scheduler + ::jobs/params {:min-age {:bogus true}}})))))) + +(t/deftest scheduler-submits-one-job-per-eligible-file + (let [profile (th/create-profile* 1)] + (doseq [i [1 2 3]] + (let [file (th/create-file* i {:profile-id (:id profile) + :project-id (:default-project-id profile)})] + ;; Make the files deterministically eligible: pending trim and + ;; modified long enough ago. + (th/db-update! :file {:has-media-trimmed false + :modified-at (ct/in-past "2h")} + {:id (:id file)}))) + (let [result (th/run-task! :file-gc-scheduler {:min-age "1h"})] + (t/is (= 3 (:processed result))) + (t/is (= 3 (count (th/db-query :job {:name "file-gc"}))))))) diff --git a/backend/test/backend_tests/file_pointer_snapshot_test.clj b/backend/test/backend_tests/file_pointer_snapshot_test.clj new file mode 100644 index 0000000000..8d1fcd34ab --- /dev/null +++ b/backend/test/backend_tests/file_pointer_snapshot_test.clj @@ -0,0 +1,1005 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.file-pointer-snapshot-test + "Snapshots share fragment rows with the live file (fdata/pointer-map), + so file-gc must keep fragments referenced by any live snapshot." + (:require + [app.binfile.common :as bfc] + [app.common.features :as cfeat] + [app.common.files.changes-builder :as pcb] + [app.common.files.migrations :as fmg] + [app.common.time :as ct] + [app.common.types.objects-map :as omap] + [app.common.types.shape :as cts] + [app.common.uuid :as uuid] + [app.db :as db] + [app.features.fdata :as fdata] + [app.features.file-snapshots :as fsnap] + [app.rpc :as-alias rpc] + [app.util.blob :as blob] + [app.util.pointer-map :as pmap] + [backend-tests.helpers :as th] + [clojure.test :as t])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + +(defn- update-file! + [& {:keys [profile-id file-id changes revn features] + :or {revn 0 + features cfeat/supported-features}}] + (let [params {::th/type :update-file + ::rpc/profile-id profile-id + :id file-id + :session-id (uuid/random) + :revn revn + :vern 0 + :features features + :changes changes} + out (th/command! params)] + (t/is (nil? (:error out))) + (:result out))) + +(defn- fragment-rows + [file-id] + (th/db-query :file-data {:file-id file-id :type "fragment"})) + +(defn- live-fragment-ids + [file-id] + (into #{} (map :id) (filter #(nil? (:deleted-at %)) (fragment-rows file-id)))) + +(defn- add-rect-change + [page-id shape-id] + [{:type :add-obj + :page-id page-id + :id shape-id + :parent-id uuid/zero + :frame-id uuid/zero + :components-v2 true + :obj (cts/setup-shape {:id shape-id + :name "rect" + :frame-id uuid/zero + :parent-id uuid/zero + :type :rect})}]) + +(defn- add-rect-changes + [page-id n] + (mapv #(first (add-rect-change page-id %)) (repeatedly n uuid/random))) + +(defn- page-pointer-ids + [file-id & {:as opts}] + (let [f (apply bfc/get-file th/*system* file-id (mapcat identity opts))] + (into {} (map (fn [[pid p]] [pid (pmap/get-id p)])) + (:pages-index (:data f))))) + +(defn- fragment-objects + [file-id fragment-id] + (some-> (th/db-get :file-data {:file-id file-id + :id fragment-id + :type "fragment"}) + :data blob/decode :objects)) + +(defn- snapshots-by-revn + [file-id revn] + (th/db-exec! ["select id, label, revn from file_change + where file_id = ? and label is not null and revn = ?" + file-id revn])) + +(t/deftest snapshot-shares-fragments-without-duplicates + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + before (live-fragment-ids file-id)] + (t/is (pos? (count before))) + + (let [f0 (bfc/get-file th/*system* file-id) + snapshot (fsnap/create! th/*system* f0 {:profile-id (:id profile)}) + after (live-fragment-ids file-id)] + ;; No new fragment rows: the snapshot reuses the live ones. + (t/is (= before after)) + (t/is (some? (:id snapshot))) + + ;; Snapshot pointers are a subset of the live pointers. + (let [main-ids (fdata/get-used-pointer-ids (:data f0)) + snap (fsnap/get-snapshot th/*system* file-id (:id snapshot)) + snap-ids (fdata/get-used-pointer-ids (:data snap))] + (t/is (pos? (count snap-ids))) + (t/is (every? #(contains? main-ids %) snap-ids)))))) + +(t/deftest gc-keeps-fragments-referenced-by-live-snapshot + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + page-id (first (:pages (:data f0))) + snap (fsnap/create! th/*system* f0 {:profile-id (:id profile)}) + old-ids (fdata/get-used-pointer-ids (:data f0))] + + ;; Modify the page: its fragment id changes, the old row is now only + ;; referenced by the snapshot. + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (add-rect-change page-id (uuid/random))) + + (let [f1 (bfc/get-file th/*system* file-id) + new-ids (fdata/get-used-pointer-ids (:data f1)) + orphan (first (remove #(contains? new-ids %) old-ids))] + (t/is (some? orphan)) + + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + + ;; The old fragment is still alive because the snapshot needs it. + (let [rows (fragment-rows file-id) + row (first (filter #(= orphan (:id %)) rows))] + (t/is (some? row)) + (t/is (nil? (:deleted-at row)))) + + ;; The snapshot still restores the pre-change content. + (let [snap-data (:data (fsnap/get-snapshot th/*system* file-id (:id snap))) + snap-ids (fdata/get-used-pointer-ids snap-data)] + (t/is (contains? snap-ids orphan)))))) + +(t/deftest snapshot-release-frees-fragments + ;; Full pin-release lifecycle through the real paths: the replaced + ;; fragment stays alive while the snapshot exists, and the next + ;; file-gc after deleting + purging the snapshot marks it. + ;; Note: every update takes an automatic snapshot, so besides the + ;; manual one there is also an automatic snapshot of the same state; + ;; the release phase deletes all of them. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + page-id (first (:pages (:data f0)))] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (add-rect-change page-id (uuid/random))) + + (let [f1 (bfc/get-file th/*system* file-id) + snap (fsnap/create! th/*system* f1 {:profile-id (:id profile)})] + (t/is (uuid? (:id snap))) + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 1 + :changes (add-rect-change page-id (uuid/random))) + + (let [f2 (bfc/get-file th/*system* file-id) + new-ids (fdata/get-used-pointer-ids (:data f2)) + old-ids (fdata/get-used-pointer-ids (:data f1)) + orphan (first (remove #(contains? new-ids %) old-ids))] + (t/is (some? orphan)) + + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + (t/is (nil? (:deleted-at (first (filter #(= orphan (:id %)) + (fragment-rows file-id)))))) + + ;; Every update takes an automatic snapshot and each of them may + ;; share the orphan, so release all of them through the real + ;; path: delete! submits a :delete-object job per snapshot that + ;; marks both its file_change and its file_data rows as deleted. + (doseq [{:keys [id]} (filter #(some? (:label %)) + (th/db-query :file-change {:file-id file-id}))] + (fsnap/delete! th/*system* {:id id + :file-id file-id + :deleted-at (ct/minus (ct/now) {:hours 1})})) + (th/run-pending-jobs) + (t/is (pos? (:processed (th/run-task! :objects-gc {})))) + + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + (let [row (first (filter #(= orphan (:id %)) (fragment-rows file-id)))] + (t/is (some? row)) + (t/is (some? (:deleted-at row)))))))) + +(t/deftest file-gc-is-idempotent-on-unchanged-file + ;; A file-gc pass over an unchanged file must not create fragment + ;; rows nor mark any: unchanged pages keep their ids. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + before (live-fragment-ids file-id)] + (t/is (pos? (count before))) + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + (let [after-first (live-fragment-ids file-id)] + (t/is (= before after-first)) + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + (t/is (= after-first (live-fragment-ids file-id))) + (t/is (every? #(nil? (:deleted-at %)) (fragment-rows file-id)))))) + +(t/deftest gc-marks-fragments-without-live-reference + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + page-id (first (:pages (:data f0))) + old-ids (fdata/get-used-pointer-ids (:data f0))] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (add-rect-change page-id (uuid/random))) + + (let [f1 (bfc/get-file th/*system* file-id) + new-ids (fdata/get-used-pointer-ids (:data f1)) + orphan (first (remove #(contains? new-ids %) old-ids))] + (t/is (some? orphan)) + ;; The update above may have left an automatic snapshot sharing the + ;; replaced fragment; drop snapshot rows first so the fragment is + ;; really unreferenced (same cleanup the rpc-file-test fragment + ;; test performs before asserting GC marking). + (th/db-delete! :file-change {:file-id file-id}) + (th/db-delete! :file-data {:file-id file-id :type "snapshot"}) + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + (let [row (first (filter #(= orphan (:id %)) (fragment-rows file-id)))] + (t/is (some? row)) + (t/is (some? (:deleted-at row))))))) + +(t/deftest pointer-context-contract + ;; The invariant the whole backend relies on: a pointer can only be + ;; derefed while a loader is bound; with-pointers binds the loader + ;; for the given file. + (let [pointer (pmap/create (uuid/next) {})] + (t/is (thrown? UnsupportedOperationException (deref pointer))) + (fdata/with-pointers th/*system* (uuid/random) + (try + (deref pointer) + (t/is false "expected a fragment-not-found error") + (catch clojure.lang.ExceptionInfo cause + (t/is (= :fragment-not-found (:code (ex-data cause))))))))) + +(t/deftest restore-returns-snapshot-content + ;; Snapshot, modify the page so the old fragment is snapshot-only, + ;; restore, and compare fully resolved content with the snapshot. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + snap (fsnap/create! th/*system* f0 {:profile-id (:id profile)})] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (add-rect-change + (first (:pages (:data f0))) + (uuid/random))) + + (db/tx-run! th/*system* + (fn [cfg] (fsnap/restore! cfg file-id (:id snap)))) + + (let [snap-data (:data (fsnap/get-snapshot th/*system* file-id (:id snap))) + snap-content (:data (fdata/realize th/*system* + {:id file-id :data snap-data})) + live-content (:data (bfc/get-file th/*system* file-id + :realize? true))] + (t/is (= snap-content live-content))))) + +(t/deftest migrating-save-keeps-snapshot-on-pre-save-content + ;; A save that triggers a file migration must attach the automatic + ;; snapshot before migrating. Otherwise the migration gives the page + ;; pointer a fresh id and `modified?`, the save's change `assoc`s on + ;; that same pointer (which then keeps its id) and the snapshot ends + ;; up naming the fragment the save writes with the post-save content. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-a (uuid/random) + page-b (uuid/random) + shape-1 (uuid/random) + shape-2 (uuid/random)] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-page :name "a" :id page-a} + {:type :add-page :name "b" :id page-b}]) + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 1 + :changes (add-rect-change page-a shape-1)) + + ;; Force a pending migration on the next save: drop one applied + ;; migration row so `need-migration?` is true again. + (th/db-delete! :file-migration {:file-id file-id + :name "0031-migrate-stroke-width-token-attr"}) + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 2 + :changes (add-rect-change page-a shape-2)) + + (let [snapshot (first (snapshots-by-revn file-id 2)) + snap-data (:data (fsnap/get-snapshot th/*system* file-id (:id snapshot))) + snap-id (pmap/get-id (get-in snap-data [:pages-index page-a])) + main-id (pmap/get-id (get-in (:data (bfc/get-file th/*system* file-id)) + [:pages-index page-a])) + snap-objs (:objects (blob/decode + (:data (th/db-get :file-data {:file-id file-id + :id snap-id + :type "fragment"}))))] + + (t/is (uuid? (:id snapshot))) + ;; The snapshot must not share the fragment the save rewrites. + (t/is (not= snap-id main-id)) + (t/is (contains? snap-objs shape-1)) + (t/is (not (contains? snap-objs shape-2))) + + ;; Restoring it gives the pre-save page on a fully migrated file. + (let [out (th/command! {::th/type :restore-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id (:id snapshot)}) + restored (bfc/get-file th/*system* file-id :realize? true) + objects (get-in restored [:data :pages-index page-a :objects])] + (t/is (nil? (:error out))) + (t/is (contains? objects shape-1)) + (t/is (not (contains? objects shape-2))) + (t/is (not (fmg/need-migration? restored))))))) + +(t/deftest offload-keeps-fragment-semantics + ;; Every file_data row (main, snapshot, fragments) moves to storage + ;; without changing ids, and reads keep working. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + snap (fsnap/create! th/*system* f0 {:profile-id (:id profile)}) + _ (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (add-rect-change + (first (:pages (:data f0))) + (uuid/random))) + before (into {} (map (juxt :id identity)) + (th/db-query :file-data {:file-id file-id})) + ;; Snapshot rows are born with a future deleted-at (they expire), + ;; and offload only moves rows with deleted-at IS NULL. + live-before (into {} (filter (comp nil? :deleted-at val)) before) + main-before (fdata/get-used-pointer-ids + (:data (bfc/get-file th/*system* file-id)))] + (t/is (pos? (count before))) + (t/is (every? #(= "db" (:backend %)) (vals before))) + + (let [result (th/run-task! :offload-file-data {:file-id file-id})] + (t/is (zero? (:skipped result))) + (t/is (= (count live-before) (:offloaded result)))) + + (let [after (into {} (map (juxt :id identity)) + (th/db-query :file-data {:file-id file-id})) + live-after (into {} (filter (comp nil? :deleted-at val)) after)] + (t/is (= (set (keys live-before)) (set (keys live-after)))) + (t/is (every? #(= "storage" (:backend %)) (vals live-after))) + ;; Same pointers after the move, and the shared snapshot loads. + (t/is (= main-before + (fdata/get-used-pointer-ids + (:data (bfc/get-file th/*system* file-id))))) + (t/is (some? (:data (fsnap/get-snapshot th/*system* file-id (:id snap)))))))) + +(t/deftest gc-reattaches-unchanged-pages-and-marks-orphan + ;; No-churn boundary when content changes: modify exactly one page of + ;; a two-page file and run file-gc. Unmodified pages must keep their + ;; fragment ids (reattach), the replaced fragment must be marked, a + ;; follow-up objects-gc must purge only that row, and the live file + ;; must still resolve fully. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + page-1 (first (:pages (:data f0))) + page-2 (uuid/next) + shape-2 (uuid/random)] + ;; Second page so "unchanged" has a witness. + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-page + :name "second" + :id page-2} + {:type :add-obj + :page-id page-2 + :id shape-2 + :parent-id uuid/zero + :frame-id uuid/zero + :components-v2 true + :obj (cts/setup-shape + {:id shape-2 + :name "rect" + :frame-id uuid/zero + :parent-id uuid/zero + :type :rect})}]) + (let [f1 (bfc/get-file th/*system* file-id) + pages-1 (:pages-index (:data f1)) + id-1-before (pmap/get-id (get pages-1 page-1)) + id-2-before (pmap/get-id (get pages-1 page-2))] + (t/is (some? id-1-before)) + (t/is (some? id-2-before)) + (t/is (not= id-1-before id-2-before)) + ;; Modify exactly page-1. + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 1 + :changes (add-rect-change page-1 (uuid/random))) + (let [f2 (bfc/get-file th/*system* file-id) + pages-2 (:pages-index (:data f2)) + id-1-after (pmap/get-id (get pages-2 page-1)) + id-2-after (pmap/get-id (get pages-2 page-2)) + orphan id-1-before] + (t/is (not= id-1-before id-1-after)) + (t/is (= id-2-before id-2-after)) + ;; Updates take automatic snapshots that would pin the orphan; + ;; drop them so it is really unreferenced (same cleanup the + ;; gc-marks test performs before asserting marking). + (th/db-delete! :file-change {:file-id file-id}) + (th/db-delete! :file-data {:file-id file-id :type "snapshot"}) + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + ;; Unchanged page kept its id through the pass. + (let [f3 (bfc/get-file th/*system* file-id) + pages-3 (:pages-index (:data f3))] + (t/is (= id-2-after (pmap/get-id (get pages-3 page-2))))) + ;; Orphan marked, nothing else touched. + (let [rows (fragment-rows file-id) + marked (filter #(some? (:deleted-at %)) rows)] + (t/is (= 1 (count marked))) + (t/is (= orphan (:id (first marked))))) + ;; objects-gc purges exactly the orphan row. + (t/is (pos? (:processed (th/run-task! :objects-gc {})))) + (let [rows (fragment-rows file-id)] + (t/is (nil? (some #(= orphan (:id %)) rows))) + (t/is (= #{id-1-after id-2-after} (live-fragment-ids file-id)))) + ;; Live file still resolves fully. + (let [live (:data (bfc/get-file th/*system* file-id :realize? true))] + (t/is (empty? (fdata/get-used-pointer-ids live)))))))) + +(t/deftest snapshot-preview-resolves-after-offload + ;; The preview path must resolve fragments that now live in the + ;; storage backend, not just in db. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + snap (fsnap/create! th/*system* f0 {:profile-id (:id profile)})] + (t/is (uuid? (:id snap))) + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (add-rect-change + (first (:pages (:data f0))) + (uuid/random))) + (let [result (th/run-task! :offload-file-data {:file-id file-id})] + (t/is (zero? (:skipped result))) + (t/is (pos? (:offloaded result)))) + (let [out (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id (:id snap) + :features cfeat/supported-features})] + (t/is (nil? (:error out))) + (t/is (empty? (fdata/get-used-pointer-ids + (get-in out [:result :data]))))))) + +(t/deftest with-tracked-tracks-and-fails-loud-without-loader + ;; Direct pin of the with-tracked contract: wrapping tracks the new + ;; pointer, and a pointer that needs loading raises loudly because + ;; no loader is bound. (`wrap` carries its value inline so it + ;; derefs fine; `create` with an id starts unloaded, so it must + ;; load — and there is nothing to load from.) + (fdata/with-tracked + (let [wrapped (pmap/wrap {:a 1}) + unloaded (pmap/create (uuid/next) {})] + (t/is (pmap/pointer-map? wrapped)) + (t/is (pmap/pointer-map? unloaded)) + (t/is (contains? @pmap/*tracked* (pmap/get-id wrapped))) + (t/is (contains? @pmap/*tracked* (pmap/get-id unloaded))) + (t/is (= {:a 1} @wrapped)) + (t/is (thrown? UnsupportedOperationException @unloaded))))) + +(t/deftest legacy-snapshot-does-not-break-liveness + ;; Snapshots written before the feature carry materialized data in + ;; file_change.data (backend "legacy-db") instead of a file_data row. + ;; file-gc must scan them without failing, and the legacy preview must + ;; still resolve. Setup pins the legacy shape: file_change row with + ;; data, no file_data snapshot row. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + page-id (first (:pages (:data f0)))] + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (add-rect-change page-id (uuid/random))) + ;; Drop every snapshot row so only the manual one below exists. + (th/db-delete! :file-change {:file-id file-id}) + (th/db-delete! :file-data {:file-id file-id :type "snapshot"}) + (let [f1 (bfc/get-file th/*system* file-id) + snap (fsnap/create! th/*system* f1 {:profile-id (:id profile)}) + snap-id (:id snap) + realized (:data (fdata/realize th/*system* {:id file-id :data (:data (fsnap/get-snapshot th/*system* file-id snap-id))}))] + ;; Convert to the legacy shape: materialize into file_change.data, + ;; drop the file_data snapshot row. + (th/db-delete! :file-data {:file-id file-id :type "snapshot"}) + (th/db-update! :file-change {:data (blob/encode realized)} {:id snap-id}) + ;; Pin the legacy shape: data on file_change, no file_data row. + ;; (db-get filters rows with deleted_at set, and system snapshots + ;; carry a future one, so opt out of that filter explicitly.) + (t/is (nil? (seq (th/db-query :file-data {:file-id file-id :type "snapshot"})))) + (t/is (some? (:data (th/db-get :file-change {:id snap-id} {::db/remove-deleted false}))))) + ;; file-gc scans the legacy snapshot without failing... + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + ;; ...the live file still resolves fully... + (let [live (:data (bfc/get-file th/*system* file-id :realize? true))] + (t/is (empty? (fdata/get-used-pointer-ids live)))) + ;; ...and the legacy preview resolves with no leftover pointers. + (let [snap-data (:data (fsnap/get-snapshot th/*system* file-id + (->> (th/db-query :file-change {:file-id file-id}) + (filter #(some? (:data %))) + (first) + (:id))))] + (t/is (some? snap-data)) + (t/is (empty? (fdata/get-used-pointer-ids snap-data)))))) + +(t/deftest offload-edit-gc-cycle-keeps-liveness + ;; Full cycle on one file: offload to cold storage, edit (which brings + ;; rows back hot), file-gc. Liveness must stay correct throughout and + ;; the file must resolve fully at the end. (R1's fix is what lets + ;; metadata-bearing files survive the first step; this file carries no + ;; metadata, so the cycle also passes without it.) + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + page-id (first (:pages (:data f0))) + before (into {} (map (juxt :id identity)) + (th/db-query :file-data {:file-id file-id}))] + (t/is (pos? (count before))) + (let [result (th/run-task! :offload-file-data {:file-id file-id})] + (t/is (zero? (:skipped result))) + (t/is (pos? (:offloaded result)))) + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (add-rect-change page-id (uuid/random))) + ;; The edit wrote hot rows back: some file_data is db-backed again. + (t/is (pos? (count (filter #(= "db" (:backend %)) + (th/db-query :file-data {:file-id file-id}))))) + (th/db-delete! :file-change {:file-id file-id}) + (th/db-delete! :file-data {:file-id file-id :type "snapshot"}) + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + (let [live (:data (bfc/get-file th/*system* file-id :realize? true))] + (t/is (empty? (fdata/get-used-pointer-ids live)))) + ;; Every referenced fragment is live. + (let [used (fdata/get-used-pointer-ids (:data (bfc/get-file th/*system* file-id))) + live (live-fragment-ids file-id)] + (t/is (pos? (count used))) + (t/is (every? live used))))) + +(t/deftest save-keeps-untouched-page-fragments + ;; A save must not rewrite the fragments of pages it does not touch, + ;; even when the page's :objects map is a hash map (45 shapes), the + ;; page itself has more than 8 keys (PersistentHashMap) and the + ;; :pages-index is a hash map (9 pages). Re-assoc with an identical + ;; value keeps map and pointer identity in array and hash maps alike. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-a (uuid/random) + others (vec (repeatedly 8 uuid/random)) + page-b (first others) + guide (uuid/random) + flow (uuid/random) + thread (uuid/random)] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (into [{:type :add-page :name "a" :id page-a}] + (map-indexed (fn [i pid] + {:type :add-page + :name (str "p" i) + :id pid}) + others))) + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 1 + :changes (into [{:type :mod-page + :id page-a + :background "#ffffff" + :background-token "token" + :pixel-grid-color "#000000" + :pixel-grid-opacity 0.5} + {:type :set-plugin-data + :object-type :page + :object-id page-a + :page-id page-a + :namespace "probe" + :key "k" + :value "1"} + {:type :set-guide + :page-id page-a + :id guide + :params {:id guide :axis :x :position 10}} + {:type :set-flow + :page-id page-a + :id flow + :params {:id flow + :name "flow" + :starting-frame uuid/zero}} + {:type :set-comment-thread-position + :page-id page-a + :comment-thread-id thread + :frame-id uuid/zero + :position {:x 10 :y 10}}] + (add-rect-changes page-a 45))) + + (let [ids-before (page-pointer-ids file-id) + page (fdata/with-pointers th/*system* file-id + (deref (get-in (:data (bfc/get-file th/*system* file-id)) + [:pages-index page-a])))] + + ;; The page is a hash map and its objects map is a hash map: the + ;; scenario the churn suspicion was about. + (t/is (instance? clojure.lang.PersistentHashMap page)) + (t/is (= 46 (count (:objects page)))) + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 2 + :changes (add-rect-change page-b (uuid/random))) + + (let [ids-after (page-pointer-ids file-id)] + (t/is (= (get ids-before page-a) (get ids-after page-a))) + (t/is (not= (get ids-before page-b) (get ids-after page-b)))) + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 3 + :changes (add-rect-change page-b (uuid/random))) + + (t/is (= (get ids-before page-a) + (get (page-pointer-ids file-id) page-a)))))) + +(t/deftest objects-map-flip-leaves-unloaded-pages-until-edited + ;; A data-level flip of `fdata/objects-map` does not rewrite pages the + ;; save does not load: `enable-objects-map` skips unloaded pointers, so + ;; the page keeps its plain fragment until it is edited. The first edit + ;; wraps it once and later saves keep the new id. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-a (uuid/random) + page-b (uuid/random)] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (into [{:type :add-page :name "a" :id page-a} + {:type :add-page :name "b" :id page-b}] + (add-rect-changes page-a 45))) + + ;; Simulate a file created with pointer-map only: strip the objects + ;; maps, drop the feature and persist through the same encode path. + (db/tx-run! th/*system* + (fn [cfg] + (fdata/with-tracked-pointers cfg file-id + (let [file (bfc/get-file cfg file-id) + file (fdata/disable-objects-map file)] + (bfc/update-file! cfg file))))) + + (let [page-plain (get (page-pointer-ids file-id) page-a)] + (t/is (not (omap/objects-map? (fragment-objects file-id page-plain)))) + + ;; Add the feature at data level, the way a migration would. + (th/db-exec! ["update file + set features = array_append(features, 'fdata/objects-map') + where id = ?" + file-id]) + + ;; A save on another page must leave the unloaded page alone. + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 1 + :changes (add-rect-change page-b (uuid/random))) + + (t/is (= page-plain (get (page-pointer-ids file-id) page-a))) + (t/is (not (omap/objects-map? (fragment-objects file-id page-plain)))) + + ;; The first edit of page A loads it and wraps it once. + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 2 + :changes (add-rect-change page-a (uuid/random))) + + (let [page-wrapped (get (page-pointer-ids file-id) page-a)] + (t/is (not= page-plain page-wrapped)) + (t/is (omap/objects-map? (fragment-objects file-id page-wrapped))) + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 3 + :changes (add-rect-change page-b (uuid/random))) + + ;; Converged: no further rewrite. + (t/is (= page-wrapped (get (page-pointer-ids file-id) page-a))))))) + +(t/deftest save-loads-only-edited-page + ;; With objects-map enabled, encoding a save must not load the + ;; fragments of pages the change does not touch: each fragment was + ;; already wrapped when it was written. A three-page file with a + ;; one-page edit loads exactly one fragment (the edited page). + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + f0 (bfc/get-file th/*system* file-id) + page-1 (first (:pages (:data f0))) + page-2 (uuid/random) + page-3 (uuid/random)] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-page :name "b" :id page-2} + {:type :add-page :name "c" :id page-3} + (first (add-rect-change page-2 (uuid/random)))]) + + (let [loads (atom 0) + load fdata/load-pointer] + (with-redefs [fdata/load-pointer + (fn [cfg file-id id] + (swap! loads inc) + (load cfg file-id id))] + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 1 + :changes (add-rect-change page-2 (uuid/random)))) + + (t/is (= 1 @loads)) + + (let [ids (page-pointer-ids file-id)] + (t/is (some? (get ids page-1))) + (t/is (some? (get ids page-3))))))) + +(t/deftest restore-does-not-load-shared-pages + ;; Restoring a snapshot reuses the shared fragments; encoding the + ;; restored file must not load the pages it does not change. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-1 (first (:pages (:data (bfc/get-file th/*system* file-id)))) + page-2 (uuid/random)] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-page :name "b" :id page-2} + (first (add-rect-change page-2 (uuid/random)))]) + + (let [f1 (bfc/get-file th/*system* file-id) + snap (fsnap/create! th/*system* f1 {:profile-id (:id profile)}) + page-2-id (get (page-pointer-ids file-id) page-2)] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 1 + :changes (add-rect-change page-1 (uuid/random))) + + (let [loads (atom 0) + load fdata/load-pointer] + (with-redefs [fdata/load-pointer + (fn [cfg file-id id] + (swap! loads inc) + (load cfg file-id id))] + (db/tx-run! th/*system* + (fn [cfg] (fsnap/restore! cfg file-id (:id snap))))) + + (t/is (zero? @loads)) + (t/is (= page-2-id (get (page-pointer-ids file-id) page-2))))))) + +(t/deftest migration-keeps-unchanged-page-fragments + ;; Applying a pending migration on read keeps the fragment ids of the + ;; pages the migration does not change; a page it changes gets a new + ;; id. The caller still receives realized data, not pointers. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-a (uuid/random) + page-b (uuid/random) + shape-b (uuid/random)] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-page :name "a" :id page-a} + {:type :add-page :name "b" :id page-b} + (first (add-rect-change page-a (uuid/random))) + (-> (first (add-rect-change page-b shape-b)) + (update :obj assoc :applied-tokens + {:stroke-width "token"}))]) + + (let [ids-before (page-pointer-ids file-id)] + ;; Force the 0031 migration on the next read. + (th/db-delete! :file-migration + {:file-id file-id + :name "0031-migrate-stroke-width-token-attr"}) + + (let [returned (db/tx-run! th/*system* + (fn [cfg] (bfc/get-file cfg file-id)))] + (t/is (not (pmap/pointer-map? (get-in returned [:data :pages-index page-a])))) + (t/is (not (pmap/pointer-map? (get-in returned [:data :pages-index page-b])))) + (t/is (not (fmg/need-migration? returned))) + + (let [tokens (get-in returned [:data :pages-index page-b + :objects shape-b :applied-tokens])] + (t/is (contains? tokens :stroke-width-top)) + (t/is (not (contains? tokens :stroke-width))))) + + (let [ids-after (page-pointer-ids file-id)] + (t/is (= (get ids-before page-a) (get ids-after page-a))) + (t/is (not= (get ids-before page-b) (get ids-after page-b)))) + + ;; A second read neither migrates nor changes ids again. + (let [ids-after (page-pointer-ids file-id) + returned (db/tx-run! th/*system* + (fn [cfg] (bfc/get-file cfg file-id)))] + (t/is (= ids-after (page-pointer-ids file-id))) + (t/is (not (fmg/need-migration? returned))))))) + +(t/deftest migration-read-only-keeps-fragments-untouched + ;; A read-only migration returns realized data but does not persist: + ;; the stored pointers and the pending migration stay as they were. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-1 (first (:pages (:data (bfc/get-file th/*system* file-id))))] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes (add-rect-change page-1 (uuid/random))) + + (th/db-delete! :file-migration + {:file-id file-id + :name "0031-migrate-stroke-width-token-attr"}) + + (let [ids-before (page-pointer-ids file-id {:migrate? false}) + returned (db/tx-run! th/*system* + (fn [cfg] (bfc/get-file cfg file-id + :read-only? true)))] + (t/is (not (pmap/pointer-map? (get-in returned [:data :pages-index page-1])))) + (t/is (empty? (th/db-query :file-migration + {:file-id file-id + :name "0031-migrate-stroke-width-token-attr"}))) + (t/is (= ids-before (page-pointer-ids file-id {:migrate? false})))) + + ;; A normal read migrates, and the unchanged page keeps its id. + (let [ids-before (page-pointer-ids file-id {:migrate? false})] + (db/tx-run! th/*system* (fn [cfg] (bfc/get-file cfg file-id))) + (t/is (= ids-before (page-pointer-ids file-id))) + (t/is (seq (th/db-query :file-migration + {:file-id file-id + :name "0031-migrate-stroke-width-token-attr"})))))) + +(t/deftest libraries-load-their-pages-with-their-own-loader + ;; Fragment rows are keyed by (file_id, id), and every caller binds the + ;; loader of the file it works on. A library's pages must therefore come + ;; back from the library loader already readable, or the first deref + ;; under the consumer's loader asks the wrong file for the fragment. + (let [profile (th/create-profile* 1) + library (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared true}) + file (th/create-file* 2 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + lib-page (uuid/random)] + + (th/link-file-to-library* {:file-id (:id file) :library-id (:id library)}) + (update-file! :profile-id (:id profile) + :file-id (:id library) + :changes (-> (pcb/empty-changes nil) + (pcb/with-library-data (:data library)) + (pcb/add-empty-page lib-page "library page") + (:redo-changes))) + + ;; Precondition: the library stores the page as a fragment pointer. + (t/is (contains? (page-pointer-ids (:id library)) lib-page)) + + (let [page (db/tx-run! th/*system* + (fn [cfg] + (fdata/with-pointers cfg (:id file) + (let [consumer (bfc/get-file cfg (:id file)) + libs (bfc/get-resolved-file-libraries cfg consumer)] + (-> (get libs (:id library)) + (get-in [:data :pages-index lib-page]) + (fdata/realize-entry))))))] + (t/is (= lib-page (:id page))) + (t/is (contains? (:objects page) uuid/zero))))) + +(t/deftest feature-off-keeps-plain-snapshots-and-gc + ;; The feature is disabled by default (`fdata/pointer-map` is not in + ;; `cfeat/default-features`, only an explicit flag or team feature + ;; enables it). Pin that the paths this branch touches stay plain for + ;; such a file: a save takes a snapshot without pointers, no fragment + ;; rows appear, file-gc writes nothing, and the preview still resolves. + (with-redefs [cfeat/get-enabled-features (fn [& _] cfeat/default-features)] + (let [features (disj cfeat/supported-features + "fdata/pointer-map" + "fdata/objects-map") + profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-id (first (get-in file [:data :pages]))] + + (t/is (not (contains? (:features file) "fdata/pointer-map"))) + (t/is (not (contains? (:features file) "fdata/objects-map"))) + (t/is (empty? (fragment-rows file-id))) + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :features features + :changes (add-rect-change page-id (uuid/random))) + + (t/is (empty? (fragment-rows file-id))) + + (let [snapshot (first (snapshots-by-revn file-id 0)) + snap (fsnap/get-snapshot th/*system* file-id (:id snapshot))] + (t/is (uuid? (:id snapshot))) + (t/is (empty? (fdata/get-used-pointer-ids (:data snap)))) + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + (t/is (empty? (fragment-rows file-id))) + + (let [out (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id (:id snapshot) + :features features})] + (t/is (nil? (:error out))) + (t/is (map? (get-in out [:result :data]))) + (t/is (empty? (fdata/get-used-pointer-ids + (get-in out [:result :data]))))))))) diff --git a/backend/test/backend_tests/files_update_validation_timing_test.clj b/backend/test/backend_tests/files_update_validation_timing_test.clj index d34ee359fa..a2477698c7 100644 --- a/backend/test/backend_tests/files_update_validation_timing_test.clj +++ b/backend/test/backend_tests/files_update_validation_timing_test.clj @@ -14,6 +14,7 @@ [app.metrics :as mtx] [app.metrics.definition :as-alias mdef] [app.rpc.commands.files-update :as files-update] + [app.util.pointer-map :as pmap] [clojure.test :as t] [integrant.core :as ig]) (:import @@ -61,11 +62,12 @@ (t/deftest process-changes-and-validate-records-all-steps (let [metrics (ig/init-key :app.metrics/metrics {:default main/default-metrics}) cfg (test-cfg metrics)] - (binding [cf/flags all-flags] + (binding [cf/flags all-flags + pmap/*tracked* (pmap/create-tracked)] (with-redefs [bfc/get-resolved-file-libraries (fn [_ _] {}) val/validate-file-affected! (fn [_ _ _] nil) - val/validate-file-schema! (fn [_] nil)] - (let [file (@#'files-update/process-changes-and-validate cfg (test-file) [] nil) + val/validate-file-schema-affected! (fn [_ _] nil)] + (let [file (@#'files-update/process-changes-and-validate cfg (test-file) [] nil nil) timings @(::files-update/validation-timings cfg)] (t/is (map? file)) (t/is (number? (:resolve-libs-ms timings))) @@ -84,15 +86,16 @@ (t/deftest process-changes-and-validate-skips-when-skip-validate (let [metrics (ig/init-key :app.metrics/metrics {:default main/default-metrics}) cfg (test-cfg metrics)] - (binding [cf/flags all-flags] + (binding [cf/flags all-flags + pmap/*tracked* (pmap/create-tracked)] (with-redefs [bfc/get-resolved-file-libraries (fn [_ _] (throw (ex-info "must not resolve libs" {}))) val/validate-file-affected! (fn [_ _ _] (throw (ex-info "must not validate" {}))) - val/validate-file-schema! (fn [_] - (throw (ex-info "must not validate schema" {})))] + val/validate-file-schema-affected! (fn [_ _] + (throw (ex-info "must not validate schema" {})))] ;; soft validations still run: they only check flags, not skip-validate - (let [file (@#'files-update/process-changes-and-validate cfg (test-file) [] true) + (let [file (@#'files-update/process-changes-and-validate cfg (test-file) [] nil true) timings @(::files-update/validation-timings cfg)] (t/is (map? file)) (t/is (nil? (:resolve-libs-ms timings))) @@ -107,14 +110,15 @@ (t/deftest process-changes-and-validate-records-timing-on-error (let [metrics (ig/init-key :app.metrics/metrics {:default main/default-metrics}) cfg (test-cfg metrics)] - (binding [cf/flags all-flags] + (binding [cf/flags all-flags + pmap/*tracked* (pmap/create-tracked)] (with-redefs [bfc/get-resolved-file-libraries (fn [_ _] {}) val/validate-file-affected! (fn [_ _ _] (throw (ex-info "bad file" {:code :referential-integrity}))) - val/validate-file-schema! (fn [_] nil)] + val/validate-file-schema-affected! (fn [_ _] nil)] (let [timings-atom (::files-update/validation-timings cfg)] (t/is (thrown? clojure.lang.ExceptionInfo - (@#'files-update/process-changes-and-validate cfg (test-file) [] nil))) + (@#'files-update/process-changes-and-validate cfg (test-file) [] nil nil))) ;; the failing step still recorded its timing via finally (t/is (number? (:validate-affected-ms @timings-atom))) (t/is (number? (:resolve-libs-ms @timings-atom))) @@ -126,3 +130,31 @@ (let [record @#'files-update/record-validation-timing] (t/is (nil? (record {} :resolve-libs-ms "resolve-libs" 12))) (t/is (nil? (record {::mtx/metrics nil} :resolve-libs-ms "resolve-libs" 12))))) + +(t/deftest retries-record-one-sample-per-step-and-one-retry-sample + (let [metrics (ig/init-key :app.metrics/metrics {:default main/default-metrics}) + cfg (test-cfg metrics) + commit (fn [name] + {:commit-id (uuid/next) + :changes [{:type :mod-page :id (uuid/next) :name name}]}) + commits [(commit "a") (commit "b") (commit "bad") (commit "c")]] + (binding [cf/flags all-flags + pmap/*tracked* (pmap/create-tracked)] + (with-redefs [bfc/get-resolved-file-libraries (fn [_ _] {}) + val/validate-file-affected! (fn [_ _ changes] + (when (some #(= "bad" (:name %)) changes) + (throw (ex-info "bad file" {:type :validation + :code :referential-integrity})))) + val/validate-file-schema-affected! (fn [_ _] nil)] + (let [file (@#'files-update/process-changes-and-validate + cfg (test-file) (into [] (mapcat :changes) commits) commits nil) + timings @(::files-update/validation-timings cfg)] + (t/is (= (mapv :commit-id (subvec commits 2)) + (:commit-ids (::files-update/rejected file)))) + (t/is (number? (:retry-ms timings))) + ;; The first pass fails at validate-affected, before validate-schema + (doseq [step ["process-changes" "soft-validate-affected" "soft-validate-schema" + "validate-affected" "retry"]] + (t/is (= 1.0 (bucket-value metrics "penpot_rpc_update_file_validation_timing" + ["step"] [step] "60000.0")) + (str "expected one sample for step " step)))))))) diff --git a/backend/test/backend_tests/helpers.clj b/backend/test/backend_tests/helpers.clj index 5148916464..93e447e324 100644 --- a/backend/test/backend_tests/helpers.clj +++ b/backend/test/backend_tests/helpers.clj @@ -17,9 +17,11 @@ [app.common.spec :as us] [app.common.time :as ct] [app.common.transit :as tr] + [app.common.types.shape :as cts] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] + [app.jobs :as jobs] [app.main :as main] [app.media] [app.media :as-alias mtx] @@ -37,7 +39,6 @@ [app.rpc.helpers :as rph] [app.util.blob :as blob] [app.util.services :as sv] - [app.worker :as wrk] [app.worker.runner] [clojure.java.io :as io] [clojure.spec.alpha :as s] @@ -105,6 +106,22 @@ (fs/create-dir "/tmp/penpot") (next))))) +(defn- remove-job-runners + "Drops every `[ :app.worker/runner]` from the system. + + The tests drive the runner loop by hand (`wdisp/run-batch` plus + `run-worker-loop` over a system they build themselves), so a live runner + of any queue races them: it takes the job the test just dispatched and + completes it behind the test's back. Matching the key by shape, instead + of listing the known queues by hand, keeps a new queue from silently + bringing that race back (which is what happened to the `binfile` runner + this key list was missing)." + [system] + (apply dissoc system + (filter #(and (vector? %) + (= :app.worker/runner (second %))) + (keys system)))) + (defn init-system [next] (let [templates [{:id "test" @@ -136,9 +153,8 @@ :app.loggers.database/reporter :app.loggers.metrics/reporter :app.worker/cron - :app.worker/dispatcher - [:app.main/default :app.worker/runner] - [:app.main/webhook :app.worker/runner])) + :app.worker/dispatcher) + (remove-job-runners)) _ (ig/load-namespaces system) system (-> (ig/expand system) (ig/init))] (try @@ -239,11 +255,37 @@ :features features} params))))))) +(defn create-job* + "A job row in the state a worker finds it: running, in the `binfile` queue, + owned by `profile-id` and holding `resource-id` when it consumes one. + `job-name` is the kind of the job (a keyword like the `::jobs/name` a caller + would pass, or the text the row holds) and it is what resolves the job-def + of the row when its result is read or checked." + [job-name {:keys [profile-id resource-id]}] + (let [id (uuid/next)] + (db/insert! *pool* :job {:id id + :name (name job-name) + :tenant (cf/get :tenant) + :queue "binfile" + :params (db/json {}) + :priority 100 + :max-retries 0 + :retry-num 0 + :status "running" + :profile-id profile-id + :resource-id resource-id + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + id)) + (defn mark-file-deleted* ([params] (mark-file-deleted* *system* params)) - ([conn {:keys [id] :as params}] - (#'files/mark-file-deleted conn {} id))) + ([system {:keys [id] :as params}] + (db/tx-run! system + (fn [cfg] + (#'files/mark-file-deleted cfg {} id))))) (defn create-team* ([i params] (create-team* *system* i params)) @@ -338,6 +380,21 @@ :session-id session-id :profile-id profile-id}))))))) +(defn add-rect-change + "A change that adds a 10x10 rect `id` to the root of `page-id`." + [page-id id] + {:type :add-obj + :page-id page-id + :id id + :parent-id uuid/zero + :frame-id uuid/zero + :obj (cts/setup-shape {:id id + :type :rect + :name "rect" + :x 0 :y 0 :width 10 :height 10 + :parent-id uuid/zero + :frame-id uuid/zero})}) + (declare command!) (defn update-file! [& {:keys [profile-id file-id changes revn] :or {revn 0}}] @@ -507,13 +564,38 @@ (method-fn params)))) (defn management-command! + "Invokes a management RPC method. + + Resolves the shared-key id from an explicit `::rpc/auth-key-id`, from + `:app.http/auth-key-id` on the data or on the supplied request, and falls + back to the first id the method declares. The fallback lets the existing + tests exercise the allowed path without repeating the key; a test that + checks a rejection declares the key explicitly, including `nil`." [{:keys [::type] :as data}] - (let [[_ method-fn] (get-in *system* [:app.rpc/management-methods type])] + (let [[mdata method-fn] (get-in *system* [:app.rpc/management-methods type])] (when-not method-fn (ex/raise :type :assertion :code :rpc-method-not-found :hint (str/ffmt "management rpc method '%' not found" (name type)))) - (let [params (prepare-rpc-params data)] + (let [request (-> data meta :app.http/request) + key-id (cond + (contains? data ::rpc/auth-key-id) + (::rpc/auth-key-id data) + + (contains? data :app.http/auth-key-id) + (:app.http/auth-key-id data) + + (and (map? request) + (contains? request :app.http/auth-key-id)) + (:app.http/auth-key-id request) + + :else + (first (::rpc/allowed-shared-keys mdata))) + data (if (or (contains? data ::rpc/auth-key-id) + (nil? key-id)) + data + (assoc data ::rpc/auth-key-id key-id)) + params (prepare-rpc-params data)] (try-on! (method-fn params))))) (def multipart-headers @@ -544,28 +626,37 @@ (management-command! (with-multipart data))) (defn run-task! + "Execute a job handler directly (in-process, no row): the handler gets a + nil context, because there is no job to describe." ([name] (run-task! name {})) ([name params] - (wrk/invoke! (-> *system* - (assoc ::wrk/task name) - (assoc ::wrk/params params))))) + (jobs/invoke (-> *system* + (assoc ::jobs/name name) + (assoc ::jobs/params params))))) -(def sql:pending-tasks - "select t.* from task as t - where t.status = 'new' - order by t.priority desc, t.scheduled_at") +(def sql:pending-jobs + "select * from job + where status = 'new' + order by priority desc, scheduled_at") -(defn run-pending-tasks! +(defn run-pending-jobs + "Execute the pending (status='new') `job` rows in-process (simulating + the dispatcher + runner for the tests). Each row gets its context and its + job-id, so heartbeats and progress reach it exactly like in the runner. + Does not touch the row status; only the handler side effects matter." [] - (db/tx-run! *system* (fn [{:keys [::db/conn] :as cfg}] - (let [tasks (->> (db/exec! conn [sql:pending-tasks]) - (map #'app.worker.runner/decode-task-row))] - (doseq [task tasks] - (let [cfg (-> cfg - (assoc :app.worker.runner/queue (:queue task)) - (assoc :app.worker.runner/id 0))] - (#'app.worker.runner/run-task cfg task))))))) + (db/tx-run! *system* + (fn [{:keys [::db/conn]}] + (let [jobs-rows (db/exec! conn [sql:pending-jobs])] + (doseq [row jobs-rows] + (jobs/invoke (-> *system* + ;; the row stores a string; invoke + ;; takes the canonical keyword + (assoc ::jobs/name (keyword (:name row))) + (assoc ::jobs/params (:params row)) + (assoc ::jobs/context (jobs/make-context row)) + (assoc ::jobs/job-id (:id row))))))))) (defn metrics-stub "A metrics instance with no collectors, where every `run!` is a no-op." @@ -717,6 +808,17 @@ [& params] (apply db/delete! *pool* params)) +(defn db-force-delete + "Deletes rows whose table is guarded by `raise_deletion_protection`, + inside a transaction that disables the guard. Test-only escape hatch: + production delete paths disable protection in their own transaction + (see `app.jobs.gc`)." + [& params] + (db/tx-run! *pool* + (fn [{:keys [::db/conn]}] + (db/exec-one! conn ["SET LOCAL rules.deletion_protection TO off"]) + (apply db/delete! conn params)))) + (defn db-query [& params] (apply db/query *pool* params)) diff --git a/backend/test/backend_tests/http_assets_test.clj b/backend/test/backend_tests/http_assets_test.clj index a5e60e9965..c9503f1cca 100644 --- a/backend/test/backend_tests/http_assets_test.clj +++ b/backend/test/backend_tests/http_assets_test.clj @@ -31,7 +31,9 @@ io.prometheus.client.Counter io.prometheus.client.Counter$Child)) -(t/use-fixtures :once th/state-init) +(t/use-fixtures :once (t/compose-fixtures + (partial th/init-config [:enable-access-tokens]) + th/init-system)) (t/use-fixtures :each (th/serial th/database-reset th/clean-storage)) @@ -464,7 +466,7 @@ ;; 2. actoken/authz reads ::http/auth-data and sets ::actoken/profile-id request {::http/auth-data {:type :token :token (:token atoken) - :claims {:tid (:id atoken)}} + :claims (actoken/decode-token th/*system* (:token atoken))} :path-params {:id (str (:id object))}} ;; Apply actoken/authz middleware @@ -517,7 +519,7 @@ (t/testing "access token auth sets ::actoken/profile-id" (let [request {::http/auth-data {:type :token :token (:token atoken) - :claims {:tid (:id atoken)}}} + :claims (actoken/decode-token th/*system* (:token atoken))}} result (with-session request)] (t/is (= (:id profile) (::actoken/profile-id result))))) @@ -887,6 +889,48 @@ response (assets/objects-handler cfg request)] (t/is (= 204 (::yres/status response))))) +;; ---------------------------------------------------------------- +;; Tests: objects-handler — job-resource bucket ownership +;; ---------------------------------------------------------------- + +(t/deftest objects-handler-job-resource-owner-can-access + (let [storage (-> (:app.storage/storage th/*system*) + (configure-storage-backend)) + cfg (make-handler-cfg storage) + owner (th/create-profile* 1) + object (create-storage-object! storage "job-resource" "artifact" {:profile-id (:id owner)}) + request {:path-params {:id (str (:id object))} + ::session/profile-id (:id owner)} + response (assets/objects-handler cfg request)] + (t/is (= 204 (::yres/status response))))) + +(t/deftest objects-handler-job-resource-non-owner-gets-404 + ;; The artifact of a job belongs to the profile that owns the job. + (let [storage (-> (:app.storage/storage th/*system*) + (configure-storage-backend)) + cfg (make-handler-cfg storage) + owner (th/create-profile* 1) + stranger (th/create-profile* 2) + object (create-storage-object! storage "job-resource" "artifact" {:profile-id (:id owner)}) + request {:path-params {:id (str (:id object))} + ::session/profile-id (:id stranger)} + response (assets/objects-handler cfg request)] + (t/is (= 404 (::yres/status response))))) + +(t/deftest objects-handler-job-resource-released-object-gets-404 + ;; Once the job is gone and the GC has reclaimed the artifact, the route + ;; has nothing to serve. + (let [storage (-> (:app.storage/storage th/*system*) + (configure-storage-backend)) + cfg (make-handler-cfg storage) + owner (th/create-profile* 1) + object (create-storage-object! storage "job-resource" "artifact" {:profile-id (:id owner)}) + request {:path-params {:id (str (:id object))} + ::session/profile-id (:id owner)}] + (t/is (= 204 (::yres/status (assets/objects-handler cfg request)))) + (sto/del-object! storage (:id object)) + (t/is (= 404 (::yres/status (assets/objects-handler cfg request)))))) + ;; ---------------------------------------------------------------- ;; Tests: asset request metrics ;; ---------------------------------------------------------------- diff --git a/backend/test/backend_tests/http_debug_test.clj b/backend/test/backend_tests/http_debug_test.clj new file mode 100644 index 0000000000..9c874601f4 --- /dev/null +++ b/backend/test/backend_tests/http_debug_test.clj @@ -0,0 +1,29 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.http-debug-test + "The /dbg validate action walks the whole file and its libraries: it + must hand `get-resolved-file-libraries` the file (not its id) and + resolve the pointer-mapped pages with the file's own loader." + (:require + [app.http.debug :as debug] + [backend-tests.helpers :as th] + [clojure.test :as t] + [yetti.response :as-alias yres])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + +(t/deftest validate-file-action-resolves-pointer-mapped-data + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + handler (deref #'debug/validate-file) + result (handler th/*system* {:params {:file-id (str (:id file))}})] + + (t/is (= 200 (::yres/status result))) + (t/is (= "NO VALIDATION ERRORS FOUND" (::yres/body result))))) diff --git a/backend/test/backend_tests/http_errors_test.clj b/backend/test/backend_tests/http_errors_test.clj index 9fbf6e0345..52b23722c8 100644 --- a/backend/test/backend_tests/http_errors_test.clj +++ b/backend/test/backend_tests/http_errors_test.clj @@ -68,3 +68,46 @@ headers (::yres/headers response)] (t/is (= 429 (::yres/status response))) (t/is (nil? (get headers "retry-after"))))) + +(defn- psql-exception + [state] + (org.postgresql.util.PSQLException. "database failure" state)) + +(t/deftest transient-database-failures-answer-503 + (doseq [state [org.postgresql.util.PSQLState/CONNECTION_FAILURE + org.postgresql.util.PSQLState/CONNECTION_UNABLE_TO_CONNECT + org.postgresql.util.PSQLState/SERIALIZATION_FAILURE + org.postgresql.util.PSQLState/DEADLOCK_DETECTED]] + (let [response (http-errors/handle (psql-exception state) {})] + (t/is (= 503 (::yres/status response)) (str state)) + (t/is (= :server-error (-> response ::yres/body :type)))))) + +(t/deftest other-database-failures-answer-500 + (let [response (http-errors/handle (psql-exception org.postgresql.util.PSQLState/UNIQUE_VIOLATION) {})] + (t/is (= 500 (::yres/status response))) + (t/is (= :database-error (-> response ::yres/body :code))))) + +(t/deftest database-timeouts-keep-answering-504 + (let [response (http-errors/handle (psql-exception org.postgresql.util.PSQLState/QUERY_CANCELED) {})] + (t/is (= 504 (::yres/status response))) + (t/is (= :statement-timeout (-> response ::yres/body :code))))) + +(t/deftest transient-connection-failures-answer-503 + (doseq [cause [(java.sql.SQLTransientConnectionException. "pool exhausted") + (java.net.SocketException. "broken pipe") + (java.net.ConnectException. "connection refused") + (java.net.SocketTimeoutException. "read timed out") + (java.net.UnknownHostException. "storage.example.com") + (InterruptedException. "shutting down")]] + (let [response (http-errors/handle cause {})] + (t/is (= 503 (::yres/status response)) (str (class cause))) + (t/is (= :server-error (-> response ::yres/body :type)))))) + +(t/deftest permanent-io-failures-answer-500 + (doseq [cause [(java.io.IOException. "generic") + (java.io.EOFException. "truncated blob") + (java.nio.file.NoSuchFileException. "/data/missing") + (com.fasterxml.jackson.core.JsonParseException. nil "bad json")]] + (let [response (http-errors/handle cause {})] + (t/is (= 500 (::yres/status response)) (str (class cause))) + (t/is (= :io-exception (-> response ::yres/body :code)))))) diff --git a/backend/test/backend_tests/http_middleware_test.clj b/backend/test/backend_tests/http_middleware_test.clj index 44042a4b2f..57766ab3b7 100644 --- a/backend/test/backend_tests/http_middleware_test.clj +++ b/backend/test/backend_tests/http_middleware_test.clj @@ -34,7 +34,9 @@ io.prometheus.client.CollectorRegistry io.undertow.server.RequestTooBigException)) -(t/use-fixtures :once th/state-init) +(t/use-fixtures :once (t/compose-fixtures + (partial th/init-config [:enable-access-tokens]) + th/init-system)) (t/use-fixtures :each th/database-reset) (t/deftest auth-middleware-1 @@ -106,7 +108,8 @@ (let [response (handler nil)] (t/is (nil? response))) - (let [response (handler {::http/auth-data {:type :token :token "foobar" :claims {:tid (:id token)}}})] + (let [response (handler {::http/auth-data {:type :token :token (:token token) + :claims (tokens/verify th/*system* {:token (:token token)})}})] (t/is (= #{} (:app.http.access-token/perms response))) (t/is (= (:id profile) (:app.http.access-token/profile-id response)))))) @@ -114,9 +117,14 @@ (let [profile (th/create-profile* 1) token (db/tx-run! th/*system* app.rpc.commands.access-token/create-access-token (:id profile) "test" nil "mcp") - handler (#'app.http.access-token/wrap-authz identity th/*system*) - request {::http/auth-data {:type :token :token "foobar" :claims {:tid (:id token)}}} + handler (#'app.http.access-token/wrap-authz + identity (assoc th/*system* :app.http.access-token/allow-mcp true)) + request {::http/auth-data {:type :token :token (:token token) + :claims (tokens/verify th/*system* {:token (:token token)})}} response (handler request)] + (t/is (nil? (:app.http.access-token/profile-id + ((#'app.http.access-token/wrap-authz identity th/*system*) request))) + "MCP tokens do not authenticate routes that have not opted in") ;; Must set ::actoken/id from claims :tid (t/is (= (:id token) (:app.http.access-token/id response))) ;; Must set ::actoken/type from database @@ -125,6 +133,30 @@ (t/is (= #{} (:app.http.access-token/perms response))) (t/is (= (:id profile) (:app.http.access-token/profile-id response))))) +(t/deftest access-token-authz-follows-flags + (let [profile (th/create-profile* 1) + create #(db/tx-run! th/*system* app.rpc.commands.access-token/create-access-token + (:id profile) "test" nil %) + request (fn [{:keys [token]}] + {::http/auth-data {:type :token :token token + :claims (tokens/verify th/*system* {:token token})}}) + auth (fn [flags token] + (with-redefs [cf/flags flags] + (let [handler (#'app.http.access-token/wrap-authz + identity (assoc th/*system* :app.http.access-token/allow-mcp true))] + (:app.http.access-token/profile-id (handler (request token)))))) + personal (create nil) + mcp (create "mcp")] + (t/testing "only mcp: MCP tokens authenticate, personal tokens do not" + (t/is (= (:id profile) (auth #{:mcp} mcp))) + (t/is (nil? (auth #{:mcp} personal)))) + (t/testing "only access-tokens: both authenticate" + (t/is (= (:id profile) (auth #{:access-tokens} mcp))) + (t/is (= (:id profile) (auth #{:access-tokens} personal)))) + (t/testing "neither flag: the middleware is not installed" + (with-redefs [cf/flags #{}] + (t/is (nil? ((:compile app.http.access-token/authz)))))))) + (defrecord MethodAwareDummyRequest [req-method headers] yreq/IRequest (method [_] req-method) @@ -416,25 +448,25 @@ :auth-token-cookie-max-age idle :auth-token-cookie-max-age-absolute absolute default)))] - (let [expanded (ig/expand-key :app.http.session.tasks/gc {})] + (let [expanded (ig/expand-key :app.http.session/job-def {})] (t/is (= idle - (get-in expanded [:app.http.session.tasks/gc + (get-in expanded [:app.http.session/job-def :app.http.session.tasks/max-age])) "task max-age should carry the configured idle window") (t/is (= absolute - (get-in expanded [:app.http.session.tasks/gc + (get-in expanded [:app.http.session/job-def :app.http.session.tasks/max-age-absolute])) "task max-age-absolute should carry the configured absolute cap"))) (with-redefs [cf/get (fn ([_k] nil) ([_k default] default))] - (let [expanded (ig/expand-key :app.http.session.tasks/gc {})] + (let [expanded (ig/expand-key :app.http.session/job-def {})] (t/is (= session/default-cookie-max-age - (get-in expanded [:app.http.session.tasks/gc + (get-in expanded [:app.http.session/job-def :app.http.session.tasks/max-age])) "task max-age should fall back to the default idle window") (t/is (= session/default-cookie-max-age-absolute - (get-in expanded [:app.http.session.tasks/gc + (get-in expanded [:app.http.session/job-def :app.http.session.tasks/max-age-absolute])) "task max-age-absolute should fall back to the default absolute cap"))))) @@ -443,15 +475,15 @@ params {:app.db/pool pool :app.http.session.tasks/max-age (ct/duration {:days 7}) :app.http.session.tasks/max-age-absolute (ct/duration {:days 30})}] - (t/is (nil? (ig/assert-key :app.http.session.tasks/gc params)) + (t/is (nil? (ig/assert-key :app.http.session/job-def params)) "absolute cap above the idle window should pass") - (t/is (nil? (ig/assert-key :app.http.session.tasks/gc + (t/is (nil? (ig/assert-key :app.http.session/job-def (assoc params :app.http.session.tasks/max-age-absolute (ct/duration {:days 7})))) "absolute cap equal to the idle window should pass") (t/is (thrown? IllegalArgumentException - (ig/assert-key :app.http.session.tasks/gc + (ig/assert-key :app.http.session/job-def (assoc params :app.http.session.tasks/max-age-absolute (ct/duration {:days 3})))) diff --git a/backend/test/backend_tests/jobs_gc_test.clj b/backend/test/backend_tests/jobs_gc_test.clj new file mode 100644 index 0000000000..ca4a8d170e --- /dev/null +++ b/backend/test/backend_tests/jobs_gc_test.clj @@ -0,0 +1,361 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.jobs-gc-test + (:require + [app.common.schema :as sm] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.jobs.gc :as gc] + [app.main :as main] + [app.metrics :as-alias mtx] + [app.metrics.definition :as-alias mdef] + [app.storage :as sto] + [backend-tests.helpers :as th] + [backend-tests.storage-test :refer [configure-storage-backend]] + [clojure.test :as t] + [integrant.core :as ig]) + (:import + io.prometheus.client.Counter + io.prometheus.client.Counter$Child)) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each (th/serial + th/database-reset + th/clean-storage)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; HELPERS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- make-metrics [] + (ig/init-key :app.metrics/metrics + {:default (select-keys main/default-metrics + [:jobs-gc-rows])})) + +(defn- counter-value [metrics labels] + (let [collector (mtx/get-collector metrics :jobs-gc-rows) + instance (::mdef/instance collector) + child (.labels ^Counter instance (into-array String labels))] + (.get ^Counter$Child child))) + +(defn- mk-storage-object + [] + (let [storage (-> (:app.storage/storage th/*system*) + (configure-storage-backend))] + (sto/put-object! storage {::sto/content (sto/content "content") + :content-type "text/plain"}))) + +(defn- mk-job + [{:keys [status profile-id resource-id expires-at modified-at] + :or {status "new"}}] + (let [id (uuid/next)] + (th/db-insert! :job {:id id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status status + :profile-id profile-id + :resource-id resource-id + :expires-at expires-at + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (or modified-at (ct/now))}) + id)) + +(t/deftest gc-records-deleted-rows + (let [metrics (make-metrics) + cfg {::db/pool th/*pool* + ::mtx/metrics metrics}] + (th/db-insert! :job {:id (uuid/next) + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "completed" + :expires-at (ct/in-past {:minutes 5}) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + (gc/execute-jobs-gc cfg {}) + (t/is (= 1.0 (counter-value metrics ["expired" "deleted"]))))) + +(t/deftest job-rows-are-protected-against-unintended-deletes + (let [job-id (mk-job {:expires-at (ct/in-past {:minutes 5})})] + (t/testing "a stray DELETE is blocked by the deletion protection trigger" + (t/is (thrown-with-msg? Exception #"unable to proceed to delete row" + (th/db-exec! ["DELETE FROM job WHERE id = ?" job-id]))) + (t/is (some? (th/db-get :job {:id job-id} :id :status)))) + (t/testing "the jobs GC is the intended deleter and disables the guard" + (th/run-task! :jobs-gc {}) + (t/is (nil? (th/db-get :job {:id job-id} :id :status)))))) + +(t/deftest gc-deletes-expired-jobs-and-touches-their-resources + (let [old-object (mk-storage-object) + live-object (mk-storage-object) + expired-id (uuid/next) + live-id (uuid/next)] + (th/db-insert! :job {:id expired-id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "completed" + :resource-id (:id old-object) + :expires-at (ct/in-past {:minutes 5}) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + (th/db-insert! :job {:id live-id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "running" + :resource-id (:id live-object) + :expires-at (ct/in-future {:minutes 5}) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + + (th/run-task! :jobs-gc {}) + + (t/is (nil? (th/db-get :job {:id expired-id} :id :status))) + (t/is (some? (th/db-get :job {:id live-id} :id :status))) + (t/is (some? (:touched-at (th/db-get :storage-object {:id (:id old-object)} + :id :touched-at)))) + (t/is (nil? (:touched-at (th/db-get :storage-object {:id (:id live-object)} + :id :touched-at))))) + + ;; a row without resource is deleted without touching anything + (let [expired-id (uuid/next)] + (th/db-insert! :job {:id expired-id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "new" + :expires-at (ct/in-past {:minutes 5}) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + (th/run-task! :jobs-gc {}) + (t/is (nil? (th/db-get :job {:id expired-id} :id :status))))) + +(t/deftest gc-params-accept-string-min-age + (t/testing "duration strings validate like durations and millis" + (t/is ((sm/validator gc/schema:jobs-gc-params) {:min-age "1h"})) + (t/is ((sm/validator gc/schema:jobs-gc-params) {:min-age 3600000})) + (t/is ((sm/validator gc/schema:jobs-gc-params) {:min-age (ct/duration {:hours 1})}))) + (t/testing "string min-age runs like a duration" + (t/is (map? (th/run-task! :jobs-gc {:min-age "1h"}))))) + +(t/deftest submit-normalizes-durations-to-millis + (let [defs {:jobs-gc (ig/init-key ::gc/job-def {})} + cfg {::jobs/defs defs + ::db/pool th/*pool* + ::mtx/metrics (get th/*system* :app.metrics/metrics)} + job-id (jobs/submit cfg {::jobs/name :jobs-gc + ::jobs/params {:min-age (ct/duration {:hours 1})}})] + (t/testing "a Duration object does not reach JSON encoding" + (t/is (= 3600000 (:min-age (:params (jobs/get-job cfg job-id)))))))) + +(t/deftest gc-retention-deletes-old-internal-terminal-rows + (let [profile (th/create-profile* 1 {}) + ;; old internal terminal row -> deleted + old-id (uuid/next)] + (th/db-insert! :job {:id old-id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "completed" + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/in-past {:days 10})}) + ;; recent internal terminal row: inside the retention window, survives + (let [recent-id (uuid/next)] + (th/db-insert! :job {:id recent-id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "completed" + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + ;; non-terminal internal row: never swept by retention, survives + (let [running-id (uuid/next)] + (th/db-insert! :job {:id running-id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "running" + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/in-past {:days 10})}) + ;; old USER terminal row (profile set): retention does not sweep + ;; the user ledger, survives + (let [user-id (uuid/next)] + (th/db-insert! :job {:id user-id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "completed" + :profile-id (:id profile) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/in-past {:days 10})}) + (th/run-task! :jobs-gc {}) + + (t/is (nil? (th/db-get :job {:id old-id} :id :status))) + (t/is (= 3 (count (th/db-query :job {:queue "default"})))) + (t/is (some? (th/db-get :job {:id recent-id} :id :status))) + (t/is (some? (th/db-get :job {:id running-id} :id :status))) + (t/is (some? (th/db-get :job {:id user-id} :id :status)))))))) + +(t/deftest gc-retention-deletes-old-aborted-rows + "Aborted jobs are terminal like failed/cancelled: old internal aborted + rows are swept by retention, recent ones survive." + (let [old-id (uuid/next) + recent-id (uuid/next)] + (doseq [[id modified-at] [[old-id (ct/in-past {:days 10})] + [recent-id (ct/now)]]] + (th/db-insert! :job {:id id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "aborted" + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at modified-at})) + (th/run-task! :jobs-gc {}) + (t/is (nil? (th/db-get :job {:id old-id} :id :status))) + (t/is (some? (th/db-get :job {:id recent-id} :id :status))))) + +(t/deftest gc-retention-touches-resources-of-retained-rows + (let [object (mk-storage-object) + job-id (uuid/next)] + (th/db-insert! :job {:id job-id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "failed" + :resource-id (:id object) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/in-past {:days 10})}) + (th/run-task! :jobs-gc {}) + (t/is (nil? (th/db-get :job {:id job-id} :id :status))) + (t/is (some? (:touched-at (th/db-get :storage-object {:id (:id object)} + :id :touched-at)))))) + +(t/deftest gc-deletes-expired-jobs-in-bounded-batches + (let [total 2500] + (th/db-exec! [(str "INSERT INTO job (name, tenant, queue, status, expires_at) " + "SELECT 'test-job', '" (cf/get :tenant) "', 'default', 'completed', " + "now() - interval '1 hour' " + "FROM generate_series(1, " total ")")]) + ;; spread a few resources across batches so every batch touches + (let [objects (repeatedly 3 mk-storage-object)] + (doseq [[object n] (map vector objects (range))] + (th/db-exec! ["UPDATE job SET resource_id = ? + WHERE id = (SELECT id FROM job OFFSET ? LIMIT 1)" + (:id object) (* n 1000)])) + (let [{:keys [deleted-expired touched-expired]} (th/run-task! :jobs-gc {})] + (t/is (= total deleted-expired)) + (t/is (= (count objects) touched-expired)) + (t/is (= 0 (count (th/db-query :job {:queue "default"})))) + (doseq [object objects] + (t/is (some? (:touched-at (th/db-get :storage-object {:id (:id object)} + :id :touched-at))))))))) + +(t/deftest gc-commits-each-batch-separately + (let [total 2500 + calls (atom 0) + orig @#'gc/touch-resources] + (th/db-exec! [(str "INSERT INTO job (name, tenant, queue, status, expires_at) " + "SELECT 'test-job', '" (cf/get :tenant) "', 'default', 'completed', " + "now() - interval '1 hour' " + "FROM generate_series(1, " total ")")]) + ;; blow up on the 3rd batch: earlier batches must stay committed + (alter-var-root #'gc/touch-resources + (constantly (fn [conn ids] + (when (= 3 (swap! calls inc)) + (throw (ex-info "boom" {}))) + (orig conn ids)))) + (try + (t/is (thrown? Exception (th/run-task! :jobs-gc {}))) + (t/is (= 500 (count (th/db-query :job {:queue "default"})))) + (finally + (alter-var-root #'gc/touch-resources (constantly orig)))) + ;; the next tick completes the sweep + (let [{:keys [deleted-expired]} (th/run-task! :jobs-gc {})] + (t/is (= 500 deleted-expired)) + (t/is (zero? (count (th/db-query :job {:queue "default"}))))))) + +(t/deftest gc-expiration-skips-active-rows + (let [running-id (uuid/next) + retry-id (uuid/next)] + (doseq [[id status] [[running-id "running"] [retry-id "retry"]]] + (th/db-insert! :job {:id id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status status + :expires-at (ct/in-past {:minutes 5}) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)})) + (th/run-task! :jobs-gc {}) + (t/testing "active rows survive expiration" + (t/is (some? (th/db-get :job {:id running-id} :id :status))) + (t/is (some? (th/db-get :job {:id retry-id} :id :status)))))) diff --git a/backend/test/backend_tests/jobs_metrics_test.clj b/backend/test/backend_tests/jobs_metrics_test.clj new file mode 100644 index 0000000000..7b2523c320 --- /dev/null +++ b/backend/test/backend_tests/jobs_metrics_test.clj @@ -0,0 +1,349 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.jobs-metrics-test + (:require + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.jobs.metrics :as jobs-metrics] + [app.main :as main] + [app.metrics :as mtx] + [app.metrics.definition :as-alias mdef] + [backend-tests.helpers :as th] + [clojure.test :as t] + [cuerdas.core :as str] + [integrant.core :as ig]) + (:import + io.prometheus.client.Collector$MetricFamilySamples + io.prometheus.client.Collector$MetricFamilySamples$Sample + io.prometheus.client.CollectorRegistry + io.prometheus.client.Counter + io.prometheus.client.Counter$Child + io.prometheus.client.Gauge + io.prometheus.client.Gauge$Child)) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + +(def ^:private metric-ids + [:jobs-submitted + :jobs-skipped + :jobs-dispatched + :jobs-retries + :jobs-orphaned + :jobs-rescheduled + :jobs-queue-wait-timing + :jobs-execution-timing + :jobs-total-timing + :jobs-dispatcher-timing + :jobs-dispatcher-batch-size + :jobs-backlog + :jobs-oldest-pending-age + :jobs-gc-rows + :jobs-gc-timing + :jobs-cron-total + :jobs-request-timing + :jobs-events-total]) + +;; The legacy histogram is registered too, so it can be asserted, but it is +;; not part of the `penpot_jobs_` contract the test above checks. +(def ^:private registry-ids (conj metric-ids :tasks-timing)) + +(defn- make-metrics [] + (ig/init-key :app.metrics/metrics + {:default (select-keys main/default-metrics registry-ids)})) + +(defn- metrics-cfg + "The cfg the `record-*` functions resolve the instance from. They take + a cfg, not an instance, so this is what a caller hands them." + [metrics] + {::mtx/metrics metrics}) + +(defn- counter-value [metrics id labels] + (let [collector (mtx/get-collector metrics id) + instance (::mdef/instance collector) + child (.labels ^Counter instance (into-array String labels))] + (.get ^Counter$Child child))) + +(defn- histogram-count + "How many observations a histogram has recorded for an exact set of + labels, read off the registry so the label set itself is checked." + [metrics id labels] + (let [definition (get main/default-metrics id)] + (->> (enumeration-seq + (.metricFamilySamples ^CollectorRegistry (mtx/get-registry metrics))) + (filter (fn [^Collector$MetricFamilySamples family] + (= (.-name family) (::mdef/name definition)))) + (mapcat (fn [^Collector$MetricFamilySamples family] + (.samples family))) + (filter (fn [^Collector$MetricFamilySamples$Sample sample] + (and (str/ends-with? (.-name sample) "_count") + (= labels (vec (.-labelValues sample)))))) + (map (fn [^Collector$MetricFamilySamples$Sample sample] (.-value sample))) + ;; 0.0 and not 0: an empty sum must compare equal to the 0.0 the + ;; assertions use, and (= 0 0.0) is false in Clojure + (reduce + 0.0)))) + +(defn- gauge-value [metrics id labels] + (let [collector (mtx/get-collector metrics id) + instance (::mdef/instance collector) + child (.labels ^Gauge instance (into-array String labels))] + (.get ^Gauge$Child child))) + +(t/deftest default-metrics-cover-the-jobs-observability-contract + (doseq [id metric-ids] + (let [definition (get main/default-metrics id)] + (t/is (some? definition) id) + (t/is (re-find #"^penpot_jobs_" (::mdef/name definition)) id))) + (t/is (= ["name" "queue" "outcome"] + (::mdef/labels (:jobs-total-timing main/default-metrics)))) + (t/is (= ["status"] + (::mdef/labels (:jobs-backlog main/default-metrics))))) + +(t/deftest submit-metric-is-not-recorded-when-the-outer-transaction-rolls-back + (let [metrics (make-metrics) + defs {:echo {::jobs/name :echo + ::jobs/schema [:map] + ::jobs/handler (fn [_context params] params) + ::jobs/decoder identity + ::jobs/validator (constantly true)}} + cfg {::jobs/defs defs + ::db/pool th/*pool* + ::mtx/metrics metrics}] + (t/is (thrown? Exception + (db/tx-run! cfg + (fn [tx-cfg] + (jobs/submit tx-cfg + {::jobs/name :echo + ::jobs/params {}}) + (throw (ex-info "rollback" {})))))) + (t/is (= 0.0 (counter-value metrics :jobs-submitted ["echo" "default"]))))) + +(t/deftest submit-and-terminal-writers-record-production-events + (let [metrics (make-metrics) + defs {:echo {::jobs/name :echo + ::jobs/schema [:map] + ::jobs/handler (fn [_context params] params) + ::jobs/decoder identity + ::jobs/validator (constantly true)}} + cfg {::jobs/defs defs + ::db/pool th/*pool* + ::mtx/metrics metrics} + job-id (jobs/submit cfg {::jobs/name :echo ::jobs/params {}})] + (jobs/claim cfg job-id (:scheduled-at (jobs/get-job cfg job-id))) + (jobs/complete cfg :job-id job-id :result {:ok true}) + (t/is (= 1.0 (counter-value metrics :jobs-submitted ["echo" "default"]))) + (t/is (= 1.0 (histogram-count metrics :jobs-total-timing ["echo" "default" "completed"]))))) + +(t/deftest skip-if-active-records-skipped-metric + (let [metrics (make-metrics) + defs {:echo {::jobs/name :echo + ::jobs/schema [:map] + ::jobs/handler (fn [_context params] params) + ::jobs/decoder identity + ::jobs/validator (constantly true)}} + cfg {::jobs/defs defs + ::db/pool th/*pool* + ::mtx/metrics metrics} + _ (jobs/submit cfg {::jobs/name :echo + ::jobs/params {} + ::jobs/label "skip-metric"}) + skipped (jobs/submit cfg {::jobs/name :echo + ::jobs/params {} + ::jobs/label "skip-metric" + ::jobs/skip-if-active true})] + (t/is (nil? skipped)) + (t/is (= 1.0 (counter-value metrics :jobs-skipped ["echo" "default"]))) + (t/is (= 1.0 (counter-value metrics :jobs-submitted ["echo" "default"])) + "the withheld submit records no submission"))) + +(t/deftest lifecycle-helpers-use-labels-as-is + (let [metrics (make-metrics) + cfg (metrics-cfg metrics)] + (jobs-metrics/record-submitted cfg "delete-object" "default") + (jobs-metrics/record-skipped cfg "delete-object" "default") + (jobs-metrics/record-dispatched cfg "delete-object" "default" 2) + (jobs-metrics/record-total cfg "delete-object" "default" :completed 42) + (jobs-metrics/record-retry cfg "delete-object" "default" :backoff) + (jobs-metrics/record-orphan cfg "webhooks") + (jobs-metrics/record-rescheduled cfg "custom") + (jobs-metrics/record-gc-rows cfg :expired :deleted 3) + (jobs-metrics/record-cron cfg :submitted :none) + (jobs-metrics/record-request cfg :replied 12) + (jobs-metrics/record-legacy-execution cfg "delete-object" 42) + + (t/is (= 1.0 (counter-value metrics :jobs-submitted ["delete-object" "default"]))) + (t/is (= 1.0 (counter-value metrics :jobs-skipped ["delete-object" "default"]))) + (t/is (= 2.0 (counter-value metrics :jobs-dispatched ["delete-object" "default"]))) + (t/is (= 1.0 (histogram-count metrics :jobs-total-timing ["delete-object" "default" "completed"]))) + (t/is (= 1.0 (counter-value metrics :jobs-retries ["delete-object" "default" "backoff"]))) + (t/is (= 1.0 (counter-value metrics :jobs-orphaned ["webhooks"]))) + (t/is (= 1.0 (counter-value metrics :jobs-rescheduled ["custom"]))) + (t/is (= 3.0 (counter-value metrics :jobs-gc-rows ["expired" "deleted"]))) + (t/is (= 1.0 (counter-value metrics :jobs-cron-total ["submitted" "none"]))) + (t/is (= 1.0 (histogram-count metrics :jobs-request-timing ["replied"]))) + + (t/testing "the legacy histogram keeps the raw job name, with no queue" + (t/is (= 1.0 (histogram-count metrics :tasks-timing ["delete-object"])))) + (t/testing "like every other jobs metric now, it does not fold an unknown name into \"other\"" + (jobs-metrics/record-legacy-execution cfg :brand-new-job 7) + (t/is (= 1.0 (histogram-count metrics :tasks-timing ["brand-new-job"]))) + (t/is (= 0.0 (histogram-count metrics :tasks-timing ["other"])))))) + +(t/deftest labels-go-to-metrics-as-is + (t/testing "queues are lowercased, with other only when missing" + (t/is (= "unexpected" (jobs-metrics/queue-label "unexpected"))) + (t/is (= "default" (jobs-metrics/queue-label "default"))) + (t/is (= "default" (jobs-metrics/queue-label :default))) + (t/is (= "other" (jobs-metrics/queue-label nil)))) + (let [metrics (make-metrics) + cfg (metrics-cfg metrics)] + (jobs-metrics/record-total cfg "echo" "unexpected" :unexpected 42) + (t/is (= 1.0 (histogram-count metrics :jobs-total-timing ["echo" "unexpected" "unexpected"]))) + (t/testing "a new value is recorded, never folded or dropped" + (jobs-metrics/record-gc-rows cfg :unknown :deleted 1) + (jobs-metrics/record-cron cfg :brand-new :whatever) + (jobs-metrics/record-request cfg :brand-new 5) + (t/is (= 1.0 (counter-value metrics :jobs-gc-rows ["unknown" "deleted"]))) + (t/is (= 1.0 (counter-value metrics :jobs-cron-total ["brand-new" "whatever"]))) + (t/is (= 1.0 (histogram-count metrics :jobs-request-timing ["brand-new"])))))) + +(t/deftest sampler-is-wired-and-does-not-start-on-read-only + (t/is (contains? main/worker-config :app.jobs.metrics/sampler)) + (let [metrics (make-metrics) + cfg {::db/pool th/*pool* + ::mtx/metrics metrics}] + (with-redefs [db/read-only? (constantly true)] + (t/is (nil? (ig/init-key :app.jobs.metrics/sampler cfg)))) + (let [sampler (ig/init-key :app.jobs.metrics/sampler cfg)] + (try + (t/is (some? sampler)) + (finally + (ig/halt-key! :app.jobs.metrics/sampler sampler)))))) + +(t/deftest unknown-names-go-to-metrics-as-is + (let [metrics (make-metrics) + cfg (metrics-cfg metrics)] + (jobs-metrics/record-submitted cfg "unknown-job" "default") + (t/is (= 1.0 (counter-value metrics :jobs-submitted ["unknown-job" "default"]))))) + +(t/deftest backlog-sampler-updates-status-and-age-gauges + (let [metrics (make-metrics) + cfg {::db/pool th/*pool* + ::mtx/metrics metrics} + now (ct/now)] + (th/db-insert! :job {:id (uuid/next) + :name "echo" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "new" + :scheduled-at now + :created-at (ct/in-past {:minutes 2}) + :modified-at now}) + (jobs-metrics/sample-backlog cfg) + (t/is (= 1.0 (gauge-value metrics :jobs-backlog ["new"]))) + (t/is (= 0.0 (gauge-value metrics :jobs-backlog ["completed"]))) + (t/is (= 0.0 (gauge-value metrics :jobs-backlog ["aborted"]))) + (t/is (>= (gauge-value metrics :jobs-oldest-pending-age []) 120.0)))) + +(t/deftest event-counter-has-no-labels-and-follows-the-events + (let [metrics (make-metrics) + cfg {::db/pool th/*pool* + ::mtx/metrics metrics} + defs {:echo {::jobs/name :echo + ::jobs/schema [:map] + ::jobs/handler (fn [_context params] params) + ::jobs/decoder identity + ::jobs/validator (constantly true)}} + cfg2 (assoc cfg ::jobs/defs defs) + job-id (jobs/submit cfg2 {::jobs/name :echo ::jobs/params {}})] + (jobs/claim cfg2 job-id (:scheduled-at (jobs/get-job cfg2 job-id))) + (jobs/complete cfg2 :job-id job-id :result {:ok true}) + (t/testing "one start and one end, counted together with no labels" + (t/is (= 2.0 (counter-value metrics :jobs-events-total [])))) + + (t/testing "a transition that affects no row adds no event and no count" + (t/is (= 0 (jobs/complete cfg2 :job-id job-id :result {:ok true}))) + (t/is (= 2.0 (counter-value metrics :jobs-events-total [])))))) + +(t/deftest event-counter-follows-a-lifecycle-write-that-outlives-the-caller + (let [metrics (make-metrics) + defs {:echo {::jobs/name :echo + ::jobs/schema [:map] + ::jobs/handler (fn [_context params] params) + ::jobs/decoder identity + ::jobs/validator (constantly true)}} + cfg {::jobs/defs defs + ::db/pool th/*pool* + ::mtx/metrics metrics} + job-id (jobs/submit cfg {::jobs/name :echo ::jobs/params {}})] + (jobs/claim cfg job-id (:scheduled-at (jobs/get-job cfg job-id))) + (t/is (= 1.0 (counter-value metrics :jobs-events-total []))) + + ;; the completion runs inside a transaction that rolls back. It owns + ;; its own, so the event and both counters survive the caller. + (db/tx-run! (assoc cfg ::db/rollback true) + (fn [{:keys [::db/conn]}] + (jobs/complete (assoc cfg ::db/conn conn) + :job-id job-id :result {:ok true}))) + + (t/testing "the end event is stored and counted" + (t/is (= 2.0 (counter-value metrics :jobs-events-total []))) + (t/is (= 2 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job_event"]))))) + + (t/testing "the terminal counter followed its own commit, not the caller one" + (t/is (= 1.0 (histogram-count metrics :jobs-total-timing ["echo" "default" "completed"])))))) + +(t/deftest progress-counter-follows-the-report-that-outlives-the-caller + (let [metrics (make-metrics) + defs {:echo {::jobs/name :echo + ::jobs/schema [:map] + ::jobs/handler (fn [_context params] params) + ::jobs/decoder identity + ::jobs/validator (constantly true)}} + cfg {::jobs/defs defs + ::db/pool th/*pool* + ::mtx/metrics metrics} + job-id (jobs/submit cfg {::jobs/name :echo ::jobs/params {}})] + (jobs/claim cfg job-id (:scheduled-at (jobs/get-job cfg job-id))) + (let [before (counter-value metrics :jobs-events-total [])] + (db/tx-run! (assoc cfg ::db/rollback true) + (fn [{:keys [::db/conn]}] + (jobs/heartbeat (assoc cfg ::db/conn conn) + :job-id job-id + :progress {:stage :work} + ::jobs/force? true))) + (t/testing "the report is stored and counted, the caller rollback drops neither" + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job_event + WHERE job_id = ? AND kind = 'progress'" + job-id])))) + (t/is (= (inc before) (counter-value metrics :jobs-events-total []))))))) + +(t/deftest the-binfile-queue-is-labelled-like-any-other + (let [metrics (make-metrics) + defs {:export-binfile {::jobs/name :export-binfile + ::jobs/schema [:map] + ::jobs/handler (fn [_context params] params) + ::jobs/decoder identity + ::jobs/validator (constantly true)}} + cfg {::jobs/defs defs + ::db/pool th/*pool* + ::mtx/metrics metrics}] + + (jobs/submit cfg {::jobs/name :export-binfile + ::jobs/params {} + ::jobs/queue :binfile}) + + (t/testing "the queue of the job is its label, never other" + (t/is (= 1.0 (counter-value metrics :jobs-submitted ["export-binfile" "binfile"]))) + (t/is (= 0.0 (counter-value metrics :jobs-submitted ["export-binfile" "other"])))))) diff --git a/backend/test/backend_tests/jobs_request_test.clj b/backend/test/backend_tests/jobs_request_test.clj new file mode 100644 index 0000000000..976102def1 --- /dev/null +++ b/backend/test/backend_tests/jobs_request_test.clj @@ -0,0 +1,315 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.jobs-request-test + (:require + [app.common.generic-pool :as gpool] + [app.common.json :as json] + [app.common.time :as ct] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.main :as main] + [app.metrics :as mtx] + [app.metrics.definition :as-alias mdef] + [app.redis :as rds] + [app.worker :as wrk] + [backend-tests.helpers :as th] + [clojure.test :as t] + [cuerdas.core :as str] + [integrant.core :as ig]) + (:import + io.lettuce.core.api.sync.RedisCommands + java.lang.AutoCloseable)) + +(t/use-fixtures :once th/state-init) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; HELPERS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- make-metrics [] + (ig/init-key :app.metrics/metrics + {:default (select-keys main/default-metrics + [:jobs-request-timing])})) + +(defn- make-pool + ([] (make-pool (get th/*system* :app.metrics/metrics))) + ([metrics] + (-> {::jobs/request-pool {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics metrics}} + (ig/expand) + (ig/init) + (get ::jobs/request-pool)))) + +(defn- queue-key + [queue] + (wrk/queue-key (cf/get :tenant) queue)) + +(defn- make-cfg + [pool] + {::jobs/request-pool pool}) + +(defn- clear-queues + [] + (let [conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)})] + (try + (let [cmd (.-cmd conn) + keys (vec (.keys ^RedisCommands cmd + (str jobs/reply-key-prefix ":" (cf/get :tenant) ":*")))] + (when (seq keys) + (rds/del conn keys)) + (rds/del conn (queue-key :media))) + (finally + (rds/close conn))))) + +(defn- test-fixture [next] + (th/database-reset + (fn [] + (clear-queues) + (next)))) + +(t/use-fixtures :each test-fixture) + +(defn- with-responder + "Start a responder on :media that answers the next request with the + provided response map (usually {:ok ...} or {:error ...}). Returns a + future that resolves to the decoded request payload [request-id, + reply-key, cmd, params]." + [pool response-fn & {:keys [delay-ms timeout-ms]}] + (let [timeout-ms (or timeout-ms 10000) + out (promise)] + (let [fut (future + (let [conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)})] + (try + (when delay-ms + (Thread/sleep (long delay-ms))) + (let [key (queue-key :media) + [_ payload] (rds/blpop conn [key] (ct/duration {:millis timeout-ms}))] + (if (nil? payload) + (deliver out ::no-request) + (let [[request-id reply-key cmd params :as decoded] (json/decode payload)] + (deliver out decoded) + (jobs/reply {::rds/pool (::jobs/pool pool) + ::mtx/metrics (::mtx/metrics pool)} + reply-key + (response-fn params))))) + (finally + (rds/close conn)))))] + {:future fut + :payload out}))) + +(defn- get-ttl + [key] + (let [conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)})] + (try + (let [cmd (.-cmd conn)] + (.ttl ^RedisCommands cmd key)) + (finally + (rds/close conn))))) + +(defn- key-exists? + [key] + (let [conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)})] + (try + (let [cmd (.-cmd conn)] + (pos? (.exists ^RedisCommands cmd (make-array String 1 key)))) + (finally + (rds/close conn))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- histogram-count [metrics id labels] + (let [definition (get main/default-metrics id)] + (->> (enumeration-seq + (.metricFamilySamples ^io.prometheus.client.CollectorRegistry + (mtx/get-registry metrics))) + (filter (fn [^io.prometheus.client.Collector$MetricFamilySamples family] + (= (.-name family) (::mdef/name definition)))) + (mapcat (fn [^io.prometheus.client.Collector$MetricFamilySamples family] + (.samples family))) + (filter (fn [^io.prometheus.client.Collector$MetricFamilySamples$Sample sample] + (and (str/ends-with? (.-name sample) "_count") + (= labels (vec (.-labelValues sample)))))) + (map (fn [^io.prometheus.client.Collector$MetricFamilySamples$Sample sample] + (.-value sample))) + (reduce + 0.0)))) + +(t/deftest request-pool-keeps-its-own-metrics + (let [metrics-a (make-metrics) + metrics-b (make-metrics) + pool-a (make-pool metrics-a) + pool-b (make-pool metrics-b)] + (t/is (identical? metrics-a (::mtx/metrics pool-a))) + (t/is (identical? metrics-b (::mtx/metrics pool-b))))) + +(t/deftest request-metrics-use-the-final-outcome + (let [metrics (make-metrics) + pool (make-pool metrics) + cfg (make-cfg pool)] + (with-responder pool (fn [_params] {:ok {:value 1}})) + (t/is (= {:value 1} + (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {}}))) + (t/is (= 1.0 (histogram-count metrics :jobs-request-timing ["replied"]))) + (t/is (thrown-with-msg? Exception #"timeout waiting for the job reply" + (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {} + ::jobs/timeout (ct/duration {:millis 100})}))) + (t/is (= 1.0 (histogram-count metrics :jobs-request-timing ["timeout"]))) + (t/is (= 0.0 (histogram-count metrics :jobs-request-timing ["sent"]))))) + +(t/deftest request-returns-the-ok-reply + (let [pool (make-pool) + cfg (make-cfg pool) + payload (with-responder pool (fn [_params] {:ok {:value 42}}))] + + (let [result (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {:x 1}})] + (t/is (= {:value 42} result))) + + (t/testing "the worker receives the expected payload shape" + (let [[request-id reply-key cmd params :as decoded] (deref (:payload payload) 10000 ::timeout)] + (t/is (string? request-id)) + (t/is (string? reply-key)) + (t/is (str/starts-with? reply-key (str jobs/reply-key-prefix ":" (cf/get :tenant) ":"))) + (t/is (= "process" cmd)) + (t/is (= {"x" 1} params) + (str "params should be plain JSON: " (pr-str decoded))))))) + +(t/deftest request-propagates-the-error-reply + (let [pool (make-pool) + cfg (make-cfg pool) + _ (with-responder pool + (fn [_params] + {:error {:code :processing-failed + :hint "something went wrong"}}))] + (t/is (thrown-with-msg? Exception #"something went wrong" + (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {:x 1}}))))) + +(t/deftest request-times-out-and-cleans-the-reply-key + (let [pool (make-pool) + cfg (make-cfg pool)] + (t/is (thrown-with-msg? Exception #"timeout waiting for the job reply" + (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {:x 1} + ::jobs/timeout (ct/duration {:millis 500})}))) + + (t/testing "no orphan reply key is left behind" + ;; there is no way to know the exact reply key (random request-id); + ;; check no reply keys exist at all after the timeout + (let [conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)})] + (try + (let [cmd (.-cmd conn) + keys (.keys ^RedisCommands cmd + (str jobs/reply-key-prefix ":" (cf/get :tenant) ":*"))] + (t/is (empty? (vec keys)))) + (finally + (rds/close conn))))))) + +(t/deftest request-pool-returns-connections-on-success-and-failure + (let [pool (make-pool) + cfg (make-cfg pool) + default-timeout (-> (cf/get-jobs-request-timeout) + (ct/plus (ct/duration {:seconds 30})))] + + (t/testing "successful request does not consume the connection" + (with-responder pool (fn [_params] {:ok {:a 1}})) + (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {:x 1}}) + + (t/testing "and the next request works with a pooled connection" + (with-responder pool (fn [_params] {:ok {:b 2}})) + (t/is (= {:b 2} (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {:x 2}}))) + + (t/testing "connection command timeout is restored after use" + (with-open [^AutoCloseable pooled (gpool/get (::jobs/pool pool))] + (let [conn @pooled] + (t/is (= (.toMillis ^java.time.Duration default-timeout) + (.toMillis ^java.time.Duration (rds/get-timeout conn))))))))))) + +(t/deftest request-supports-larger-per-call-timeout + (let [pool (make-pool) + cfg (make-cfg pool) + _ (with-responder pool (fn [_params] {:ok {:slow 1}}))] + + (t/testing "an override bigger than the default works (command timeout raised per call)" + (t/is (= {:slow 1} + (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {:x 1} + ::jobs/timeout (ct/duration {:seconds 130})})))))) + +(t/deftest request-raises-connection-timeout-to-per-call-override + (let [pool (make-pool) + cfg (make-cfg pool) + seen (atom nil) + orig rds/set-timeout + override (ct/duration {:seconds 999}) + _ (with-responder pool (fn [_params] {:ok {:fast 1}}))] + ;; an override above the pool default is honored, not capped + (with-redefs [rds/set-timeout (fn [conn timeout] + (reset! seen timeout) + (orig conn timeout))] + (t/is (= {:fast 1} + (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {:x 1} + ::jobs/timeout override})))) + (t/is (= (.toMillis ^java.time.Duration (ct/plus override (ct/duration {:seconds 30}))) + (.toMillis ^java.time.Duration @seen))))) + +(t/deftest late-reply-is-cleaned-by-the-worker-expire + (let [pool (make-pool) + cfg (make-cfg pool) + ;; the responder replies 2s after the caller timeout (500ms) + responder (with-responder pool + (fn [_params] {:ok {:late 1}}) + :delay-ms 2000)] + + (t/is (thrown-with-msg? Exception #"timeout waiting for the job reply" + (jobs/request cfg {::jobs/queue :media + ::jobs/cmd :process + ::jobs/params {:x 1} + ::jobs/timeout (ct/duration {:millis 500})}))) + + (t/testing "the late reply key exists (recreated by the worker) with a short TTL" + (deref (:future responder) 10000 ::timeout) + (let [conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)})] + (try + (let [cmd (.-cmd conn) + keys (vec (.keys ^RedisCommands cmd + (str jobs/reply-key-prefix ":" (cf/get :tenant) ":*")))] + (t/is (= 1 (count keys))) + (let [ttl (.ttl ^RedisCommands cmd (first keys))] + (t/is (pos? ttl)) + (t/is (<= ttl 60)))) + (finally + (rds/close conn))))))) + +(t/deftest request-creates-no-job-row + (let [pool (make-pool) + cfg (make-cfg pool) + _ (with-responder pool (fn [_params] {:ok {}}))] + (jobs/request cfg {::jobs/queue :media ::jobs/cmd :process ::jobs/params {}}) + (t/is (zero? (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job"])))))) diff --git a/backend/test/backend_tests/jobs_storage_test.clj b/backend/test/backend_tests/jobs_storage_test.clj new file mode 100644 index 0000000000..3da1f29525 --- /dev/null +++ b/backend/test/backend_tests/jobs_storage_test.clj @@ -0,0 +1,205 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.jobs-storage-test + "The storage helpers of the unified jobs substrate: the objects a job + owns through `job.resource_id`." + (:require + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs.storage :as js] + [app.storage :as sto] + [backend-tests.helpers :as th] + [clojure.test :as t] + [cuerdas.core :as str] + [datoteka.fs :as fs])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each (th/serial th/database-reset th/clean-storage)) + +(defn- get-storage + [] + (assoc (:app.storage/storage th/*system*) ::sto/backend :fs)) + +(defn- make-cfg + [] + {::db/pool th/*pool* + ::sto/storage (get-storage)}) + +(defn- get-object-row + "The `storage_object` row, deleted rows included." + [id] + (th/db-get :storage-object {:id id} {::db/remove-deleted false})) + +(defn- put-artifact + [cfg profile-id content] + (js/put-resource cfg profile-id {:content (sto/content content) + :filename "artifact.penpot" + :mtype "application/zip"})) + +(t/deftest put-resource-stores-the-artifact-for-its-owner + (let [cfg (make-cfg) + profile-id (:id (th/create-profile* 1)) + result (put-artifact cfg profile-id "penpot-bytes") + object (sto/get-object (get-storage) (:resource-id result)) + row (get-object-row (:resource-id result))] + + (t/testing "the object lives in the job-resource bucket" + (t/is (= sto/job-resource-bucket (:bucket (meta object))))) + + (t/testing "the owner and the content type travel in the metadata" + (t/is (= profile-id (:profile-id (meta object)))) + (t/is (= "application/zip" (:content-type (meta object))))) + + (t/testing "the object is readable: retention comes from the job row" + (t/is (nil? (:deleted-at row)))) + + (t/testing "it is touched right away so a crash leaves it reclaimable" + (t/is (some? (:touched-at row)))) + + (t/testing "the descriptor identifies the artifact" + (t/is (uuid? (:resource-id result))) + (t/is (= 12 (:size result))) + (t/is (= "artifact.penpot" (:filename result))) + (t/is (= "application/zip" (:mtype result))) + (t/is (str/includes? (str (:resource-uri result)) + (str (:resource-id result))))))) + +(t/deftest put-resource-requires-an-owner + (let [cfg (make-cfg)] + (t/testing "an artifact without an owner is a caller bug" + (t/is (thrown? Throwable + (js/put-resource cfg nil {:content (sto/content "x") + :filename "x.penpot" + :mtype "application/zip"})))))) + +(t/deftest load-input-gives-the-handler-a-local-path + (let [cfg (make-cfg) + profile-id (:id (th/create-profile* 1)) + staged (put-artifact cfg profile-id "package-bytes") + context {:id (uuid/next) + :resource-id (:resource-id staged) + :profile-id profile-id} + path (js/load-input cfg context)] + + (t/is (some? path)) + (t/is (= "package-bytes" (slurp (fs/file path)))))) + +(t/deftest load-input-refuses-an-input-of-another-profile + (let [cfg (make-cfg) + owner (:id (th/create-profile* 1)) + stranger (:id (th/create-profile* 2)) + staged (put-artifact cfg owner "package-bytes") + context {:id (uuid/next) + :resource-id (:resource-id staged) + :profile-id stranger}] + + (t/testing "a handler never reads a package that is not its job's" + (t/is (thrown? Exception (js/load-input cfg context)))))) + +(t/deftest load-input-refuses-a-missing-or-released-input + (let [cfg (make-cfg) + profile-id (:id (th/create-profile* 1)) + staged (put-artifact cfg profile-id "package-bytes") + context {:id (uuid/next) + :resource-id (:resource-id staged) + :profile-id profile-id}] + + (t/testing "a job without a resource reference has nothing to load" + (t/is (thrown? Exception (js/load-input cfg (dissoc context :resource-id))))) + + (t/testing "an input that was already released cannot be read again" + (t/is (true? (js/release-input cfg context))) + (t/is (thrown? Exception (js/load-input cfg context)))))) + +(t/deftest release-input-is-idempotent-and-never-throws + (let [cfg (make-cfg) + profile-id (:id (th/create-profile* 1)) + staged (put-artifact cfg profile-id "package-bytes") + context {:id (uuid/next) + :resource-id (:resource-id staged) + :profile-id profile-id}] + + (t/is (true? (js/release-input cfg context))) + (t/is (some? (:deleted-at (get-object-row (:resource-id staged))))) + + (t/testing "releasing twice is a no-op, not an error" + (t/is (false? (js/release-input cfg context)))) + + (t/testing "releasing a job without a resource is a no-op too" + (t/is (nil? (js/release-input cfg (dissoc context :resource-id))))))) + +(t/deftest release-input-clears-the-reference-of-the-job + ;; a job that no longer owns its package must not keep pointing at a + ;; deleted object: the object and the pointer are dropped together + (let [cfg (make-cfg) + profile-id (:id (th/create-profile* 1)) + staged (put-artifact cfg profile-id "package-bytes") + job-id (uuid/next) + context {:id job-id + :resource-id (:resource-id staged) + :profile-id profile-id}] + + (th/db-insert! :job {:id job-id + :name "import-binfile" + :tenant (cf/get :tenant) + :queue "binfile" + :params (db/json {}) + :priority 100 + :max-retries 0 + :retry-num 0 + :status "running" + :profile-id profile-id + :resource-id (:resource-id staged) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + + (t/is (true? (js/release-input cfg context))) + + (t/testing "the object is marked as deleted" + (t/is (some? (:deleted-at (get-object-row (:resource-id staged)))))) + + (t/testing "and the job row no longer points at it" + (t/is (nil? (:resource-id (th/db-get :job {:id job-id}))))) + + (t/testing "releasing twice leaves the row alone" + (t/is (false? (js/release-input cfg context))) + (t/is (nil? (:resource-id (th/db-get :job {:id job-id}))))))) + +(t/deftest release-resource-releases-a-package-no-job-owns + ;; the creation path stages the package before the job exists: when the + ;; submit fails the package is released with no job id, so the object + ;; goes and no row may be touched by it + (let [cfg (make-cfg) + profile-id (:id (th/create-profile* 1)) + staged (put-artifact cfg profile-id "package-bytes") + job-id (uuid/next)] + (th/db-insert! :job {:id job-id + :name "import-binfile" + :tenant (cf/get :tenant) + :queue "binfile" + :params (db/json {}) + :priority 100 + :max-retries 0 + :retry-num 0 + :status "running" + :profile-id profile-id + :resource-id nil + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + + (let [before (th/db-get :job {:id job-id})] + (t/is (true? (js/release-resource cfg (:resource-id staged)))) + + (t/testing "the object is marked as deleted" + (t/is (some? (:deleted-at (get-object-row (:resource-id staged)))))) + + (t/testing "and the unrelated job row is exactly as it was" + (t/is (= before (th/db-get :job {:id job-id}))))))) diff --git a/backend/test/backend_tests/jobs_test.clj b/backend/test/backend_tests/jobs_test.clj new file mode 100644 index 0000000000..15c7e6d83b --- /dev/null +++ b/backend/test/backend_tests/jobs_test.clj @@ -0,0 +1,1782 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.jobs-test + (:require + [app.common.exceptions :as ex] + [app.common.schema :as sm] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.metrics :as-alias mtx] + [app.msgbus :as mbus] + [backend-tests.helpers :as th] + [clojure.test :as t] + [cuerdas.core :as str] + [integrant.core :as ig])) + +(t/use-fixtures :once th/state-init) + +(defn- test-fixture [next] + (th/database-reset next)) + +(t/use-fixtures :each test-fixture) + +(defn- raised + "Run `f` and answer with the exception it raised, or nil when it did not." + [f] + (try (f) nil (catch Throwable cause cause))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; JOB-DEF (plain handler + precompiled init-key) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn echo-handler + "Plain handler function, importable and testable without integrant. + Dual behavior: when invoked in-process (no job-id in cfg), heartbeat/progress + are no-ops. When invoked via the runner (with job-id in cfg), they write to + the job row. This allows the same handler to be tested both ways." + [cfg params] + (when (::jobs/job-id cfg) + (jobs/heartbeat cfg) + (jobs/heartbeat cfg :progress {:stage :half + :counters {:work {:current 1 :total 2}}})) + params) + +(def schema:echo-params + [:map + [:text ::sm/text] + [:object :keyword] + [:deleted-at ::ct/inst] + [:id ::sm/uuid] + [:file-id {:optional true} ::sm/uuid]]) + +(defmethod ig/init-key ::echo-job + [_ cfg] + {::jobs/name :echo + ::jobs/schema schema:echo-params + ::jobs/handler (fn [_context params] + (echo-handler cfg params)) + ::jobs/decoder (sm/decoder schema:echo-params sm/json-transformer) + ::jobs/validator (sm/validator schema:echo-params)}) + +(def schema:typed-result + "A result with a uuid keyed map, the shape that needs a declared schema + to survive the plain JSON round trip of the `job.result` column." + [:map + [:file-ids [:vector ::sm/uuid]] + [:resolution [:map-of ::sm/uuid [:map [:id ::sm/uuid]]]]]) + +(defmethod ig/init-key ::typed-job + [_ cfg] + {::jobs/name :typed + ::jobs/schema schema:echo-params + ::jobs/handler (fn [_context params] + (echo-handler cfg params)) + ::jobs/decoder (sm/decoder schema:echo-params sm/json-transformer) + ::jobs/validator (sm/validator schema:echo-params) + ;; the substrate compiles the decoder from this schema and checks the + ;; result against it before storing it + ::jobs/result-schema schema:typed-result}) + +(defn- get-job-defs + [] + (-> {::jobs/defs {:echo (ig/ref ::echo-job) + :typed (ig/ref ::typed-job)} + ::echo-job {::db/pool th/*pool*} + ::typed-job {::db/pool th/*pool*}} + (ig/expand) + (ig/init) + (get ::jobs/defs))) + +(defn- make-cfg + [defs] + {::jobs/defs defs + ::db/pool th/*pool* + ::mtx/metrics (get th/*system* :app.metrics/metrics)}) + +(defn- make-params + [] + {:text "hello" + :object :snapshot + :deleted-at (ct/now) + :id (uuid/next) + :file-id (uuid/next)}) + +(defn- insert-job-statement? + "True when a `db/exec-one!` call is the job INSERT, whatever the + argument shape (a `[sql & params]` vector or a bare SQL string)." + [args] + (let [stmt (second args) + sql (if (string? stmt) stmt (first stmt))] + (and (string? sql) (str/starts-with? sql "insert into job")))) + +(def ^:private test-error + {:type :internal + :code :boom + :hint "boom"}) + +(defn- get-progresss + "Progress events of a job, oldest first, with decoded payloads." + [job-id] + (->> (th/db-exec! ["SELECT payload FROM job_event + WHERE job_id = ? AND kind = 'progress' + ORDER BY created_at ASC, id ASC" + job-id]) + (mapv #(jobs/decode-progress (:payload %))))) + +(defn- get-events + "Every event of a job, in insertion order." + [job-id] + (->> (th/db-exec! ["SELECT kind, payload FROM job_event + WHERE job_id = ? ORDER BY id ASC" job-id]) + (mapv (fn [{:keys [kind payload]}] + {:kind kind :payload (db/decode-json-pgobject payload)})))) + +(defn- get-outcomes + "Outcomes of the end events of a job, oldest first." + [job-id] + (mapv (comp :outcome :payload) (filter #(= "end" (:kind %)) (get-events job-id)))) + +(defn- get-kinds + [job-id] + (mapv :kind (get-events job-id))) + +(defn- fake-msgbus + "A minimal msgbus that records every publication on the atom." + [messages] + (reify mbus/IMsgBus + (-sub [_ _ _] nil) + (-pub [_ topic message] + (swap! messages conj {:topic topic :message message}) + nil) + (-purge [_ _] nil))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest submit-persists-optional-profile-and-resource-references + (let [cfg (make-cfg (get-job-defs)) + profile-id (:id (th/create-profile* 1)) + resource-id (uuid/next)] + ;; the resource reference points to a real storage object: the column + ;; is a foreign key kept for garbage collection + (th/db-insert! :storage-object {:id resource-id :backend "test"}) + (let [internal-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)}) + owned-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params) + ::jobs/profile-id profile-id + ::jobs/resource-id resource-id}) + internal-row (jobs/get-job cfg internal-id) + owned-row (jobs/get-job cfg owned-id)] + + (t/testing "profile_id stays nullable for internal jobs" + (t/is (nil? (:profile-id internal-row))) + (t/is (nil? (:resource-id internal-row)))) + + (t/testing "an explicit profile is persisted as the owner of the job" + (t/is (= profile-id (:profile-id owned-row)))) + + (t/testing "the resource reference is a column, not part of params" + (t/is (= resource-id (:resource-id owned-row))) + (t/is (not (contains? (:params owned-row) :resource-id))))))) + +(t/deftest submit-rejects-invalid-profile-and-resource-references + (let [cfg (make-cfg (get-job-defs))] + (t/testing "a non uuid profile-id is rejected before the insert" + (t/is (thrown? Exception + (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params) + ::jobs/profile-id "not-a-uuid"})))) + (t/testing "a non uuid resource-id is rejected before the insert" + (t/is (thrown? Exception + (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params) + ::jobs/resource-id "not-a-uuid"})))) + (t/testing "no job row was created by the rejected submits" + (t/is (= 0 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job"]))))))) + +(t/deftest submit-persists-an-expiry-date + (let [cfg (make-cfg (get-job-defs)) + expires-at (ct/plus (ct/now) (ct/duration {:days 7}))] + + (t/testing "an explicit expiry is persisted as the retention of the job" + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params) + ::jobs/expires-at expires-at}) + row (jobs/get-job cfg job-id)] + (t/is (= (inst-ms expires-at) (inst-ms (:expires-at row)))))) + + (t/testing "without the option the column stays nil, as before" + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)}) + row (jobs/get-job cfg job-id)] + (t/is (nil? (:expires-at row))))))) + +(t/deftest submit-rejects-an-invalid-expiry-date + (let [cfg (make-cfg (get-job-defs))] + (t/testing "a value that is not an instant is rejected before the insert" + (t/is (thrown? Exception + (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params) + ::jobs/expires-at "not-an-instant"})))) + (t/testing "no job row was created by the rejected submit" + (t/is (= 0 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job"]))))))) + +(t/deftest progress-report-validates-its-payload + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + + (t/testing "stage is mandatory" + (t/is (thrown? Exception + (jobs/heartbeat cfg :job-id job-id + :progress {:counters {:work {:current 1}}})))) + + (t/testing "stage must be a keyword, not a label" + (t/is (thrown? Exception + (jobs/heartbeat cfg :job-id job-id + :progress {:stage "work"})))) + + (t/testing "unknown keys are rejected, at the top and inside a counter" + (t/is (thrown? Exception + (jobs/heartbeat cfg :job-id job-id + :progress {:stage :work :step 1}))) + (t/is (thrown? Exception + (jobs/heartbeat cfg :job-id job-id + :progress {:stage :work + :counters {:work {:current 1 + :step 1}}})))) + + (t/testing "a counter current must not be negative" + (t/is (thrown-with-msg? Exception #"negative" + (jobs/heartbeat cfg :job-id job-id + :progress {:stage :work + :counters {:work {:current -1}}})))) + + (t/testing "a counter total must be positive" + (t/is (thrown-with-msg? Exception #"positive" + (jobs/heartbeat cfg :job-id job-id + :progress {:stage :work + :counters {:work {:current 0 + :total 0}}})))) + + (t/testing "a counter current must not be greater than its total" + (t/is (thrown-with-msg? Exception #"not lower than current" + (jobs/heartbeat cfg :job-id job-id + :progress {:stage :work + :counters {:work {:current 5 + :total 4}}})))) + + (t/testing "keys are limited to a name and not a payload" + (t/is (thrown-with-msg? Exception #"64" + (jobs/heartbeat cfg :job-id job-id + :progress {:stage (keyword (apply str (repeat 65 "x")))})))) + + (t/testing "a milestone without counters is accepted" + (t/is (= 2 (jobs/heartbeat cfg :job-id job-id :progress {:stage :relations})))) + + (t/testing "a milestone with counters is accepted and stored as it is" + (swap! jobs/progresses dissoc job-id) + (swap! jobs/heartbeats dissoc job-id) + (t/is (= 2 (jobs/heartbeat cfg :job-id job-id + :progress {:stage :pages + :counters {:files {:current 2 :total 5} + :pages {:current 3 :total 8}}}))) + (t/is (= [{:stage :relations} + {:stage :pages + :counters {:files {:current 2 :total 5} + :pages {:current 3 :total 8}}}] + (get-progresss job-id)))) + + (t/testing "a rejected report never reaches the durable log" + (t/is (= 2 (count (get-progresss job-id))))))) + +(t/deftest progress-event-publishes-msgbus-for-profile-jobs + (let [cfg (make-cfg (get-job-defs)) + profile-id (:id (th/create-profile* 1)) + messages (atom []) + job-cfg (assoc cfg ::mbus/msgbus (fake-msgbus messages)) + job-id (jobs/submit job-cfg {::jobs/name :echo + ::jobs/params (make-params) + ::jobs/profile-id profile-id})] + (t/is (pos? (jobs/heartbeat job-cfg :job-id job-id + :progress {:stage :pages + :counters {:files {:current 3 :total 7}}}))) + (t/testing "the event is published on the topic of the job profile" + (t/is (= 1 (count @messages))) + (let [{:keys [topic message]} (first @messages)] + (t/is (= profile-id topic)) + (t/is (= :job-event (:type message))) + (t/is (= job-id (:job-id message))) + (t/is (= profile-id (:profile-id message))) + (t/is (= "progress" (:kind message))) + (t/is (= {:stage :pages + :counters {:files {:current 3 :total 7}}} + (:payload message))) + (t/is (some? (:event-id message))) + (t/is (some? (:created-at message))))))) + +(t/deftest progress-event-of-internal-job-is-not-published + (let [cfg (make-cfg (get-job-defs)) + messages (atom []) + job-cfg (assoc cfg ::mbus/msgbus (fake-msgbus messages)) + job-id (jobs/submit job-cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (t/is (pos? (jobs/heartbeat job-cfg :job-id job-id :progress {:stage :work}))) + (t/testing "the event is stored but nothing is published" + (t/is (= 1 (count (get-progresss job-id)))) + (t/is (= [] @messages))))) + +(t/deftest progress-event-of-profile-job-requires-msgbus + (let [cfg (make-cfg (get-job-defs)) + profile-id (:id (th/create-profile* 1)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params) + ::jobs/profile-id profile-id})] + (t/testing "the forced route propagates the missing msgbus" + (t/is (thrown-with-msg? Exception #"require ::mbus/msgbus" + (jobs/heartbeat cfg + :job-id job-id + :progress {:stage :work} + ::jobs/force? true))) + (t/is (= [] (get-progresss job-id)) + "the event must not be stored without its notification")) + + (t/testing "the handler route logs the failure and keeps the job alive" + (th/db-update! :job {:modified-at (ct/in-past {:minutes 5})} {:id job-id}) + (swap! jobs/heartbeats dissoc job-id) + (t/is (= 1 (jobs/heartbeat cfg :job-id job-id + :progress {:stage :work}))) + (t/is (= [] (get-progresss job-id)))))) + +(t/deftest submit-validates-params-with-job-schema + (let [cfg (make-cfg (get-job-defs))] + (t/is (thrown-with-msg? Exception #"check error" + (jobs/submit cfg {::jobs/name :echo + ::jobs/params {:text "hello" + :object :snapshot + :deleted-at (ct/now) + :id (uuid/next) + :file-id "not-an-uuid"}})))) + + (t/testing "missing job definition raises a clear error" + (let [cfg (make-cfg (get-job-defs))] + (t/is (thrown-with-msg? Exception #"no job definition" + (jobs/submit cfg {::jobs/name :unknown + ::jobs/params {}}))))) + + (t/testing "the job name is a keyword, and only a keyword" + (let [cfg (make-cfg (get-job-defs))] + (t/is (thrown? clojure.lang.ExceptionInfo + (jobs/submit cfg {::jobs/name "echo" + ::jobs/params {}}))) + (t/is (thrown? clojure.lang.ExceptionInfo + (jobs/invoke (assoc cfg + ::jobs/name "echo" + ::jobs/params {}))))))) + +(t/deftest submit-persists-row-with-json-params + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/queue :test + ::jobs/delay 1000 + ::jobs/priority 200 + ::jobs/label "test-label" + ::jobs/max-retries 5}) + row (jobs/get-job cfg job-id)] + + (t/is (uuid? job-id)) + (t/is (= "echo" (:name row))) + (t/is (= (cf/get :tenant) (:tenant row))) + (t/is (= "test" (:queue row))) + (t/is (= "test-label" (:label row))) + (t/is (= 200 (:priority row))) + (t/is (= 5 (:max-retries row))) + (t/is (= "new" (:status row))) + + (t/testing "scheduled_at respects the submitted delay" + (let [delay (ct/duration {:seconds 1})] + (t/is (pos? (- (inst-ms (:scheduled-at row)) (inst-ms (ct/now)))) + (str "scheduled-at should be in the future: " + (pr-str (:scheduled-at row)))))) + + (t/testing "the row exposes params and no legacy columns" + (t/is (some? (:params row))) + (t/is (not (contains? row :props))) + (t/is (not (contains? row :progress))) + (t/is (not (contains? row :target)))) + + (t/testing "params are stored as plain JSON (no transit tags) and decoded" + (let [params (:params row)] + (t/is (map? params)) + (t/is (nil? (some-> (th/db-exec-one! ["SELECT params::text FROM job WHERE id = ?" + job-id]) + :params + (str/index-of "~#")))) + + (t/testing "round-trip: decoded values have proper clojure types" + (let [decoded (jobs/decode-params (jobs/get-job-def (get cfg ::jobs/defs) :echo) + (:params row))] + (t/is (keyword? (:object decoded))) + (t/is (= :snapshot (:object decoded))) + (t/is (uuid? (:id decoded))) + (t/is (uuid? (:file-id decoded))) + (t/is (ct/inst? (:deleted-at decoded))))))))) + +(t/deftest submit-dedupe-replaces-not-due-new-rows + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + label "dedupe-label" + id1 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label + ::jobs/dedupe true + ::jobs/delay 10000}) + id2 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label + ::jobs/dedupe true + ::jobs/delay 10000})] + + (t/is (uuid? id1)) + (t/is (uuid? id2)) + (t/is (not= id1 id2)) + + (t/testing "the first new row (not due yet) is removed" + (t/is (nil? (jobs/get-job cfg id1))) + + (t/testing "and only the new row remains" + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job + WHERE label = ?" label])))))))) + +(t/deftest submit-dedupe-keeps-due-and-running-rows + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + label "dedupe-active-label" + id1 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label}) + _ (th/db-update! :job {:status "running"} {:id id1}) + id2 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label + ::jobs/dedupe true})] + + (t/is (some? (jobs/get-job cfg id1))) + (t/is (= 2 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job + WHERE label = ?" label]))))) + + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + label "dedupe-due-label" + id1 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label}) + _ (th/db-update! :job {:scheduled-at (ct/minus (ct/now) + (ct/duration {:seconds 5}))} + {:id id1}) + id2 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label + ::jobs/dedupe true})] + + (t/is (some? (jobs/get-job cfg id1))) + (t/is (= 2 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job + WHERE label = ?" label])))))) + +(t/deftest submit-dedupe-leaves-another-tenants-rows-alone + ;; The DELETE matches name, queue and label. Without the tenant in the + ;; match it would reach into another instance's not-started jobs. + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + label "dedupe-shared-label" + foreign-id (uuid/next)] + (th/db-insert! :job {:id foreign-id + :name "echo" + :tenant "other-tenant" + :queue "default" + :label label + :params (db/json params) + :status "new" + :priority 100 + :max-retries 3 + :retry-num 0 + :scheduled-at (ct/plus (ct/now) (ct/duration {:seconds 10})) + :created-at (ct/now) + :modified-at (ct/now)}) + (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/queue :default + ::jobs/label label + ::jobs/dedupe true + ::jobs/delay 10000}) + (t/is (some? (jobs/get-job cfg foreign-id)) + "another tenant's matching job must survive our dedupe") + (t/testing "and only our own row was written" + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job + WHERE label = ? + AND tenant = ?" + label (cf/get :tenant)]))))))) + +(t/deftest submit-skip-if-active-skips-live-jobs + (doseq [status ["new" "scheduled" "running" "retry"]] + (t/testing (str "live status " status) + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + label (str "skip-active-" status) + id1 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label}) + _ (th/db-update! :job {:status status} {:id id1}) + id2 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label + ::jobs/skip-if-active true})] + (t/is (nil? id2)) + (t/testing "no row deleted, none inserted" + (t/is (some? (jobs/get-job cfg id1))) + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job + WHERE label = ?" label]))))))))) + +(t/deftest submit-skip-if-active-inserts-past-terminal-jobs + (doseq [status ["completed" "failed" "cancelled" "aborted"]] + (t/testing (str "terminal status " status) + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + label (str "skip-terminal-" status) + id1 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label}) + _ (th/db-update! :job {:status status} {:id id1}) + id2 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label + ::jobs/skip-if-active true})] + (t/is (uuid? id2)) + (t/is (= 2 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job + WHERE label = ?" label])))))))) + +(t/deftest submit-skip-if-active-requires-an-explicit-label + (let [cfg (make-cfg (get-job-defs)) + params (make-params)] + (t/is (thrown-with-msg? Exception #"skip-if-active requires" + (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/skip-if-active true}))) + (t/is (thrown-with-msg? Exception #"skip-if-active requires" + (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label " " + ::jobs/skip-if-active true}))))) + +(t/deftest submit-skip-if-active-inserts-when-nothing-is-live + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + label "skip-idle-label" + id1 (jobs/submit cfg {::jobs/name :echo + ::jobs/params params + ::jobs/label label + ::jobs/skip-if-active true})] + (t/is (uuid? id1)) + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job + WHERE label = ?" label])))))) + +(t/deftest submit-dedupe-rolls-back-on-insert-failure + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + opts {::jobs/name :echo + ::jobs/params params + ::jobs/dedupe true + ::jobs/label "atomic-label"} + kept (jobs/submit cfg opts) + orig @#'db/exec-one!] + ;; fault the INSERT: the DELETE before it must roll back too + (alter-var-root #'db/exec-one! + (constantly (fn [& args] + (when (insert-job-statement? args) + (throw (ex-info "boom" {}))) + (apply orig args)))) + (try + (t/is (thrown? Exception (jobs/submit cfg opts))) + (t/testing "the original row survives, no duplicate left behind" + (t/is (some? (jobs/get-job cfg kept))) + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job WHERE label = ?" "atomic-label"]))))) + (finally + (alter-var-root #'db/exec-one! (constantly orig)))))) + +(t/deftest submit-dedupe-atomic-on-autocommit-conn + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + opts {::jobs/name :echo + ::jobs/params params + ::jobs/dedupe true + ::jobs/label "atomic-autocommit-label"} + kept (jobs/submit cfg opts) + orig @#'db/exec-one!] + ;; a raw connection outside any transaction: DELETE+INSERT must + ;; still share one transaction opened on it + (with-open [conn (db/open th/*pool*)] + (alter-var-root #'db/exec-one! + (constantly (fn [& args] + (when (insert-job-statement? args) + (throw (ex-info "boom" {}))) + (apply orig args)))) + (try + (t/is (thrown? Exception + (jobs/submit (assoc cfg ::db/conn conn) opts))) + (t/testing "the original row survives, no duplicate left behind" + (t/is (some? (jobs/get-job cfg kept))) + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job WHERE label = ?" "atomic-autocommit-label"]))))) + (finally + (alter-var-root #'db/exec-one! (constantly orig))))))) + +(t/deftest submit-requires-metrics-on-bare-connectable + (let [defs (get-job-defs) + prev @@#'jobs/defs-registry] + (try + (reset! @#'jobs/defs-registry defs) + (t/is (thrown-with-msg? Exception #"missing ::mtx/metrics" + (jobs/submit th/*pool* {::jobs/name :echo + ::jobs/params (make-params)}))) + (finally + (reset! @#'jobs/defs-registry prev))))) + +(t/deftest plain-handler-is-testable-without-integrant + (let [params (make-params)] + (t/is (= params (echo-handler {} params)))) + + (let [cfg (make-cfg (get-job-defs)) + params (make-params) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params params})] + (t/testing "handler receives cfg with job-id context for heartbeats" + (t/is (= params (echo-handler (assoc cfg ::jobs/job-id job-id) + params)))))) + +(t/deftest submit-strips-rollback-testing-flag-from-params + (let [cfg (make-cfg (get-job-defs)) + params (assoc (make-params) :rollback? true) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params params})] + (t/testing "durable rows never carry the in-process escape hatch" + (t/is (nil? (:rollback? (:params (jobs/get-job cfg job-id)))))))) + +(t/deftest heartbeat-respects-throttle + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + + (t/testing "first heartbeat writes" + ;; Backdate modified-at so the beat is unambiguous: the assertion + ;; compares against the value we set, not against created-at, which + ;; can land on the same millisecond. + (let [backdated (ct/in-past {:seconds 5})] + (th/db-update! :job {:modified-at backdated} {:id job-id}) + (t/is (= 1 (jobs/heartbeat cfg :job-id job-id))) + (let [row (jobs/get-job cfg job-id)] + (t/is (> (inst-ms (:modified-at row)) (inst-ms backdated)))))) + + (t/testing "immediate second heartbeat does not write (throttled)" + (let [row1 (jobs/get-job cfg job-id) + _ (jobs/heartbeat cfg :job-id job-id) + row2 (jobs/get-job cfg job-id)] + (t/is (= (inst-ms (:modified-at row1)) + (inst-ms (:modified-at row2)))))))) + +(t/deftest heartbeat-interrupts-on-terminal-states + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (t/testing "a terminal state raises the interrupt instead of touching the row" + (th/db-update! :job {:status "completed" + :modified-at (ct/in-past {:days 10})} + {:id job-id}) + (swap! @#'jobs/heartbeats dissoc job-id) + (let [before (jobs/get-job cfg job-id) + cause (raised #(jobs/heartbeat cfg :job-id job-id))] + (t/is (= :interrupt (th/ex-type cause))) + (t/is (= :job-interrupted (th/ex-code cause))) + (t/is (= job-id (:job-id (ex-data cause)))) + (t/is (= "completed" (:status (ex-data cause)))) + (t/is (= (inst-ms (:modified-at before)) + (inst-ms (:modified-at (jobs/get-job cfg job-id))))))))) + +(t/deftest heartbeat-interrupts-when-the-row-is-gone + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (th/db-force-delete :job {:id job-id}) + (let [cause (raised #(jobs/heartbeat cfg :job-id job-id))] + (t/is (= :interrupt (th/ex-type cause))) + (t/is (= :job-interrupted (th/ex-code cause))) + (t/is (= "gone" (:status (ex-data cause))))))) + +(t/deftest a-transient-progress-failure-is-not-an-interrupt + "The handler path logs and ignores a progress insert that fails, but an + interrupt must not be swallowed with it: only an interrupt means the job + is gone." + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (swap! jobs/progresses dissoc job-id) + (swap! jobs/heartbeats dissoc job-id) + ;; the insert is the boundary that fails: the job itself is active, so + ;; the beat answers with the touch it did write instead of raising + (with-redefs-fn {#'jobs/report-progress (fn [& _] (throw (ex-info "boom" {})))} + #(t/is (= 1 (jobs/heartbeat cfg :job-id job-id :progress {:stage :work})))) + (t/is (= [] (get-progresss job-id))))) + +(t/deftest progress-events-respect-throttle-and-interrupt-on-terminal-states + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + + (t/testing "first progress report appends a progress event" + (t/is (= 2 (jobs/heartbeat cfg :job-id job-id + :progress {:stage :pages + :counters {:pages {:current 1 :total 10}}}))) + (t/is (= [{:stage :pages :counters {:pages {:current 1 :total 10}}}] + (get-progresss job-id)))) + + (t/testing "immediate second progress report is throttled" + (t/is (= 0 (jobs/heartbeat cfg :job-id job-id + :progress {:stage :pages + :counters {:pages {:current 2 :total 10}}}))) + (t/is (= [{:stage :pages :counters {:pages {:current 1 :total 10}}}] + (get-progresss job-id))) + + (t/testing "after the throttle window elapses it appends again" + (swap! @#'jobs/progresses + (fn [m] + (update-in m [job-id] + #(ct/minus % + (ct/duration {:seconds 2}))))) + (jobs/heartbeat cfg :job-id job-id + :progress {:stage :pages + :counters {:pages {:current 3 :total 10}}}) + (t/is (= [{:stage :pages :counters {:pages {:current 1 :total 10}}} + {:stage :pages :counters {:pages {:current 3 :total 10}}}] + (get-progresss job-id)))))) + + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (t/testing "terminal states never get a progress event" + (th/db-update! :job {:status "completed"} {:id job-id}) + (swap! @#'jobs/progresses dissoc job-id) + (swap! @#'jobs/heartbeats dissoc job-id) + (let [cause (raised #(jobs/heartbeat cfg :job-id job-id + :progress {:stage :work}))] + (t/is (= :interrupt (th/ex-type cause))) + (t/is (= [] (get-progresss job-id))))))) + +(t/deftest throttle-prune-removes-stale-entries-keeps-fresh + "When the throttle map exceeds prune-threshold, stale entries (older than + prune-window) are removed and fresh entries are kept." + (let [cfg (make-cfg (get-job-defs)) + job-id-1 (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)}) + job-id-2 (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)}) + now (ct/now) + stale-time (ct/minus now (ct/duration {:hours 2})) ;; older than 1h window + fresh-time (ct/minus now (ct/duration {:minutes 30}))] ;; within 1h window + + ;; Fill the heartbeats map past the threshold with one stale and one fresh entry + (with-redefs [jobs/prune-threshold 0] ;; force prune on next call + (reset! @#'jobs/heartbeats + {job-id-1 stale-time + job-id-2 fresh-time}) + + ;; Trigger a heartbeat for a new job (should trigger prune) + (let [job-id-3 (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (jobs/heartbeat cfg :job-id job-id-3) + + ;; After prune: stale entry (job-id-1) should be gone, fresh (job-id-2) should remain + (let [state @jobs/heartbeats] + (t/is (not (contains? state job-id-1)) "stale entry removed") + (t/is (contains? state job-id-2) "fresh entry kept") + (t/is (contains? state job-id-3) "new entry added")))))) + +(t/deftest cancel-skips-terminal-jobs + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + + (t/testing "pending job can be cancelled" + (t/is (= 1 (jobs/cancel cfg job-id))) + (t/is (= "cancelled" (:status (jobs/get-job cfg job-id)))) + (t/is (= ["cancelled"] (get-outcomes job-id))) + (t/testing "already cancelled job is not affected again" + (t/is (zero? (jobs/cancel cfg job-id))) + (t/is (= ["cancelled"] (get-outcomes job-id)))))) + (doseq [status ["completed" "failed"]] + (t/testing (str status " job cannot be cancelled") + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (th/db-update! :job {:status status} {:id job-id}) + (t/is (zero? (jobs/cancel cfg job-id))) + (t/is (= status (:status (jobs/get-job cfg job-id)))) + (t/is (= [] (get-outcomes job-id))))))) + +(t/deftest cancel-affects-pending-and-running-jobs + (let [cfg (make-cfg (get-job-defs))] + (doseq [status ["scheduled" "retry" "running"]] + (t/testing (str status " job can be cancelled") + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (th/db-update! :job {:status status} {:id job-id}) + (t/is (= 1 (jobs/cancel cfg job-id))) + (t/is (= "cancelled" (:status (jobs/get-job cfg job-id)))) + (t/is (= ["cancelled"] (get-outcomes job-id)))))))) + +(t/deftest get-user-status-maps-internal-statuses + (t/are [status expected] (= expected (jobs/get-user-status status)) + "new" "pending" + "scheduled" "pending" + "retry" "pending" + :new "pending" + "running" "running" + "completed" "completed" + "failed" "failed" + "cancelled" "failed" + "aborted" "failed")) + +(t/deftest job-defs-registry-validates-definitions + (t/testing "valid job-def map passes the assert" + (t/is (some? (get-job-defs)))) + + (t/testing "registry name mismatch is detected" + (t/is (thrown? Exception + (ig/init {::jobs/defs {::echo-job + {::jobs/name :other + ::jobs/schema schema:echo-params + ::jobs/handler (fn [_context params] + (echo-handler nil params)) + ::jobs/decoder (sm/decoder schema:echo-params sm/json-transformer) + ::jobs/validator (sm/validator schema:echo-params)}}})))) + + (t/testing "missing decoder/validator is detected" + (t/is (thrown? Exception + (ig/init {::jobs/defs {::echo-job + {::jobs/name :echo + ::jobs/schema schema:echo-params + ::jobs/handler (fn [_context params] + params)}}}))))) + +(t/deftest job-def-carries-optional-family-and-resource-role + (let [job-def {::jobs/name :echo + ::jobs/family :export + ::jobs/resource-role :output + ::jobs/schema schema:echo-params + ::jobs/handler (fn [_context params] params) + ::jobs/decoder (sm/decoder schema:echo-params sm/json-transformer) + ::jobs/validator (sm/validator schema:echo-params)}] + + (t/testing "the metadata is accepted and readable from the registry" + (let [defs (-> (ig/init {::jobs/defs {:echo job-def}}) + (get ::jobs/defs)) + echo (jobs/get-job-def defs :echo)] + (t/is (= :export (::jobs/family echo))) + (t/is (= :output (::jobs/resource-role echo))))) + + (t/testing "a family is a keyword, not a free string" + (t/is (thrown? Exception + (ig/init {::jobs/defs {:echo (assoc job-def + ::jobs/family "export")}})))) + + (t/testing "a resource role is a keyword too" + (t/is (thrown? Exception + (ig/init {::jobs/defs {:echo (assoc job-def + ::jobs/resource-role "output")}})))))) + +(t/deftest generic-schema-round-trip-preserves-type-sensitive-fields + "For each registered job-def, verify that type-sensitive fields (uuids, insts) + survive the JSON round-trip (db/json → decode). This catches the F2 class of + bug where uuid types are lost during the transit→JSON switch." + (let [defs (get-job-defs)] + (t/is (pos? (count defs)) "should have at least one job-def") + + ;; Test the echo job-def which we know exists in the test registry + (t/testing "echo job-def preserves types through JSON round-trip" + (let [echo-def (get defs :echo) + _ (t/is (some? echo-def) "echo job-def should exist") + ;; Create a representative params map with type-sensitive fields + sample-params {:text "test" + :object :snapshot + :deleted-at (ct/now) + :id (uuid/random) + :file-id (uuid/random)} + + ;; Round-trip through JSON (simulates the job table storage) + ;; db/json returns a PGobject, which is what decode-params expects + pg-obj (db/json sample-params) + decoded (jobs/decode-params echo-def pg-obj)] + + ;; Verify type-sensitive fields survived + (t/is (uuid? (:id decoded)) "id should be uuid after decode") + (t/is (uuid? (:file-id decoded)) "file-id should be uuid after decode") + (t/is (inst? (:deleted-at decoded)) "deleted-at should be inst after decode"))))) + +(t/deftest invoke-falls-back-to-global-registry + "Verify that invoke! uses the global registry fallback when ::defs is not + on the cfg, consistent with submit!." + ;; Rebind the private registry atom instead of overwriting global state: + ;; it auto-restores on exit, so no later test can observe it. Note + ;; get-job-defs itself populates the global via ig/init (established + ;; pattern, also used by the sibling tests); the snapshot below is + ;; taken after that, so it pins exactly this test's rebinding. + (let [defs (get-job-defs) + before @@#'jobs/defs-registry] + (with-redefs [jobs/defs-registry (atom defs)] + ;; cfg WITHOUT ::jobs/defs to exercise the fallback; the echo + ;; job-def is known to exist in the rebound registry + (let [cfg {::db/pool th/*pool*} + result (jobs/invoke (assoc cfg ::jobs/name :echo + ::jobs/params (make-params)))] + ;; Should not throw; should find the job-def via the fallback. + ;; The result is the params map (echo-handler returns params). + (t/is (some? result)))) + (t/testing "global registry untouched" + (t/is (= before @@#'jobs/defs-registry))))) + +(t/deftest heartbeat-and-progress-noop-when-job-id-nil + "When *job-id* is nil (in-process invoke! without a job row), + heartbeat! and progress! are no-ops — they do not write to the + database or update the throttle atoms." + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + + (t/testing "heartbeat! is a no-op when *job-id* is nil" + (let [row-before (jobs/get-job cfg job-id)] + (binding [jobs/*job-id* nil] + (jobs/heartbeat cfg)) + (let [row-after (jobs/get-job cfg job-id)] + (t/is (= (inst-ms (:modified-at row-before)) + (inst-ms (:modified-at row-after))) + "modified-at should not change")))) + + (t/testing "progress reporting is a no-op when *job-id* is nil" + (binding [jobs/*job-id* nil] + (t/is (nil? (jobs/heartbeat cfg :progress {:stage :work})))) + (t/is (= [] (get-progresss job-id)) + "no progress event should be stored")))) + +(t/deftest progress-interrupts-on-terminal-states + "A progress report never stores an event when the job is already in a + terminal state (completed, failed, cancelled, aborted): the row is locked and + checked before the insert, so a report can never race a terminal + transition, and the beat raises the interrupt instead." + (let [cfg (make-cfg (get-job-defs))] + (doseq [status ["completed" "failed" "cancelled" "aborted"]] + (t/testing (str "progress interrupts on " status " status") + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (th/db-update! :job {:status status} {:id job-id}) + ;; reset the throttles so should-write? would allow the writes + (swap! jobs/progresses dissoc job-id) + (swap! jobs/heartbeats dissoc job-id) + (binding [jobs/*job-id* job-id] + (let [cause (raised #(jobs/heartbeat cfg :progress {:stage :work}))] + (t/is (= :interrupt (th/ex-type cause))) + (t/is (= status (:status (ex-data cause)))))) + (t/is (= [] (get-progresss job-id)))))))) + +(t/deftest defs-halt-clears-module-registry + (let [prev @@#'jobs/defs-registry] + ;; init populates the module registry + (get-job-defs) + (t/is (contains? @@#'jobs/defs-registry :echo)) + (try + (ig/halt-key! ::jobs/defs nil) + (t/is (= {} @@#'jobs/defs-registry)) + (finally + (reset! @#'jobs/defs-registry prev))))) + +(t/deftest heartbeat-bypasses-caller-transaction + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + ;; backdate so the beat is strictly greater (no same-millis flake) + (th/db-update! :job {:modified-at (ct/in-past {:minutes 5})} {:id job-id}) + ;; beat inside a transaction that is rolled back: the beat must + ;; still be visible (it went through the pool, not the tx) + (db/tx-run! (assoc cfg ::db/rollback true) + (fn [{:keys [::db/conn]}] + (jobs/heartbeat (assoc cfg ::db/conn conn) :job-id job-id))) + (let [row (jobs/get-job cfg job-id)] + (t/is (> (inst-ms (:modified-at row)) + (inst-ms (ct/in-past {:minutes 5}))))))) + +(t/deftest progress-bypasses-caller-transaction + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (db/tx-run! (assoc cfg ::db/rollback true) + (fn [{:keys [::db/conn]}] + (jobs/heartbeat (assoc cfg ::db/conn conn) + :job-id job-id + :progress {:stage :work}))) + (t/is (= [{:stage :work}] (get-progresss job-id))))) + +(t/deftest progress-notification-survives-a-rolled-back-caller + (let [cfg (make-cfg (get-job-defs)) + profile-id (:id (th/create-profile* 1)) + messages (atom []) + job-cfg (assoc cfg ::mbus/msgbus (fake-msgbus messages)) + job-id (jobs/submit job-cfg {::jobs/name :echo + ::jobs/params (make-params) + ::jobs/profile-id profile-id})] + ;; the report owns its transaction, so it commits and publishes even + ;; though the transaction that triggered it rolls back + (db/tx-run! (assoc job-cfg ::db/rollback true) + (fn [{:keys [::db/conn]}] + (jobs/heartbeat (assoc job-cfg ::db/conn conn) + :job-id job-id + :progress {:stage :work}))) + (t/testing "the event is stored" + (t/is (= [{:stage :work}] (get-progresss job-id)))) + (t/testing "and the notification went out with its own commit" + (t/is (= 1 (count @messages))) + (t/is (= "progress" (:kind (:message (first @messages)))))))) + +(t/deftest heartbeat-requires-a-cfg-map + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (t/testing "a bare pool is a caller error, not something to accommodate" + (t/is (thrown? Exception + (jobs/heartbeat th/*pool* + :job-id job-id + :progress {:stage :work})))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; HANDLER CONTEXT +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- mk-row + "A job row as the runner and make-context see it." + [overrides] + (merge {:id (uuid/next) + :name "echo" + :tenant (cf/get :tenant) + :queue "default" + :label nil + :profile-id nil + :resource-id nil + :retry-num 0 + :max-retries 3} + overrides)) + +(t/deftest make-context-selects-exactly-the-agreed-keys + (let [context (jobs/make-context (mk-row {}))] + (t/is (= #{:id :name :label :resource-id :profile-id} (set (keys context)))) + (t/testing "the queue is a routing detail of the dispatcher, not context" + (t/is (not (contains? context :queue)))) + (t/testing "nor is the tenant that owns the queue" + (t/is (not (contains? context :tenant)))) + (t/testing "the retry keys belong to the runner, not to a handler" + (t/is (not (contains? context :retry-num))) + (t/is (not (contains? context :max-retries))) + (t/is (not (contains? context :attempt)))) + (t/testing "the context is a plain map, not the row" + (t/is (not= (set (keys (mk-row {}))) (set (keys context))))))) + +(t/deftest make-context-normalizes-an-empty-label + (t/testing "the empty label is the submit default for no label" + (t/is (nil? (:label (jobs/make-context (mk-row {:label ""}))))) + (t/is (nil? (:label (jobs/make-context (mk-row {:label nil})))))) + (t/testing "a real label is carried as is" + (t/is (= "my-label" (:label (jobs/make-context (mk-row {:label "my-label"}))))))) + +(t/deftest make-context-always-carries-the-resource-reference + (t/testing "the key exists even when there is no resource" + (let [context (jobs/make-context (mk-row {}))] + (t/is (contains? context :resource-id)) + (t/is (nil? (:resource-id context))))) + (t/testing "a resource reference is carried as is" + (let [resource-id (uuid/next)] + (t/is (= resource-id + (:resource-id (jobs/make-context (mk-row {:resource-id resource-id})))))))) + +(t/deftest make-context-carries-the-job-owner + (t/testing "the key exists even when the job has no owner" + (let [context (jobs/make-context (mk-row {}))] + (t/is (contains? context :profile-id)) + (t/is (nil? (:profile-id context))))) + (t/testing "the owner of a user job is carried as is" + (let [profile-id (uuid/next)] + (t/is (= profile-id + (:profile-id (jobs/make-context (mk-row {:profile-id profile-id})))))))) + +(t/deftest make-context-rejects-invalid-rows + (t/testing "a missing id is rejected" + (t/is (thrown? Exception (jobs/make-context (mk-row {:id nil}))))) + (t/testing "a missing name is rejected" + (t/is (thrown? Exception (jobs/make-context (mk-row {:name nil}))))) + (t/testing "a non uuid owner is rejected" + (t/is (thrown? Exception (jobs/make-context (mk-row {:profile-id "nope"}))))) + (t/testing "a non uuid resource reference is rejected" + (t/is (thrown? Exception (jobs/make-context (mk-row {:resource-id "nope"})))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IN-PROCESS INVOCATION +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def received-contexts (atom [])) + +(defn- capture-defs + "Job-defs whose handler records the context it receives." + [] + {:echo {::jobs/name :echo + ::jobs/schema schema:echo-params + ::jobs/handler (fn [context params] + (swap! received-contexts conj context) + params) + ::jobs/decoder (sm/decoder schema:echo-params sm/json-transformer) + ::jobs/validator (sm/validator schema:echo-params)}}) + +(t/deftest invoke-delivers-a-nil-context-without-a-row + (reset! received-contexts []) + (let [cfg (make-cfg (capture-defs)) + params (make-params)] + (t/is (= params (jobs/invoke (assoc cfg ::jobs/name :echo + ::jobs/params params)))) + (t/is (= [nil] @received-contexts)))) + +(t/deftest invoke-delivers-an-explicit-context + (reset! received-contexts []) + (let [cfg (make-cfg (capture-defs)) + params (make-params) + context (jobs/make-context (mk-row {}))] + (jobs/invoke (assoc cfg ::jobs/name :echo + ::jobs/params params + ::jobs/context context)) + (t/is (= [context] @received-contexts)) + (t/testing "an explicit context does not enable heartbeat by itself" + (t/is (= [] (get-progresss (:id context))))))) + +(t/deftest invoke-validates-an-explicit-context + (let [cfg (make-cfg (capture-defs))] + (t/testing "a partial context is rejected" + (t/is (thrown? Exception + (jobs/invoke (assoc cfg + ::jobs/name :echo + ::jobs/params (make-params) + ::jobs/context {:id (uuid/next)}))))) + (t/testing "an extra key is rejected: the schema is closed" + (t/is (thrown? Exception + (jobs/invoke (assoc cfg + ::jobs/name :echo + ::jobs/params (make-params) + ::jobs/context + (assoc (jobs/make-context (mk-row {})) + :params {})))))))) + +(t/deftest invoke-job-id-is-independent-from-the-context + (reset! received-contexts []) + (let [cfg (make-cfg (capture-defs)) + params (make-params) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params params})] + (jobs/invoke (assoc cfg ::jobs/name :echo + ::jobs/params params + ::jobs/job-id job-id)) + (t/is (= [nil] @received-contexts) + "a job-id without a context still delivers a nil context") + (t/testing "the job-id is what makes the durable writes reach the row" + (t/is (pos? (jobs/heartbeat cfg :job-id job-id + :progress {:stage :work}))) + (t/is (= [{:stage :work}] (get-progresss job-id)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; LIFECYCLE EVENTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- mk-running + "A job already claimed by a worker, ready for a terminal transition." + ([cfg] (mk-running cfg :echo)) + ([cfg name] + (let [job-id (jobs/submit cfg {::jobs/name name + ::jobs/params (make-params)})] + (jobs/claim cfg job-id (:scheduled-at (jobs/get-job cfg job-id))) + job-id))) + +(t/deftest lifecycle-writers-require-metrics + ;; Every one of the six writers must check the metrics instance before + ;; it writes. Their metric lands inside a `db/after-commit` callback, + ;; and those swallow exceptions, so without the check a miswired cfg + ;; persists the row and loses the count with nothing but a log line. + (let [cfg (make-cfg (get-job-defs)) + bare (dissoc cfg ::mtx/metrics) + count-jobs (fn [] (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job"]))) + state (fn [id] (:status (jobs/get-job cfg id)))] + (t/testing "the guards exist at all" + ;; They are `clojure.core/assert` forms, and those are elided when + ;; *assert* is false at compile time. Without the flag the checks + ;; are not in the compiled code and every assertion below would + ;; fail for a reason that has nothing to do with the writers. + (t/is (true? *assert*) + "run the suite with PENPOT_FLAGS=\"enable-backend-asserts\"")) + + (t/testing "submit never reaches the insert, and writes no row" + (let [before (count-jobs)] + (t/is (thrown-with-msg? Exception #"missing ::mtx/metrics" + (jobs/submit bare {::jobs/name :echo + ::jobs/params (make-params)}))) + (t/is (= before (count-jobs))))) + + (t/testing "claim leaves the pending job pending" + (let [id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (t/is (thrown-with-msg? Exception #"missing ::mtx/metrics" + (jobs/claim bare id (:scheduled-at (jobs/get-job cfg id))))) + (t/is (= "new" (state id))) + (t/is (= [] (get-kinds id))))) + + (t/testing "retry-job leaves the running job alone" + (let [id (mk-running cfg)] + (t/is (thrown-with-msg? Exception #"missing ::mtx/metrics" + (jobs/retry-job bare id 1 (ct/in-past {:minutes 1}) + test-error :backoff))) + (t/is (= "running" (state id))) + (t/is (= ["start"] (get-kinds id))))) + + (t/testing "complete leaves the running job alone" + (let [id (mk-running cfg)] + (t/is (thrown-with-msg? Exception #"missing ::mtx/metrics" + (jobs/complete bare :job-id id :result {:v 1}))) + (t/is (= "running" (state id))) + (t/is (= ["start"] (get-kinds id))))) + + (t/testing "fail leaves the running job alone" + (let [id (mk-running cfg)] + (t/is (thrown-with-msg? Exception #"missing ::mtx/metrics" + (jobs/fail bare id test-error))) + (t/is (= "running" (state id))) + (t/is (= ["start"] (get-kinds id))))) + + (t/testing "cancel leaves the pending job pending" + (let [id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (t/is (thrown-with-msg? Exception #"missing ::mtx/metrics" + (jobs/cancel bare id))) + (t/is (= "new" (state id))) + (t/is (= [] (get-kinds id))))))) + +(t/deftest lifecycle-writes-own-their-transaction + (let [cfg (make-cfg (get-job-defs)) + ;; a lifecycle write reports on work that already happened, so it + ;; never joins the transaction of whoever called it. The row it + ;; needs is committed beforehand: an independent connection + ;; cannot see the uncommitted work of the transaction around it. + rolled-back + (fn [f] + (db/tx-run! (assoc cfg ::db/rollback true) + (fn [{:keys [::db/conn]}] + (f (assoc cfg ::db/conn conn))))) + state (fn [job-id] + {:status (:status (jobs/get-job cfg job-id)) + :kinds (get-kinds job-id)})] + + (t/testing "claim starts the job and stores the start event" + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)}) + at (:scheduled-at (jobs/get-job cfg job-id))] + (t/is (= 1 (rolled-back #(jobs/claim % job-id at)))) + (t/is (= {:status "running" :kinds ["start"]} (state job-id))))) + + (t/testing "retry-job schedules the next attempt and stores the retry event" + (let [job-id (mk-running cfg)] + (t/is (= 1 (rolled-back #(jobs/retry-job % job-id 1 (ct/now) test-error :backoff)))) + (t/is (= {:status "retry" :kinds ["start" "retry"]} (state job-id))))) + + (t/testing "complete stores the end event with its result" + (let [job-id (mk-running cfg)] + (t/is (= 1 (rolled-back #(jobs/complete % :job-id job-id :result {:v 1})))) + (t/is (= {:status "completed" :kinds ["start" "end"]} (state job-id))))) + + (t/testing "fail stores the end event with its error" + (let [job-id (mk-running cfg)] + (t/is (= 1 (rolled-back #(jobs/fail % job-id test-error)))) + (t/is (= {:status "failed" :kinds ["start" "end"]} (state job-id))))) + + (t/testing "cancel stores the end event" + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (t/is (= 1 (rolled-back #(jobs/cancel % job-id)))) + (t/is (= {:status "cancelled" :kinds ["end"]} (state job-id))))))) + +(t/deftest claim-records-a-start-event-with-the-attempt + (let [cfg (make-cfg (get-job-defs))] + (t/testing "the first attempt is 1" + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (jobs/claim cfg job-id (:scheduled-at (jobs/get-job cfg job-id))) + (t/is (= [{:kind "start" :payload {:attempt 1}}] (get-events job-id))))) + + (t/testing "a retried job claims attempt retry-num + 1" + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (th/db-update! :job {:retry-num 2} {:id job-id}) + (jobs/claim cfg job-id (:scheduled-at (jobs/get-job cfg job-id))) + (t/is (= [{:kind "start" :payload {:attempt 3}}] (get-events job-id))))))) + +(t/deftest claim-that-affects-no-row-writes-no-event + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg)] + (t/is (= 0 (jobs/claim cfg job-id (:scheduled-at (jobs/get-job cfg job-id))))) + (t/testing "only the first claim left a start event" + (t/is (= ["start"] (get-kinds job-id)))))) + +(t/deftest retry-job-records-a-retry-event-per-reason + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg)] + (t/testing "a backoff increments the counter and reports the new attempt" + (jobs/retry-job cfg job-id 1 (ct/now) test-error :backoff) + (t/is (= {:attempt 2 :reason "backoff"} + (:payload (last (get-events job-id)))))) + + (t/testing "a noop keeps the counter and re-reports the same attempt" + (jobs/retry-job cfg job-id 1 (ct/now) test-error :noop) + (t/is (= {:attempt 2 :reason "noop"} + (:payload (last (get-events job-id)))))) + + (t/testing "the counter on the row is the one we asked for" + (t/is (= 1 (:retry-num (jobs/get-job cfg job-id))))) + + (t/testing "a terminal job gets no retry and no event" + (th/db-update! :job {:status "failed"} {:id job-id}) + (t/is (= 0 (jobs/retry-job cfg job-id 2 (ct/now) test-error :backoff))) + (t/is (= ["start" "retry" "retry"] (get-kinds job-id)))))) + +(t/deftest complete-records-an-end-event + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg)] + (t/is (= 1 (jobs/complete cfg :job-id job-id :result {:value 1}))) + (t/is (= ["start" "end"] (get-kinds job-id))) + (t/is (= ["completed"] (get-outcomes job-id))) + (t/testing "a terminal job gets no second end event" + (t/is (= 0 (jobs/complete cfg :job-id job-id :result {:value 2}))) + (t/is (= ["completed"] (get-outcomes job-id)))))) + +(t/deftest fail-records-an-end-event-and-a-structured-error + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg)] + (t/is (= 1 (jobs/fail cfg job-id test-error))) + (t/is (= ["failed"] (get-outcomes job-id))) + (t/testing "the error decodes back to the map we passed" + (t/is (= test-error (jobs/decode-job-error (:error (jobs/get-job cfg job-id)))))) + (t/testing "the event carries no error, only the outcome" + (t/is (= {:outcome "failed"} (:payload (last (get-events job-id)))))) + (t/testing "a terminal job gets no second end event" + (t/is (= 0 (jobs/fail cfg job-id test-error))) + (t/is (= ["failed"] (get-outcomes job-id)))))) + +(t/deftest cancel-records-an-end-event + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)})] + (t/is (= 1 (jobs/cancel cfg job-id))) + (t/is (= ["cancelled"] (get-outcomes job-id))) + (t/testing "a terminal job gets no second end event" + (t/is (= 0 (jobs/cancel cfg job-id))) + (t/is (= ["cancelled"] (get-outcomes job-id)))))) + +(t/deftest get-job-decodes-the-result-with-the-schema-of-the-def + "A result with a declared schema comes back typed. Without it, the plain + JSON of the column keywordizes every key and leaves every uuid as text; + without the substrate compiling the decoder from that schema, the key of + the uuid map comes back as a keyword and this test fails." + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg :typed) + file-id (uuid/next) + lib-id (uuid/next)] + + (t/is (= 1 (jobs/complete cfg :job-id job-id + :result {:file-ids [lib-id] + :resolution {file-id {:id file-id}}}))) + + (let [result (:result (jobs/get-job cfg job-id))] + (t/testing "the uuid keyed map keeps the type of its key and values" + (t/is (= [file-id] (keys (:resolution result)))) + (t/is (uuid? (:id (get (:resolution result) file-id))))) + + (t/testing "the vector of uuids keeps the type of its elements" + (t/is (= [lib-id] (:file-ids result))))))) + +(t/deftest get-job-leaves-a-result-without-a-schema-untouched + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg) + file-id (uuid/next)] + + (t/is (= 1 (jobs/complete cfg :job-id job-id + :result {:resolution {file-id {:id file-id}}}))) + + (t/testing "the raw JSON shape survives: keywordized keys, uuid as text" + (let [result (:result (jobs/get-job cfg job-id)) + key (keyword (str file-id))] + (t/is (= [key] (keys (:resolution result)))) + (t/is (= (str file-id) (get-in result [:resolution key :id]))))))) + +(t/deftest get-job-reads-a-row-whose-job-def-is-gone + (let [job-id (mk-running (make-cfg (get-job-defs))) + ;; a registry that no longer knows the kind of the row (a retired + ;; job-def): reading its result must not raise + row (jobs/get-job (make-cfg {}) job-id)] + (t/is (some? row)) + (t/is (= "echo" (:name row))))) + +(t/deftest complete-refuses-a-result-that-does-not-conform + "The schema a job-def declares is checked before the result is stored: the + row keeps no result and the job is not completed." + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg :typed)] + + (t/is (thrown-with-msg? Exception #"invalid result of a job" + (jobs/complete cfg :job-id job-id + :result {:resolution "not-a-map"}))) + + (t/testing "the row was not completed and holds no result" + (let [row (jobs/get-job cfg job-id)] + (t/is (= "running" (:status row))) + (t/is (nil? (:result row))))) + + (t/testing "a job that is no longer completable is skipped without checking" + (th/db-update! :job {:status "aborted"} {:id job-id}) + (t/is (zero? (jobs/complete cfg :job-id job-id + :result {:resolution "not-a-map"})))))) + +(t/deftest a-stored-result-that-does-not-conform-is-read-decode-only + "A row written by another version may hold a result the schema of today no + longer accepts: reading it decodes and never raises." + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg :typed)] + + (th/db-update! :job + {:status "completed" + :result (db/json {:resolution "not-a-map"})} + {:id job-id}) + + (let [row (jobs/get-job cfg job-id)] + (t/is (= "completed" (:status row))) + (t/is (= {:resolution "not-a-map"} (:result row)))))) + +(t/deftest decode-result-accepts-a-pgobject + (let [job-def (jobs/get-job-def (get (make-cfg (get-job-defs)) ::jobs/defs) :typed) + file-id (uuid/next)] + + (t/testing "a raw column value is decoded with the schema of the def" + (let [decoded (jobs/decode-result job-def + (db/json {:file-ids [file-id] + :resolution {file-id {:id file-id}}}))] + (t/is (= [file-id] (:file-ids decoded))) + (t/is (= [file-id] (keys (:resolution decoded)))))) + + (t/testing "no result answers nil" + (t/is (nil? (jobs/decode-result job-def nil)))))) + +(t/deftest aborted-job-is-terminal-for-all-writers + "An aborted job (system-side terminal set by the orphan sweep) cannot be + moved by any lifecycle writer: first-terminal-wins, with no event and no + second transition." + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg) + at (:scheduled-at (jobs/get-job cfg job-id))] + (th/db-update! :job {:status "aborted" + :error (db/json jobs/orphan-error)} + {:id job-id}) + (t/is (zero? (jobs/claim cfg job-id at))) + (t/is (zero? (jobs/retry-job cfg job-id 1 (ct/now) test-error :backoff))) + (t/is (zero? (jobs/complete cfg :job-id job-id :result {:v 1}))) + (t/is (zero? (jobs/fail cfg job-id test-error))) + (t/is (zero? (jobs/cancel cfg job-id))) + (t/is (= "aborted" (:status (jobs/get-job cfg job-id)))) + (t/is (= ["start"] (get-kinds job-id)) + "only the claim start event, no end event from any writer"))) + +(defn- with-failing-event + "Run `f` with the event insert forced to fail, the way a database that + rejects the row would. Everything the transition writes must go back + with the transaction that carried it." + [f] + (with-redefs [jobs/insert-event (fn [& _] + (throw (ex-info "event insert failed" {})))] + (f))) + +(defn- job-columns + "Every column a transition can move, decoded, so a before/after + comparison is a real one." + [cfg job-id] + (let [row (jobs/get-job cfg job-id)] + (cond-> (select-keys row [:status :modified-at :completed-at :retry-num + :scheduled-at :resource-id :result :error]) + (:error row) (update :error db/decode-json-pgobject) + (:result row) (update :result db/decode-json-pgobject)))) + +(t/deftest a-failing-event-write-rolls-back-every-writer + (let [cfg (make-cfg (get-job-defs)) + unchanged + (fn [job-id before] + (t/is (= before (job-columns cfg job-id)) + "no column the transition touches survived") + (t/is (not-any? #(= "end" %) (get-kinds job-id)) + "and no event of the failed transition was left behind"))] + + (t/testing "claim" + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)}) + at (:scheduled-at (jobs/get-job cfg job-id)) + before (job-columns cfg job-id)] + (with-failing-event #(t/is (thrown? Exception (jobs/claim cfg job-id at)))) + (unchanged job-id before) + (t/is (= [] (get-kinds job-id))))) + + (t/testing "retry-job" + (let [job-id (mk-running cfg) + before (job-columns cfg job-id)] + (with-failing-event + #(t/is (thrown? Exception + (jobs/retry-job cfg job-id 1 (ct/in-past {:minutes 1}) + test-error :backoff)))) + (unchanged job-id before) + (t/is (= ["start"] (get-kinds job-id))))) + + (t/testing "complete" + (let [job-id (mk-running cfg) + before (job-columns cfg job-id)] + (with-failing-event + #(t/is (thrown? Exception + (jobs/complete cfg :job-id job-id :result {:v 1})))) + (unchanged job-id before) + (t/is (= ["start"] (get-kinds job-id))))) + + (t/testing "fail" + (let [job-id (mk-running cfg) + before (job-columns cfg job-id)] + (with-failing-event #(t/is (thrown? Exception (jobs/fail cfg job-id test-error)))) + (unchanged job-id before) + (t/is (= ["start"] (get-kinds job-id))))) + + (t/testing "cancel" + (let [job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)}) + before (job-columns cfg job-id)] + (with-failing-event #(t/is (thrown? Exception (jobs/cancel cfg job-id)))) + (unchanged job-id before) + (t/is (= [] (get-kinds job-id))))) + + (t/testing "a resource a failed completion associated is undone with it" + (let [job-id (mk-running cfg) + resource-id (uuid/next)] + (th/db-insert! :storage-object {:id resource-id :backend "test"}) + (with-failing-event + #(t/is (thrown? Exception + (jobs/complete cfg :job-id job-id :result {:v 1} + :resource-id resource-id)))) + (let [row (jobs/get-job cfg job-id)] + (t/is (nil? (:resource-id row))) + (t/is (= "running" (:status row)))))))) + +(t/deftest insert-event-requires-a-transaction + ;; The event insert is only valid inside the transaction that owns the + ;; job row: outside one the assert fires instead of writing. Like every + ;; assert-based guard, this only holds with the backend asserts flag. + (t/is (true? *assert*) + "run the suite with PENPOT_FLAGS=\"enable-backend-asserts\"") + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg)] + (t/testing "a pool cfg with no transaction raises" + (t/is (thrown? AssertionError + (@#'jobs/insert-event cfg job-id "progress" {:stage :half})))) + (t/testing "a bare connection with no transaction raises too" + (db/run! cfg (fn [conn-cfg] + (t/is (thrown? AssertionError + (@#'jobs/insert-event conn-cfg job-id "progress" + {:stage :half})))))))) + +(t/deftest complete-associates-a-resource-id-once + (let [cfg (make-cfg (get-job-defs)) + resource-id (uuid/next)] + (th/db-insert! :storage-object {:id resource-id :backend "test"}) + + (t/testing "a job without resource gets one at completion" + (let [job-id (mk-running cfg)] + (t/is (= 1 (jobs/complete cfg :job-id job-id + :result {:v 1} + :resource-id resource-id))) + (t/is (= resource-id (:resource-id (jobs/get-job cfg job-id)))))) + + (t/testing "completing without a resource leaves the column untouched" + (let [job-id (mk-running cfg)] + (t/is (= 1 (jobs/complete cfg :job-id job-id :result {:v 1}))) + (t/is (nil? (:resource-id (jobs/get-job cfg job-id)))))) + + (t/testing "a job that already has a resource refuses a second one" + (let [job-id (mk-running cfg)] + ;; a job that already carries a resource, as submit left it + (th/db-update! :job {:resource-id resource-id} {:id job-id}) + (t/is (thrown-with-msg? Exception #"already has a resource" + (jobs/complete cfg + :job-id job-id + :result {:v 1} + :resource-id (uuid/next)))) + (t/testing "and the job is still running" + (t/is (= "running" (:status (jobs/get-job cfg job-id))))))) + + (t/testing "a resource that is not a uuid is rejected before any write" + (let [job-id (mk-running cfg)] + (t/is (thrown-with-msg? Exception #"must be a uuid" + (jobs/complete cfg + :job-id job-id + :result {:v 1} + :resource-id "nope"))) + (t/is (= "running" (:status (jobs/get-job cfg job-id)))))))) + +(t/deftest job-error-schema-is-shared-and-validated + (t/testing "type and code are keywords so they decode back from JSON" + (t/is (= {:type :internal :code :orphan :hint "gone"} + (jobs/check-job-error {:type :internal :code :orphan :hint "gone"})))) + + (t/testing "a missing key is rejected" + (t/is (thrown? Exception (jobs/check-job-error {:type :internal :code :orphan}))) + (t/is (thrown? Exception (jobs/check-job-error {:type :internal + :code "orphan" + :hint "gone"})))) + + (t/testing "extra details are allowed" + (t/is (= {:type :internal :code :orphan :hint "gone" :attempt 3} + (jobs/check-job-error {:type :internal + :code :orphan + :hint "gone" + :attempt 3})))) + + (t/testing "fail validates before writing" + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg)] + (t/is (thrown? Exception (jobs/fail cfg job-id {:code :orphan}))) + (t/is (= "running" (:status (jobs/get-job cfg job-id)))) + (t/is (= ["start"] (get-kinds job-id))))) + + (t/testing "the orphan error is a valid job error" + (t/is (= jobs/orphan-error (jobs/check-job-error jobs/orphan-error))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; CONTENTION +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The safety of the writers rests on two things: a conditional UPDATE +;; that only the first caller can win, and a row lock the loser waits +;; on. Both are only meaningful when two callers arrive at once, so the +;; tests below put real threads in front of them. + +(defn- race + "Run every thunk on a thread of its own and return what each returned, + in the order they were given. They all wait on the same gate, so they + contend for whatever they share instead of running one after another." + [& thunks] + (let [gate (promise) + results (mapv (fn [f] + (future + @gate + (f))) + thunks)] + (deliver gate true) + (mapv deref results))) + +(t/deftest the-first-terminal-transition-wins + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg)] + (t/testing "a completion and a failure of the same job, at the same time" + (let [[completed failed] (race #(jobs/complete cfg :job-id job-id :result {:v 1}) + #(jobs/fail cfg job-id test-error))] + (t/is (= #{0 1} (set [completed failed])) + "one transition is applied and the other affects no row")) + + (t/testing "and the durable history agrees with the row that won" + (let [kinds (get-kinds job-id) + outcomes (get-outcomes job-id) + row (jobs/get-job cfg job-id)] + (t/is (= ["start" "end"] kinds) + "exactly one end event, written by whoever won") + (t/is (= 1 (count outcomes))) + (t/is (= (first outcomes) (:status row)) + "the stored outcome is the status of the row")))))) + +(t/deftest the-first-claim-wins + (let [cfg (make-cfg (get-job-defs)) + job-id (jobs/submit cfg {::jobs/name :echo + ::jobs/params (make-params)}) + at (:scheduled-at (jobs/get-job cfg job-id))] + (t/testing "two workers claim the same advertised job at the same time" + (let [[a b] (race #(jobs/claim cfg job-id at) + #(jobs/claim cfg job-id at))] + (t/is (= [0 1] (sort [a b]))) + (t/is (= "running" (:status (jobs/get-job cfg job-id)))) + (t/is (= ["start"] (get-kinds job-id)) + "a job is started once however many workers ask for it"))))) + +(t/deftest a-report-never-lands-on-a-terminal-job + (let [cfg (make-cfg (get-job-defs)) + job-id (mk-running cfg)] + (t/testing "a progress report and a completion of the same job, at once" + ;; the report either lands before the end or loses the race and gets + ;; the interrupt: a beat on a job that is no longer active raises + (let [report (fn [] + (if (some? (raised #(jobs/heartbeat cfg :job-id job-id + :progress {:stage :work} + ::jobs/force? true))) + :interrupted + :reported)) + [outcome completed] (race report #(jobs/complete cfg :job-id job-id :result {:v 1}))] + (t/is (contains? #{:interrupted :reported} outcome) + "the report lands or gets the interrupt, never anything else") + (t/is (= 1 completed) "the completion always wins, it owns the row") + (t/testing "the report either lost the race or was stored before the end" + (let [kinds (get-kinds job-id)] + (t/is (= "start" (first kinds))) + (t/is (= "end" (last kinds)) + "the end is always the last event, so no report came after it") + (t/is (<= 2 (count kinds) 3) + "at most the one report that made it in before the end")))) + (t/testing "a job that ended is not resurrected by a late report" + (let [before (get-kinds job-id) + cause (raised #(jobs/heartbeat cfg :job-id job-id + :progress {:stage :work} + ::jobs/force? true))] + (t/is (= :interrupt (th/ex-type cause))) + (t/is (= before (get-kinds job-id)) "the late report stored nothing")))))) + +(t/deftest progress-event-reaches-the-cfg-of-its-own-callback + "The event helper needs the msgbus and the metrics from the cfg, and it + must find them on the map that `db/tx-run!` hands to the callback: the + writers pass the whole cfg, not a rebuilt one. A cfg missing either + dependency cannot produce a published event, so the publication is + what proves the cfg arrived intact." + (let [cfg (make-cfg (get-job-defs)) + profile-id (:id (th/create-profile* 1)) + messages (atom []) + job-cfg (assoc cfg ::mbus/msgbus (fake-msgbus messages)) + job-id (jobs/submit job-cfg {::jobs/name :echo + ::jobs/params (make-params) + ::jobs/profile-id profile-id})] + (t/is (pos? (jobs/heartbeat job-cfg :job-id job-id + :progress {:stage :work}))) + (t/testing "the event was stored" + (t/is (= [{:stage :work}] (get-progresss job-id)))) + (t/testing "and the msgbus on that cfg was reachable from the callback" + (t/is (= 1 (count @messages))) + (t/is (= "progress" (:kind (:message (first @messages)))))))) diff --git a/backend/test/backend_tests/loggers_audit_consumer_task_test.clj b/backend/test/backend_tests/loggers_audit_consumer_task_test.clj new file mode 100644 index 0000000000..db7de3ace4 --- /dev/null +++ b/backend/test/backend_tests/loggers_audit_consumer_task_test.clj @@ -0,0 +1,835 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.loggers-audit-consumer-task-test + (:require + [app.common.json :as json] + [app.common.schema :as sm] + [app.common.time :as ct] + [app.common.transit :as transit] + [app.common.uri :as u] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.http.client :as-alias http] + [app.jobs :as jobs] + [app.loggers.audit :as audit] + [app.loggers.audit.consumer-task :as consumer] + [app.main :as main] + [app.metrics :as mtx] + [app.metrics.definition :as-alias mdef] + [app.setup :as-alias setup] + [backend-tests.helpers :as th] + [clojure.test :as t] + [cuerdas.core :as str] + [integrant.core :as ig] + [mockery.core :refer [with-mocks]]) + (:import + io.prometheus.client.Counter + io.prometheus.client.Counter$Child)) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + +(def ^:private archive-uri "http://nexus.example/archive") + +(defn- insert-pendings! + "Queue the row with the real producer helper: pendings for the + enabled consumers, like append-audit-entry does." + [row-id] + (consumer/queue-pendings (:app.db/pool th/*system*) [row-id])) + +(defn- pending-count + [] + (:pending (first (th/db-exec! ["SELECT count(*) AS pending FROM audit_log_pending"])))) + +(defn- insert-audit-row! + ([profile-id name] + (insert-audit-row! profile-id name {:team-id (uuid/next)})) + ([profile-id name props] + (let [row (th/db-insert! :audit-log + {:id (uuid/next) + :name name + :type "action" + :source "backend" + :profile-id profile-id + :ip-addr (db/inet "127.0.0.1") + :props (db/tjson props) + :context (db/tjson {}) + :tracked-at (ct/now) + :created-at (ct/now)})] + (insert-pendings! (:id row)) + row))) + +(defn- admin-console-event + [admin-console-mock] + (let [[_ _ params] (:call-args @admin-console-mock)] + (first (:events params)))) + +(defn- admin-console-event-props + [admin-console-mock] + (:props (admin-console-event admin-console-mock))) + +(defn- run-both! + "Run both consumer jobs, Nexus first (the historical single-run + order). The admin-console job takes no params (closed empty schema)." + [params] + (th/run-task! :audit-log-consumer-nexus params) + (th/run-task! :audit-log-consumer-admin-console {})) + +(t/deftest consumer-jobs-drain-their-own-queues + ;; Two separate jobs, one queue each: the nexus job sends without + ;; touching the admin console, and only the admin-console job + ;; deletes the last pending and the row. Create profile before + ;; enabling :admin-console — the system admin-console client is nil + ;; in tests, and team bootstrap would call it. + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:nexus :admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 204}}] + (insert-audit-row! (:id prof) "create-project") + (insert-audit-row! (:id prof) "create-file") + (th/run-task! :audit-log-consumer-nexus {:uri archive-uri}) + (t/is (false? (:called? @admin-console-mock))) + (t/is (:called? @http-mock)) + (t/is (= 1 (:call-count @http-mock))) + ;; Nexus drained its own queue; both rows wait on the admin console. + (t/is (= 2 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 2 (pending-count))) + (th/run-task! :audit-log-consumer-admin-console {}) + (t/is (= 1 (:call-count @admin-console-mock))) + (let [[_ method params] (:call-args @admin-console-mock)] + (t/is (= :ingest-audit-log method)) + (t/is (= 2 (count (:events params)))) + (t/is (= #{"create-project" "create-file"} + (into #{} (map :name) (:events params)))) + (t/is (every? uuid? (map :id (:events params))))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-events-filters-admin-console-event-names + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:nexus :admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 204}}] + (insert-audit-row! (:id prof) "create-project") + (insert-audit-row! (:id prof) "create-organization-attribute") + (insert-audit-row! (:id prof) "update-organization-attribute") + (insert-audit-row! (:id prof) "auditlog-exported") + (insert-audit-row! (:id prof) "unrelated-event") + (run-both! {:uri archive-uri}) + (t/is (:called? @admin-console-mock)) + (t/is (:called? @http-mock)) + (let [[_ _ params] (:call-args @admin-console-mock)] + (t/is (= #{"create-project" + "create-organization-attribute" + "update-organization-attribute" + "auditlog-exported"} + (into #{} (map :name) (:events params))))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-events-skips-admin-console-when-no-matching-names + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:nexus :admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 204}}] + (insert-audit-row! (:id prof) "unrelated-event") + (run-both! {:uri archive-uri}) + (t/is (false? (:called? @admin-console-mock))) + (t/is (:called? @http-mock)) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-events-admin-console-only-marks-without-uri + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 204}}] + (insert-audit-row! (:id prof) "create-project") + (th/run-task! :audit-log-consumer-admin-console {}) + (t/is (:called? @admin-console-mock)) + (t/is (false? (:called? @http-mock))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-events-admin-console-only-marks-non-allowlisted + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 204}}] + (insert-audit-row! (:id prof) "unrelated-event") + (th/run-task! :audit-log-consumer-admin-console {}) + (t/is (false? (:called? @admin-console-mock))) + (t/is (false? (:called? @http-mock))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-events-admin-console-error-still-drains-nexus + ;; Mirror of the nexus-throw test: an admin-console failure aborts + ;; only the admin-console job; Nexus already drained its own queue + ;; in its job. The row stays on the admin-console pending. + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:nexus :admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call + :throw (ex-info "nitrate down" {})} + http-mock {:target 'app.http.client/req :return {:status 204}}] + (insert-audit-row! (:id prof) "create-project") + (th/run-task! :audit-log-consumer-nexus {:uri archive-uri}) + (t/is (thrown? clojure.lang.ExceptionInfo + (th/run-task! :audit-log-consumer-admin-console {}))) + ;; Nexus already drained its queue; the admin-console failure no + ;; longer blocks it, but the row stays on the admin-console pending. + (t/is (:called? @admin-console-mock)) + (t/is (:called? @http-mock)) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 1 (count rows)))) + (t/is (= 1 (pending-count))))))) + +(t/deftest consumer-events-uses-nexus-without-admin-console + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:nexus}] + (with-mocks [http-mock {:target 'app.http.client/req :return {:status 204}} + admin-console-mock {:target 'app.nitrate/call :return nil}] + (insert-audit-row! (:id prof) "create-project") + (th/run-task! :audit-log-consumer-nexus {:uri archive-uri}) + (t/is (false? (:called? @admin-console-mock))) + (t/is (:called? @http-mock)) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-events-skips-mark-when-nexus-fails + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:nexus}] + (with-mocks [http-mock {:target 'app.http.client/req :return {:status 500}}] + (insert-audit-row! (:id prof) "create-project") + (t/is (thrown? clojure.lang.ExceptionInfo + (th/run-task! :audit-log-consumer-nexus {:uri archive-uri}))) + (t/is (:called? @http-mock)) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 1 (count rows)))))))) + +(t/deftest consumer-events-both-flags-skips-mark-when-nexus-fails + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:nexus :admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 500}}] + (insert-audit-row! (:id prof) "create-project") + (t/is (thrown? clojure.lang.ExceptionInfo + (th/run-task! :audit-log-consumer-nexus {:uri archive-uri}))) + (th/run-task! :audit-log-consumer-admin-console {}) + (t/is (:called? @admin-console-mock)) + (t/is (:called? @http-mock)) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 1 (count rows)))))))) + +(t/deftest consumer-events-encodes-db-rows-for-admin-console + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:nexus :admin-console} + cf/config (assoc cf/config + :admin-console-uri "http://ac.example/admin-console/" + :admin-console-shared-key "ac-key" + :nexus-shared-key "nexus-key")] + (let [admin-console-bodies (atom []) + shared-keys {:admin-console "ac-key" :nexus "nexus-key"} + client (ig/init-key :app.nitrate/client + {::http/client (Object.) + ::setup/shared-keys shared-keys}) + ;; Each consumer job is a job-def: the handler closes over the + ;; cfg it was given at init and takes a nil context, because + ;; an in-process invocation has no job row to describe. + nexus-job-def (ig/init-key :app.loggers.audit.consumer-task/nexus-job-def + {::db/pool (:app.db/pool th/*system*) + ::setup/shared-keys shared-keys + ::http/client (:app.http.client/client th/*system*) + ::mtx/metrics (th/metrics-stub) + :app.nitrate/client client}) + admin-console-job-def (ig/init-key :app.loggers.audit.consumer-task/admin-console-job-def + {::db/pool (:app.db/pool th/*system*) + ::mtx/metrics (th/metrics-stub) + :app.nitrate/client client})] + (with-mocks [http-mock {:target 'app.http.client/req + :return + (fn [_ req & _] + (when (str/includes? (str (:uri req)) "api/audit-log") + (swap! admin-console-bodies conj (:body req))) + {:status 204})}] + (let [team-id (uuid/next) + row (th/db-insert! :audit-log + {:id (uuid/next) + :name "create-project" + :type "action" + :source "backend" + :profile-id (:id prof) + :ip-addr (db/inet "127.0.0.1") + :props (db/tjson {:team-id team-id}) + :context (db/tjson {}) + :tracked-at (ct/now) + :created-at (ct/now)})] + (insert-pendings! (:id row)) + ;; Admin console first so its body is captured before Nexus + ;; deletes the last pending and the row. + ((::jobs/handler admin-console-job-def) nil {}) + ((::jobs/handler nexus-job-def) nil {:uri archive-uri}) + (t/is (= 1 (count @admin-console-bodies))) + (let [body (json/decode (first @admin-console-bodies) :key-fn json/read-kebab-key) + event (first (:events body))] + (t/is (= 1 (count (:events body)))) + (t/is (= (str (:id row)) (str (:id event)))) + (t/is (= "create-project" (:name event))) + (t/is (= "127.0.0.1" (:ip-addr event))) + (t/is (string? (:created-at event))) + (t/is (string? (:tracked-at event))) + (t/is (= (str team-id) (str (get-in event [:props :team-id]))))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))))) + +(t/deftest consumer-nexus-default-uri-appends-api-audit + ;; Without an explicit :uri param, the job posts to the Nexus base + ;; URI plus the ingest path, mirroring generate-nitrate-uri. + (let [prof (th/create-profile* 1 {:is-active true}) + uris (atom [])] + (with-redefs [cf/flags #{:nexus} + cf/config (assoc cf/config :nexus-uri (u/uri "http://nexus.example"))] + (with-mocks [http-mock {:target 'app.http.client/req + :return (fn [_ params & _] + (swap! uris conj (str (:uri params))) + {:status 204})}] + (insert-audit-row! (:id prof) "create-project") + (th/run-task! :audit-log-consumer-nexus {}) + (t/is (= ["http://nexus.example/api/audit"] @uris)) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"])))))))) + +(t/deftest consumer-nexus-job-requires-uri-when-nexus-flag-set + (with-redefs [cf/flags #{:nexus} + cf/config (dissoc cf/config :nexus-uri)] + (let [data (ex-data (try + (th/run-task! :audit-log-consumer-nexus {}) + (catch Throwable cause + cause)))] + (t/is (= :task-not-configured (:code data)))))) + +(t/deftest consumer-events-enriches-team-name-and-emails + (let [actor (th/create-profile* 1 {:is-active true}) + member (th/create-profile* 2 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id actor)})] + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil}] + (insert-audit-row! (:id actor) "create-project" + {:team-id (:id team) + :profile-id (:id actor) + :user-id (:id member) + :member-id (:id member) + ;; Producers store this as a string UUID. + :user-who-send-invitation (str (:id actor))}) + (th/run-task! :audit-log-consumer-admin-console {}) + (t/is (:called? @admin-console-mock)) + (let [event (admin-console-event admin-console-mock) + props (:props event)] + (t/is (= (:name team) (:team-name props))) + (t/is (= (:email actor) (:profile-email props))) + (t/is (= (:email member) (:user-email props))) + (t/is (= (:email member) (:member-email props))) + (t/is (= (:email actor) (:user-who-send-invitation-email props))) + ;; Top-level actor fields (event :profile-id), distinct from props. + (t/is (= (:email actor) (:profile-email event))) + (t/is (= (:fullname actor) (:profile-name event)))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-events-enriches-soft-deleted-team-and-profile + (let [actor (th/create-profile* 1 {:is-active true}) + other (th/create-profile* 2 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id actor)})] + (th/db-update! :team {:deleted-at (ct/now)} {:id (:id team)}) + (th/db-update! :profile {:deleted-at (ct/now)} {:id (:id other)}) + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil}] + (insert-audit-row! (:id actor) "create-project" + {:team-id (:id team) + :profile-id (:id other)}) + (th/run-task! :audit-log-consumer-admin-console {}) + (let [event (admin-console-event admin-console-mock) + props (:props event)] + (t/is (= (:name team) (:team-name props))) + (t/is (= (:email other) (:profile-email props))) + (t/is (= (:email actor) (:profile-email event))) + (t/is (= (:fullname actor) (:profile-name event)))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-events-enriches-soft-deleted-actor-profile + (let [actor (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id actor)})] + (th/db-update! :profile {:deleted-at (ct/now)} {:id (:id actor)}) + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil}] + (insert-audit-row! (:id actor) "create-project" + {:team-id (:id team)}) + (th/run-task! :audit-log-consumer-admin-console {}) + (let [event (admin-console-event admin-console-mock)] + (t/is (= (:email actor) (:profile-email event))) + (t/is (= (:fullname actor) (:profile-name event)))))))) + +(t/deftest consumer-events-omits-actor-profile-when-no-usable-id + (let [actor (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id actor)}) + missing (uuid/next)] + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil}] + (insert-audit-row! uuid/zero "create-project" {:team-id (:id team)}) + (insert-audit-row! missing "create-team" {:team-id (:id team)}) + (th/run-task! :audit-log-consumer-admin-console {}) + (t/is (:called? @admin-console-mock)) + (let [[_ _ params] (:call-args @admin-console-mock) + events (:events params) + by-name (into {} (map (juxt :name identity) events)) + zero-evt (get by-name "create-project") + missing-evt (get by-name "create-team")] + (t/is (not (contains? zero-evt :profile-email))) + (t/is (not (contains? zero-evt :profile-name))) + (t/is (not (contains? missing-evt :profile-email))) + (t/is (not (contains? missing-evt :profile-name)))))))) + +(t/deftest consumer-events-does-not-overwrite-existing-enrich-props + (let [actor (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id actor)})] + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil}] + (insert-audit-row! (:id actor) "create-project" + {:team-id (:id team) + :team-name "already-set-name" + :profile-id (:id actor) + :profile-email "already-set@example.com" + :user-id (:id actor) + :user-email "already-user@example.com" + :member-id (:id actor) + :member-email "already-member@example.com" + :user-who-send-invitation (str (:id actor)) + :user-who-send-invitation-email "already-inviter@example.com"}) + (th/run-task! :audit-log-consumer-admin-console {}) + (let [props (admin-console-event-props admin-console-mock)] + (t/is (= "already-set-name" (:team-name props))) + (t/is (= "already-set@example.com" (:profile-email props))) + (t/is (= "already-user@example.com" (:user-email props))) + (t/is (= "already-member@example.com" (:member-email props))) + (t/is (= "already-inviter@example.com" + (:user-who-send-invitation-email props)))))))) + +(t/deftest consumer-events-does-not-overwrite-existing-actor-profile-fields + ;; Top-level :profile-email / :profile-name are not stored on the row; + ;; exercise fill-only via the enrich helper directly. + (let [actor (th/create-profile* 1 {:is-active true}) + event {:profile-id (:id actor) + :profile-email "preset@example.com" + :profile-name "Preset Name" + :props {}}] + (db/tx-run! th/*system* + (fn [cfg] + (let [enriched (first (#'consumer/enrich-events-for-admin-console + cfg [event]))] + (t/is (= "preset@example.com" (:profile-email enriched))) + (t/is (= "Preset Name" (:profile-name enriched)))))))) + +(t/deftest consumer-events-fills-missing-actor-profile-field-independently + ;; Each top-level key fills on its own; guards an or→and regression in + ;; collect-actor-ids-needing-profile. + (let [actor (th/create-profile* 1 {:is-active true})] + (db/tx-run! th/*system* + (fn [cfg] + (let [email-only (first (#'consumer/enrich-events-for-admin-console + cfg [{:profile-id (:id actor) + :profile-email "preset@example.com" + :props {}}])) + name-only (first (#'consumer/enrich-events-for-admin-console + cfg [{:profile-id (:id actor) + :profile-name "Preset Name" + :props {}}]))] + (t/is (= "preset@example.com" (:profile-email email-only))) + (t/is (= (:fullname actor) (:profile-name email-only))) + (t/is (= (:email actor) (:profile-email name-only))) + (t/is (= "Preset Name" (:profile-name name-only)))))))) + +(t/deftest consumer-events-omits-empty-actor-profile-name + ;; profile.fullname is NOT NULL DEFAULT ''; empty must not become + ;; :profile-name "". + (let [actor (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id actor)})] + (th/db-update! :profile {:fullname ""} {:id (:id actor)}) + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil}] + (insert-audit-row! (:id actor) "create-project" {:team-id (:id team)}) + (th/run-task! :audit-log-consumer-admin-console {}) + (let [event (admin-console-event admin-console-mock)] + (t/is (= (:email actor) (:profile-email event))) + (t/is (not (contains? event :profile-name)))))))) + +(t/deftest consumer-events-enrich-skips-missing-and-non-uuid-ids + (let [actor (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil}] + (insert-audit-row! (:id actor) "create-project" + {:team-id (uuid/next) + :profile-id (uuid/next) + :user-id "not-a-uuid" + :member-id 42}) + (th/run-task! :audit-log-consumer-admin-console {}) + (t/is (:called? @admin-console-mock)) + (let [props (admin-console-event-props admin-console-mock)] + (t/is (nil? (:team-name props))) + (t/is (nil? (:profile-email props))) + (t/is (nil? (:user-email props))) + (t/is (nil? (:member-email props))) + (t/is (= "not-a-uuid" (:user-id props))) + (t/is (= 42 (:member-id props)))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-events-forwards-export-job-not-legacy-export + (let [actor (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id actor)})] + (with-redefs [cf/flags #{:admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil}] + (insert-audit-row! (:id actor) "create-export-binfile-job" + {:team-id (:id team) + :job-id (uuid/next) + :files 1 + :export-type "detach-libraries"}) + (insert-audit-row! (:id actor) "export-binfile" + {:file-id (uuid/next)}) + (th/run-task! :audit-log-consumer-admin-console {}) + (let [[_ _ params] (:call-args @admin-console-mock) + events (:events params) + props (:props (first events))] + (t/is (= ["create-export-binfile-job"] (mapv :name events))) + (t/is (= (:id team) (:team-id props))) + (t/is (= (:name team) (:team-name props)))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +(t/deftest consumer-params-accept-empty-cron-props + ;; Cron always submits (or props {}) — system deps live on the + ;; job-def cfg, not in params. Requiring them here blocks every tick. + ;; Both schemas are closed: a stale manual invocation must fail + ;; validation loudly instead of skipping silently. + (let [nexus-valid? (sm/validator consumer/schema:audit-log-consumer-nexus-params) + admin-console-valid? (sm/validator consumer/schema:audit-log-consumer-admin-console-params)] + (t/is (true? (nexus-valid? {}))) + (t/is (false? (nexus-valid? {:enabled true}))) + (t/is (true? (nexus-valid? {:uri (u/uri archive-uri)}))) + (t/is (true? (admin-console-valid? {}))) + (t/is (false? (admin-console-valid? {:enabled true}))) + (t/is (false? (admin-console-valid? {:uri (u/uri archive-uri)}))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; METRICS SMOKE TEST +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; The job-def cfg of these tests carries an isolated metrics registry +;; (pattern from `jobs-metrics-test`), so assertions read exact values +;; instead of the shared test-system registry. `th/run-task!` would use +;; the latter and its counts would leak across tests. +(def ^:private consumer-metric-ids + [:audit-consumer-events :audit-consumer-discarded]) + +(defn- make-consumer-metrics + [] + (ig/init-key :app.metrics/metrics + {:default (select-keys main/default-metrics consumer-metric-ids)})) + +(defn- invoke-nexus! + "Invoke the nexus consumer handler in-process with an isolated + metrics registry. The handler takes a nil context because there is + no job row to describe (same as `th/run-task!`)." + [metrics params] + (let [job-def (ig/init-key :app.loggers.audit.consumer-task/nexus-job-def + {::db/pool (:app.db/pool th/*system*) + ::setup/shared-keys {:admin-console "ac-key" :nexus "nexus-key"} + ::http/client (:app.http.client/client th/*system*) + ::mtx/metrics metrics})] + ((::jobs/handler job-def) nil params))) + +(defn- invoke-admin-console! + "Invoke the admin-console consumer handler in-process with an + isolated metrics registry. The admin-console client is the (nil in + tests) system one: tests mock `app.nitrate/call`, so it is never + dereferenced." + [metrics] + (let [job-def (ig/init-key :app.loggers.audit.consumer-task/admin-console-job-def + {::db/pool (:app.db/pool th/*system*) + :app.nitrate/client (:app.nitrate/client th/*system*) + ::mtx/metrics metrics})] + ((::jobs/handler job-def) nil {}))) + +(defn- invoke-both! + "Invoke both consumer handlers sharing one isolated metrics + registry, Nexus first (the historical single-run order)." + [metrics params] + (invoke-nexus! metrics params) + (invoke-admin-console! metrics)) + +(defn- counter-value + [metrics id labels] + (let [collector (mtx/get-collector metrics id) + instance (::mdef/instance collector) + child (.labels ^Counter instance (into-array String labels))] + (.get ^Counter$Child child))) + +(t/deftest consumer-records-metrics-on-success + ;; Smoke test: the run records its domain metrics into the wired + ;; registry (isolated per test, pattern from `jobs-metrics-test`). + ;; Logging itself is not asserted: `dbg` summaries run below the + ;; test log level and failure `warn`s only repeat the outcome the + ;; metrics already prove. + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-consumer-metrics)] + (with-redefs [cf/flags #{:nexus :admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 204}}] + (insert-audit-row! (:id prof) "create-project") + (insert-audit-row! (:id prof) "unrelated-event") + (invoke-both! metrics {:uri archive-uri}) + (t/is (= 2.0 (counter-value metrics :audit-consumer-events ["nexus" "archived"]))) + (t/is (= 1.0 (counter-value metrics :audit-consumer-events ["admin-console" "archived"]))) + (t/is (= 1.0 (counter-value metrics :audit-consumer-events ["admin-console" "skipped"]))) + (t/is (= 0 (pending-count))) + (let [rows (th/db-exec! ["select * from audit_log"])] + (t/is (= 0 (count rows)))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PENDING-QUEUE TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- full-event + "A complete event for submit*, which (unlike submit) assumes every + required field is filled." + [profile-id] + {:name "create-file" :type "action" :source "backend" + :profile-id profile-id :ip-addr "127.0.0.1" + :props {} :context {} + :created-at (ct/now) :tracked-at (ct/now)}) + +(t/deftest consumer-submit-creates-pendings-for-enabled-consumers + ;; The producer hook: a row is born pending for the enabled + ;; consumers, in the same transaction as the row itself. Goes + ;; through submit* because the suite stubs submit itself (see + ;; helpers/init-config). + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:audit-log :nexus :admin-console}] + (audit/submit* {::db/pool (:app.db/pool th/*system*)} + (full-event (:id prof))) + (t/is (= 1 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 2 (pending-count)))))) + +(t/deftest consumer-submit-single-flag-creates-one-pending + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:audit-log :nexus}] + (audit/submit* {::db/pool (:app.db/pool th/*system*)} + (full-event (:id prof))) + (t/is (= 1 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 1 (pending-count)))))) + +(t/deftest consumer-submit-rollback-keeps-no-pendings + ;; A rollback takes the row and its queue together: a row never + ;; outlives its pendings, and no phantom pending pins nothing. + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:audit-log :nexus :admin-console}] + (db/tx-run! {::db/pool (:app.db/pool th/*system*) + ::db/rollback true} + (fn [cfg] + (audit/submit* cfg (full-event (:id prof))) + (t/is (= 1 (count (db/exec! cfg ["select * from audit_log"])))) + (t/is (= 2 (count (db/exec! cfg ["select * from audit_log_pending"])))))) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 0 (pending-count)))))) + +(t/deftest consumer-keeps-row-until-last-consumer-done + ;; Nexus refuses the chunk: the nexus job aborts (failed count + ;; recorded, isolated registry) while the admin-console job still + ;; drains its own queue. The row and the nexus pending stay; once + ;; nexus accepts, everything is gone. + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-consumer-metrics)] + (with-redefs [cf/flags #{:nexus :admin-console}] + (insert-audit-row! (:id prof) "create-project") + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 500}}] + (let [err (try + (invoke-nexus! metrics {:uri archive-uri}) + nil + (catch clojure.lang.ExceptionInfo cause cause))] + (t/is (= :consumer-chunk-refused (:code (ex-data err))))) + (invoke-admin-console! metrics) + (t/is (:called? @admin-console-mock)) + (t/is (= 1.0 (counter-value metrics :audit-consumer-events ["nexus" "failed"]))) + (t/is (= 1 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 1 (pending-count)))) + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 204}}] + (run-both! {:uri archive-uri}) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 0 (pending-count))))))) + +(t/deftest consumer-sweeps-orphan-rows + ;; A row with no pendings (old archived leftover, or born while no + ;; consumer was on) is deleted without any send. + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:nexus :admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 204}}] + ;; Raw insert, no pendings on purpose. + (th/db-insert! :audit-log + {:id (uuid/next) + :name "create-project" + :type "action" + :source "backend" + :profile-id (:id prof) + :ip-addr (db/inet "127.0.0.1") + :props (db/tjson {}) + :context (db/tjson {}) + :tracked-at (ct/now) + :created-at (ct/now)}) + (run-both! {:uri archive-uri}) + (t/is (false? (:called? @admin-console-mock))) + (t/is (false? (:called? @http-mock))) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"])))))))) + +(t/deftest consumer-nexus-transport-failure-records-failed + ;; A transport exception (timeout, DNS, refused) counts the events + ;; as failed and aborts the nexus job: the chunk rolls back and + ;; the rows stay for the next tick. + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-consumer-metrics)] + (with-redefs [cf/flags #{:nexus}] + (with-mocks [http-mock {:target 'app.http.client/req + :throw (ex-info "timeout" {})}] + (insert-audit-row! (:id prof) "create-project") + (t/is (thrown? clojure.lang.ExceptionInfo + (invoke-nexus! metrics {:uri archive-uri}))) + (t/is (= 1.0 (counter-value metrics :audit-consumer-events ["nexus" "failed"]))) + (t/is (= 1 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 1 (pending-count))))))) + +(t/deftest consumer-sends-chunks-in-chronological-order + ;; Nexus numbers by arrival for order-sensitive consumers, so + ;; multi-chunk drains must arrive oldest-first no matter how the + ;; queue is scanned. + (let [prof (th/create-profile* 1 {:is-active true}) + base (ct/now) + bodies (atom [])] + (with-redefs [cf/flags #{:nexus}] + (with-mocks [http-mock {:target 'app.http.client/req + :return (fn [_ params & _] + (swap! bodies conj (:body params)) + {:status 204})}] + (doseq [i (range 130)] + (let [row (insert-audit-row! (:id prof) (str "event-" i))] + (th/db-update! :audit-log + {:created-at (.plusSeconds ^java.time.Instant base (long i))} + {:id (:id row)}))) + (th/run-task! :audit-log-consumer-nexus {:uri archive-uri}) + (let [received (mapcat #(:events (transit/decode %)) @bodies) + times (map :created-at received)] + (t/is (= 130 (count received))) + (t/is (= (sort times) times)) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"]))))))))) + +(t/deftest consumer-reaps-disabled-consumer-pendings + ;; R3 reap by its real path: an admin-console pending born while + ;; admin-console was on is dropped once it is off; nexus still + ;; archives its queue and the row is gone with nothing stuck. The + ;; dropped pending counts as discarded (isolated registry). + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-consumer-metrics)] + (with-redefs [cf/flags #{:nexus}] + (with-mocks [http-mock {:target 'app.http.client/req :return {:status 204}} + admin-console-mock {:target 'app.nitrate/call :return nil}] + (let [row (insert-audit-row! (:id prof) "create-project")] + ;; Admin console was on at insert time: both pendings on purpose. + (th/db-exec! ["INSERT INTO audit_log_pending (audit_log_id, consumer) + VALUES (?::uuid, 2) + ON CONFLICT DO NOTHING" + (:id row)]) + (t/is (= 2 (pending-count))) + (invoke-nexus! metrics {:uri archive-uri}) + (t/is (:called? @http-mock)) + (t/is (false? (:called? @admin-console-mock))) + (t/is (= 1.0 (counter-value metrics :audit-consumer-discarded ["reaped"]))) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 0 (pending-count)))))))) + +(t/deftest consumer-sweeps-rows-born-with-no-consumer-on + ;; Accepted R3 consequence by its real path (not a hand-made + ;; orphan): ingested with no send flag on, the row is swept + ;; unarchived once the task runs, with zero sends. + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-consumer-metrics)] + (with-redefs [cf/flags #{:audit-log}] + (audit/submit* {::db/pool (:app.db/pool th/*system*)} + (full-event (:id prof))) + (t/is (= 1 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 0 (pending-count)))) + (with-redefs [cf/flags #{:audit-log :nexus}] + (with-mocks [http-mock {:target 'app.http.client/req :return {:status 204}}] + (invoke-nexus! metrics {:uri archive-uri}) + (t/is (false? (:called? @http-mock))) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 0 (pending-count))) + (t/is (= 1.0 (counter-value metrics :audit-consumer-discarded ["swept"]))) + (t/is (= 0.0 (counter-value metrics :audit-consumer-events ["nexus" "archived"]))))))) + +(t/deftest consumer-nexus-throw-still-drains-admin-console + ;; Consumer independence, now structural: a Nexus transport throw + ;; aborts only the nexus job, and the admin-console job still drains + ;; its own queue. Nexus keeps its pending (so the row stays); the + ;; admin console completes its own queue. + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-consumer-metrics)] + (with-redefs [cf/flags #{:nexus :admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req + :throw (ex-info "timeout" {})}] + (insert-audit-row! (:id prof) "create-project") + (t/is (thrown? clojure.lang.ExceptionInfo + (invoke-nexus! metrics {:uri archive-uri}))) + (invoke-admin-console! metrics) + (t/is (:called? @admin-console-mock)) + (t/is (= 1.0 (counter-value metrics :audit-consumer-events ["nexus" "failed"]))) + (t/is (= 1 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 1 (pending-count))))))) + +(t/deftest consumer-reap-then-sweeps-without-sends + ;; End to end of the reap path: a row born with only an + ;; admin-console pending is reaped once :admin-console goes off, + ;; then swept with zero sends on either destination. + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:admin-console}] + (insert-audit-row! (:id prof) "create-project") + (t/is (= 1 (pending-count)))) + (with-redefs [cf/flags #{:nexus}] + (with-mocks [http-mock {:target 'app.http.client/req :return {:status 204}} + admin-console-mock {:target 'app.nitrate/call :return nil}] + (th/run-task! :audit-log-consumer-nexus {:uri archive-uri}) + (t/is (false? (:called? @http-mock))) + (t/is (false? (:called? @admin-console-mock))) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 0 (pending-count))))))) + +(t/deftest consumer-fully-filtered-admin-console-chunk-counts-skipped + ;; An admin-console chunk with every event outside the allowlist + ;; never calls ingest, but still counts its rows as skipped and + ;; completes its queue. + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-consumer-metrics)] + (with-redefs [cf/flags #{:nexus :admin-console}] + (with-mocks [admin-console-mock {:target 'app.nitrate/call :return nil} + http-mock {:target 'app.http.client/req :return {:status 204}}] + (insert-audit-row! (:id prof) "unrelated-event") + (invoke-both! metrics {:uri archive-uri}) + (t/is (false? (:called? @admin-console-mock))) + (t/is (= 1.0 (counter-value metrics :audit-consumer-events ["admin-console" "skipped"]))) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 0 (pending-count))))))) diff --git a/backend/test/backend_tests/loggers_webhooks_test.clj b/backend/test/backend_tests/loggers_webhooks_test.clj index 5bad72051b..e2c540119c 100644 --- a/backend/test/backend_tests/loggers_webhooks_test.clj +++ b/backend/test/backend_tests/loggers_webhooks_test.clj @@ -7,11 +7,12 @@ (ns backend-tests.loggers-webhooks-test (:require [app.common.time :as ct] + [app.common.transit :as transit] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] [app.http :as http] - [app.loggers.audit :as audit] + [app.jobs :as jobs] [app.storage :as sto] [backend-tests.helpers :as th] [clojure.test :as t] @@ -27,58 +28,49 @@ (db/pgobject? context) (assoc :context (db/decode-transit-pgobject context)))) (t/deftest process-event-handler-with-no-webhooks - (with-mocks [submit-mock {:target 'app.worker/submit! :return nil}] + (with-mocks [submit-mock {:target 'app.jobs/submit :return nil}] (let [prof (th/create-profile* 1 {:is-active true}) res (th/run-task! :process-webhook-event - {:type "command" - :name "create-project" - :props {:team-id (:default-team-id prof)}})] + {:event-blob + (transit/encode-str + {:type "command" + :name "create-project" + :props {:team-id (:default-team-id prof)}})})] (t/is (= 0 (:call-count @submit-mock))) (t/is (nil? res))))) (t/deftest process-event-handler - (with-mocks [submit-mock {:target 'app.worker/submit! :return nil}] + (with-mocks [submit-mock {:target 'app.jobs/submit :return nil}] (let [prof (th/create-profile* 1 {:is-active true}) whk (th/create-webhook* {:team-id (:default-team-id prof)}) res (th/run-task! :process-webhook-event - {:type "command" - :name "create-project" - :props {:team-id (:default-team-id prof)}})] + {:event-blob + (transit/encode-str + {:type "command" + :name "create-project" + :props {:team-id (:default-team-id prof)}})})] (t/is (= 1 (:call-count @submit-mock))) (t/is (nil? res))))) -(t/deftest process-event-handler-persists-origin-event-id - ;; End-to-end regression test for the `webhook/trigger` row: its props - ;; `:event-id` must hold the origin event row id. It used to be nil - ;; because `process-event` never forwarded the id to the worker task. - (with-mocks [submit-mock {:target 'app.worker/submit! :return nil}] - (with-redefs [cf/flags #{:audit-log :webhooks}] - (let [prof (th/create-profile* 1 {:is-active true}) - event {:type "action" - :name "delete-project" - :profile-id (:id prof) - :props {:team-id (:default-team-id prof)} - :context {:version "2.0.0" :initiator "app"} - :tracked-at (ct/now) - :created-at (ct/now) - :source "backend" - :app.loggers.webhooks/event? true}] - (audit/submit* th/*system* event) - (let [params (-> @submit-mock :call-args-list ffirst :app.worker/params)] - (t/is (some? (:id params))) - ;; Run the handler the way the worker would. - (th/run-task! :process-webhook-event params) - (let [[origin trigger :as rows] (->> (th/db-exec! ["select * from audit_log order by created_at"]) - (mapv decode-row))] - (t/is (= 2 (count rows))) - (t/is (= "delete-project" (:name origin))) - (t/is (= "webhook" (:name trigger))) - (t/is (= "trigger" (:type trigger))) - (t/is (= "delete-project" (get-in trigger [:props :name]))) - (t/is (= (:id origin) (get-in trigger [:props :event-id]))) - (t/is (= 0 (get-in trigger [:props :total-affected]))))))))) +(t/deftest process-event-submits-consumed-config-subset + (with-mocks [submit-mock {:target 'app.jobs/submit :return nil}] + (let [prof (th/create-profile* 1 {:is-active true}) + whk (th/create-webhook* {:team-id (:default-team-id prof)})] + (th/run-task! :process-webhook-event + {:event-blob + (transit/encode-str + {:type "command" + :name "create-project" + :props {:team-id (:default-team-id prof)}})}) + (t/is (= 1 (:call-count @submit-mock))) + (let [options (second (:call-args @submit-mock)) + config (get-in options [::jobs/params :config])] + (t/is (= #{:id :uri :mtype} (set (keys config)))) + (t/is (= (:id whk) (:id config))) + (t/is (= (:uri whk) (:uri config))) + (t/is (= (:mtype whk) (:mtype config))))))) (t/deftest run-webhook-handler-1 (with-mocks [http-mock {:target 'app.http.client/req :return {:status 200}}] @@ -88,7 +80,7 @@ :name "create-project" :props {:team-id (:default-team-id prof)}} res (th/run-task! :run-webhook - {:event evt + {:event (transit/encode-str evt) :config whk})] (t/is (= 1 (:call-count @http-mock))) @@ -110,7 +102,7 @@ :name "create-project" :props {:team-id (:default-team-id prof)}} res (th/run-task! :run-webhook - {:event evt + {:event (transit/encode-str evt) :config whk})] (t/is (= 1 (:call-count @http-mock))) @@ -128,11 +120,11 @@ ;; RUN 2 times more (th/run-task! :run-webhook - {:event evt + {:event (transit/encode-str evt) :config whk}) (th/run-task! :run-webhook - {:event evt + {:event (transit/encode-str evt) :config whk}) @@ -145,3 +137,64 @@ (t/is (= "unexpected-status:400" (:error-code whk'))) (t/is (= 3 (:error-count whk'))) (t/is (false? (:is-active whk'))))))) + +(t/deftest webhook-json-round-trip-preserves-uuid-types + "Integration test: drives process-webhook-event through the real pipeline + (submit → decode → handler) to verify uuid types survive: the JSON hop + carries only the opaque transit blob, and the handler decodes the typed + event from it." + (with-mocks [http-mock {:target 'app.http.client/req :return {:status 200}}] + (let [prof (th/create-profile* 1 {:is-active true}) + whk (th/create-webhook* {:team-id (:default-team-id prof)}) + evt {:type "command" + :name "create-project" + :props {:team-id (:default-team-id prof)}}] + + ;; Submit the job through the real pipeline (this creates a job row) + (th/run-task! :process-webhook-event + {:event-blob (transit/encode-str evt)}) + + ;; Now run the pending jobs (simulates dispatcher + runner) + ;; This will decode the params from JSON and invoke the handler + (th/run-pending-jobs) + + ;; The handler should have been invoked and created a webhook_delivery row + ;; If uuid types were lost, the lookup would fail with a type error + (let [rows (th/db-query :webhook-delivery {:webhook-id (:id whk)})] + (t/is (= 1 (count rows))) + (t/is (nil? (-> rows first :error-code)))) + + ;; Refresh webhook + (let [whk' (th/db-get :webhook {:id (:id whk)})] + (t/is (nil? (:error-code whk'))))))) + +(t/deftest webhook-transit-round-trip-preserves-uuid-types + "The run-webhook event travels as the original transit blob inside the + JSON job props, so transit deliveries keep instant/UUID types (no wire + change vs the legacy worker), whatever shape the event has." + (with-mocks [http-mock {:target 'app.http.client/req :return {:status 200}}] + (let [prof (th/create-profile* 1 {:is-active true}) + whk (th/create-webhook* {:team-id (:default-team-id prof) + :mtype "application/transit+json"}) + evt {:type "command" + :name "create-project" + :created-at (ct/now) + :tracked-at (ct/now) + :props {:team-id (:default-team-id prof) + :comment-id (uuid/next)}}] + + ;; through the real pipeline: process submits run-webhook, the + ;; runner decodes it from JSON and delivers it + (th/run-task! :process-webhook-event + {:event-blob (transit/encode-str evt)}) + (th/run-pending-jobs) + + (t/is (= 1 (:call-count @http-mock))) + (let [req (second (:call-args @http-mock)) + event (transit/decode-str (:body req))] + (t/is (= "application/transit+json" (get-in req [:headers "content-type"]))) + (t/is (uuid? (get-in event [:props :team-id]))) + (t/is (uuid? (get-in event [:props :comment-id]))) + (t/is (inst? (:created-at event))) + (t/is (inst? (:tracked-at event))))))) + diff --git a/backend/test/backend_tests/mcp_oauth_clients_test.clj b/backend/test/backend_tests/mcp_oauth_clients_test.clj new file mode 100644 index 0000000000..04db71a48b --- /dev/null +++ b/backend/test/backend_tests/mcp_oauth_clients_test.clj @@ -0,0 +1,219 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.mcp-oauth-clients-test + (:require + [app.mcp.oauth-clients :as clients] + [app.mcp.oauth-state :as state] + [app.util.json :as json] + [backend-tests.helpers :as th] + [clojure.string :as str] + [clojure.test :as t] + [mockery.core :refer [with-mocks]])) + +(t/use-fixtures :once th/state-init) + +(t/deftest registered-clients + (let [registration (clients/register! {} + {:client_name "Example" + :redirect_uris ["https://client.example/callback"]}) + client (clients/resolve-client {} (:client_id registration))] + (t/is (string? (:client_secret registration))) + (t/is (nil? (:client_secret client))) + (t/is (state/secret= (:secret-hash client) (state/digest (:client_secret registration)))) + (t/is (clients/redirect-matches? client "https://client.example/callback")) + (doseq [redirect ["https://client.example/callback?extra=1" + "https://client.example/callback/../evil" + "https://evil.example/callback"]] + (t/is (not (clients/redirect-matches? client redirect)))))) + +(t/deftest registrations-are-self-contained-and-distinct + (doseq [method ["none" "client_secret_basic" "client_secret_post"]] + (let [metadata {:client_name "Example α" + :logo_uri "https://client.example/logo.png" + :redirect_uris ["https://client.example/callback?source=mcp&mode=oauth"] + :token_endpoint_auth_method method} + first (clients/register! {} metadata) + second (clients/register! {} metadata) + client (clients/resolve-client {} (:client_id first))] + (t/is (not= (:client_id first) (:client_id second))) + (t/is (<= (count (:client_id first)) 2048)) + (t/is (= metadata (select-keys client (keys metadata)))) + (t/is (= (:client_id first) (:client_id client))) + (t/is (nil? (:client_secret client))) + (if (= method "none") + (do (t/is (nil? (:client_secret first))) + (t/is (nil? (:secret-hash client)))) + (t/is (state/secret= (:secret-hash client) + (state/digest (:client_secret first)))))))) + +(t/deftest oversized-registration-is-rejected + (let [metadata {:redirect_uris [(str "https://client.example/" (apply str (repeat 1800 "a")))]}] + (t/is (= "invalid_client_metadata" + (try (clients/register! {} metadata) + (catch clojure.lang.ExceptionInfo cause (:oauth-error (ex-data cause)))))))) + +(t/deftest unusable-client-logos-are-ignored + ;; An optional image must not prevent a client from authorizing. + (doseq [logo [nil "" 42 "/logo.png" "http://client.example/logo.png" + "javascript:alert(1)" "data:image/svg+xml," + "https://user:secret@client.example/logo.png" + (str "https://client.example/" (apply str (repeat 2048 "a")))]] + (let [client (clients/validate-metadata + {:redirect_uris ["https://client.example/callback"] + :logo_uri logo} + false)] + (t/is (not (contains? client :logo_uri)))))) + +(t/deftest client-id-size-boundary + (let [callback "https://client.example/callback/" + metadata {:redirect_uris [callback] :token_endpoint_auth_method "none"} + registration (clients/register! {} metadata) + [_ encoded] (str/split (:client_id registration) #"\." 2) + payload-size (alength (.decode (java.util.Base64/getUrlDecoder) encoded)) + ;; 1,525 payload bytes encode to 2,034 characters plus the 14-char prefix. + padding (apply str (repeat (- 1525 payload-size) "a")) + metadata (assoc metadata :redirect_uris [(str callback padding)]) + largest (clients/register! {} metadata)] + (t/is (= 2048 (count (:client_id largest)))) + (t/is (= (:redirect_uris metadata) + (:redirect_uris (clients/resolve-client {} (:client_id largest))))) + (t/is (= "invalid_client_metadata" + (try (clients/register! {} (update-in metadata [:redirect_uris 0] str "a")) + (catch clojure.lang.ExceptionInfo cause (:oauth-error (ex-data cause)))))))) + +(t/deftest malformed-client-identifiers-are-rejected + (doseq [id ["unknown" "penpot-mcp-v2.e30" "penpot-mcp-v1.e30" + "penpot-mcp-v1.W10" "penpot-mcp-v1.%%" "penpot-mcp-v1.bm90LWpzb24" + (str "penpot-mcp-v1." (apply str (repeat 2048 "a")))]] + (t/is (= "invalid_client" + (try (clients/resolve-client {} id) + (catch clojure.lang.ExceptionInfo cause (:oauth-error (ex-data cause)))))))) + +(t/deftest client-metadata-validation + (doseq [redirect ["https://client.example/callback#fragment" "javascript:alert(1)" + "http://client.example/callback" "https://user:secret@client.example/cb"]] + (t/is (thrown? clojure.lang.ExceptionInfo + (clients/validate-metadata {:redirect_uris [redirect]} false)))) + (t/is (clients/redirect-matches? {:redirect_uris ["http://127.0.0.1:3000/callback"]} + "http://127.0.0.1:12345/callback")) + (t/is (= "none" (:token_endpoint_auth_method + (clients/validate-metadata + {:redirect_uris ["https://chatgpt.com/connector_platform_oauth_redirect"] + :token_endpoint_auth_methods_supported ["none" "private_key_jwt"]} + true)))) + (t/is (thrown? clojure.lang.ExceptionInfo + (clients/validate-metadata + {:redirect_uris ["https://example.com/cb"] + :grant_types ["authorization_code" "refresh_token"]} false)))) + +(t/deftest client-metadata-documents-can-advertise-additional-grants + (let [metadata {:redirect_uris ["https://client.example/callback"] + :grant_types ["authorization_code" "refresh_token"]}] + (t/is (= ["authorization_code"] + (:grant_types (clients/validate-metadata metadata true)))))) + +(t/deftest client-metadata-documents-require-authorization-code-support + (doseq [grants [[] ["refresh_token"] "authorization_code" + ["authorization_code" nil] ["authorization_code" 1]]] + (t/is (thrown? clojure.lang.ExceptionInfo + (clients/validate-metadata + {:redirect_uris ["https://client.example/callback"] + :grant_types grants} + true))))) + +(t/deftest localhost-callbacks-allow-only-port-changes + (let [client {:redirect_uris ["http://localhost/callback"]}] + (t/is (clients/redirect-uri? "http://localhost/callback")) + (t/is (clients/redirect-matches? client "http://localhost:52351/callback")) + (doseq [redirect ["http://localhost.evil.example:52351/callback" + "http://127.0.0.1:52351/callback" + "http://localhost:52351/other" + "http://localhost:52351/callback?extra=1" + "http://localhost:52351/callback#fragment" + "http://user@localhost:52351/callback"]] + (t/is (not (clients/redirect-matches? client redirect)))) + (doseq [redirect ["http://localhost.evil.example/callback" + "http://user@localhost/callback"]] + (t/is (not (clients/redirect-uri? redirect)))))) + +(t/deftest codex-published-client-metadata + ;; Public metadata from https://chatgpt.com/oauth/codex/client.json. + ;; Keep this fixture offline so upstream changes cannot make CI flaky. + (let [client-id "https://chatgpt.com/oauth/codex/client.json" + metadata {:client_id client-id + :client_uri "https://chatgpt.com/codex" + :application_type "native" + :redirect_uris ["http://127.0.0.1/callback" "http://localhost/callback"] + :token_endpoint_auth_method "none" + :token_endpoint_auth_methods_supported ["none"] + :grant_types ["authorization_code" "refresh_token"] + :response_types ["code"] + :client_name "Codex" + :logo_uri "https://persistent.oaistatic.com/sonic/misc/openai-logo.png"}] + (with-mocks [dns {:target 'app.util.ssrf/resolve-host + :return (into-array java.net.InetAddress + [(java.net.InetAddress/getByName "93.184.216.34")])} + sender {:target 'app.http.client/send! + :return (fn [& _] + {:status 200 + :headers {"content-type" "application/json" "cache-control" "no-store"} + :body (java.io.ByteArrayInputStream. (json/encode metadata))})}] + (let [client (clients/resolve-client th/*system* client-id)] + (t/is (= client-id (:client_id client))) + (t/is (= (:logo_uri metadata) (:logo_uri client))) + (t/is (= ["authorization_code"] (:grant_types client))) + (t/is (= "none" (:token_endpoint_auth_method client))) + (t/is (clients/redirect-matches? client "http://127.0.0.1:52351/callback")))))) + +(t/deftest client-metadata-documents + (let [client-id "https://client.example/client.json" + calls (atom 0) + metadata (atom {:client_id client-id + :client_name "Example" + :redirect_uris ["https://client.example/callback"] + :token_endpoint_auth_methods_supported ["none" "private_key_jwt"]})] + (with-mocks [dns {:target 'app.util.ssrf/resolve-host + :return (into-array java.net.InetAddress + [(java.net.InetAddress/getByName "93.184.216.34")])} + sender {:target 'app.http.client/send! + :return (fn [_ _ _] + (swap! calls inc) + {:status 200 + :headers {"content-type" "application/json" "cache-control" "no-store"} + :body (java.io.ByteArrayInputStream. + (json/encode @metadata))})}] + (t/is (= "none" (:token_endpoint_auth_method (clients/resolve-client th/*system* client-id)))) + (swap! metadata assoc :redirect_uris ["https://client.example/changed"]) + (t/is (not (clients/redirect-matches? (clients/resolve-client th/*system* client-id) + "https://client.example/callback"))) + (t/is (= 2 @calls)) + (swap! metadata assoc :client_id "https://different.example/client.json") + (t/is (thrown? clojure.lang.ExceptionInfo (clients/resolve-client th/*system* client-id))) + (swap! metadata assoc :client_id client-id :client_name (apply str (repeat 65537 "a"))) + (t/is (thrown? clojure.lang.ExceptionInfo (clients/resolve-client th/*system* client-id)))))) + +(t/deftest client-metadata-rejects-unsafe-fetches + (with-mocks [sender {:target 'app.http.client/send! + :return (fn [& _] (throw (AssertionError. "Unsafe request reached HTTP client")))}] + (doseq [client-id ["https://127.0.0.1/client.json" + "https://169.254.169.254/client.json" + "https://[::1]/client.json" + "https://user:secret@example.com/client.json" + "https://example.com/client.json#fragment"]] + (t/is (thrown? clojure.lang.ExceptionInfo (clients/resolve-client th/*system* client-id)))) + (t/is (not (:called? @sender))))) + +(t/deftest client-metadata-rejects-redirects + (with-mocks [dns {:target 'app.util.ssrf/resolve-host + :return (into-array java.net.InetAddress + [(java.net.InetAddress/getByName "93.184.216.34")])} + sender {:target 'app.http.client/send! + :return {:status 302 + :headers {"location" "https://127.0.0.1/internal"} + :body (java.io.ByteArrayInputStream. (byte-array 0))}}] + (t/is (thrown? clojure.lang.ExceptionInfo + (clients/resolve-client th/*system* "https://redirect.example/client.json"))))) diff --git a/backend/test/backend_tests/mcp_oauth_http_test.clj b/backend/test/backend_tests/mcp_oauth_http_test.clj new file mode 100644 index 0000000000..3a19d8bd93 --- /dev/null +++ b/backend/test/backend_tests/mcp_oauth_http_test.clj @@ -0,0 +1,253 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.mcp-oauth-http-test + (:require + [app.common.uri :as uri] + [app.config :as cf] + [app.db :as db] + [app.http :as http] + [app.http.errors :as errors] + [app.http.mcp-oauth :as oauth] + [app.http.session :as session] + [app.mcp.oauth-clients :as clients] + [app.mcp.oauth-state :as state] + [app.rpc.commands.access-token :as access-token] + [app.setup.clock :as clock] + [app.util.json :as json] + [backend-tests.helpers :as th] + [clojure.string :as str] + [clojure.test :as t] + [yetti.response :as yres]) + (:import java.net.URLEncoder java.nio.charset.StandardCharsets)) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset + (fn [run] + (binding [cf/config (assoc cf/config :tenant (str (java.util.UUID/randomUUID)))] + (run)))) + +(defn- form [params] + (str/join "&" (for [[key value] params] + (str (name key) "=" (URLEncoder/encode (str value) StandardCharsets/UTF_8))))) + +(defn- request [path & {:as options}] + (let [handler (#'http/router-handler (:app.http/router th/*system*))] + (handler (th/make-dummy-request (assoc options :path path))))) + +(defn- body [response] (json/decode (::yres/body response))) + +(defn- login-cookie [profile] + (let [session (session/create-session (:app.http.session/manager th/*system*) + {:profile-id (:id profile)}) + token (:token (#'session/assign-token th/*system* session))] + {(cf/get :auth-token-cookie-name) token})) + +(defn- begin [client] + (let [reply (request "/api/mcp/oauth/authorize" + :query (form {:client_id (:client_id client) + :redirect_uri (first (:redirect_uris client)) + :response_type "code" :scope "mcp" + :resource (oauth/resource) + :state "client-state" + :code_challenge (state/digest (apply str (repeat 43 "a"))) + :code_challenge_method "S256"}))] + (t/is (= 303 (::yres/status reply))) + (get (uri/query-string->map (.getQuery (java.net.URI. (get-in reply [::yres/headers "location"])))) + :request))) + +(t/deftest discovery-and-registration + (let [metadata (body (request "/.well-known/oauth-authorization-server/api/mcp/oauth")) + resource (body (request "/api/mcp/oauth/resource")) + registered (request "/api/mcp/oauth/register" :method :post + :headers {"content-type" "application/json"} + :body-bytes (json/encode-str {:redirect_uris ["https://example.com/callback"] + :token_endpoint_auth_method "none"}))] + (t/is (= (oauth/issuer) (:issuer metadata))) + (t/is (= ["S256"] (:code_challenge_methods_supported metadata))) + (t/is (= (oauth/resource) (:resource resource))) + (t/is (= 201 (::yres/status registered))) + (t/is (= "none" (:token_endpoint_auth_method (body registered)))) + (t/is (nil? (:client_secret (body registered)))))) + +(t/deftest complete-authorization-flow + (let [profile (th/create-profile* 1 {:is-active true}) + cookies (login-cookie profile) + client (clients/register! th/*system* {:redirect_uris ["https://example.com/callback"] + :logo_uri "https://example.com/logo.png" + :token_endpoint_auth_method "none"}) + handle (begin client) + details (request "/api/mcp/oauth/request" :query (form {:request handle}) :cookies cookies) + origin (str (cf/get-public-uri)) + origin (str/replace origin #"/$" "") + decision {:request handle :decision "approve" :csrf (:csrf (body details))} + submit #(request "/api/mcp/oauth/decision" :method :post :cookies cookies + :headers {"content-type" "application/x-www-form-urlencoded" "origin" origin} + :body-bytes (form %))] + (t/is (= 401 (::yres/status (request "/api/mcp/oauth/request" :query (form {:request handle}))))) + (t/is (= 200 (::yres/status details))) + (t/is (= "https://example.com/logo.png" (:logo-uri (body details)))) + (t/is (= 400 (::yres/status (submit (assoc decision :csrf "wrong"))))) + (let [approved (submit decision) + callback (java.net.URI. (:redirect-uri (body approved))) + params (uri/query-string->map (.getQuery callback)) + exchange {:grant_type "authorization_code" :code (:code params) + :client_id (:client_id client) :redirect_uri "https://example.com/callback" + :resource (oauth/resource) :code_verifier (apply str (repeat 43 "a"))} + redeem #(request "/api/mcp/oauth/token" :method :post + :headers {"content-type" "application/x-www-form-urlencoded"} + :body-bytes (form %))] + (t/is (= 200 (::yres/status approved))) + (t/is (= "client-state" (:state params))) + (t/is (= (oauth/issuer) (:iss params))) + (t/is (= 400 (::yres/status (submit decision)))) + ;; Metadata is public and editable, but changing it changes the client ID. + ;; Even with the correct verifier, an edited ID cannot use this approval. + (let [[prefix encoded] (str/split (:client_id client) #"\." 2) + payload (json/decode (.decode (java.util.Base64/getUrlDecoder) encoded))] + (doseq [changed [(assoc payload :client_name "Different application") + (assoc payload :redirect_uris ["https://attacker.example/callback"])]] + (let [id (str prefix "." (.encodeToString (.withoutPadding (java.util.Base64/getUrlEncoder)) + (json/encode changed)))] + (t/is (string? (:client_id (clients/resolve-client {} id)))) + (t/is (= "invalid_grant" (:error (body (redeem (assoc exchange :client_id id))))))))) + (t/is (= 400 (::yres/status (redeem (assoc exchange :code_verifier (apply str (repeat 43 "b"))))))) + (let [token-response (redeem exchange) + token (body token-response) + stored (first (db/query th/*pool* :access-token {:profile-id (:id profile) :type "mcp"}))] + (t/is (= 200 (::yres/status token-response))) + (t/is (= "no-store" (get-in token-response [::yres/headers "cache-control"]))) + (t/is (= (:token stored) (:access_token token))) + (t/is (= "Bearer" (:token_type token))) + (t/is (nil? (:expires_in token))) + (t/is (nil? (:refresh_token token))) + (t/is (= 400 (::yres/status (redeem exchange)))))))) + +(t/deftest denial-and-session-binding + (let [profile (th/create-profile* 1 {:is-active true}) + other (th/create-profile* 2 {:is-active true}) + cookies (login-cookie profile) + client (clients/register! th/*system* {:redirect_uris ["https://example.com/callback"] + :token_endpoint_auth_method "none"}) + handle (begin client) + details (body (request "/api/mcp/oauth/request" :query (form {:request handle}) :cookies cookies)) + params {:request handle :csrf (:csrf details) :decision "deny"} + submit #(request "/api/mcp/oauth/decision" :method :post :cookies cookies + :headers {"content-type" "application/x-www-form-urlencoded" "origin" %} + :body-bytes (form params))] + (t/is (= 403 (::yres/status + (request "/api/mcp/oauth/request" :query (form {:request handle}) + :cookies (login-cookie other))))) + (t/is (= 403 (::yres/status (submit "https://attacker.example")))) + (let [denied (submit (str/replace (str (cf/get-public-uri)) #"/$" "")) + callback (java.net.URI. (:redirect-uri (body denied))) + result (uri/query-string->map (.getQuery callback))] + (t/is (= "access_denied" (:error result))) + (t/is (= "client-state" (:state result))) + (t/is (empty? (db/query th/*pool* :access-token {:profile-id (:id profile)}))) + (t/is (not (:mcp-enabled (db/decode-transit-pgobject + (:props (th/db-get :profile {:id (:id profile)}))))))))) + +(t/deftest oauth-input-and-cors-errors + (doseq [data ["{" "[]" (apply str (repeat 16385 "a"))]] + (let [reply (request "/api/mcp/oauth/register" :method :post + :headers {"content-type" "application/json"} + :body-bytes data)] + (t/is (= 400 (::yres/status reply))) + (t/is (= "*" (get-in reply [::yres/headers "access-control-allow-origin"]))))) + (let [reply (request "/api/mcp/oauth/token" :method :post + :headers {"content-type" "application/x-www-form-urlencoded"} + :body-bytes "code=a&code=b")] + (t/is (= 400 (::yres/status reply)))) + (t/is (= 204 (::yres/status (request "/api/mcp/oauth/token" :method :options))))) + +(t/deftest confidential-client-exchange + (let [profile (th/create-profile* 1 {:is-active true}) + stored (db/tx-run! th/*system* access-token/ensure-mcp-access! (:id profile)) + verifier (apply str (repeat 43 "a"))] + (doseq [method ["client_secret_basic" "client_secret_post"]] + (let [client (clients/register! th/*system* + {:redirect_uris ["https://example.com/callback"] + :token_endpoint_auth_method method}) + code (state/create! th/*system* :code + {:client-id (:client_id client) + :redirect-uri "https://example.com/callback" + :resource (oauth/resource) + :challenge (state/digest verifier) + :profile-id (str (:id profile)) + :token-id (str (:id stored))} + 120) + params {:grant_type "authorization_code" :client_id (:client_id client) + :code code :code_verifier verifier :redirect_uri "https://example.com/callback"} + redeem (fn [secret] + (request "/api/mcp/oauth/token" :method :post + :headers (cond-> {"content-type" "application/x-www-form-urlencoded"} + (= method "client_secret_basic") + (assoc "authorization" + (str "Basic " (.encodeToString + (java.util.Base64/getEncoder) + (.getBytes (str (:client_id client) ":" secret) "UTF-8"))))) + :body-bytes (form (cond-> params + (= method "client_secret_post") + (assoc :client_secret secret)))))] + (t/is (= 401 (::yres/status (redeem "wrong")))) + (t/is (= (:token stored) (:access_token (body (redeem (:client_secret client)))))))))) + +(t/deftest exchange-rejects-expired-token + (let [profile (th/create-profile* 1 {:is-active true}) + stored (db/tx-run! th/*system* access-token/ensure-mcp-access! (:id profile)) + verifier (apply str (repeat 43 "a")) + client (clients/register! th/*system* {:redirect_uris ["https://example.com/callback"] + :token_endpoint_auth_method "none"}) + code (state/create! th/*system* :code + {:client-id (:client_id client) + :redirect-uri "https://example.com/callback" + :resource (oauth/resource) + :challenge (state/digest verifier) + :profile-id (str (:id profile)) + :token-id (str (:id stored))} + 120)] + (db/exec! th/*pool* ["UPDATE access_token SET expires_at = now() - interval '1 day' WHERE id = ?" + (:id stored)]) + (let [reply (request "/api/mcp/oauth/token" :method :post + :headers {"content-type" "application/x-www-form-urlencoded"} + :body-bytes (form {:grant_type "authorization_code" :code code + :client_id (:client_id client) :code_verifier verifier + :redirect_uri "https://example.com/callback"}))] + (t/is (= 400 (::yres/status reply))) + (t/is (= "invalid_grant" (:error (body reply))))))) + +(t/deftest unexpected-errors-are-logged + (let [logged (atom []) + cookies (login-cookie (th/create-profile* 1 {:is-active true})) + fail (fn [& _] (throw (RuntimeException. "unexpected")))] + (with-redefs [errors/handle (fn [cause _] (swap! logged conj (ex-message cause))) + clients/register! fail + clock/get-clock fail] + (t/testing "endpoint failures" + (let [reply (request "/api/mcp/oauth/register" :method :post + :headers {"content-type" "application/json"} + :body-bytes (json/encode-str {:redirect_uris ["https://example.com/callback"]}))] + (t/is (= 503 (::yres/status reply))) + (t/is (= {:error "temporarily_unavailable"} (body reply))) + (t/is (= "*" (get-in reply [::yres/headers "access-control-allow-origin"]))))) + (t/testing "session middleware failures" + (let [reply (request "/api/mcp/oauth/request" :query (form {:request "x"}) :cookies cookies)] + (t/is (= 503 (::yres/status reply))) + (t/is (= {:error "temporarily_unavailable"} (body reply))))) + (t/testing "OAuth rejections are not logged" + (t/is (= 400 (::yres/status (request "/api/mcp/oauth/token" :method :post + :headers {"content-type" "application/x-www-form-urlencoded"} + :body-bytes "code=a&code=b")))))) + (t/is (= ["unexpected" "unexpected"] @logged)))) + +(t/deftest authorization-rejects-unregistered-callback + (let [client (clients/register! th/*system* {:redirect_uris ["https://example.com/callback"]}) + reply (request "/api/mcp/oauth/authorize" + :query (form {:client_id (:client_id client) + :redirect_uri "https://attacker.example/callback"}))] + (t/is (= 400 (::yres/status reply))) + (t/is (nil? (get-in reply [::yres/headers "location"]))))) diff --git a/backend/test/backend_tests/mcp_oauth_state_test.clj b/backend/test/backend_tests/mcp_oauth_state_test.clj new file mode 100644 index 0000000000..196c8378a2 --- /dev/null +++ b/backend/test/backend_tests/mcp_oauth_state_test.clj @@ -0,0 +1,53 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.mcp-oauth-state-test + (:require + [app.config :as cf] + [app.mcp.oauth-state :as state] + [backend-tests.helpers :as th] + [clojure.test :as t] + [mockery.core :refer [with-mocks]])) + +(t/use-fixtures :once th/state-init) + +(t/deftest authorization-state + (let [cfg th/*system* + handle (state/create! cfg :code {:client-id "test"} 60) + record (state/read! cfg :code handle)] + (t/is (= {:client-id "test"} (:value record))) + (t/is (nil? (state/read! cfg :request handle))) + (t/is (false? (state/consume! cfg :code handle (assoc record :raw "wrong")))) + (t/is (= record (state/read! cfg :code handle))) + (let [results (mapv deref (repeatedly 8 #(future (state/consume! cfg :code handle record))))] + (t/is (= 1 (count (filter true? results))))) + (t/is (nil? (state/read! cfg :code handle))))) + +(t/deftest bind-pending-request-once + (let [cfg th/*system* + handle (state/create! cfg :request {:client-id "test"} 60) + record (state/read! cfg :request handle)] + (t/is (true? (state/replace! cfg :request handle record {:session-id "first"}))) + (t/is (false? (state/replace! cfg :request handle record {:session-id "second"}))) + (t/is (= {:session-id "first"} (:value (state/read! cfg :request handle)))))) + +(t/deftest pkce-and-secrets + ;; RFC 7636 appendix B + (t/is (= "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" + (state/digest "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")))) + +(t/deftest expiry-and-tenant-isolation + (let [cfg th/*system* + handle (state/create! cfg :request {:test true} 1)] + (binding [cf/config (assoc cf/config :tenant "another-tenant")] + (t/is (nil? (state/read! cfg :request handle)))) + (Thread/sleep 1100) + (t/is (nil? (state/read! cfg :request handle))))) + +(t/deftest state-storage-fails-closed + (with-mocks [store {:target 'app.redis/set :return nil}] + (t/is (thrown? clojure.lang.ExceptionInfo + (state/create! th/*system* :request {:test true} 60))))) diff --git a/backend/test/backend_tests/metrics_test.clj b/backend/test/backend_tests/metrics_test.clj index 8abd4f0408..916c74aabd 100644 --- a/backend/test/backend_tests/metrics_test.clj +++ b/backend/test/backend_tests/metrics_test.clj @@ -6,6 +6,7 @@ (ns backend-tests.metrics-test (:require + [app.common.exceptions :as ex] [app.main :as main] [app.metrics :as mtx] [app.metrics.definition :as-alias mdef] @@ -96,6 +97,23 @@ (get-collector [_ _] collector))] (t/is (true? (mtx/run! instance :id :x :inc 1 :labels ["ok"]))))) +(t/deftest instance-resolves-the-metrics-from-a-cfg + ;; the resolver is a map read and nothing more: it hands back the very + ;; instance, not a copy and not a wrapper + (let [metrics (ig/init-key :app.metrics/metrics {:default {}})] + (t/is (identical? metrics (mtx/instance {::mtx/metrics metrics}))))) + +(t/deftest instance-raises-on-a-cfg-without-metrics + ;; A missing instance is a wiring bug, and the resolver is the one place + ;; that says so: a module that resolves it at the top of a write sees the + ;; failure before anything is persisted. + (let [cause (try + (mtx/instance {}) + (catch Throwable e e))] + (t/is (some? cause)) + (t/is (= :missing-metrics (:code (ex-data cause)))) + (t/is (re-find #"::mtx/metrics" (:hint (ex-data cause)))))) + (t/deftest definitions-schema-accepts-all-consumed-keys (t/is (true? (valid-definitions? {:test-timing diff --git a/backend/test/backend_tests/migrations_test.clj b/backend/test/backend_tests/migrations_test.clj new file mode 100644 index 0000000000..12597a4968 --- /dev/null +++ b/backend/test/backend_tests/migrations_test.clj @@ -0,0 +1,218 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.migrations-test + (:require + [app.db :as db] + [app.migrations :as migrations] + [app.util.migrations :as mg] + [backend-tests.helpers :as th] + [clojure.spec.alpha :as s] + [clojure.string :as str] + [clojure.test :as t])) + +(t/use-fixtures :once th/state-init) + +(t/deftest migrations-registry-satisfies-steps-spec + (t/testing "every entry is a {:name :fn} step (guards against stray forms)" + (t/is (s/valid? ::mg/steps migrations/migrations) + (s/explain-str ::mg/steps migrations/migrations)))) + +(defn- table-columns + [table] + (->> (th/db-exec! ["SELECT column_name FROM information_schema.columns + WHERE table_schema='public' AND table_name=?" table]) + (map :column-name) + set)) + +(defn- table-indexes + [table] + (->> (th/db-exec! ["SELECT indexname FROM pg_indexes + WHERE schemaname='public' AND tablename=?" table]) + (map :indexname) + set)) + +(defn- table-foreign-keys + [table] + (->> (th/db-exec! ["SELECT conname, confdeltype FROM pg_constraint + WHERE contype='f' AND conrelid = ?::regclass" table]) + (map (fn [{:keys [conname confdeltype]}] + [conname confdeltype])) + set)) + +(defn- deferrable-foreign-keys + "Names of the deferrable foreign keys of a table." + [table] + (->> (th/db-exec! ["SELECT conname FROM pg_constraint + WHERE contype='f' AND condeferrable AND conrelid = ?::regclass" table]) + (map :conname) + set)) + +(t/deftest job-table-exists-with-expected-columns + (t/is (= #{; dispatch/lifecycle columns + "id" "name" "tenant" "queue" "label" "priority" "scheduled_at" + "retry_num" "max_retries" "status" "created_at" "modified_at" + "started_at" "completed_at" "params" + ; optional ledger columns + "profile_id" "error" "result" + "resource_id" "expires_at"} + (table-columns "job")))) + +(t/deftest job-tenant-column-is-not-nullable + (t/testing "every job belongs to an instance, so the column is required" + (t/is (= "NO" + (:is-nullable (th/db-exec-one! ["SELECT is_nullable + FROM information_schema.columns + WHERE table_name = ? + AND column_name = ?" + "job" "tenant"])))))) + +(t/deftest job-queue-stores-the-bare-queue-name + (let [job-id (th/mk-uuid "job-bare-queue")] + (th/db-insert! :job {:id job-id + :name "test" + :tenant "acme" + :queue "webhooks"}) + (try + (t/is (= "acme" (:tenant (th/db-get :job {:id job-id})))) + (t/is (= "webhooks" (:queue (th/db-get :job {:id job-id})))) + (t/testing "the tenant is not smuggled inside the queue" + (t/is (not (str/includes? (:queue (th/db-get :job {:id job-id})) "acme")))) + (finally + (th/db-force-delete :job {:id job-id}))))) + +(t/deftest job-table-has-no-target-nor-progress-columns + (t/testing "target and job.progress are gone: progress lives in job_event" + (t/is (empty? (filter #{"target" "progress"} (table-columns "job")))))) + +(t/deftest job-event-table-exists-with-expected-columns + (t/is (= #{"id" "job_id" "kind" "payload" "created_at"} + (table-columns "job_event")))) + +(t/deftest job-event-kind-check-rejects-unknown-kinds + (let [job-id (th/mk-uuid "job-event-check")] + (th/db-insert! :job {:id job-id + :name "test" + :tenant "acme" + :queue "default"}) + (try + (t/testing "the four lifecycle kinds are accepted" + (doseq [kind ["start" "progress" "retry" "end"]] + (t/is (some? (th/db-insert! :job-event {:job-id job-id + :kind kind}))))) + (t/testing "any other kind is rejected" + (t/is (thrown? Exception + (th/db-insert! :job-event {:job-id job-id + :kind "unknown"})))) + (finally + (th/db-force-delete :job {:id job-id}))))) + +(t/deftest job-event-index-serves-the-history-read + (let [indexdef (:indexdef (th/db-exec-one! ["SELECT indexdef FROM pg_indexes + WHERE schemaname = 'public' + AND tablename = 'job_event' + AND indexname = 'job_event__job_kind_created_idx'"]))] + (t/is (str/includes? indexdef "(job_id, kind, created_at DESC, id DESC)")))) + +(t/deftest job-event-cascades-on-job-deletion + (let [job-id (th/mk-uuid "job-event-cascade")] + (th/db-insert! :job {:id job-id :name "test" :tenant "acme" :queue "default"}) + (th/db-insert! :job-event {:job-id job-id :kind "progress"}) + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job_event WHERE job_id = ?" job-id])))) + (th/db-force-delete :job {:id job-id}) + (t/is (= 0 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job_event WHERE job_id = ?" job-id])))))) + +(t/deftest job-event-foreign-key-is-deferrable + (t/testing "every new job reference is DEFERRABLE" + (t/is (contains? (deferrable-foreign-keys "job_event") "job_event_job_id_fkey")))) + +(t/deftest job-table-has-expected-indexes + (t/is (contains? (table-indexes "job") "job__dispatcher__idx")) + (t/is (contains? (table-indexes "job") "job__orphan__idx")) + (t/is (contains? (table-indexes "job") "job__profile__idx"))) + +(defn- indexdef + [index-name] + (:indexdef (th/db-exec-one! ["SELECT indexdef FROM pg_indexes + WHERE schemaname = 'public' + AND tablename = 'job' + AND indexname = ?" index-name]))) + +(t/deftest job-dispatcher-index-serves-ordering + (let [def (indexdef "job__dispatcher__idx")] + (t/testing "tenant leads because the filter is an equality it can answer" + (t/is (str/includes? def "(tenant, priority DESC, scheduled_at)"))) + (t/testing "status only in the predicate, it is not a key column" + (t/is (str/includes? def "WHERE")) + (t/is (not (str/includes? def "(status, scheduled_at)")))) + (t/testing "queue does not lead: the dispatcher claims every queue of the tenant" + (t/is (not (str/includes? def "(tenant, queue")))))) + +(t/deftest job-sweep-indexes-are-tenant-scoped + (t/testing "orphan sweep" + (t/is (str/includes? (indexdef "job__orphan__idx") "(tenant, modified_at)"))) + (t/testing "reschedule of lost scheduled rows" + (t/is (str/includes? (indexdef "job__scheduled__idx") "(tenant, scheduled_at)"))) + (t/testing "cron no-overlap check and submit dedupe" + (t/is (str/includes? (indexdef "job__name_label__idx") "(tenant, name, queue, label)")))) + +(t/deftest job-table-has-sweep-path-indexes + (t/testing "jobs-GC expiration scan" + (t/is (contains? (table-indexes "job") "job__expires__idx"))) + (t/testing "jobs-GC retention scan" + (t/is (contains? (table-indexes "job") "job__retention__idx")))) + +(t/deftest job-table-has-expected-foreign-keys + (t/testing "profile_id keeps profile deletion explicit (no action)" + (t/is (contains? (table-foreign-keys "job") + ["job_profile_id_fkey" "a"]))) + + (t/testing "resource_id is set to null when the storage object is deleted" + (t/is (contains? (table-foreign-keys "job") + ["job_resource_id_fkey" "n"])))) + +(t/deftest job-status-check-constraint-rejects-unknown-statuses + (t/is (thrown? Exception + (th/db-insert! :job {:id (th/mk-uuid "job-check") + :name "test" + :tenant "acme" + :queue "default" + :status "unknown"})))) + +(t/deftest job-status-check-constraint-accepts-known-statuses + (doseq [status ["new" "scheduled" "running" "retry" + "completed" "failed" "cancelled" "aborted"]] + (th/db-insert! :job {:id (th/mk-uuid "job-status" status) + :name "test" + :tenant "acme" + :queue "default" + :status status}) + (let [{:keys [status priority retry-num max-retries params scheduled-at + created-at modified-at]} + (-> (th/db-get :job {:id (th/mk-uuid "job-status" status)} :status + :priority :retry-num :max-retries :params :scheduled-at + :created-at :modified-at) + (update :params db/decode-json-pgobject))] + (t/is (= status status)) + (t/is (= 100 priority)) + (t/is (= 0 retry-num)) + (t/is (= 3 max-retries)) + (t/is (= {} params)) + (t/is (some? scheduled-at)) + (t/is (some? created-at)) + (t/is (some? modified-at)) + (th/db-force-delete :job {:id (th/mk-uuid "job-status" status)})))) + +(t/deftest job-has-only-the-deletion-protection-trigger + (let [triggers (->> (th/db-exec! ["SELECT tgname FROM pg_trigger + WHERE tgrelid = ?::regclass + AND NOT tgisinternal" "job"]) + (map :tgname) + set)] + (t/testing "no modified-at trigger: the application updates it" + (t/is (not (contains? triggers "job__modified_at__tgr")))) + (t/testing "deletion protection is the only trigger on job" + (t/is (= #{"deletion_protection__tgr"} triggers))))) diff --git a/backend/test/backend_tests/nitrate_ingest_audit_log_test.clj b/backend/test/backend_tests/nitrate_ingest_audit_log_test.clj new file mode 100644 index 0000000000..82971f4d93 --- /dev/null +++ b/backend/test/backend_tests/nitrate_ingest_audit_log_test.clj @@ -0,0 +1,87 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.nitrate-ingest-audit-log-test + (:require + [app.common.json :as json] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.http.client :as-alias http] + [app.nitrate :as nitrate] + [app.rpc :as-alias rpc] + [app.setup :as-alias setup] + [backend-tests.helpers :as th] + [clojure.test :as t] + [cuerdas.core :as str] + [integrant.core :as ig] + [mockery.core :refer [with-mocks]])) + +(t/use-fixtures :once th/state-init) + +(t/deftest ingest-audit-log-posts-events-batch + (binding [cf/flags (conj cf/flags :admin-console) + cf/config (assoc cf/config :admin-console-uri "http://ac.example/admin-console/")] + (with-mocks [http-mock {:target 'app.http.client/req + :return {:status 204}}] + (let [client (ig/init-key :app.nitrate/client + {::http/client (Object.) + ::setup/shared-keys {:admin-console "test-shared-key"}}) + cfg {:app.nitrate/client client} + p1 (uuid/next) + p2 (uuid/next) + e1 (uuid/next) + e2 (uuid/next) + team-id (uuid/next) + now (ct/now)] + (nitrate/call cfg :ingest-audit-log + {::rpc/profile-id p1 + :events [{:id e1 + :name "create-project" + :type "action" + :profile-id p1 + :profile-email "ada@example.com" + :profile-name "Ada" + :props {:team-id team-id} + :context {} + :created-at now + :tracked-at now + :source "backend" + :ip-addr "127.0.0.1"} + {:id e2 + :name "create-file" + :type "action" + :profile-id p2 + :props {} + :context {} + :created-at now + :tracked-at now + :source "backend" + :ip-addr "10.0.0.1"}]}) + (t/is (= 1 (:call-count @http-mock))) + (let [[_ req] (:call-args @http-mock) + body (json/decode (:body req) :key-fn json/read-kebab-key) + e1' (first (:events body)) + e2' (second (:events body))] + (t/is (= :post (:method req))) + (t/is (str/ends-with? (str (:uri req)) "api/audit-log")) + (t/is (= 2 (count (:events body)))) + (t/is (= "create-project" (:name e1'))) + (t/is (= "create-file" (:name e2'))) + (t/is (= (str e1) (str (:id e1')))) + (t/is (= (str e2) (str (:id e2')))) + (t/is (= (str p1) (str (:profile-id e1')))) + (t/is (= (str p2) (str (:profile-id e2')))) + (t/is (= "ada@example.com" (:profile-email e1'))) + (t/is (= "Ada" (:profile-name e1'))) + (t/is (nil? (:profile-email e2'))) + (t/is (nil? (:profile-name e2'))) + (t/is (= "127.0.0.1" (:ip-addr e1'))) + (t/is (= "10.0.0.1" (:ip-addr e2'))) + (t/is (string? (:created-at e1'))) + (t/is (string? (:tracked-at e1'))) + (t/is (= (str now) (:created-at e1'))) + (t/is (= (str team-id) (str (get-in e1' [:props :team-id]))))))))) diff --git a/backend/test/backend_tests/nitrate_test.clj b/backend/test/backend_tests/nitrate_test.clj new file mode 100644 index 0000000000..0b3086dfbe --- /dev/null +++ b/backend/test/backend_tests/nitrate_test.clj @@ -0,0 +1,43 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.nitrate-test + (:require + [app.config :as cf] + [app.nitrate] + [clojure.test :as t])) + +(def ^:private generate-nitrate-uri + (ns-resolve 'app.nitrate 'generate-nitrate-uri)) + +(t/deftest generate-nitrate-uri-test + (binding [cf/config (assoc cf/config + :admin-console-uri + "http://ac.example/admin-console")] + (doseq [[label segments expected] + [["single segment" + ["api/connectivity"] + "http://ac.example/admin-console/api/connectivity"] + ["resource identifier as the final segment" + ["api/teams/" "team-id"] + "http://ac.example/admin-console/api/teams/team-id"] + ["nested team membership path" + ["api/teams/" "team-id/" "users/" "profile-id"] + "http://ac.example/admin-console/api/teams/team-id/users/profile-id"] + ["nested organization membership path" + ["api/organizations/" "organization-id/" "members/" "profile-id"] + "http://ac.example/admin-console/api/organizations/organization-id/members/profile-id"] + ["organization action path" + ["api/organizations/" "organization-id/" "add-team"] + "http://ac.example/admin-console/api/organizations/organization-id/add-team"] + ["user action path" + ["api/users/" "profile-id/" "cleanup-after-deletion"] + "http://ac.example/admin-console/api/users/profile-id/cleanup-after-deletion"] + ["team action path" + ["api/teams/" "team-id/" "sso"] + "http://ac.example/admin-console/api/teams/team-id/sso"]]] + (t/testing label + (t/is (= expected (apply generate-nitrate-uri segments))))))) diff --git a/backend/test/backend_tests/objects_gc_test.clj b/backend/test/backend_tests/objects_gc_test.clj index 5a3052901f..6696a7f2f0 100644 --- a/backend/test/backend_tests/objects_gc_test.clj +++ b/backend/test/backend_tests/objects_gc_test.clj @@ -86,7 +86,7 @@ cfg {::db/pool th/*pool* ::sto/storage (::sto/storage th/*system*) - ::ogc/timestamp deleted-at} + ::ogc/threshold deleted-at} total (#'ogc/delete-profiles-chunk cfg [(:id profile)])] (t/testing "the profile is gone and the chunk reports it" @@ -149,7 +149,7 @@ :id (:id team)}) ;; Run the queued cascade so the default project is marked deleted too, ;; then purge everything the way production does. - (th/run-pending-tasks!) + (th/run-pending-jobs) (th/reset-mock! audit-mock) (th/run-task! :objects-gc {:skip-delay true}) (t/is (nil? (th/db-get :team {:id (:id team)} {::db/remove-deleted false}))) @@ -171,7 +171,7 @@ (th/command! {::th/type :delete-project ::rpc/profile-id (:id profile) :id (:id project)}) - (th/run-pending-tasks!) + (th/run-pending-jobs) (th/reset-mock! audit-mock) (th/run-task! :objects-gc {:skip-delay true}) (t/is (nil? (th/db-get :project {:id (:id project)} {::db/remove-deleted false}))) @@ -195,7 +195,7 @@ (th/command! {::th/type :delete-file ::rpc/profile-id (:id profile) :id (:id file)}) - (th/run-pending-tasks!) + (th/run-pending-jobs) (th/reset-mock! audit-mock) (th/run-task! :objects-gc {:skip-delay true}) (t/is (nil? (th/db-get :file {:id (:id file)} {::db/remove-deleted false}))) @@ -216,7 +216,7 @@ (th/command! {::th/type :delete-team ::rpc/profile-id (:id profile) :id (:id team)}) - (th/run-pending-tasks!) + (th/run-pending-jobs) (th/reset-mock! audit-mock) (th/run-task! :objects-gc {:skip-delay true}) (t/is (nil? (th/db-get :team {:id (:id team)} {::db/remove-deleted false}))) diff --git a/backend/test/backend_tests/offload_file_data_test.clj b/backend/test/backend_tests/offload_file_data_test.clj new file mode 100644 index 0000000000..e7158f0567 --- /dev/null +++ b/backend/test/backend_tests/offload_file_data_test.clj @@ -0,0 +1,611 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.offload-file-data-test + (:require + [app.binfile.common :as bfc] + [app.binfile.v3 :as v3] + [app.common.features :as cfeat] + [app.common.time :as ct] + [app.common.types.shape :as cts] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.features.fdata :as fdata] + [app.jobs :as jobs] + [app.jobs.metrics :as metrics] + [app.main :as main] + [app.metrics :as mtx] + [app.metrics.definition :as-alias mdef] + [app.rpc :as-alias rpc] + [app.storage :as sto] + [app.storage.tmp :as tmp] + [app.tasks.offload-file-data :as offload] + [backend-tests.helpers :as th] + [clojure.test :as t] + [datoteka.io :as io] + [integrant.core :as ig]) + (:import + io.prometheus.client.Counter + io.prometheus.client.Counter$Child)) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + +(defn- db-rows + [file-id] + (th/db-query :file-data {:file-id file-id})) + +(defn- create-file + [i profile] + (th/create-file* i {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false})) + +(t/deftest offloads-file-data-to-storage + (let [profile (th/create-profile* 1) + file (create-file 1 profile) + before (db-rows (:id file))] + + (t/is (pos? (count before))) + (t/is (every? #(= "db" (:backend %)) before)) + + (let [result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (= (count before) (:offloaded result))) + (t/is (zero? (:skipped result)))) + + (let [after (db-rows (:id file))] + (t/is (= (count before) (count after))) + (t/is (every? #(= "storage" (:backend %)) after)) + (t/is (every? #(nil? (:data %)) after))))) + +(t/deftest skips-file-locked-by-another-transaction + (let [profile (th/create-profile* 1) + file (create-file 1 profile) + before (db-rows (:id file)) + locked (promise) + release (promise) + holder (future + (db/tx-run! th/*system* + (fn [{:keys [::db/conn]}] + (db/exec-one! conn ["SELECT id FROM file WHERE id = ? FOR UPDATE" (:id file)]) + (deliver locked true) + @release + :held)))] + + (t/is (= true (deref locked 10000 ::timeout))) + + (try + (let [result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (= 0 (:offloaded result))) + (t/is (= 1 (:skipped result)))) + (finally + (deliver release true) + (deref holder 10000 ::timeout))) + + (let [after (db-rows (:id file))] + (t/is (= (count before) (count after))) + (t/is (every? #(= "db" (:backend %)) after))))) + +(t/deftest skips-missing-and-deleted-files + (let [result (th/run-task! :offload-file-data {:file-id (uuid/next)})] + (t/is (= 0 (:offloaded result))) + (t/is (= 1 (:skipped result)))) + + (let [profile (th/create-profile* 1) + file (create-file 1 profile) + _ (th/db-update! :file {:deleted-at (ct/now)} {:id (:id file)}) + before (db-rows (:id file)) + result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (= 0 (:offloaded result))) + (t/is (= 1 (:skipped result))) + (let [after (db-rows (:id file))] + (t/is (= (count before) (count after))) + (t/is (every? #(= "db" (:backend %)) after))))) + +(defn- row-bytes + [{:keys [data]}] + (if (bytes? data) + (alength ^bytes data) + 0)) + +(t/deftest summary-reports-rows-and-bytes + (let [profile (th/create-profile* 1) + file (create-file 1 profile) + before (db-rows (:id file)) + expected-bytes (reduce + 0 (map row-bytes before))] + + (t/is (pos? (count before))) + (t/is (every? some? (map :data before))) + (t/is (pos? expected-bytes)) + + (let [result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (= (count before) (:offloaded result))) + (t/is (= expected-bytes (:bytes result))) + (t/is (zero? (:skipped result)))) + + ;; A second run is a no-op: nothing left on the hot storage. + (let [result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (= 0 (:offloaded result))) + (t/is (= 0 (:bytes result))) + (t/is (zero? (:skipped result)))))) + +(t/deftest storage-failure-aborts-only-that-file + (let [profile (th/create-profile* 1) + file-a (create-file 1 profile) + file-b (create-file 2 profile) + real-put @#'sto/put-object! + failing (fn [storage params] + (if (= (:file-id params) (:id file-a)) + (throw (ex-info "storage is down" {:file-id (:id file-a)})) + (real-put storage params)))] + + ;; The failing file aborts as a whole: every row stays on hot storage. + (with-redefs [sto/put-object! failing] + (t/is (thrown? Exception + (th/run-task! :offload-file-data {:file-id (:id file-a)})))) + + (let [rows-a (db-rows (:id file-a))] + (t/is (pos? (count rows-a))) + (t/is (every? #(= "db" (:backend %)) rows-a)) + (t/is (every? some? (map :data rows-a)))) + + ;; The other file is independent and offloads while storage is down for A. + (with-redefs [sto/put-object! failing] + (let [result (th/run-task! :offload-file-data {:file-id (:id file-b)})] + (t/is (pos? (:offloaded result))) + (t/is (pos? (:bytes result))) + (t/is (zero? (:skipped result))))) + + (t/is (every? #(= "storage" (:backend %)) (db-rows (:id file-b)))) + + ;; Once storage recovers, a retry converges with no leftovers on hot. + (let [result (th/run-task! :offload-file-data {:file-id (:id file-a)})] + (t/is (pos? (:offloaded result))) + (t/is (pos? (:bytes result))) + (t/is (zero? (:skipped result)))) + + (let [rows-a (db-rows (:id file-a))] + (t/is (every? #(= "storage" (:backend %)) rows-a)) + (t/is (every? #(nil? (:data %)) rows-a))))) + +(defn- make-metrics + [] + (ig/init-key :app.metrics/metrics + {:default (select-keys main/default-metrics + [:file-data-offload-rows + :file-data-offload-bytes + :file-data-offload-timing])})) + +(defn- offload-cfg + [metrics] + (-> th/*system* + (assoc ::mtx/metrics metrics) + (assoc ::sto/storage (assoc (::sto/storage th/*system*) + ::mtx/metrics metrics)))) + +(defn- counter-value + [metrics id labels] + (let [collector (mtx/get-collector metrics id) + instance (::mdef/instance collector) + child (.labels ^Counter instance (into-array String labels))] + (.get ^Counter$Child child))) + +(def ^:private known-types + ["main" "snapshot" "fragment"]) + +(defn- total-counter + [metrics id] + (reduce + 0 (map #(counter-value metrics id [%]) known-types))) + +(t/deftest metric-definitions-are-pinned + (let [defs main/default-metrics] + (t/is (= "penpot_file_data_offload_rows_total" + (get-in defs [:file-data-offload-rows ::mdef/name]))) + (t/is (= ["type"] + (get-in defs [:file-data-offload-rows ::mdef/labels]))) + (t/is (= :counter + (get-in defs [:file-data-offload-rows ::mdef/type]))) + (t/is (= "penpot_file_data_offload_bytes_total" + (get-in defs [:file-data-offload-bytes ::mdef/name]))) + (t/is (= ["type"] + (get-in defs [:file-data-offload-bytes ::mdef/labels]))) + (t/is (= :counter + (get-in defs [:file-data-offload-bytes ::mdef/type]))) + (t/is (= "penpot_file_data_offload_timing_ms" + (get-in defs [:file-data-offload-timing ::mdef/name]))) + (t/is (= ["result"] + (get-in defs [:file-data-offload-timing ::mdef/labels]))) + (t/is (= :histogram + (get-in defs [:file-data-offload-timing ::mdef/type]))) + (t/is (= [1 10 50 100 500 1000 5000 10000 30000 60000 300000] + (get-in defs [:file-data-offload-timing ::mdef/buckets]))))) + +(t/deftest success-emits-rows-and-bytes + (let [metrics (make-metrics) + profile (th/create-profile* 1) + file (create-file 1 profile) + result (offload/execute-offload-file-data (offload-cfg metrics) + {:file-id (:id file)})] + (t/is (pos? (:offloaded result))) + (t/is (= (double (:offloaded result)) + (total-counter metrics :file-data-offload-rows))) + (t/is (= (double (:bytes result)) + (total-counter metrics :file-data-offload-bytes))) + (t/is (pos? (counter-value metrics :file-data-offload-rows ["main"]))) + (t/is (pos? (total-counter metrics :file-data-offload-rows))))) + +(t/deftest skip-records-skip-and-nothing-else + (let [durations (volatile! []) + rows (volatile! []) + bytes (volatile! [])] + (with-redefs [metrics/record-offload-duration (fn [_ result _] (vswap! durations conj result) nil) + metrics/record-offload-rows (fn [_ type amount] (vswap! rows conj [type amount]) nil) + metrics/record-offload-bytes (fn [_ type amount] (vswap! bytes conj [type amount]) nil)] + (let [result (offload/execute-offload-file-data th/*system* {:file-id (uuid/next)})] + (t/is (= 0 (:offloaded result))) + (t/is (= 1 (:skipped result))))) + (t/is (= [:skip] @durations)) + (t/is (empty? @rows)) + (t/is (empty? @bytes)))) + +(t/deftest error-records-error-and-rethrows + (let [profile (th/create-profile* 1) + file (create-file 1 profile) + durations (volatile! []) + real-put @#'sto/put-object!] + (with-redefs [sto/put-object! (fn [_ _] (throw (ex-info "storage is down" {}))) + metrics/record-offload-duration (fn [_ result _] (vswap! durations conj result) nil)] + (t/is (thrown? Exception + (offload/execute-offload-file-data th/*system* {:file-id (:id file)})))) + (t/is (= [:error] @durations)) + ;; Nothing was migrated: the whole file aborted. + (t/is (every? #(= "db" (:backend %)) (db-rows (:id file)))))) + +(t/deftest missing-metrics-raises + (let [params {::db/pool th/*pool* + ::sto/storage (::sto/storage th/*system*)}] + (t/is (thrown? AssertionError + (ig/assert-key :app.tasks.offload-file-data/job-def params))) + (t/is (nil? (ig/assert-key :app.tasks.offload-file-data/job-def + (assoc params ::mtx/metrics (:app.metrics/metrics th/*system*))))))) + +(t/deftest submit-validates-params-upfront + (let [cfg th/*system*] + (t/testing "valid params submit a durable job" + (t/is (uuid? (jobs/submit cfg {::jobs/name :offload-file-data + ::jobs/params {:file-id (uuid/next)}})))) + (t/testing "invalid params fail at submit instead of burning retries" + (t/is (thrown? Exception + (jobs/submit cfg {::jobs/name :offload-file-data + ::jobs/params {}}))) + (t/is (thrown? Exception + (jobs/submit cfg {::jobs/name :offload-file-data + ::jobs/params {:file-id "not-a-uuid"}})))))) + +(t/deftest file-gc-without-flag-does-not-enqueue-offload + (t/is (not (contains? cf/flags :tiered-file-data-storage))) + (let [profile (th/create-profile* 1) + file (create-file 1 profile)] + (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) + (th/run-pending-jobs) + (t/testing "no offload job was enqueued" + (t/is (empty? (th/db-query :job {:name "offload-file-data"})))) + (t/testing "every row stays on hot storage" + (let [rows (db-rows (:id file))] + (t/is (pos? (count rows))) + (t/is (every? #(= "db" (:backend %)) rows)) + (t/is (every? some? (map :data rows))))))) + +(defn- update-file! + [& {:keys [profile-id file-id changes revn] :or {revn 0}}] + (let [params {::th/type :update-file + ::rpc/profile-id profile-id + :id file-id + :session-id (uuid/random) + :revn revn + :vern 0 + :features cfeat/supported-features + :changes changes} + out (th/command! params)] + (t/is (nil? (:error out))) + (:result out))) + +(defn- add-page-with-rect + [profile file page-id shape-id] + (update-file! + :file-id (:id file) + :profile-id (:id profile) + :changes + [{:type :add-page + :name "test" + :id page-id} + {:type :add-obj + :page-id page-id + :id shape-id + :parent-id uuid/zero + :frame-id uuid/zero + :components-v2 true + :obj (cts/setup-shape + {:id shape-id + :name "rect" + :frame-id uuid/zero + :parent-id uuid/zero + :type :rect})}])) + +(defn- get-file-data + [profile file-id] + (let [out (th/command! {::th/type :get-file + ::rpc/profile-id (:id profile) + :id file-id})] + (t/is (nil? (:error out))) + (get-in out [:result :data]))) + +(t/deftest offloaded-file-reads-identically + (let [profile (th/create-profile* 1 {:is-active true}) + file (create-file 1 profile) + page-id (uuid/next) + shape-id (uuid/next)] + (add-page-with-rect profile file page-id shape-id) + + (let [before-bfc (:data (bfc/get-file th/*system* (:id file) {:realize? true})) + before-rpc (get-file-data profile (:id file))] + + (t/is (some? (get-in before-bfc [:pages-index page-id :objects shape-id]))) + (t/is (= before-bfc before-rpc)) + + (let [result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (pos? (:offloaded result))) + (t/is (every? #(= "storage" (:backend %)) (remove :deleted-at (db-rows (:id file)))))) + + (t/testing "direct read resolves cold storage transparently" + (t/is (= before-bfc + (:data (bfc/get-file th/*system* (:id file) {:realize? true}))))) + + (t/testing "rpc read resolves cold storage transparently" + (t/is (= before-rpc (get-file-data profile (:id file)))))))) + +(t/deftest snapshot-survives-offload + (let [profile (th/create-profile* 1 {:is-active true}) + file (create-file 1 profile) + page-id (uuid/next) + shape-id (uuid/next)] + (add-page-with-rect profile file page-id shape-id) + + (let [out (th/command! {::th/type :create-file-snapshot + ::rpc/profile-id (:id profile) + :file-id (:id file) + :label "before-offload"})] + (t/is (nil? (:error out))) + (t/is (uuid? (get-in out [:result :id])))) + + (let [result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (pos? (:offloaded result)))) + + (t/testing "live snapshot rows moved to cold storage" + (let [snapshots (th/db-query :file-data {:file-id (:id file) + :type "snapshot" + :deleted-at nil})] + (t/is (pos? (count snapshots))) + (t/is (every? #(= "storage" (:backend %)) snapshots)) + (t/is (every? #(nil? (:data %)) snapshots)))) + + (t/testing "snapshots are still listed" + (let [out (th/command! {::th/type :get-file-snapshots + ::rpc/profile-id (:id profile) + :file-id (:id file)})] + (t/is (nil? (:error out))) + (t/is (some #(= "before-offload" (:label %)) (:result out))))) + + (t/testing "snapshot restores from cold storage" + (let [snap-id (->> (:result (th/command! {::th/type :get-file-snapshots + ::rpc/profile-id (:id profile) + :file-id (:id file)})) + (filter #(= "before-offload" (:label %))) + first + :id) + out (th/command! {::th/type :restore-file-snapshot + ::rpc/profile-id (:id profile) + :file-id (:id file) + :id snap-id})] + (t/is (some? snap-id)) + (t/is (nil? (:error out))) + (t/is (some? (get-in (get-file-data profile (:id file)) + [:pages-index page-id :objects shape-id]))))))) + +(t/deftest write-after-offload-returns-to-hot-storage + (let [profile (th/create-profile* 1 {:is-active true}) + file (create-file 1 profile) + page-id (uuid/next) + shape-id (uuid/next)] + + (let [result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (pos? (:offloaded result)))) + + (let [main (th/db-get :file-data {:file-id (:id file) :type "main"})] + (t/is (= "storage" (:backend main))) + (t/is (nil? (:data main)))) + + (add-page-with-rect profile file page-id shape-id) + + (t/testing "the new main row is back on hot storage" + (let [main (th/db-get :file-data {:file-id (:id file) :type "main"})] + (t/is (= "db" (:backend main))) + (t/is (bytes? (:data main))))) + + (t/testing "the written content reads back" + (t/is (some? (get-in (get-file-data profile (:id file)) + [:pages-index page-id :objects shape-id])))))) + +(t/deftest export-import-roundtrip-after-offload + (let [profile (th/create-profile* 1 {:is-active true}) + file (create-file 1 profile) + page-id (uuid/next) + shape-id (uuid/next)] + (add-page-with-rect profile file page-id shape-id) + + (let [before (:data (bfc/get-file th/*system* (:id file) {:realize? true})) + _ (th/run-task! :offload-file-data {:file-id (:id file)}) + output (tmp/tempfile :suffix ".zip")] + + (v3/export-files! + (-> th/*system* + (assoc ::bfc/file-ids #{(:id file)}) + (assoc ::bfc/export-type :detach-libraries) + (assoc ::bfc/output (io/output-stream output)))) + + (let [result (-> th/*system* + (assoc ::bfc/project-id (:default-project-id profile)) + (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) + (assoc ::bfc/input output) + (v3/import-files!)) + imported (:data (bfc/get-file th/*system* (first (:file-ids result)) + {:realize? true}))] + (t/is (= 1 (count (:file-ids result)))) + (t/is (= (set (keys (:pages-index before))) + (set (keys (:pages-index imported))))) + (t/is (= (get-in before [:pages-index page-id :name]) + (get-in imported [:pages-index page-id :name]))) + (t/is (some? (get-in imported [:pages-index page-id :objects shape-id]))))))) + +(t/deftest deleted-offloaded-file-is-fully-purged + (let [storage (sto/resolve th/*system*) + profile (th/create-profile* 1 {:is-active true}) + file (create-file 1 profile) + page-id (uuid/next)] + + (add-page-with-rect profile file page-id (uuid/next)) + + (let [result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (pos? (:offloaded result)))) + + (let [ref-ids (into #{} + (comp (map :metadata) + (map fdata/decode-metadata) + (keep :storage-ref-id)) + (db-rows (:id file)))] + (t/is (pos? (count ref-ids))) + (t/is (every? some? (map #(sto/get-object storage %) ref-ids))) + + (let [out (th/command! {::th/type :delete-file + ::rpc/profile-id (:id profile) + :id (:id file)})] + (t/is (nil? (:error out)))) + (th/run-pending-jobs) + + (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))] + (th/run-task! :objects-gc {}))] + (t/is (pos? (:processed res)))) + + (t/testing "every file data row is gone" + (t/is (empty? (db-rows (:id file))))) + + ;; The touches above landed 8 days in the future, so the touched + ;; collector needs a clock past them plus its deletion delay. + (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 9}))] + (th/run-task! :storage-gc-touched {}))] + (t/is (pos? (:delete res)))) + + (t/testing "every referenced storage object is reclaimed" + (let [objects (mapv #(sto/get-object storage %) ref-ids)] + (t/is (every? nil? objects))))))) + +(t/deftest file-gc-with-stale-revn-enqueues-nothing + (let [profile (th/create-profile* 1) + file (create-file 1 profile) + before (db-rows (:id file)) + stale (inc (or (:revn file) 0))] + (t/is (not (th/run-task! :file-gc {:file-id (:id file) :revn stale}))) + (th/run-pending-jobs) + (t/testing "no offload job was enqueued" + (t/is (empty? (th/db-query :job {:name "offload-file-data"})))) + (t/testing "every row stays on hot storage" + (let [after (db-rows (:id file))] + (t/is (= (count before) (count after))) + (t/is (every? #(= "db" (:backend %)) after)))))) + +(t/deftest concurrent-edit-and-offload-stay-coherent + (let [profile (th/create-profile* 1 {:is-active true}) + file (create-file 1 profile) + page-1 (uuid/next) + shape-1 (uuid/next) + page-2 (uuid/next) + shape-2 (uuid/next)] + (add-page-with-rect profile file page-1 shape-1) + + (let [locked (promise) + release (promise) + holder (future + (db/tx-run! th/*system* + (fn [{:keys [::db/conn]}] + (db/exec-one! conn ["SELECT id FROM file WHERE id = ? FOR UPDATE" (:id file)]) + (deliver locked true) + @release + :held))) + _ (t/is (= true (deref locked 10000 ::timeout))) + updater (future + (add-page-with-rect profile file page-2 shape-2))] + + (try + ;; Give the updater time to block on the held row lock. + (th/sleep 500) + (t/is (not (realized? updater))) + + (t/testing "offload skips while the row is locked" + (let [result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (= 0 (:offloaded result))) + (t/is (= 1 (:skipped result))))) + (finally + (deliver release true))) + + (t/is (not= ::timeout (deref holder 10000 ::timeout))) + (t/is (not= ::timeout (deref updater 30000 ::timeout)))) + + (t/testing "the blocked edit won as a whole" + (t/is (some? (get-in (get-file-data profile (:id file)) + [:pages-index page-2 :objects shape-2])))) + + (t/testing "a later offload migrates the new bytes, never stale ones" + (let [current (get-file-data profile (:id file)) + result (th/run-task! :offload-file-data {:file-id (:id file)})] + (t/is (pos? (:offloaded result))) + (t/is (= current (get-file-data profile (:id file)))))))) + +(t/deftest partial-failure-leaves-reclaimable-orphans + (let [profile (th/create-profile* 1) + file (create-file 1 profile) + _ (t/is (>= (count (db-rows (:id file))) 2)) + calls (volatile! 0) + orphans (volatile! []) + real-put @#'sto/put-object! + mock (fn [storage params] + (if (= (:file-id params) (:id file)) + (let [n (vswap! calls inc)] + (if (= 1 n) + (let [object (real-put storage params)] + (vswap! orphans conj (:id object)) + object) + (throw (ex-info "storage is down" {})))) + (real-put storage params)))] + + (with-redefs [sto/put-object! mock] + (t/is (thrown? Exception + (th/run-task! :offload-file-data {:file-id (:id file)})))) + + (t/testing "the aborted file keeps every row on hot storage" + (let [rows (db-rows (:id file))] + (t/is (every? #(= "db" (:backend %)) rows)) + (t/is (every? some? (map :data rows))))) + + (t/testing "the orphaned blob is reclaimed once old enough" + (t/is (= 1 (count @orphans))) + (let [storage (sto/resolve th/*system*)] + (t/is (some? (sto/get-object storage (first @orphans)))) + (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:hours 3}))] + (th/run-task! :storage-gc-touched {}))] + (t/is (pos? (:delete res)))) + (t/is (nil? (sto/get-object storage (first @orphans)))))))) diff --git a/backend/test/backend_tests/rpc/commands/jobs_test.clj b/backend/test/backend_tests/rpc/commands/jobs_test.clj new file mode 100644 index 0000000000..5ced5072ee --- /dev/null +++ b/backend/test/backend_tests/rpc/commands/jobs_test.clj @@ -0,0 +1,750 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.rpc.commands.jobs-test + "The creation of a user-facing job: what it answers, what it freezes and + what it refuses before anything is stored." + (:require + [app.binfile.common :as bfc] + [app.binfile.v3 :as v3] + [app.common.exceptions :as ex] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.loggers.audit :as-alias audit] + [app.rpc :as-alias rpc] + [app.rpc.climit :as-alias climit] + [app.rpc.commands.jobs :as cmd-jobs] + [app.rpc.quotes :as-alias quotes] + [app.storage :as sto] + [app.storage.tmp :as tmp] + [backend-tests.helpers :as th] + [clojure.java.io :as jio] + [clojure.test :as t] + [cuerdas.core :as str] + [datoteka.fs :as fs] + [datoteka.io :as io] + [mockery.core :refer [with-mocks]]) + (:import + java.util.zip.ZipEntry + java.util.zip.ZipFile + java.util.zip.ZipOutputStream)) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each (th/serial th/database-reset th/clean-storage)) + +(def ^:private fixture + "A real penpot export, the same fixture the binfile tests read." + "backend_tests/test_files/svg-attrs-camel-case.penpot") + +(defn- import-fixture! + "A file in the default project of the profile, imported from the + fixture through the core." + [profile] + (-> th/*system* + (assoc ::bfc/project-id (:default-project-id profile)) + (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) + (assoc ::bfc/input (-> fixture io/resource jio/file)) + (v3/import-files!))) + +(defn- create-export-binfile-job + [profile-id file-ids & {:as extra}] + (th/command! (merge {::th/type :create-export-binfile-job + ::rpc/profile-id profile-id + :params {:file-ids file-ids + :export-type :detach-libraries}} + extra))) + +(defn- count-jobs + [] + (count (th/db-exec! ["SELECT id FROM job"]))) + +(defn- count-storage-objects + [] + (count (th/db-exec! ["SELECT id FROM storage_object"]))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest create-export-binfile-job-answers-with-a-pending-job + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + before (count-storage-objects) + out (create-export-binfile-job (:id profile) #{file-id})] + + (t/is (th/success? out)) + + (let [result (:result out) + job (jobs/get-job th/*system* (:id result))] + + (t/testing "the answer is enough to follow the job" + (t/is (uuid? (:id result))) + (t/is (= "pending" (:status result))) + (t/is (= "export-binfile" (:name result))) + (t/is (ct/inst? (:created-at result))) + (t/is (pos? (- (inst-ms (:expires-at result)) (inst-ms (ct/now)))))) + + (t/testing "and the row freezes what the job will do" + (let [job-def (jobs/get-job-def (::jobs/defs th/*system*) :export-binfile) + params (jobs/decode-params job-def (:params job))] + (t/is (= "new" (:status job))) + (t/is (= "binfile" (:queue job))) + (t/is (= 0 (:max-retries job))) + (t/is (= (:id profile) (:profile-id job))) + (t/is (= #{file-id} (:file-ids params))) + (t/is (= :detach-libraries (:export-type params))))) + + (t/testing "creating a job does not run it" + ;; no runner is running here, so a call that waited for one would + ;; hang instead of answering: seeing the job pending and no + ;; artifact produced is what proves the call does not wait + (t/is (= before (count-storage-objects))))))) + +(t/deftest create-export-binfile-job-requires-the-export-type + ;; the type is part of the contract of the job: a caller that does not + ;; say how to handle the libraries gets an error, not a package it did + ;; not ask for + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + out (th/command! {::th/type :create-export-binfile-job + ::rpc/profile-id (:id profile) + :params {:file-ids #{file-id}}})] + + (t/is (not (th/success? out))) + (t/is (= :params-validation (th/ex-code (:error out)))) + (t/testing "and nothing was created" + (t/is (zero? (count-jobs)))))) + +(t/deftest create-export-binfile-job-requires-at-least-one-file + (let [profile (th/create-profile* 1) + out (create-export-binfile-job (:id profile) #{})] + + (t/is (not (th/success? out))) + (t/is (= :validation (th/ex-type (:error out)))) + (t/is (= :no-files-to-export (th/ex-code (:error out)))) + (t/testing "and nothing was created" + (t/is (zero? (count-jobs)))))) + +(t/deftest create-export-binfile-job-checks-the-read-permission + (let [owner (th/create-profile* 1) + other (th/create-profile* 2) + file-id (first (:file-ids (import-fixture! owner))) + out (create-export-binfile-job (:id other) #{file-id})] + + (t/testing "a file the caller cannot read is not exported" + (t/is (not (th/success? out))) + (t/is (= :not-found (th/ex-type (:error out))))) + + (t/testing "and no job was created for it" + (t/is (zero? (count-jobs)))))) + +(t/deftest create-export-binfile-job-enforces-the-quote + (with-mocks [mock {:target 'app.config/get + :return (th/config-get-mock + {:quotes-export-jobs-per-profile 1})}] + + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile)))] + + (t/testing "the first job fits in the quote" + (t/is (th/success? (create-export-binfile-job (:id profile) #{file-id}))) + (t/is (= 1 (count-jobs)))) + + (t/testing "the one that would go over it is refused" + (let [out (create-export-binfile-job (:id profile) #{file-id})] + (t/is (not (th/success? out))) + (t/is (= :restriction (th/ex-type (:error out)))) + (t/is (= :max-quote-reached (th/ex-code (:error out)))) + (t/is (= "export-jobs-per-profile" (:target (ex-data (:error out))))))) + + (t/testing "so the quote is what stopped the second job" + (t/is (= 1 (count-jobs))))))) + +(t/deftest create-export-binfile-job-adds-the-job-id-to-the-audit-props + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + out (create-export-binfile-job (:id profile) #{file-id}) + props (::audit/props (meta (:result out)))] + + (t/testing "the audit of the call points at the job and its team" + (t/is (= (:id (:result out)) (:job-id props))) + (t/is (= 1 (:files props))) + (t/is (= "detach-libraries" (:export-type props))) + (t/is (= (:default-team-id profile) (:team-id props))) + (t/is (= #{:job-id :files :export-type :team-id} (set (keys props)))) + (t/is (not (contains? (:result out) :team-id)))))) + +(t/deftest create-export-binfile-job-records-the-team-when-files-share-one + (let [profile (th/create-profile* 1) + file-a (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile)}) + file-b (th/create-file* 2 {:profile-id (:id profile) + :project-id (:default-project-id profile)}) + out (create-export-binfile-job (:id profile) + #{(:id file-a) (:id file-b)}) + props (::audit/props (meta (:result out)))] + (t/is (th/success? out)) + (t/is (= (:default-team-id profile) (:team-id props))) + (t/is (= 2 (:files props))) + (t/is (not (contains? (:result out) :team-id))))) + +(t/deftest create-export-binfile-job-omits-the-team-when-files-span-teams + (let [profile (th/create-profile* 1) + team (th/create-team* 1 {:profile-id (:id profile)}) + project (th/create-project* 1 {:profile-id (:id profile) + :team-id (:id team)}) + file-a (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile)}) + file-b (th/create-file* 2 {:profile-id (:id profile) + :project-id (:id project)}) + out (create-export-binfile-job (:id profile) + #{(:id file-a) (:id file-b)}) + props (::audit/props (meta (:result out)))] + (t/is (th/success? out)) + (t/is (nil? (:team-id props))) + (t/is (= #{:job-id :files :export-type} (set (keys props)))) + (t/is (not (contains? (:result out) :team-id))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IMPORT +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- tempfile-with + "A temporary file holding `content`, as the http layer would leave one + for a plain upload." + [content] + (let [path (fs/create-tempfile :dir tmp/default-tmp-dir + :prefix "test-upload-")] + (with-open [out (io/output-stream path)] + (io/write* out (.getBytes ^String content "UTF-8"))) + path)) + +(defn- upload-chunk + "One chunk of the fixture, as the upload command takes it. `content` + replaces the fixture with something that is no package at all." + [& {:keys [content]}] + (if (some? content) + {:filename "package.penpot" + :path (tempfile-with content) + :mtype "application/zip" + :size (count content)} + (let [source (-> fixture io/resource jio/file)] + {:filename "package.penpot" + :path (th/tempfile fixture) + :mtype "application/zip" + :size (.length source)}))) +(defn- upload-chunked! + "Upload one chunk and return the session id. By default the chunk is the + fixture; `:path` uploads another file and `:content` uploads a string + that is no package at all." + [profile & {:keys [content path]}] + (let [session-id (-> (th/command! {::th/type :create-upload-session + ::rpc/profile-id (:id profile) + :total-chunks 1}) + :result :session-id) + out (th/multipart-command! {::th/type :upload-chunk + ::rpc/profile-id (:id profile) + :session-id session-id + :index 0 + :content (if (some? path) + (let [size (.length (jio/file path))] + {:filename "package.penpot" + :path path + :mtype "application/zip" + :size size}) + (upload-chunk :content content))})] + (t/is (nil? (:error out))) + session-id)) + +(defn- create-import-binfile-job + [profile-id project-id & {:keys [upload-id] :as extra}] + (th/command! (merge {::th/type :create-import-binfile-job + ::rpc/profile-id profile-id + :params {:project-id project-id + :name "imported"} + :upload-id upload-id} + extra))) + +(defn- session-row + [session-id] + (th/db-get :upload-session {:id session-id} {::db/remove-deleted false})) + +(defn- package-with-refer + "The fixture with the key of its manifest renamed to the old spelling, + `refer`, and keeping the value it already had." + [] + (let [source (-> fixture io/resource jio/file) + target (fs/create-tempfile :dir tmp/default-tmp-dir + :prefix "test-refer-")] + (with-open [zip (ZipFile. source) + out (ZipOutputStream. (io/output-stream target))] + (doseq [entry (enumeration-seq (.entries zip))] + (.putNextEntry out (ZipEntry. (.getName entry))) + (when-not (.isDirectory entry) + (let [content (slurp (.getInputStream zip entry)) + content (if (= "manifest.json" (.getName entry)) + (str/replace content #"\"referer\"(?=\s*:)" "\"refer\"") + content)] + (.write out (.getBytes content "UTF-8")))) + (.closeEntry out))) + target)) + +(defn- manifest-of + "The manifest of a package, as the reader of the import sees it." + [path] + (v3/get-manifest th/*system* path)) + +(defn- live-storage-objects + [] + (count (th/db-exec! ["SELECT id FROM storage_object WHERE deleted_at IS NULL"]))) + +(defn- session-row + [session-id] + (th/db-get :upload-session {:id session-id} {::db/remove-deleted false})) + +(defn- package-with-refer + "The fixture with the key of its manifest renamed to the old spelling, + `refer`, and keeping the value it already had." + [] + (let [source (-> fixture io/resource jio/file) + target (fs/create-tempfile :dir tmp/default-tmp-dir + :prefix "test-refer-")] + (with-open [zip (ZipFile. source) + out (ZipOutputStream. (io/output-stream target))] + (doseq [entry (enumeration-seq (.entries zip))] + (.putNextEntry out (ZipEntry. (.getName entry))) + (when-not (.isDirectory entry) + (let [content (slurp (.getInputStream zip entry)) + content (if (= "manifest.json" (.getName entry)) + (str/replace content #"\"referer\"(?=\s*:)" "\"refer\"") + content)] + (.write out (.getBytes content "UTF-8")))) + (.closeEntry out))) + target)) + +(defn- manifest-of + "The manifest of a package, as the reader of the import sees it." + [path] + (v3/get-manifest th/*system* path)) + +(defn- assembled-tempfiles + "The temporary files the chunk assembly leaves behind. The copies this + test uploads are its own business." + [] + (->> (.list (jio/file tmp/default-tmp-dir)) + (filter #(str/starts-with? % "penpot.chunked-upload.")) + (seq))) + +(t/deftest create-import-binfile-job-audits-the-old-spelling-of-the-manifest-tool + (let [profile (th/create-profile* 1) + package (package-with-refer) + expected (:referer (manifest-of (-> fixture io/resource jio/file))) + session-id (upload-chunked! profile :path package) + out (create-import-binfile-job (:id profile) (:default-project-id profile) + :upload-id session-id) + props (::audit/props (meta (:result out)))] + + (t/testing "the package names its tool with the old spelling only" + (t/is (some? expected)) + (t/is (nil? (:referer (manifest-of package)))) + (t/is (= expected (:refer (manifest-of package))))) + + (t/testing "and the audit carries that value anyway" + (t/is (th/success? out)) + (t/is (= expected (:referer props)))))) + +(t/deftest create-import-binfile-job-limits-its-own-concurrency + ;; the command assembles and stores the package inside the request, so + ;; it declares the same kind of limit the legacy import command has + (let [[mdata _] (get (::rpc/methods th/*system*) :create-import-binfile-job)] + (t/is (= [[:create-import-binfile-job/by-profile ::rpc/profile-id] + [:create-import-binfile-job/global]] + (::climit/id mdata))))) + +(t/deftest create-export-binfile-job-accepts-the-params-of-a-json-client + ;; a JSON body carries text and lists, not uuids or sets: the command + ;; has to read them with the decoder of the job-def, like the runner + ;; does when it reads a job from its row + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + out (th/command! {::th/type :create-export-binfile-job + ::rpc/profile-id (:id profile) + :params {:file-ids [(str file-id)] + :export-type "detach-libraries"}})] + + (t/is (th/success? out)) + + (let [job-def (jobs/get-job-def (::jobs/defs th/*system*) :export-binfile) + params (jobs/decode-params job-def + (:params (jobs/get-job th/*system* + (:id (:result out)))))] + (t/is (= #{file-id} (:file-ids params))) + (t/is (= :detach-libraries (:export-type params)))))) + +(t/deftest create-import-binfile-job-accepts-the-params-of-a-json-client + (let [profile (th/create-profile* 1) + session-id (upload-chunked! profile) + out (th/command! {::th/type :create-import-binfile-job + ::rpc/profile-id (:id profile) + :params {:project-id (str (:default-project-id profile)) + :name "imported"} + :upload-id session-id})] + + (t/is (th/success? out)))) + +(t/deftest create-import-binfile-job-never-takes-a-path-from-the-caller + (let [profile (th/create-profile* 1) + out (create-import-binfile-job (:id profile) (:default-project-id profile) + :file {:filename "package.penpot" + :path (jio/file "/etc/passwd") + :mtype "application/zip" + :size 0})] + + (t/testing "a plain upload is not a way to name a package" + (t/is (not (th/success? out))) + (t/is (= :params-validation (th/ex-code (:error out))))) + + (t/testing "and no job is created" + (t/is (zero? (count-jobs)))))) + +(t/deftest create-import-binfile-job-answers-with-a-pending-job + (let [profile (th/create-profile* 1) + out (create-import-binfile-job (:id profile) (:default-project-id profile) + :upload-id (upload-chunked! profile))] + + (t/is (th/success? out)) + + (let [result (:result out) + job (jobs/get-job th/*system* (:id result)) + staged (sto/get-object (:app.storage/storage th/*system*) (:resource-id job))] + + (t/testing "the answer is enough to follow the job" + (t/is (uuid? (:id result))) + (t/is (= "pending" (:status result))) + (t/is (= "import-binfile" (:name result)))) + + (t/testing "the row points at the package the job will consume" + (t/is (some? (:resource-id job))) + (t/is (= "binfile" (:queue job))) + (t/is (= sto/job-resource-bucket (:bucket (meta staged)))) + (t/is (= (:id profile) (:profile-id (meta staged))))) + + (t/testing "and freezes the destination and the manifest metadata" + (let [job-def (jobs/get-job-def (::jobs/defs th/*system*) :import-binfile) + params (jobs/decode-params job-def (:params job))] + (t/is (= (:default-project-id profile) (:project-id params))) + (t/is (= "imported" (:name params))) + (t/is (= 3 (:version params))) + (t/is (string? (:generated-by params))))) + + (t/testing "the audit points at the job and the manifest, not the package" + (let [props (::audit/props (meta (:result out)))] + (t/is (= (:id result) (:job-id props))) + (t/is (= #{:job-id :generated-by :referer} (set (keys props))))))))) + +(t/deftest create-import-binfile-job-assembles-the-chunks-of-its-upload + (let [profile (th/create-profile* 1) + session-id (upload-chunked! profile) + out (create-import-binfile-job (:id profile) (:default-project-id profile) + :upload-id session-id)] + + (t/is (th/success? out)) + + (t/testing "the assembled package is the resource of the job" + (let [job (jobs/get-job th/*system* (:id (:result out)))] + (t/is (some? (:resource-id job))))) + + (t/testing "the upload session is consumed" + (t/is (some? (:deleted-at (session-row session-id))))) + + (t/testing "and the temporary file of the assembly is gone" + (t/is (empty? (assembled-tempfiles)))))) + +(t/deftest create-import-binfile-job-rejects-an-incomplete-upload + (let [profile (th/create-profile* 1) + session-id (-> (th/command! {::th/type :create-upload-session + ::rpc/profile-id (:id profile) + :total-chunks 2}) + :result :session-id) + out (create-import-binfile-job (:id profile) (:default-project-id profile) + :upload-id session-id)] + + (t/is (not (th/success? out))) + (t/is (= :missing-chunks (th/ex-code (:error out)))) + (t/is (zero? (count-jobs))))) + +(t/deftest create-import-binfile-job-refuses-an-upload-of-another-profile + (let [owner (th/create-profile* 1) + other (th/create-profile* 2) + session-id (upload-chunked! owner) + out (create-import-binfile-job (:id other) (:default-project-id other) + :upload-id session-id)] + + (t/testing "the upload belongs to the profile that made it" + (t/is (not (th/success? out))) + (t/is (= :not-found (th/ex-type (:error out))))) + + (t/testing "and it is not consumed by the attempt" + (t/is (nil? (:deleted-at (session-row session-id))))) + + (t/testing "nor is any job created" + (t/is (zero? (count-jobs)))))) + +(t/deftest create-import-binfile-job-checks-the-edition-permission + (let [owner (th/create-profile* 1) + other (th/create-profile* 2) + session-id (upload-chunked! other) + out (create-import-binfile-job (:id other) (:default-project-id owner) + :upload-id session-id)] + + (t/testing "a job that cannot edit the destination is refused" + (t/is (not (th/success? out)))) + + (t/testing "before the upload is assembled" + (t/is (nil? (:deleted-at (session-row session-id))))) + + (t/testing "and no job is created" + (t/is (zero? (count-jobs)))))) + +(t/deftest create-import-binfile-job-refuses-a-version-that-does-not-exist + (let [profile (th/create-profile* 1) + session-id (upload-chunked! profile) + ;; the chunks of the upload are alive: the GC reclaims them later + before (live-storage-objects) + out (create-import-binfile-job (:id profile) (:default-project-id profile) + :params {:project-id (:default-project-id profile) + :name "imported" + :version 2} + :upload-id session-id)] + + (t/is (not (th/success? out))) + (t/is (= :params-validation (th/ex-code (:error out)))) + (t/is (zero? (count-jobs))) + + (t/testing "no package was staged for it" + (t/is (= before (live-storage-objects)))) + + (t/testing "and the upload is not consumed by the rejection" + (t/is (nil? (:deleted-at (session-row session-id))))) + + (t/testing "so nothing had to be assembled either" + (t/is (empty? (assembled-tempfiles)))))) + +(t/deftest create-import-binfile-job-rejects-malformed-params-before-assembling + ;; the shape of the caller params is checked before the chunks are + ;; assembled: a name over the limit never copies the package to disk + (let [profile (th/create-profile* 1) + session-id (upload-chunked! profile) + before (live-storage-objects) + out (with-redefs [cmd-jobs/assemble-upload + (fn [& _] + (throw (ex-info "assembled" {})))] + (create-import-binfile-job (:id profile) (:default-project-id profile) + :params {:project-id (:default-project-id profile) + :name (apply str (repeat 251 "x"))} + :upload-id session-id))] + + (t/is (not (th/success? out))) + (t/is (= :params-validation (th/ex-code (:error out)))) + (t/is (zero? (count-jobs))) + + (t/testing "no package was staged for it" + (t/is (= before (live-storage-objects)))) + + (t/testing "so nothing had to be assembled either" + (t/is (empty? (assembled-tempfiles)))))) + +(t/deftest create-import-binfile-job-leaves-no-temporary-when-the-package-is-corrupt + (let [profile (th/create-profile* 1) + ;; a header that says "version 3" (the zip magic) over bytes that + ;; are not a zip at all: what fails is reading the manifest, once + ;; the chunks have already been assembled + session-id (upload-chunked! profile + {:content (str "PK\u0003\u0004" "not a zip")}) + before (live-storage-objects) + out (create-import-binfile-job (:id profile) (:default-project-id profile) + :upload-id session-id)] + + (t/is (not (th/success? out))) + (t/is (zero? (count-jobs))) + + (t/testing "no package was staged" + (t/is (= before (live-storage-objects)))) + + (t/testing "and the file assembled from the chunks is gone" + (t/is (empty? (assembled-tempfiles)))))) + +(t/deftest create-import-binfile-job-releases-the-package-when-the-submit-fails + (let [profile (th/create-profile* 1) + session-id (upload-chunked! profile) + ;; the chunks of the upload are alive: the GC reclaims them later + before (live-storage-objects) + out (with-redefs [jobs/submit (fn [& _] + (ex/raise :type :internal + :code :boom + :hint "cannot create the job"))] + (create-import-binfile-job (:id profile) (:default-project-id profile) + :upload-id session-id))] + + (t/testing "the failure reaches the caller" + (t/is (not (th/success? out)))) + + (t/testing "the staged package does not stay alive" + (t/is (= before (live-storage-objects)))) + + (t/testing "and no job row was left behind" + (t/is (zero? (count-jobs)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; READ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- get-job! + [profile-id job-id] + (th/command! {::th/type :get-job + ::rpc/profile-id profile-id + :id job-id})) + +(defn- claim-job! + "Drive a pending job to `running`, as the dispatcher would: the terminal + writers only accept a job that is being executed." + [job-id] + (jobs/claim th/*system* job-id + (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at)))) + +(t/deftest get-job-answers-with-the-state-of-the-job + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (:id (:result (create-export-binfile-job (:id profile) #{file-id}))) + out (get-job! (:id profile) job-id)] + + (t/is (th/success? out)) + (let [result (:result out)] + (t/is (= job-id (:id result))) + (t/is (= "pending" (:status result))) + (t/is (= "export-binfile" (:name result))) + (t/is (ct/inst? (:created-at result))) + (t/is (ct/inst? (:expires-at result))) + + (t/testing "a job that has not finished has nothing to report yet" + (t/is (not (contains? result :result))) + (t/is (not (contains? result :error))))))) + +(t/deftest get-job-answers-with-the-result-of-a-finished-job + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (:id (:result (create-export-binfile-job (:id profile) #{file-id})))] + + (t/is (pos? (claim-job! job-id))) + (t/is (pos? (jobs/complete th/*system* :job-id job-id + :result {:value 42 :file-ids [file-id]}))) + + (let [result (:result (get-job! (:id profile) job-id))] + (t/is (= "completed" (:status result))) + (t/testing "the result comes back as the map the handler produced" + ;; the row holds plain json, so a uuid comes back as the text it is + (t/is (= {:value 42 :file-ids [(str file-id)]} (:result result)))) + (t/is (not (contains? result :error)))))) + +(t/deftest get-job-answers-with-the-error-of-a-failed-job + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (:id (:result (create-export-binfile-job (:id profile) #{file-id})))] + + (t/is (pos? (claim-job! job-id))) + (t/is (pos? (jobs/fail th/*system* job-id {:type :internal + :code :boom + :hint "boom"}))) + + (let [result (:result (get-job! (:id profile) job-id))] + (t/is (= "failed" (:status result))) + (t/testing "the error is the public summary, with its keywords back" + (t/is (= {:type :internal :code :boom :hint "boom"} (:error result)))) + (t/testing "and it carries no result" + (t/is (not (contains? result :result))))))) + +(t/deftest get-job-hides-a-job-of-another-profile + (let [owner (th/create-profile* 1) + other (th/create-profile* 2) + file-id (first (:file-ids (import-fixture! owner))) + job-id (:id (:result (create-export-binfile-job (:id owner) #{file-id})))] + + (t/testing "the owner reads it" + (t/is (th/success? (get-job! (:id owner) job-id)))) + + (t/testing "another profile gets the answer of a job that does not exist" + (let [out (get-job! (:id other) job-id)] + (t/is (not (th/success? out))) + (t/is (= :not-found (th/ex-type (:error out)))))) + + (t/testing "and so does an unknown id" + (let [out (get-job! (:id owner) (uuid/next))] + (t/is (not (th/success? out))) + (t/is (= :not-found (th/ex-type (:error out)))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; CANCEL +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- cancel-job + [profile-id job-id] + (th/command! {::th/type :cancel-job + ::rpc/profile-id profile-id + :id job-id})) + +(t/deftest cancel-job-cancels-a-running-job + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (:id (:result (create-export-binfile-job (:id profile) #{file-id})))] + + (t/is (pos? (claim-job! job-id))) + + (let [out (cancel-job (:id profile) job-id)] + (t/is (th/success? out)) + (t/testing "the answer names the cancelled job" + (t/is (= job-id (:id (:result out)))) + (t/is (= "cancelled" (:status (:result out))))) + (t/testing "and the row is cancelled" + (t/is (= "cancelled" (:status (jobs/get-job th/*system* job-id)))))))) + +(t/deftest cancel-job-hides-a-job-of-another-profile + (let [owner (th/create-profile* 1) + other (th/create-profile* 2) + file-id (first (:file-ids (import-fixture! owner))) + job-id (:id (:result (create-export-binfile-job (:id owner) #{file-id})))] + + (t/testing "another profile gets the answer of a job that does not exist" + (let [out (cancel-job (:id other) job-id)] + (t/is (not (th/success? out))) + (t/is (= :not-found (th/ex-type (:error out)))))) + + (t/testing "and the job keeps running untouched" + (t/is (pos? (claim-job! job-id))) + (t/is (= "running" (:status (jobs/get-job th/*system* job-id))))))) + +(t/deftest cancel-job-answers-a-finished-job-without-error + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (:id (:result (create-export-binfile-job (:id profile) #{file-id})))] + + (t/is (pos? (claim-job! job-id))) + (t/is (pos? (jobs/complete th/*system* :job-id job-id + :result {:value 42}))) + + (t/testing "cancelling what is already over answers its state" + (let [out (cancel-job (:id profile) job-id)] + (t/is (th/success? out)) + (t/is (= "completed" (:status (:result out)))))))) + +(t/deftest cancel-job-rejects-a-malformed-id + (let [profile (th/create-profile* 1) + out (cancel-job (:id profile) "nope")] + (t/is (not (th/success? out))) + (t/is (= :validation (th/ex-type (:error out)))))) diff --git a/backend/test/backend_tests/rpc_access_tokens_test.clj b/backend/test/backend_tests/rpc_access_tokens_test.clj index 5fa3a1496a..359bbf66fd 100644 --- a/backend/test/backend_tests/rpc_access_tokens_test.clj +++ b/backend/test/backend_tests/rpc_access_tokens_test.clj @@ -6,12 +6,15 @@ (ns backend-tests.rpc-access-tokens-test (:require + [app.common.time :as ct] [app.common.uuid :as uuid] [app.db :as db] [app.http :as http] [app.http.access-token :as actoken] [app.rpc :as-alias rpc] + [app.rpc.commands.access-token :as access-token] [app.storage :as sto] + [app.tokens :as tokens] [backend-tests.helpers :as th] [clojure.test :as t] [mockery.core :refer [with-mocks]])) @@ -207,7 +210,7 @@ (t/is (not (contains? all-ids (:id second-mcp)))) (t/is (contains? all-ids (:id third-mcp)))))))) -(t/deftest mcp-tokens-cannot-be-used-as-access-tokens +(t/deftest access-token-decoder-accepts-mcp-and-personal-issuers (let [prof (th/create-profile* 1 {:is-active true}) cfg th/*system*] @@ -218,9 +221,9 @@ :name "mcp token"}) mcp-token (:token result) - ;; Try to decode as access token (should fail) decoded (actoken/decode-token cfg mcp-token)] - (t/is (nil? decoded)))) + (t/is (= "urn:penpot:mcp-token" (:iss decoded))) + (t/is (= (:id prof) (:uid decoded))))) (t/testing "Regular access tokens use access-token issuer claim" (let [{:keys [result]} (th/command! {::th/type :create-access-token @@ -232,3 +235,71 @@ decoded (actoken/decode-token cfg access-token)] (t/is (some? decoded)) (t/is (= (:id prof) (:uid decoded))))))) + +(t/deftest oauth-mcp-token-provisioning + (let [prof (th/create-profile* 1 {:is-active true}) + cfg th/*system* + provision #(db/tx-run! cfg access-token/ensure-mcp-access! (:id prof))] + (t/testing "approval enables MCP and creates a fixed token" + (let [token (provision) + props (-> (th/db-get :profile {:id (:id prof)}) + :props db/decode-transit-pgobject)] + (t/is (string? (:token token))) + (t/is (nil? (:expires-at token))) + (t/is (true? (:mcp-enabled props))) + (t/is (= token (provision))))) + (t/testing "approval preserves a token generated in settings" + (let [{:keys [result error]} + (th/command! {::th/type :create-access-token + ::rpc/profile-id (:id prof) + :type "mcp" + :name "From settings"})] + (t/is (nil? error)) + (t/is (= (:token result) (:token (provision)))))) + (t/testing "approval replaces an expired token" + (db/exec! th/*pool* [(str "UPDATE access_token SET expires_at = now() - interval '1 day'" + " WHERE profile_id = ?") + (:id prof)]) + (let [expired (first (db/query th/*pool* :access-token {:profile-id (:id prof) :type "mcp"})) + token (provision)] + (t/is (not= (:token expired) (:token token))) + (t/is (nil? (:expires-at token))) + (t/is (= [(:id token)] + (map :id (db/query th/*pool* :access-token {:profile-id (:id prof) :type "mcp"})))))) + (t/testing "approval replaces a token issued with the personal-token issuer" + (db/delete! th/*pool* :access-token {:profile-id (:id prof)}) + (let [token-id (uuid/next) + now (ct/now) + old (tokens/generate cfg {:iss "access-token" :uid (:id prof) + :iat now :tid token-id})] + (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (db/insert! conn :access-token + {:id token-id :name "MCP" :token old :type "mcp" + :profile-id (:id prof) :created-at now :updated-at now + :perms (db/create-array conn "text" [])}))) + (let [token (provision)] + (t/is (not= old (:token token))) + (t/is (= actoken/mcp-token-issuer + (:iss (actoken/decode-token cfg (:token token))))) + (t/is (= [(:id token)] + (map :id (db/query th/*pool* :access-token + {:profile-id (:id prof) :type "mcp"}))))))) + (t/testing "approval replaces a token this instance cannot decrypt" + (db/delete! th/*pool* :access-token {:profile-id (:id prof)}) + (let [now (ct/now)] + (db/tx-run! cfg (fn [{:keys [::db/conn]}] + (db/insert! conn :access-token + {:id (uuid/next) :name "MCP" :token "not-a-jwe" :type "mcp" + :profile-id (:id prof) :created-at now :updated-at now + :perms (db/create-array conn "text" [])}))) + (let [token (provision)] + (t/is (not= "not-a-jwe" (:token token))) + (t/is (= [(:id token)] + (map :id (db/query th/*pool* :access-token + {:profile-id (:id prof) :type "mcp"}))))))) + (t/testing "concurrent approvals share one token" + (db/delete! th/*pool* :access-token {:profile-id (:id prof)}) + (let [results (mapv deref (repeatedly 4 #(future (provision))))] + (t/is (= 1 (count (set (map :token results))))) + (t/is (= 1 (count (db/query th/*pool* :access-token + {:profile-id (:id prof) :type "mcp"})))))))) diff --git a/backend/test/backend_tests/rpc_audit_test.clj b/backend/test/backend_tests/rpc_audit_test.clj index 81ca636dbe..8ae01500ad 100644 --- a/backend/test/backend_tests/rpc_audit_test.clj +++ b/backend/test/backend_tests/rpc_audit_test.clj @@ -8,17 +8,30 @@ (:require [app.common.pprint :as pp] [app.common.time :as ct] + [app.common.transit :as transit] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] + [app.jobs :as-alias jobs] [app.loggers.audit :as audit] + [app.main :as main] + [app.metrics :as mtx] + [app.metrics.definition :as-alias mdef] [app.rpc :as-alias rpc] [app.rpc.climit :as climit] + [app.rpc.commands.audit :as cmd-audit] [app.util.services :as sv] [backend-tests.helpers :as th] + [clojure.string :as str] [clojure.test :as t] + [integrant.core :as ig] [mockery.core :refer [with-mocks]] - [yetti.request])) + [yetti.request]) + (:import + io.prometheus.client.Collector$MetricFamilySamples + io.prometheus.client.CollectorRegistry + io.prometheus.client.Counter + io.prometheus.client.Counter$Child)) (t/use-fixtures :once th/state-init) (t/use-fixtures :each th/database-reset) @@ -95,7 +108,7 @@ (assoc base :app.http/auth-key-id :nexus))))))) (t/deftest push-events-1 - (with-redefs [app.config/flags #{:audit-log}] + (with-redefs [app.config/flags #{:audit-log :nexus}] (let [prof (th/create-profile* 1 {:is-active true}) team-id (:default-team-id prof) proj-id (:default-project-id prof) @@ -125,7 +138,9 @@ (t/is (= 1 (count rows))) (t/is (= (:id prof) (:profile-id row))) (t/is (= "navigate" (:name row))) - (t/is (= "frontend" (:source row))))))) + (t/is (= "frontend" (:source row))) + ;; The row is born pending for the enabled consumer. + (t/is (= 1 (count (th/db-exec! ["select * from audit_log_pending"])))))))) (t/deftest push-events-2 (with-redefs [app.config/flags #{:audit-log}] @@ -226,36 +241,41 @@ (t/deftest push-events-both-flags-creates-two-rows ;; When both :audit-log and :telemetry flags are active, two rows ;; should be stored: one full audit entry and one telemetry entry. - (with-redefs [cf/flags #{:audit-log :telemetry}] - (let [prof (th/create-profile* 1 {:is-active true}) - params {::th/type :push-audit-events - ::rpc/profile-id (:id prof) - :events [{:name "navigate" - :props {:route "dashboard"} - :context {:browser "Chrome" - :version "2.0.0" - :initiator "app"} - :timestamp (ct/now) - :type "action"}]} - params (with-meta params - {:app.http/request http-request}) - out (th/command! params)] + ;; The profile is created before enabling :admin-console: team + ;; bootstrap calls the real nitrate client, which is unconfigured + ;; in tests. + (let [prof (th/create-profile* 1 {:is-active true})] + (with-redefs [cf/flags #{:audit-log :telemetry :nexus :admin-console}] + (let [params {::th/type :push-audit-events + ::rpc/profile-id (:id prof) + :events [{:name "navigate" + :props {:route "dashboard"} + :context {:browser "Chrome" + :version "2.0.0" + :initiator "app"} + :timestamp (ct/now) + :type "action"}]} + params (with-meta params + {:app.http/request http-request}) + out (th/command! params)] - (t/is (nil? (:error out))) + (t/is (nil? (:error out))) - (let [[row1 row2 :as rows] (->> (th/db-exec! ["select * from audit_log order by source"]) - (mapv decode-row))] - (t/is (= 2 (count rows))) - ;; First row: full audit-log entry - (t/is (= "frontend" (:source row1))) - (t/is (contains? (:props row1) :route)) - (t/is (not= "0.0.0.0" (str (:ip-addr row1)))) - ;; Second row: telemetry entry - (t/is (= "telemetry:frontend" (:source row2))) - (t/is (= "0.0.0.0" (str (:ip-addr row2)))) - (let [day-now (ct/truncate (ct/now) :days)] - (t/is (= day-now (:created-at row2))) - (t/is (= day-now (:tracked-at row2)))))))) + (let [[row1 row2 :as rows] (->> (th/db-exec! ["select * from audit_log order by source"]) + (mapv decode-row))] + (t/is (= 2 (count rows))) + ;; First row: full audit-log entry + (t/is (= "frontend" (:source row1))) + (t/is (contains? (:props row1) :route)) + (t/is (not= "0.0.0.0" (str (:ip-addr row1)))) + ;; Second row: telemetry entry + (t/is (= "telemetry:frontend" (:source row2))) + (t/is (= "0.0.0.0" (str (:ip-addr row2)))) + (let [day-now (ct/truncate (ct/now) :days)] + (t/is (= day-now (:created-at row2))) + (t/is (= day-now (:tracked-at row2)))) + ;; Both rows are born pending for the enabled consumers. + (t/is (= 4 (count (th/db-exec! ["select * from audit_log_pending"]))))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; BACKEND PROCESS-EVENT PATH (RPC commands) @@ -336,7 +356,7 @@ ;; id to the `:process-webhook-event` worker task. Regression test: ;; the id used to be generated inside `append-audit-entry` without ;; ever reaching the worker params, so `:event-id` always landed nil. - (with-mocks [submit-mock {:target 'app.worker/submit! :return nil}] + (with-mocks [submit-mock {:target 'app.jobs/submit :return nil}] (with-redefs [cf/flags #{:audit-log :webhooks}] (let [prof (th/create-profile* 1 {:is-active true}) event {:type "action" @@ -353,15 +373,16 @@ (mapv decode-row))] (t/is (= 1 (count rows))) (t/is (= 1 (:call-count @submit-mock))) - (let [params (-> @submit-mock :call-args-list ffirst :app.worker/params)] - (t/is (some? (:id params))) - (t/is (= (:id row) (:id params))))))))) + (let [params (-> @submit-mock :call-args-list first second ::jobs/params) + task-event (transit/decode-str (:event-blob params))] + (t/is (some? (:id task-event))) + (t/is (= (:id row) (:id task-event))))))))) (t/deftest backend-process-event-telemetry-row-gets-own-id ;; When both `:audit-log` and `:telemetry` are active, the shadow ;; telemetry row must not reuse the full row id (shared PK), while ;; the webhook task still carries the full row id. - (with-mocks [submit-mock {:target 'app.worker/submit! :return nil}] + (with-mocks [submit-mock {:target 'app.jobs/submit :return nil}] (with-redefs [cf/flags #{:audit-log :telemetry :webhooks}] (let [prof (th/create-profile* 1 {:is-active true}) event {:type "action" @@ -378,9 +399,10 @@ (mapv decode-row))] (t/is (= 2 (count rows))) (t/is (not= (:id row1) (:id row2))) - (let [params (-> @submit-mock :call-args-list ffirst :app.worker/params)] + (let [params (-> @submit-mock :call-args-list first second ::jobs/params) + task-event (transit/decode-str (:event-blob params))] (t/is (= (:id (first (filter #(= "backend" (:source %)) rows))) - (:id params))))))))) + (:id task-event))))))))) (t/deftest push-events-disabled-when-no-flags-and-no-telemetry ;; When neither audit-log nor telemetry is enabled, no rows should @@ -853,3 +875,89 @@ (t/is (= (:id caller) (:profile-id (prepare-event (:id caller) result))) (str "override " (pr-str bad) " must fall back to the caller")))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; INGEST METRICS (bulk frontend path) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; handle-events resolves its metrics from the cfg (same pattern as +;; the jobs substrate), so these tests drive it directly with an +;; isolated registry instead of the shared test-system one. +(defn- make-ingest-metrics + [] + (ig/init-key :app.metrics/metrics + {:default (select-keys main/default-metrics [:audit-ingest-events])})) + +(defn- ingest-counter + [metrics kind outcome] + (let [collector (mtx/get-collector metrics :audit-ingest-events) + instance (::mdef/instance collector) + child (.labels ^Counter instance (into-array String [kind outcome]))] + (.get ^Counter$Child child))) + +(defn- counter-label-sets + "Exact label sets present for the ingest counter, read off the + registry so phantom series cannot hide. Matches the family by + prefix (Prometheus strips the `_total` suffix from the family + name) and only counts `_total` samples, not `_created` ones." + [metrics] + (let [full (get main/default-metrics :audit-ingest-events) + base (str/replace (::mdef/name full) #"_total$" "")] + (->> (enumeration-seq + (.metricFamilySamples ^CollectorRegistry (mtx/get-registry metrics))) + (filter (fn [^Collector$MetricFamilySamples family] + (str/starts-with? (.-name family) base))) + (mapcat (fn [^Collector$MetricFamilySamples family] + (.samples family))) + (filter (fn [sample] (str/ends-with? (.-name sample) "_total"))) + (into #{} (map (fn [sample] (vec (.-labelValues sample)))))))) + +(defn- push-params + [profile-id] + (with-meta {::rpc/profile-id profile-id + ::rpc/request-at (ct/now) + :events [{:name "navigate" :type "action" + :props {:route "dashboard"} + :timestamp (ct/now)}]} + ;; A map request (like th/command! builds): the reify IRequest + ;; stub only serves the IP headers and blows up on x-client. + {:app.http/request (th/make-dummy-request)})) + +(t/deftest push-handle-events-records-inserted + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-ingest-metrics)] + (with-redefs [cf/flags #{:audit-log :telemetry :nexus :admin-console}] + (@#'cmd-audit/handle-events {::db/pool th/*pool* ::mtx/metrics metrics} + (push-params (:id prof))) + (t/is (= 1.0 (ingest-counter metrics "full" "inserted"))) + (t/is (= 1.0 (ingest-counter metrics "shadow" "inserted"))) + (t/is (= 2 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 4 (count (th/db-exec! ["select * from audit_log_pending"]))))))) + +(t/deftest push-handle-events-records-failed-on-error + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-ingest-metrics)] + (with-redefs [cf/flags #{:audit-log :telemetry :nexus :admin-console}] + (with-mocks [insert-mock {:target 'app.db/insert-many! + :throw (ex-info "boom" {})}] + (t/is (thrown? clojure.lang.ExceptionInfo + (@#'cmd-audit/handle-events {::db/pool th/*pool* ::mtx/metrics metrics} + (push-params (:id prof))))) + (t/is (= 1.0 (ingest-counter metrics "full" "failed"))) + (t/is (= 1.0 (ingest-counter metrics "shadow" "failed"))) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log_pending"])))))))) + +(t/deftest push-handle-events-single-face-emits-no-empty-series + ;; With only :audit-log on, exactly one ingest series exists: the + ;; pos? guard in record-ingested emits no phantom shadow series. + ;; (No send flags either, so R3 leaves the row pending-less.) + (let [prof (th/create-profile* 1 {:is-active true}) + metrics (make-ingest-metrics)] + (with-redefs [cf/flags #{:audit-log}] + (@#'cmd-audit/handle-events {::db/pool th/*pool* ::mtx/metrics metrics} + (push-params (:id prof))) + (t/is (= 1.0 (ingest-counter metrics "full" "inserted"))) + (t/is (= #{["full" "inserted"]} (counter-label-sets metrics))) + (t/is (= 1 (count (th/db-exec! ["select * from audit_log"])))) + (t/is (= 0 (count (th/db-exec! ["select * from audit_log_pending"]))))))) diff --git a/backend/test/backend_tests/rpc_binfile_test.clj b/backend/test/backend_tests/rpc_binfile_test.clj index f15a261040..0a829d23db 100644 --- a/backend/test/backend_tests/rpc_binfile_test.clj +++ b/backend/test/backend_tests/rpc_binfile_test.clj @@ -167,10 +167,10 @@ output (tmp/tempfile :suffix ".zip")] (bf.v3/export-files! (-> th/*system* - (assoc ::bfc/ids #{(:id file)}) + (assoc ::bfc/file-ids #{(:id file)}) (assoc ::bfc/embed-assets false) - (assoc ::bfc/include-libraries false)) - (io/output-stream output)) + (assoc ::bfc/include-libraries false) + (assoc ::bfc/output (io/output-stream output)))) output)) (defn- import-file diff --git a/backend/test/backend_tests/rpc_commands_mcp_feedback_test.clj b/backend/test/backend_tests/rpc_commands_mcp_feedback_test.clj new file mode 100644 index 0000000000..7987596481 --- /dev/null +++ b/backend/test/backend_tests/rpc_commands_mcp_feedback_test.clj @@ -0,0 +1,179 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.rpc-commands-mcp-feedback-test + (:require + [app.common.json :as json] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.email :as email] + [app.http :as http] + [app.jobs :as jobs] + [app.rpc :as-alias rpc] + [app.rpc.commands.access-token :as access-token] + [app.tokens :as tokens] + [backend-tests.helpers :as th] + [clojure.test :as t] + [yetti.response :as-alias yres])) + +(t/use-fixtures :once (t/compose-fixtures + (partial th/init-config [:enable-access-tokens + :enable-client-header-check-middleware]) + th/init-system)) +(t/use-fixtures :each th/database-reset) + +(def ^:private feedback + {:subject "Export fails" + :feedback "Exporting a valid shape repeatedly fails. Expected a PNG."}) + +(defn- create-identity + [] + (let [profile (th/create-profile* (uuid/next) {:is-active true}) + token (db/tx-run! th/*system* access-token/create-access-token + (:id profile) "MCP" nil "mcp")] + {:profile profile :token token})) + +(defn- request + ([token] (request token feedback)) + ([token params] (request token params {})) + ([token params options] + (with-redefs [cf/config (assoc cf/config :mcp-feedback-email "mcp@example.com")] + ((#'http/router-handler (::http/router th/*system*)) + (th/make-dummy-request + (merge {:path "/api/main/methods/send-mcp-feedback" + :method :post + :headers (cond-> {"content-type" "application/json" + "accept" "application/json" + "x-client" "penpot-mcp"} + token (assoc "authorization" (str "Token " token))) + :body-bytes (json/encode params)} + options)))))) + +(defn- emails + [] + (map #(jobs/decode-params (jobs/get-job-def (::jobs/defs th/*system*) :sendmail) (:params %)) + (db/query th/*system* :job {:name "sendmail"}))) + +(t/deftest queues-plain-text-feedback + (let [{:keys [token profile]} (create-identity) + response (request (:token token)) + email (first (emails))] + (t/is (= 200 (::yres/status response))) + (t/is (= 1 (count (emails)))) + (t/is (= "mcp@example.com" (:to email))) + (t/is (= (:email profile) (:reply-to email))) + (t/is (= "[MCP feedback] Export fails" (:subject email))) + (t/is (= {"text/plain" (str "Submitted by: " (:fullname profile) + " <" (:email profile) ">\n" + "Profile ID: " (:id profile) "\n\n" + "Feedback:\n\n" (:feedback feedback))} + (:body email))) + (t/is (nil? (:attachments email))))) + +(t/deftest rejects-invalid-credentials + (let [{:keys [profile token]} (create-identity)] + (doseq [credential [nil "" "invalid" + (tokens/generate th/*system* {:iss "other" + :uid (:id profile) :tid (:id token)}) + (tokens/generate th/*system* {:iss "access-token" + :uid (:id profile) :tid (:id token)}) + (tokens/generate th/*system* {:iss "urn:penpot:mcp-token" + :uid (uuid/next) :tid (:id token)}) + (tokens/generate th/*system* {:iss "urn:penpot:mcp-token" + :uid (:id profile) :tid (uuid/next)}) + (tokens/generate th/*system* {:iss "urn:penpot:mcp-token" + :uid (:id profile) :tid (:id token) + :exp (ct/minus (ct/now) (ct/duration "1h"))})]] + (t/is (= 401 (::yres/status (request credential))))) + (t/is (empty? (emails))))) + +(t/deftest rejects-expired-revoked-and-wrong-type-tokens + (let [{:keys [token profile]} (create-identity)] + (db/update! th/*system* :access-token + {:expires-at (ct/minus (ct/now) (ct/duration "1h"))} {:id (:id token)}) + (t/is (= 401 (::yres/status (request (:token token))))) + (db/update! th/*system* :access-token {:expires-at nil :type "personal"} {:id (:id token)}) + (t/is (= 401 (::yres/status (request (:token token))))) + (db/update! th/*system* :access-token {:type "mcp"} {:id (:id token)}) + (db/tx-run! th/*system* access-token/create-access-token (:id profile) "Replacement" nil "mcp") + (t/is (= 401 (::yres/status (request (:token token))))) + (t/is (empty? (emails))))) + +(t/deftest rejects-unavailable-profiles + (doseq [changes [{:is-active false} + {:is-blocked true} + {:deleted-at (ct/minus (ct/now) (ct/duration "1h"))}]] + (let [{:keys [token profile]} (create-identity)] + (db/update! th/*system* :profile changes {:id (:id profile)}) + (t/is (= (if (:deleted-at changes) 404 403) + (::yres/status (request (:token token))))))) + (t/is (empty? (emails)))) + +(t/deftest validates-feedback-before-queueing + (let [{:keys [token]} (create-identity)] + (doseq [params [{} + (assoc feedback :subject "") + (assoc feedback :subject " ") + (assoc feedback :subject "a\r\nBcc: victim@example.com") + (assoc feedback :subject "a\tb") + (assoc feedback :subject "a\u0085b") + (assoc feedback :subject "a\u0000b") + (assoc feedback :feedback "a\u0000b") + (assoc feedback :feedback "a\u001bb") + (assoc feedback :subject (apply str (repeat 121 "x"))) + (assoc feedback :feedback " \n") + (assoc feedback :feedback (apply str (repeat 5001 "x"))) + (assoc feedback :to "victim@example.com") + (assoc feedback :attachments {}) + (assoc feedback :feedback 42)]] + (t/is (= 400 (::yres/status (request (:token token) params))))) + (t/is (empty? (emails))) + (t/is (= 200 (::yres/status + (request (:token token) {:subject (apply str (repeat 120 "x")) + :feedback (apply str (repeat 5000 "x"))})))) + (t/is (= 200 (::yres/status + (request (:token token) {:subject "Tabs and breaks" + :feedback "line one\r\n\tline two\n"})))))) + +(t/deftest audit-event-omits-feedback-text + (let [{:keys [profile]} (create-identity)] + (with-redefs [cf/config (assoc cf/config :mcp-feedback-email "mcp@example.com")] + (let [result (th/command-raw! (merge feedback {::th/type :send-mcp-feedback + ::rpc/profile-id (:id profile) + ::rpc/auth-type :token + ::rpc/token-type "mcp"}))] + (t/is (= {} (:app.loggers.audit/replace-props (meta result)))))))) + +(t/deftest disabled-without-feedback-email + (let [{:keys [token]} (create-identity)] + (with-redefs [cf/config (dissoc cf/config :mcp-feedback-email)] + (let [response ((#'http/router-handler (::http/router th/*system*)) + (th/make-dummy-request + {:path "/api/main/methods/send-mcp-feedback" :method :post + :headers {"authorization" (str "Token " (:token token)) + "content-type" "application/json" + "x-client" "penpot-mcp"} + :body-bytes (json/encode feedback)}))] + (t/is (= 400 (::yres/status response))))) + (t/is (empty? (emails))))) + +(t/deftest feedback-is-post-only + (let [{:keys [token]} (create-identity)] + (t/is (= 405 (::yres/status (request (:token token) feedback {:method :get}))))) + (t/is (empty? (emails)))) + +(t/deftest mcp-token-does-not-authorize-ordinary-rpc + (let [{:keys [token]} (create-identity)] + (t/is (= 403 (::yres/status + (request (:token token) {} {:path "/api/rpc/command/get-teams"})))))) + +(t/deftest does-not-acknowledge-a-failed-queue-submission + (let [{:keys [token]} (create-identity)] + (with-redefs [email/send (fn [& _] (throw (ex-info "Queue unavailable" {})))] + (t/is (= 500 (::yres/status (request (:token token))))))) + (t/is (empty? (emails)))) diff --git a/backend/test/backend_tests/rpc_demo_test.clj b/backend/test/backend_tests/rpc_demo_test.clj index 64fc64b8e1..0a54134118 100644 --- a/backend/test/backend_tests/rpc_demo_test.clj +++ b/backend/test/backend_tests/rpc_demo_test.clj @@ -9,8 +9,8 @@ [app.auth :as auth] [app.common.time :as ct] [app.config :as cf] + [app.jobs :as jobs] [app.rpc.commands.profile :as profile] - [app.worker :as wrk] [backend-tests.helpers :as th] [clojure.test :as t])) @@ -80,8 +80,8 @@ (t/deftest create-demo-profile-uses-global-delay-by-default (with-redefs [cf/flags (conj cf/flags :demo-users)] (let [captured (atom nil)] - (with-redefs [wrk/submit! (fn [& {:keys [::wrk/task ::wrk/delay]}] - (reset! captured {:task task :delay delay}))] + (with-redefs [jobs/submit (fn [_cfg {:keys [::jobs/name ::jobs/delay]}] + (reset! captured {:task name :delay delay}))] (let [{:keys [error result]} (th/command! {::th/type :create-demo-profile})] (t/is (nil? error)) (t/is (some? (:email result))) @@ -91,8 +91,8 @@ (t/deftest create-demo-profile-accepts-short-expires-in (with-redefs [cf/flags (conj cf/flags :demo-users)] (let [captured (atom nil)] - (with-redefs [wrk/submit! (fn [& {:keys [::wrk/task ::wrk/delay]}] - (reset! captured {:task task :delay delay}))] + (with-redefs [jobs/submit (fn [_cfg {:keys [::jobs/name ::jobs/delay]}] + (reset! captured {:task name :delay delay}))] (let [{:keys [error result]} (th/command! {::th/type :create-demo-profile :expires-in "10m"})] (t/is (nil? error)) @@ -140,13 +140,13 @@ decoded (profile/decode-row saved)] (t/is (= :svg (get-in decoded [:props :renderer]))))))) -(t/deftest create-demo-profile-omits-renderer-by-default +(t/deftest create-demo-profile-stores-wasm-renderer-by-default (with-redefs [cf/flags (conj cf/flags :demo-users)] (let [{:keys [error result]} (th/command! {::th/type :create-demo-profile})] (t/is (nil? error)) (let [saved (th/db-get :profile {:email (:email result)}) decoded (profile/decode-row saved)] - (t/is (false? (contains? (:props decoded) :renderer))))))) + (t/is (= :wasm (get-in decoded [:props :renderer]))))))) (t/deftest create-demo-profile-rejects-unknown-renderer (with-redefs [cf/flags (conj cf/flags :demo-users)] diff --git a/backend/test/backend_tests/rpc_feedback_test.clj b/backend/test/backend_tests/rpc_feedback_test.clj index b4a6ae650d..63bff4c143 100644 --- a/backend/test/backend_tests/rpc_feedback_test.clj +++ b/backend/test/backend_tests/rpc_feedback_test.clj @@ -7,9 +7,17 @@ (ns backend-tests.rpc-feedback-test (:require [app.common.schema :as sm] + [app.config :as cf] + [app.db :as db] + [app.email :as eml] + [app.rpc :as-alias rpc] [app.rpc.commands.feedback :as feedback] + [backend-tests.helpers :as th] [clojure.test :as t])) +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + (t/deftest send-user-feedback-schema-validation (let [schema feedback/schema:send-user-feedback] @@ -37,3 +45,20 @@ :content "Test content" :error-report (apply str (repeat 1048577 "x"))}] (t/is (not (sm/valid? schema params))))))) + +(t/deftest send-user-feedback-reaches-send-with-connection + (with-redefs [cf/flags (conj cf/flags :user-feedback) + cf/config (assoc cf/config :user-feedback-destination "fb@example.com")] + (let [profile (th/create-profile* 1 {}) + captured (atom nil)] + (with-redefs [eml/send-template (fn [cfg params] + (reset! captured {:cfg cfg :params params}) + nil)] + (let [{:keys [error]} (th/command! {::th/type :send-user-feedback + ::rpc/profile-id (:id profile) + :subject "s" + :content "c"})] + (t/is (nil? error)))) + (t/testing "send! is reached with a caller connection" + (t/is (some? (:cfg @captured))) + (t/is (some? (::db/conn (:cfg @captured)))))))) diff --git a/backend/test/backend_tests/rpc_file_security_test.clj b/backend/test/backend_tests/rpc_file_security_test.clj index 15ac56c53d..1ecaf3de7f 100644 --- a/backend/test/backend_tests/rpc_file_security_test.clj +++ b/backend/test/backend_tests/rpc_file_security_test.clj @@ -218,6 +218,14 @@ (t/is (some? (:error out))) (t/is (= :not-found (th/ex-type (:error out)))))) +(t/deftest get-file-for-analysis-rejects-a-key-it-does-not-declare + (let [out (th/management-command! {::th/type :get-file-for-analysis + ::rpc/auth-key-id :exporter + :file-id (uuid/random)})] + (t/is (some? (:error out))) + (t/is (= :authorization (th/ex-type (:error out)))) + (t/is (= :shared-key-not-allowed (th/ex-code (:error out)))))) + ;; --- Audit prop coverage (through the commands, into real events) ;; ;; NOTE: the test system neutralizes `audit/submit`, so no audit row diff --git a/backend/test/backend_tests/rpc_file_snapshot_test.clj b/backend/test/backend_tests/rpc_file_snapshot_test.clj index aed2b5ce39..91fea25e0b 100644 --- a/backend/test/backend_tests/rpc_file_snapshot_test.clj +++ b/backend/test/backend_tests/rpc_file_snapshot_test.clj @@ -6,19 +6,28 @@ (ns backend-tests.rpc-file-snapshot-test (:require + [app.binfile.common :as bfc] + [app.common.data :as d] [app.common.features :as cfeat] + [app.common.files.defaults :as cfd] + [app.common.files.migrations :as fmg] [app.common.pprint :as pp] [app.common.thumbnails :as thc] [app.common.time :as ct] + [app.common.types.objects-map :as omap] [app.common.types.shape :as cts] [app.common.uuid :as uuid] [app.config :as cf] [app.db :as db] [app.db.sql :as sql] + [app.features.fdata :as fdata] + [app.features.file-snapshots :as fsnap] [app.http :as http] + [app.msgbus :as mbus] [app.rpc :as-alias rpc] [app.storage :as sto] [backend-tests.helpers :as th] + [clojure.set :as set] [clojure.test :as t] [cuerdas.core :as str])) @@ -84,11 +93,19 @@ ::rpc/profile-id (:id profile) :file-id (:id file) :id @snapshot-id} - out (th/command! params)] + calls (atom []) + out (with-redefs [mbus/pub! (fn [_cfg & {:keys [message]}] + (swap! calls conj message))] + (th/command! params)) + sent (first (filter #(= :file-restored (:type %)) @calls))] ;; (th/print-result! out) (t/is (nil? (:error out))) - (t/is (nil? (:result out)))) + (t/is (nil? (:result out))) + ;; Peers learn who restored which version + (t/is (= (:id profile) (:profile-id sent))) + (t/is (= "label1" (:label sent))) + (t/is (ct/inst? (:created-at sent)))) (let [[row1 row2 :as rows] (th/db-query :file-change @@ -131,13 +148,163 @@ (t/is (= (:code data) :system-snapshots-cant-be-deleted))))) ;; this will run pending task triggered by deleting user snapshot - (th/run-pending-tasks!) + (th/run-pending-jobs) (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:days 8}))] (let [res (th/run-task! :objects-gc {})] ;; delete 2 snapshots and 2 file data entries (t/is (= 4 (:processed res))))))))) +(t/deftest snapshot-preview-resolves-fragments + ;; Regression test (R1): previewing an automatic snapshot of a + ;; pointer-mapped file must return plain data, not pointer + ;; placeholders the preview UI cannot render. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-id (first (get-in file [:data :pages])) + shape-id (uuid/random)] + + ;; An update at revn 0 takes an automatic (system) snapshot of the + ;; pre-change state; with pointer-map that snapshot shares fragments + ;; with the live file. + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-obj + :page-id page-id + :id shape-id + :parent-id uuid/zero + :frame-id uuid/zero + :components-v2 true + :obj (cts/setup-shape + {:id shape-id + :name "rect" + :frame-id uuid/zero + :parent-id uuid/zero + :type :rect})}]) + + ;; Single labeled snapshot here, but order explicitly so a + ;; future edit adding more snapshots cannot pick the wrong one. + (let [snap-id (->> (th/db-query :file-change {:file-id file-id} {:order-by [:created-at]}) + (filter #(some? (:label %))) + (first) + (:id)) + _ (t/is (uuid? snap-id)) + out (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id snap-id + :features cfeat/supported-features})] + (t/is (nil? (:error out))) + (let [data (get-in (:result out) [:data])] + (t/is (map? data)) + (t/is (empty? (fdata/get-used-pointer-ids data))))))) + +(defn- live-fragment-ids + [file-id] + (into #{} + (comp (filter #(nil? (:deleted-at %))) + (map :id)) + (th/db-query :file-data {:file-id file-id :type "fragment"}))) + +(t/deftest user-snapshot-shares-fragments + ;; User-pinned versions share fragment rows like automatic snapshots + ;; instead of storing a materialized copy. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-id (first (get-in file [:data :pages])) + before (live-fragment-ids file-id) + out (th/command! {::th/type :create-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :label "v1"}) + _ (t/is (nil? (:error out))) + snap-id (:id (:result out))] + + ;; No new fragment rows: the version reuses the live ones. + (t/is (uuid? snap-id)) + (t/is (= before (live-fragment-ids file-id))) + + ;; The version pointers are a subset of the live pointers. + (let [live (fdata/get-used-pointer-ids + (:data (bfc/get-file th/*system* file-id))) + snap (fdata/get-used-pointer-ids + (:data (fsnap/get-snapshot th/*system* file-id snap-id)))] + (t/is (pos? (count snap))) + (t/is (every? #(contains? live %) snap))) + + (let [shape-id (uuid/random)] + + ;; Modify the file, then restore the version and compare resolved + ;; content with the version preview (both materialized). + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-obj + :page-id page-id + :id shape-id + :parent-id uuid/zero + :frame-id uuid/zero + :components-v2 true + :obj (cts/setup-shape + {:id shape-id + :name "rect" + :frame-id uuid/zero + :parent-id uuid/zero + :type :rect})}])) + (let [preview-out (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id snap-id + :features cfeat/supported-features}) + _ (t/is (nil? (:error preview-out))) + preview (:result preview-out) + restore-out (th/command! {::th/type :restore-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id snap-id}) + _ (t/is (nil? (:error restore-out))) + live (:data (bfc/get-file th/*system* file-id :realize? true)) + ;; The preview resolves pointers but keeps objects-maps + ;; encoded; realize them too so both sides are plain data. + preview-data (:data (fdata/realize-objects + nil {:data (:data preview)}))] + (t/is (= preview-data live))))) + +(t/deftest user-snapshot-of-file-needing-migration-is-materialized + ;; A file with pending migrations cannot share fragments: the + ;; migration path realizes the data, so the stored snapshot holds + ;; plain maps. This pins the shape so it cannot drift silently. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file)] + (th/db-update! :file {:version (dec (:version file))} {:id file-id}) + (let [out (th/command! {::th/type :create-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :label "v1"}) + _ (t/is (nil? (:error out))) + snap-id (:id (:result out)) + snap (fsnap/get-snapshot th/*system* file-id snap-id)] + (t/is (uuid? snap-id)) + (t/is (empty? (fdata/get-used-pointer-ids (:data snap)))) + ;; ...while preview keeps working on the migrated content. + (let [preview (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id snap-id + :features cfeat/supported-features})] + (t/is (nil? (:error preview))) + (t/is (map? (get-in preview [:result :data]))))))) + (t/deftest snapshots-locking (let [profile-1 (th/create-profile* 1 {:is-active true}) profile-2 (th/create-profile* 2 {:is-active true}) @@ -220,3 +387,364 @@ ;; (th/print-result! out) (t/is (nil? (:error out))) (t/is (true? (:result out))))))) + +(t/deftest snapshot-creation-serializes-with-file-gc + ;; A user snapshot shares fragment rows with the live file, so its + ;; creation must serialize with a concurrent :file-gc pass (which + ;; takes FOR UPDATE + SKIP LOCKED on the file row): creation holds + ;; FOR SHARE, the GC skips locked files instead of racing it. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file)] + + (t/testing "gc skips a file locked for share" + (db/tx-run! th/*system* + (fn [cfg] + ;; Hold FOR SHARE to commit, like create-file-snapshot does. + (bfc/get-file cfg file-id :lock-for-share? true) + ;; The GC uses SKIP LOCKED on a different connection, so it + ;; must skip instead of blocking or marking. + (t/is (not (th/run-task! :file-gc {:file-id file-id})))))) + + (t/testing "creation cannot slip between gc scan and mark" + ;; Deterministic interleaving: pause the GC right after its + ;; snapshot scan, then create a snapshot. With the FOR SHARE lock + ;; the creation waits at the read and sees post-GC fragment ids; + ;; without it, it reads pre-GC ids and pins a fragment the pass + ;; is about to mark. + (let [orig-reduce @#'fsnap/reduce-snapshots + orig-create @#'fsnap/create! + scan-started (promise) + release-scan (promise) + reached-insert (promise) + do-create (fn [] + (th/command! {::th/type :create-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :label "pinned"}))] + (with-redefs [fsnap/reduce-snapshots + (fn [cfg file-id xform f init] + (deliver scan-started true) + @release-scan + (orig-reduce cfg file-id xform f init)) + fsnap/create! + (fn [& args] + (deliver reached-insert true) + (apply orig-create args))] + (let [gc-done (future (th/run-task! :file-gc {:file-id file-id}))] + (t/is (= true (deref scan-started 10000 ::timeout))) + (let [created (future (do-create))] + ;; The creation must not reach the insert while the GC + ;; pass holds the file row: with FOR SHARE it waits at the + ;; read instead of snapshotting pre-GC fragment ids. + (t/is (= ::timeout (deref reached-insert 10000 ::timeout))) + (deliver release-scan true) + (t/is (some? (deref gc-done 30000 ::timeout))) + (let [out (deref created 30000 ::timeout)] + (t/is (not= ::timeout out)) + (t/is (nil? (:error out))) + (t/is (uuid? (get-in out [:result :id]))))))))) + + (t/testing "snapshot taken around gc still resolves" + ;; The racing pass marks nothing (the file is unchanged, so + ;; reattach keeps every id), therefore there is nothing to purge; + ;; the snapshot keeps resolving afterwards. + (t/is (zero? (:processed (th/run-task! :objects-gc {})))) + (t/is (true? (th/run-task! :file-gc {:file-id file-id}))) + ;; The pinned snapshot (not an arbitrary autosnapshot: those may + ;; reference fragments an earlier pass already marked) must keep + ;; resolving after another GC cycle. + (let [snap-id (->> (th/db-query :file-change {:file-id file-id} {:order-by [:created-at]}) + (filter #(= "pinned" (:label %))) + (first) + (:id)) + out (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id snap-id + :features cfeat/supported-features})] + (t/is (uuid? snap-id)) + (t/is (nil? (:error out))) + (t/is (empty? (fdata/get-used-pointer-ids + (get-in out [:result :data])))))))) + +(t/deftest restore-backup-serializes-with-file-gc + ;; The backup snapshot taken by restore shares fragment rows with + ;; the live file, so its creation must serialize with a concurrent + ;; :file-gc pass, like ::create-file-snapshot does: with FOR SHARE + ;; held by the GC pass, the restore waits at the locked read instead + ;; of snapshotting pre-GC fragment ids that the pass is about to + ;; mark as deleted. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-id (first (get-in file [:data :pages])) + shape-id (uuid/random) + snap-id (:id (:result (th/command! {::th/type :create-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :label "v1"})))] + (t/is (uuid? snap-id)) + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-obj + :page-id page-id + :id shape-id + :parent-id uuid/zero + :frame-id uuid/zero + :components-v2 true + :obj (cts/setup-shape + {:id shape-id + :name "rect" + :frame-id uuid/zero + :parent-id uuid/zero + :type :rect})}]) + (let [orig-reduce @#'fsnap/reduce-snapshots + orig-create @#'fsnap/create! + scan-started (promise) + release-scan (promise) + reached-insert (promise) + do-restore (fn [] + (th/command! {::th/type :restore-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id snap-id}))] + (with-redefs [fsnap/reduce-snapshots + (fn [cfg file-id xform f init] + (deliver scan-started true) + @release-scan + (orig-reduce cfg file-id xform f init)) + fsnap/create! + (fn [& args] + (deliver reached-insert true) + (apply orig-create args))] + (let [gc-done (future (th/run-task! :file-gc {:file-id file-id}))] + (t/is (= true (deref scan-started 10000 ::timeout))) + (let [restored (future (do-restore))] + (t/is (= ::timeout (deref reached-insert 10000 ::timeout))) + (deliver release-scan true) + (t/is (some? (deref gc-done 30000 ::timeout))) + (let [out (deref restored 30000 ::timeout)] + (t/is (not= ::timeout out)) + (t/is (nil? (:error out)))))))) + (let [backup-ids (->> (th/db-query :file-change {:file-id file-id}) + (map (juxt :id :label)) + (filter (fn [[_ label]] + (and (string? label) + (str/starts-with? label "snapshot-")))) + (map first) + (set)) + backup-ids (set/intersection + backup-ids + (into #{} + (map :id) + (th/db-query :file-data {:file-id file-id + :type "snapshot"}))) + live (into #{} + (map :id) + (filter #(nil? (:deleted-at %)) + (th/db-query :file-data {:file-id file-id + :type "fragment"})))] + (t/is (= 1 (count backup-ids))) + (doseq [backup-id backup-ids + :let [snap (fsnap/get-snapshot th/*system* file-id backup-id)]] + (t/is (every? live (fdata/get-used-pointer-ids (:data snap)))) + (let [out (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id backup-id + :features cfeat/supported-features})] + (t/is (nil? (:error out))) + (t/is (empty? (fdata/get-used-pointer-ids + (get-in out [:result :data]))))))))) + +(t/deftest snapshot-preview-applies-pending-migrations + ;; A snapshot taken while a migration was pending stores the old + ;; format. The preview must apply the pending migration in memory + ;; (the same pure step a normal read runs) instead of handing the old + ;; format to the UI, and must not persist anything: the stored + ;; version keeps its data so restoring it re-migrates on next read. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-id (first (get-in file [:data :pages])) + shape-id (uuid/random)] + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-obj + :page-id page-id + :id shape-id + :parent-id uuid/zero + :frame-id uuid/zero + :components-v2 true + :obj (-> (cts/setup-shape {:id shape-id + :name "rect" + :frame-id uuid/zero + :parent-id uuid/zero + :type :rect}) + (assoc :applied-tokens {:stroke-width "token"}))}]) + + ;; Force the 0031 migration on the next save. + (th/db-delete! :file-migration {:file-id file-id + :name "0031-migrate-stroke-width-token-attr"}) + + (update-file! :profile-id (:id profile) + :file-id file-id + :revn 1 + :changes [{:type :add-page :name "b" :id (uuid/random)}]) + + (let [snapshot (->> (th/db-query :file-change {:file-id file-id}) + (filter #(and (some? (:label %)) + (= 1 (:revn %)))) + (first)) + snapshot-id (:id snapshot) + stored (fdata/realize th/*system* + (assoc (fsnap/get-snapshot th/*system* file-id snapshot-id) + :id file-id)) + out (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id snapshot-id + :features cfeat/supported-features})] + + (t/is (uuid? snapshot-id)) + + ;; The stored version keeps the old format and the pending + ;; migration: the preview must not touch the stored row. + (t/is (not (contains? (set (:migrations stored)) + "0031-migrate-stroke-width-token-attr"))) + (t/is (= {:stroke-width "token"} + (get-in stored [:data :pages-index page-id + :objects shape-id :applied-tokens]))) + + ;; The preview migrates it in memory. + (t/is (nil? (:error out))) + (t/is (= file-id (get-in out [:result :data :id]))) + (let [tokens (get-in out [:result :data :pages-index page-id + :objects shape-id :applied-tokens])] + (t/is (not (contains? tokens :stroke-width))) + (t/is (contains? tokens :stroke-width-top)) + (t/is (contains? tokens :stroke-width-bottom))) + + ;; The preview declared the migrated set in the payload. + (t/is (contains? (set (get-in out [:result :migrations])) + "0031-migrate-stroke-width-token-attr"))) + + ;; Nothing was persisted by the preview. + (let [snapshot-id (:id (->> (th/db-query :file-change {:file-id file-id}) + (filter #(and (some? (:label %)) + (= 1 (:revn %)))) + (first))) + stored (fdata/realize th/*system* + (assoc (fsnap/get-snapshot th/*system* file-id snapshot-id) + :id file-id))] + (t/is (= {:stroke-width "token"} + (get-in stored [:data :pages-index page-id + :objects shape-id :applied-tokens])))))) + +(t/deftest snapshot-preview-realizes-objects-for-clients-without-the-feature + ;; `:features` works like in `get-file`: a client that does not + ;; declare `fdata/objects-map` receives plain objects instead of the + ;; storage structure of the fragment. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + page-id (first (get-in file [:data :pages])) + shape-id (uuid/random) + _ (update-file! :profile-id (:id profile) + :file-id file-id + :revn 0 + :changes [{:type :add-obj + :page-id page-id + :id shape-id + :parent-id uuid/zero + :frame-id uuid/zero + :components-v2 true + :obj (cts/setup-shape + {:id shape-id + :name "rect" + :frame-id uuid/zero + :parent-id uuid/zero + :type :rect})}]) + + snapshot-id (:id (:result (th/command! {::th/type :create-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :label "v1"}))) + preview (fn [features] + (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id snapshot-id + :features features})) + with-feature (preview cfeat/supported-features) + without-feature (preview (disj cfeat/supported-features + "fdata/objects-map"))] + + (t/is (nil? (:error with-feature))) + (t/is (nil? (:error without-feature))) + (t/is (omap/objects-map? + (get-in with-feature [:result :data :pages-index page-id :objects]))) + (t/is (not (omap/objects-map? + (get-in without-feature [:result :data :pages-index page-id :objects])))))) + +(t/deftest snapshot-preview-migrates-legacy-snapshots-without-migrations + ;; Snapshots written before the `migrations` column existed (SQL + ;; migration 0140) store NULL and the format version of their time. + ;; The decoder must hand an ordered set (a vector would make + ;; `need-migration?` compare indexes), and the preview must + ;; regenerate the applied set from the stored version, migrate the + ;; data in memory and leave the row untouched. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + file-id (:id file) + snap-id (:id (:result (th/command! {::th/type :create-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :label "legacy"})))] + + (t/is (d/ordered-set? (:migrations (fsnap/get-snapshot th/*system* file-id snap-id)))) + + ;; Turn the version into a pre-0140 snapshot: no applied set and an + ;; old format version. + (th/db-exec! ["UPDATE file_change SET migrations = NULL, version = 60 WHERE id = ?" + snap-id]) + + (let [stored (fsnap/get-snapshot th/*system* file-id snap-id) + out (th/command! {::th/type :get-file-snapshot + ::rpc/profile-id (:id profile) + :file-id file-id + :id snap-id + :features cfeat/supported-features}) + result (:result out)] + + (t/is (nil? (:migrations stored))) + (t/is (= 60 (:version stored))) + + (t/is (nil? (:error out))) + (t/is (d/ordered-set? (:migrations result))) + ;; The preview regenerates everything the stored version lacks + ;; instead of trusting the absent applied set. + (t/is (= cfd/version (:version result))) + (t/is (= (into #{} fmg/available-migrations) + (set (:migrations result)))) + (t/is (set/subset? (into #{} (fmg/generate-migrations-from-version cfd/version)) + (set (:migrations result)))) + + ;; Nothing is persisted. + (let [stored (fsnap/get-snapshot th/*system* file-id snap-id)] + (t/is (nil? (:migrations stored))) + (t/is (= 60 (:version stored))))))) diff --git a/backend/test/backend_tests/rpc_file_test.clj b/backend/test/backend_tests/rpc_file_test.clj index db82002200..593c769546 100644 --- a/backend/test/backend_tests/rpc_file_test.clj +++ b/backend/test/backend_tests/rpc_file_test.clj @@ -22,6 +22,7 @@ [app.db.sql :as sql] [app.features.fdata :as fdata] [app.http :as http] + [app.loggers.audit :as-alias audit] [app.rpc :as-alias rpc] [app.rpc.commands.files :as files] [app.storage :as sto] @@ -157,8 +158,9 @@ (t/is (true? (th/run-task! :file-gc {:file-id (:id file) :revn (:revn file)}))) ;; Check the number of fragments before adding the page + ;; (no churn: the preventive GC above reuses the single id). (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] - (t/is (= 2 (count rows)))) + (t/is (= 1 (count rows)))) ;; Add page (update-file! @@ -172,20 +174,29 @@ :id page-id}]) ;; Check the number of fragments before adding the page + ;; (add-page adds exactly one fragment; the existing page keeps + ;; its id). (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] - (t/is (= 3 (count rows)))) + (t/is (= 2 (count rows)))) + + ;; Snapshots share fragment rows with the live file; drop them so + ;; this GC-mechanics test asserts only on unreferenced fragments + ;; (snapshot retention itself is covered in file-pointer-snapshot-test). + (th/db-delete! :file-change {:file-id (:id file)}) + (th/db-delete! :file-data {:file-id (:id file) :type "snapshot"}) ;; The file-gc should mark for remove unused fragments (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) - ;; Check the number of fragments + ;; Check the number of fragments (no churn: unchanged pages keep + ;; their ids, so nothing is marked and nothing new is persisted). (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] - (t/is (= 5 (count rows))) - (t/is (= 3 (count (filterv :deleted-at rows))))) + (t/is (= 2 (count rows))) + (t/is (= 0 (count (filterv :deleted-at rows))))) - ;; The objects-gc should remove unused fragments + ;; The objects-gc has nothing to remove. (let [res (th/run-task! :objects-gc {})] - (t/is (= 3 (:processed res)))) + (t/is (= 0 (:processed res)))) ;; Check the number of fragments (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] @@ -215,12 +226,18 @@ (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] (t/is (= 3 (count rows)))) + ;; Same snapshot cleanup as above: isolate GC mechanics from + ;; snapshot retention. + (th/db-delete! :file-change {:file-id (:id file)}) + (th/db-delete! :file-data {:file-id (:id file) :type "snapshot"}) + ;; The file-gc should mark for remove unused fragments (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) - ;; The objects-gc should remove unused fragments + ;; Only the replaced page fragment is marked; the live pages keep + ;; their ids instead of being re-persisted. (let [res (th/run-task! :objects-gc {})] - (t/is (= 3 (:processed res)))) + (t/is (= 1 (:processed res)))) ;; Check the number of fragments; (let [rows (th/db-query :file-data {:file-id (:id file) @@ -240,14 +257,15 @@ ;; snapshots previously deleted. (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) - ;; Check the number of fragments; + ;; Check the number of fragments; without churn the two live + ;; pages simply stay where they are (was 4 with 2 re-persisted). (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] ;; (pp/pprint rows) - (t/is (= 4 (count rows))) + (t/is (= 2 (count rows))) (t/is (= 2 (count (remove :deleted-at rows))))) (let [res (th/run-task! :objects-gc {})] - (t/is (= 2 (:processed res)))) + (t/is (= 0 (:processed res)))) (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] (t/is (= 2 (count rows)))))) @@ -338,8 +356,10 @@ (t/is (ct/inst? (:deleted-at row2)))) (let [res (th/run-task! :objects-gc {})] - ;; delete 2 fragments and 1 media object - (t/is (= 3 (:processed res)))) + ;; 1 (was 2): no churn orphans anymore, only the unused media + ;; row is purged. The replaced page fragment stays pinned by + ;; the automatic snapshot. + (t/is (= 1 (:processed res)))) ;; check file media objects (let [rows (th/db-query :file-media-object {:file-id (:id file)})] @@ -365,10 +385,11 @@ ;; as deleted. (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) - ;; This only clears fragments, the file media objects still referenced because - ;; snapshots are preserved + ;; The deleted shape's fragment is pinned by the automatic + ;; snapshot taken before the deletion, and its media is still + ;; referenced there too: nothing is marked, nothing to purge. (let [res (th/run-task! :objects-gc {})] - (t/is (= 2 (:processed res)))) + (t/is (= 0 (:processed res)))) ;; Delete all snapshots (th/db-exec! ["update file_data set deleted_at = now() where file_id = ? and type = 'snapshot'" (:id file)]) @@ -383,9 +404,11 @@ (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) (let [res (th/run-task! :objects-gc {})] + ;; 3 (was 4): the two orphaned page fragments plus the media + ;; row, all unreferenced now that the snapshots are purged. ;; this will remove the file media objects marked as deleted ;; on prev file-gc - (t/is (= 2 (:processed res)))) + (t/is (= 3 (:processed res)))) ;; Now that file-gc have deleted the file-media-object usage, ;; lets execute the touched-gc task, we should see that two of @@ -509,12 +532,15 @@ (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) (let [res (th/run-task! :objects-gc {})] - (t/is (= 2 (:processed res)))) + ;; 0 (was 2, then 1): no churn orphans and every replaced page + ;; fragment stays pinned by an automatic snapshot. + (t/is (= 0 (:processed res)))) (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment" :deleted-at nil})] - (t/is (= (count rows) 1))) + ;; 2 live (was 1): the snapshot-pinned fragment survives. + (t/is (= (count rows) 2))) ;; retrieve file and check trimmed attribute (let [row (th/db-get :file {:id (:id file)})] @@ -552,10 +578,11 @@ ;; as deleted. (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) - ;; This only removes unused fragments, file media are still - ;; referenced on snapshots. + ;; The deleted shapes' fragments stay pinned by the automatic + ;; snapshots taken before the deletion, and the media is still + ;; referenced there too: nothing is marked, nothing to purge. (let [res (th/run-task! :objects-gc {})] - (t/is (= 2 (:processed res)))) + (t/is (= 0 (:processed res)))) ;; Mark all snapshots to be a non-snapshot file change (th/db-exec! ["update file set has_media_trimmed = false where id = ?" (:id file)]) @@ -566,7 +593,10 @@ ;; "expired/deleted" (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) (let [res (th/run-task! :objects-gc {})] - (t/is (= 6 (:processed res)))) + ;; 7 (was 6, then 8): the two orphaned page fragments plus the + ;; five media rows, all unreferenced once the snapshot file_data + ;; rows are gone. No churn duplicates are created anymore. + (t/is (= 7 (:processed res)))) (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment" @@ -715,14 +745,16 @@ ;; Now that file-gc have marked for deletion the object ;; thumbnail lets execute the objects-gc task which remove - ;; the rows and mark as touched the storage object rows + ;; the rows and mark as touched the storage object rows. + ;; 1 (was 5, then 3): without churn there are no orphaned + ;; fragment rows to purge, only the deleted thumbnail row. (let [res (th/run-task! :objects-gc {})] - (t/is (= 5 (:processed res)))) + (t/is (= 1 (:processed res)))) ;; Now that objects-gc have deleted the object thumbnail lets ;; execute the touched-gc task (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:hours 3}))] - (th/run-task! "storage-gc-touched" {}))] + (th/run-task! :storage-gc-touched {}))] (t/is (= 1 (:freeze res)))) ;; check file media objects @@ -748,7 +780,9 @@ (let [res (th/run-task! :objects-gc {})] ;; (pp/pprint res) - (t/is (= 3 (:processed res)))) + ;; 1 (was 3, then 2): without churn there are no fragment + ;; orphans; only the deleted thumbnail row is purged. + (t/is (= 1 (:processed res)))) ;; We still have th storage objects in the table (let [rows (th/db-query :storage-object {:deleted-at nil})] @@ -816,6 +850,20 @@ (t/is (th/ex-info? error)) (t/is (th/ex-of-type? error :not-found)))) +(t/deftest delete-file-audit-includes-team-id + ;; Assert result metadata directly: wrap-audit is inactive in the + ;; default test flags, so mocking audit/submit would never see this. + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:project-id (:default-project-id profile) + :profile-id (:id profile)}) + result (db/tx-run! th/*system* + (fn [cfg] + (#'files/delete-file cfg {:id (:id file) + :profile-id (:id profile)}))) + props (::audit/props (meta result))] + (t/is (= (:default-team-id profile) (:team-id props))) + (t/is (= (:project-id file) (:project-id props))))) + (t/deftest permissions-checks-set-file-shared (let [profile1 (th/create-profile* 1) profile2 (th/create-profile* 2) @@ -1088,7 +1136,7 @@ out (th/command! params)] (t/is (nil? (:error out)))) - (th/run-pending-tasks!) + (th/run-pending-jobs) ;; query the list of files after soft deletion (let [data {::th/type :get-project-files @@ -1372,12 +1420,82 @@ (t/is (= 1 (count (remove :deleted-at rows))))) (let [res (th/run-task! :objects-gc {})] - (t/is (= 4 (:processed res)))) + ;; 1 (was 4, then 3): no fragment churn anymore — unchanged + ;; pages keep their ids, so only the deleted object-thumbnail + ;; row itself is purged. + (t/is (= 1 (:processed res)))) (let [rows (th/db-query :file-tagged-object-thumbnail {:file-id (:id file)})] ;; (app.common.pprint/pprint rows) (t/is (= 1 (count rows))))))) +(t/deftest file-data-for-thumbnail-frame-on-other-page + (let [prof (th/create-profile* 1 {:is-active true}) + file (th/create-file* 1 {:profile-id (:id prof) + :project-id (:default-project-id prof) + :is-shared false}) + page1-id (get-in file [:data :pages 0]) + page2-id (uuid/next) + rect-id (uuid/next) + frame-id (uuid/next) + + get-data (fn [] + (th/command! {::th/type :get-file-data-for-thumbnail + ::rpc/profile-id (:id prof) + :file-id (:id file) + :features cfeat/supported-features}))] + + (th/update-file* {:file-id (:id file) + :profile-id (:id prof) + :revn 0 + :vern 0 + :changes [{:type :add-obj + :page-id page1-id + :id rect-id + :parent-id uuid/zero + :frame-id uuid/zero + :obj (cts/setup-shape + {:id rect-id + :name "rect" + :type :rect})} + {:type :add-page + :id page2-id + :name "page 2"} + {:type :add-obj + :page-id page2-id + :id frame-id + :parent-id uuid/zero + :frame-id uuid/zero + :obj (cts/setup-shape + {:id frame-id + :name "frame" + :type :frame})}]}) + + (t/testing "no marked frame falls back to the first page" + (let [{:keys [error result]} (get-data)] + (t/is (nil? error)) + (t/is (= page1-id (get-in result [:page :id]))) + (t/is (nil? (get-in result [:page :thumbnail-frame-id]))) + (t/is (contains? (get-in result [:page :objects]) rect-id)))) + + (th/update-file* {:file-id (:id file) + :profile-id (:id prof) + :revn 1 + :vern 0 + :changes [{:type :mod-obj + :page-id page2-id + :id frame-id + :operations [{:type :set + :attr :use-for-thumbnail + :val true}]}]}) + + (t/testing "marked frame on another page is used" + (let [{:keys [error result]} (get-data)] + (t/is (nil? error)) + (t/is (= page2-id (get-in result [:page :id]))) + (t/is (= frame-id (get-in result [:page :thumbnail-frame-id]))) + (t/is (contains? (get-in result [:page :objects]) frame-id)))))) + (t/deftest file-thumbnail-ops (let [prof (th/create-profile* 1 {:is-active true}) file (th/create-file* 1 {:profile-id (:id prof) @@ -1428,7 +1546,9 @@ (t/is (= 1 (count (remove (comp some? :deleted-at) rows))))) (let [res (th/run-task! :objects-gc {})] - (t/is (= 2 (:processed res)))) + ;; 1 (was 2): without churn there is no re-persisted fragment + ;; orphan; only the superseded thumbnail row is purged. + (t/is (= 1 (:processed res)))) (let [rows (th/db-query :file-thumbnail {:file-id (:id file)})] (t/is (= 1 (count rows))))))) @@ -1448,8 +1568,9 @@ ;; Preventive objects-gc (let [result (th/run-task! :objects-gc {})] - ;; deletes the fragment created by file-gc - (t/is (= 1 (:processed result)))) + ;; nothing to delete: without churn the preventive file-gc above + ;; marked and created nothing. + (t/is (= 0 (:processed result)))) ;; Check the number of fragments before adding the page (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] @@ -1466,16 +1587,17 @@ (t/is (true? (th/run-task! :file-gc {:file-id (:id file)})))) ;; The FileGC task will schedule an inner taskq - (th/run-pending-tasks!) + (th/run-pending-jobs) (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:hours 3}))] (th/run-task! :storage-gc-touched {}))] (t/is (= 2 (:freeze res))) (t/is (= 0 (:delete res)))) - ;; Clean objects after file-gc + ;; Clean objects after file-gc: nothing to purge, the single + ;; fragment was kept (no churn re-persist) and offloaded as is. (let [result (th/run-task! :objects-gc {})] - (t/is (= 1 (:processed result)))) + (t/is (= 0 (:processed result)))) ;; Check the number of fragments before adding the page (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] @@ -1525,48 +1647,49 @@ ;; The file-gc should mark for remove unused fragments (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) - ;; The file-gc task, recreates all fragments, so after it we have - ;; now the double of fragments, and the old ones are marked as - ;; deleted, and the new ones are on DB - (let [[row1 row2 row3 row4 :as rows] + ;; The file-gc only marks fragments nothing references: the live + ;; pages keep their ids (no churn re-persist), row1 stays alive on + ;; cold storage pinned by the automatic snapshot, row2 is the new + ;; hot page. + (let [[row1 row2 :as rows] (th/db-query :file-data {:file-id (:id file) :type "fragment"} {:order-by [:created-at]})] ;; (pp/pprint rows) - (t/is (= 4 (count rows))) + ;; No churn: live pages keep their ids, so there are no + ;; re-persisted duplicates and nothing is marked. row1 stays on + ;; cold storage pinned by the automatic snapshot. + (t/is (= 2 (count rows))) (t/is (nil? (:data row1))) - (t/is (ct/inst? (:deleted-at row1))) + (t/is (nil? (:deleted-at row1))) (t/is (= "storage" (:backend row1))) (t/is (bytes? (:data row2))) (t/is (= "db" (:backend row2))) - (t/is (ct/inst? (:deleted-at row2))) + (t/is (nil? (:deleted-at row2)))) - (t/is (bytes? (:data row3))) - (t/is (= "db" (:backend row3))) - (t/is (nil? (:deleted-at row3))) - - (t/is (bytes? (:data row4))) - (t/is (= "db" (:backend row4))) - (t/is (nil? (:deleted-at row4)))) - - ;; The objects-gc should remove the marked to delete fragments + ;; The objects-gc has nothing to remove: no fragment was marked. (let [res (th/run-task! :objects-gc {})] - (t/is (= 2 (:processed res)))) + (t/is (= 0 (:processed res)))) (let [rows (th/db-query :file-data {:file-id (:id file) :type "fragment"})] + ;; 2 rows (was 3): the snapshot-pinned offloaded fragment plus + ;; the single live hot row. No churn duplicates. (t/is (= 2 (count rows))) - (t/is (every? #(bytes? (:data %)) rows)) - (t/is (every? #(= "db" (:backend %)) rows))) + (t/is (= 1 (count (filter #(bytes? (:data %)) rows)))) + (t/is (= 1 (count (filter #(= "storage" (:backend %)) rows)))) + (t/is (= 1 (count (filter #(= "db" (:backend %)) rows))))) ;; we ensure that once object-gc is passed and marked two storage ;; objects to delete (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:hours 3}))] (th/run-task! :storage-gc-touched {}))] (t/is (= 0 (:freeze res))) - (t/is (= 2 (:delete res)))) + ;; 1 (was 2): only one storage object was touched by the purge + ;; above, the pinned fragment's object is still referenced. + (t/is (= 1 (:delete res)))) (let [storage (sto/resolve th/*system*)] (t/is (uuid? (:id @sobject))) @@ -2156,6 +2279,9 @@ :id (:id library)}) ;; The task swallows absorption errors, so verify the persisted result. + ;; Absorption runs inside the job, and the job only gets the + ;; components of its own cfg, so this also needs + ;; `:app.tasks.delete-object/job-def` to carry `::sto/storage`. (let [deleted (db/get* th/*pool* :file {:id (:id library)} {::db/remove-deleted false}) out (th/command! {::th/type :get-file @@ -2248,7 +2374,7 @@ (t/is (= (:id library) (cfo/get-tokens-source (:data (bfc/get-file th/*system* (:id file)))))) - (th/run-pending-tasks!) + (th/run-pending-jobs) (check-tokens-absorbed prepared))) (t/deftest unpublish-shared-library-absorbs-its-tokens-lib @@ -2457,7 +2583,7 @@ (t/is (nil? (:error out))) (t/is (nil? (:result out))) - (th/run-pending-tasks!) + (th/run-pending-jobs) ;; get deleted files (let [data {::th/type :get-team-deleted-files @@ -2929,6 +3055,133 @@ (t/is (th/ex-info? (:error out))) (t/is (th/ex-of-type? (:error out) :validation)) (t/is (th/ex-of-code? (:error out) :params-validation)))) +(t/deftest get-file-plugin-data + (let [profile (th/create-profile* 1 {:is-active true}) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false})] + + ;; A file without plugin data returns an empty map + (let [out (th/command! {::th/type :get-file-plugin-data + ::rpc/profile-id (:id profile) + :id (:id file)})] + (t/is (nil? (:error out))) + (t/is (= {} (:result out)))) + + ;; Set file-level plugin data through a change + (update-file! + :file-id (:id file) + :profile-id (:id profile) + :revn 0 + :vern 0 + :changes + [{:type :set-plugin-data + :object-type :file + :namespace :shared/traceability + :key "req-id" + :value "REQ-123"}]) + + ;; The full plugin-data map is returned + (let [out (th/command! {::th/type :get-file-plugin-data + ::rpc/profile-id (:id profile) + :id (:id file)})] + (t/is (nil? (:error out))) + (t/is (= {:shared/traceability {"req-id" "REQ-123"}} (:result out)))) + + ;; A namespace filter narrows the result + (let [out (th/command! {::th/type :get-file-plugin-data + ::rpc/profile-id (:id profile) + :id (:id file) + :namespace :shared/traceability})] + (t/is (nil? (:error out))) + (t/is (= {:shared/traceability {"req-id" "REQ-123"}} (:result out)))) + + ;; A filter on an absent namespace returns an empty map + (let [out (th/command! {::th/type :get-file-plugin-data + ::rpc/profile-id (:id profile) + :id (:id file) + :namespace :plugin/absent})] + (t/is (nil? (:error out))) + (t/is (= {} (:result out)))))) + +(t/deftest get-file-plugin-data-forbidden + (let [owner (th/create-profile* 1 {:is-active true}) + other (th/create-profile* 2 {:is-active true}) + file (th/create-file* 1 {:profile-id (:id owner) + :project-id (:default-project-id owner) + :is-shared false}) + out (th/command! {::th/type :get-file-plugin-data + ::rpc/profile-id (:id other) + :id (:id file)})] + + (t/is (not (nil? (:error out)))) + (let [edata (-> out :error ex-data)] + (t/is (= :not-found (:type edata)))))) + +(t/deftest get-file-tokens-without-tokens + (let [profile (th/create-profile* 1 {:is-active true}) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + out (th/command! {::th/type :get-file-tokens + ::rpc/profile-id (:id profile) + :file-id (:id file)})] + (t/is (nil? (:error out))) + (t/is (nil? (:result out))))) + +(t/deftest get-file-tokens-with-tokens + (let [profile (th/create-profile* 1 {:is-active true}) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false}) + set-id (uuid/next) + token-id (uuid/next)] + + (update-file! + :file-id (:id file) + :profile-id (:id profile) + :changes + [{:type :set-token-set + :id set-id + :attrs {:id set-id :name "brand-core"}} + {:type :set-token + :set-id set-id + :token-id token-id + :attrs {:id token-id + :name "color.primary-base" + :type :color + :value "#ff0000"}}]) + + (update-file! + :file-id (:id file) + :profile-id (:id profile) + :revn 1 + :changes + [{:type :set-tokens-status + :theme-ids #{} + :set-ids #{set-id}}]) + + (let [out (th/command! {::th/type :get-file-tokens + ::rpc/profile-id (:id profile) + :file-id (:id file)}) + dtcg (:result out)] + (t/is (nil? (:error out))) + (t/is (= "#ff0000" (get-in dtcg ["brand-core" "color" "primary-base" "$value"]))) + (t/is (= "color" (get-in dtcg ["brand-core" "color" "primary-base" "$type"]))) + (t/is (= ["brand-core"] (get-in dtcg ["$metadata" "tokenSetOrder"]))) + (t/is (= #{"brand-core"} (set (get-in dtcg ["$metadata" "activeSets"]))))))) + +(t/deftest get-file-tokens-forbidden + (let [owner (th/create-profile* 1 {:is-active true}) + other (th/create-profile* 2 {:is-active true}) + file (th/create-file* 1 {:profile-id (:id owner) + :project-id (:default-project-id owner) + :is-shared false}) + out (th/command! {::th/type :get-file-tokens + ::rpc/profile-id (:id other) + :file-id (:id file)})] + (t/is (th/ex-info? (:error out))) + (t/is (th/ex-of-type? (:error out) :not-found)))) (t/deftest delete-file-audit-event-carries-team-id ;; The event already carries the project id, so the resolver could find the diff --git a/backend/test/backend_tests/rpc_file_thumbnails_test.clj b/backend/test/backend_tests/rpc_file_thumbnails_test.clj index 357ebfa60c..2a61841061 100644 --- a/backend/test/backend_tests/rpc_file_thumbnails_test.clj +++ b/backend/test/backend_tests/rpc_file_thumbnails_test.clj @@ -86,7 +86,7 @@ ;; run the task again (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:hours 3}))] - (th/run-task! "storage-gc-touched" {}))] + (th/run-task! :storage-gc-touched {}))] (t/is (= 2 (:freeze res)))) (let [[row1 row2 :as rows] (th/db-query :file-tagged-object-thumbnail @@ -119,8 +119,11 @@ ;; thumbnails (th/run-task! :file-gc {:file-id (:id file)}) + ;; Only the unused object-thumbnail row is purged: the replaced + ;; page fragment stays pinned by the automatic snapshot that shares + ;; it, and reattach means file-gc writes no churn fragments. (let [result (th/run-task! :objects-gc {})] - (t/is (= 3 (:processed result)))) + (t/is (= 1 (:processed result)))) ;; check if row2 related thumbnail row still exists (let [[row :as rows] (th/db-query :file-tagged-object-thumbnail @@ -223,8 +226,11 @@ ;; thumbnails (t/is (true? (th/run-task! :file-gc {:file-id (:id file)}))) + ;; Only the superseded revn thumbnail row is purged: the file + ;; itself is unchanged, so it keeps its fragment ids (no-churn) + ;; and no fragment row is marked. (let [result (th/run-task! :objects-gc {})] - (t/is (= 2 (:processed result)))) + (t/is (= 1 (:processed result)))) ;; check if row1 related thumbnail row still exists (let [[row :as rows] (th/db-query :file-thumbnail diff --git a/backend/test/backend_tests/rpc_file_update_repair_test.clj b/backend/test/backend_tests/rpc_file_update_repair_test.clj index e681d559ff..f8eba29f53 100644 --- a/backend/test/backend_tests/rpc_file_update_repair_test.clj +++ b/backend/test/backend_tests/rpc_file_update_repair_test.clj @@ -10,6 +10,7 @@ [app.common.features :as cfeat] [app.common.files.repair :as cfr] [app.common.files.validate :as cfv] + [app.common.test-helpers.components :as thc] [app.common.test-helpers.compositions :as tho] [app.common.test-helpers.files :as thf] [app.common.test-helpers.ids-map :as thi] @@ -249,13 +250,39 @@ page-id (thi/id :page1) changes [(set-attr page-id (thi/id :other) :name "renamed")] cfg {::files-update/validation-timings (atom {})} - result (with-redefs [bfc/get-resolved-file-libraries (fn [_ _] {})] - (@#'files-update/process-changes-and-validate cfg file changes nil)) + result (binding [pmap/*tracked* (pmap/create-tracked)] + (with-redefs [bfc/get-resolved-file-libraries (fn [_ _] {})] + (@#'files-update/process-changes-and-validate cfg file changes nil nil))) stored result] (t/is (some? (::files-update/repair stored))) (t/is (nil? (cfv/validate-file stored {}))) (t/is (nil? (get-in stored [:data :components (thi/id :component1) :objects]))))))) +(t/deftest repair-on-copy-page-keeps-copy-of-lost-main + (thi/reset-idmap!) + (with-repair-flags + (fn [] + (let [file (-> (thf/sample-file :file1 :page-label :page1) + (assoc :revn 0) + (tho/add-simple-component :component1 :root1 :child1) + (thf/add-sample-page :page2) + (thc/instantiate-component :component1 :copy1) + (thc/update-component :component1 :main-instance-id (uuid/next))) + page-id (thi/id :page2) + changes [(set-attr page-id (thi/id :copy1) :name "renamed")] + cfg {::files-update/validation-timings (atom {})} + stored (binding [pmap/*tracked* (pmap/create-tracked)] + (with-redefs [bfc/get-resolved-file-libraries (fn [_ _] {})] + (@#'files-update/process-changes-and-validate cfg file changes nil nil))) + copy1 (get-in stored [:data :pages-index page-id :objects (thi/id :copy1)])] + (t/is (some? (::files-update/repair stored))) + (t/is (nil? (cfv/validate-file stored {}))) + (t/is (= (thi/id :root1) + (get-in stored [:data :components (thi/id :component1) :main-instance-id]))) + (t/is (= "renamed" (:name copy1))) + (t/is (= (thi/id :root1) (:shape-ref copy1))) + (t/is (= (thi/id :component1) (:component-id copy1))))))) + (t/deftest repair-records-timings-with-schema-validation (thi/reset-idmap!) (with-full-repair-flags @@ -267,8 +294,9 @@ changes [(set-attr (thi/id :page1) (thi/id :shape1) :name "renamed")] timings (atom {}) cfg {::files-update/validation-timings timings} - result (with-redefs [bfc/get-resolved-file-libraries (fn [_ _] {})] - (@#'files-update/process-changes-and-validate cfg file changes nil))] + result (binding [pmap/*tracked* (pmap/create-tracked)] + (with-redefs [bfc/get-resolved-file-libraries (fn [_ _] {})] + (@#'files-update/process-changes-and-validate cfg file changes nil nil)))] (t/is (some? (::files-update/repair result))) (t/is (number? (:repair-ms @timings))) (t/is (number? (:reprocess-changes-ms @timings))) diff --git a/backend/test/backend_tests/rpc_file_update_test.clj b/backend/test/backend_tests/rpc_file_update_test.clj new file mode 100644 index 0000000000..c5a8ba9b6d --- /dev/null +++ b/backend/test/backend_tests/rpc_file_update_test.clj @@ -0,0 +1,514 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.rpc-file-update-test + (:require + [app.common.exceptions :as ex] + [app.common.features :as cfeat] + [app.common.files.changes :as cpc] + [app.common.files.validate :as val] + [app.common.logging :as l] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.msgbus :as mbus] + [app.rpc :as-alias rpc] + [app.util.blob :as blob] + [backend-tests.helpers :as th] + [clojure.test :as t])) + +(defn- with-validation + [next] + (with-redefs [cf/flags (conj cf/flags :file-validation :file-schema-validation)] + (next))) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset with-validation) + +(defn- create-file + [] + (let [profile (th/create-profile* 1) + file (th/create-file* 1 {:profile-id (:id profile) + :project-id (:default-project-id profile) + :is-shared false})] + {:profile-id (:id profile) + :file-id (:id file) + :page-id (-> file :data :pages first)})) + +(defn- break-parent + "A change that leaves the shape pointing to a parent that does not exist." + [page-id id] + {:type :mod-obj + :page-id page-id + :id id + :operations [{:type :set :attr :parent-id :val (uuid/next)}]}) + +(defn- conflicting-page-change + "A change that fails while applied: `:add-page` refuses an id and name + together with a page." + [] + {:type :add-page + :id (uuid/next) + :name "page" + :page {:id (uuid/next) :name "page" :objects {}}}) + +(defn- update-file + [{:keys [profile-id file-id]} params] + (th/command! (merge {::th/type :update-file + ::rpc/profile-id profile-id + :id file-id + :session-id (uuid/next) + :revn 0 + :vern 0 + :features cfeat/supported-features} + params))) + +(defn- commit + [& changes] + {:commit-id (uuid/next) :changes (vec changes)}) + +(defn- get-file-data + [{:keys [profile-id file-id]}] + (-> (th/command! {::th/type :get-file + ::rpc/profile-id profile-id + :id file-id}) + :result + :data)) + +(defn- get-file-changes + [{:keys [file-id]}] + (->> (th/db-query :file-change {:file-id file-id} {:order-by [:revn]}) + (filter :changes) + (mapv #(update % :changes blob/decode)))) + +(defn- capture-messages + [next] + (let [messages (atom [])] + (with-redefs [mbus/pub! (fn [_ & {:keys [message]}] + (swap! messages conj message))] + (let [out (next)] + {:out out :messages @messages})))) + +(t/deftest saves-the-commits-before-the-first-invalid-one + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + shape-c (uuid/next) + commit1 (commit (th/add-rect-change page-id shape-a)) + commit2 (commit (break-parent page-id shape-a)) + commit3 (commit (th/add-rect-change page-id shape-c)) + + {:keys [out messages]} + (capture-messages + #(update-file ctx {:changes-with-metadata [commit1 commit2 commit3]}))] + + (t/is (nil? (:error out))) + + (t/testing "the response names the rejected commits and the failure" + (let [rejected (-> out :result :rejected)] + (t/is (= [(:commit-id commit2) (:commit-id commit3)] (:commit-ids rejected))) + (t/is (= (:commit-id commit2) (:failed-commit-id rejected))) + (t/is (= :validation (-> rejected :error :type))) + (t/is (= :referential-integrity (-> rejected :error :code))) + (t/is (nil? (:preexisting-damage rejected))))) + + (t/testing "the file holds the accepted commit only" + (let [objects (-> (get-file-data ctx) :pages-index (get page-id) :objects)] + (t/is (= uuid/zero (get-in objects [shape-a :parent-id]))) + (t/is (not (contains? objects shape-c))))) + + (t/testing "the change log holds the accepted commit only" + (let [rows (get-file-changes ctx)] + (t/is (= 1 (count rows))) + (t/is (= 1 (:revn (first rows)))) + (t/is (= (:changes commit1) (:changes (first rows)))))) + + (t/testing "peers get the accepted commit only" + (let [file-changes (filter #(= :file-change (:type %)) messages)] + (t/is (= 1 (count file-changes))) + (t/is (= (:changes commit1) (:changes (first file-changes)))))))) + +(t/deftest saves-nothing-when-the-first-commit-is-invalid + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + _ (update-file ctx {:changes [(th/add-rect-change page-id shape-a)]}) + commit1 (commit (break-parent page-id shape-a)) + commit2 (commit (th/add-rect-change page-id (uuid/next))) + + {:keys [out messages]} + (capture-messages + #(update-file ctx {:revn 1 :changes-with-metadata [commit1 commit2]}))] + + (t/is (nil? (:error out))) + (t/is (= [(:commit-id commit1) (:commit-id commit2)] + (-> out :result :rejected :commit-ids))) + (t/is (= 1 (:revn (th/db-get :file {:id (:file-id ctx)})))) + (t/is (= 1 (count (get-file-changes ctx)))) + (t/is (empty? messages)))) + +(t/deftest rejects-a-commit-that-throws-while-applied + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + commit1 (commit (th/add-rect-change page-id shape-a)) + commit2 (commit (conflicting-page-change)) + out (update-file ctx {:changes-with-metadata [commit1 commit2]})] + + (t/is (nil? (:error out))) + (t/is (= [(:commit-id commit2)] (-> out :result :rejected :commit-ids))) + (t/is (= :conflict (-> out :result :rejected :error :type))) + (t/is (= 1 (:revn (th/db-get :file {:id (:file-id ctx)})))))) + +(t/deftest rejects-a-commit-that-only-fails-the-schema + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + shape-c (uuid/next) + commit1 (commit (th/add-rect-change page-id shape-a)) + commit2 (commit {:type :mod-obj + :page-id page-id + :id shape-a + :operations [{:type :set :attr :name :val "schema-breaker"}]}) + commit3 (commit (th/add-rect-change page-id shape-c)) + validate val/validate-file-schema-affected! + ;; Changes and shapes are schema checked while applied, so no real + ;; change reaches the file schema check invalid: one is simulated + out (with-redefs [val/validate-file-schema-affected! + (fn [file changes] + (when (= "schema-breaker" + (get-in file [:data :pages-index page-id :objects shape-a :name])) + (ex/raise :type :validation :code :schema-validation :hint "schema")) + (validate file changes))] + (update-file ctx {:changes-with-metadata [commit1 commit2 commit3]})) + objects (-> (get-file-data ctx) :pages-index (get page-id) :objects)] + + (t/is (nil? (:error out))) + (t/is (= [(:commit-id commit2) (:commit-id commit3)] + (-> out :result :rejected :commit-ids))) + (t/is (= :schema-validation (-> out :result :rejected :error :code))) + (t/is (nil? (-> out :result :rejected :preexisting-damage)) + "the data was valid before the rejected commit") + (t/is (= "rect" (get-in objects [shape-a :name]))) + (t/is (not (contains? objects shape-c))))) + +(t/deftest flags-a-rejection-on-data-damaged-before-the-commit + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + _ (update-file ctx {:changes [(th/add-rect-change page-id shape-a) + {:type :mod-obj + :page-id page-id + :id shape-a + :operations [{:type :set :attr :name :val "damaged"}]}]}) + commit1 (commit (th/add-rect-change page-id (uuid/next))) + validate val/validate-file-schema-affected! + ;; The page fails the schema check whenever it holds the "damaged" + ;; shape, as stored data that an older version saved invalid would + out (with-redefs [val/validate-file-schema-affected! + (fn [file changes] + (when (= "damaged" + (get-in file [:data :pages-index page-id :objects shape-a :name])) + (ex/raise :type :validation :code :schema-validation :hint "schema")) + (validate file changes))] + (update-file ctx {:revn 1 :changes-with-metadata [commit1]})) + rejected (-> out :result :rejected)] + + (t/is (nil? (:error out))) + (t/is (= [(:commit-id commit1)] (:commit-ids rejected))) + (t/is (true? (:preexisting-damage rejected))) + (t/is (= 1 (:revn (th/db-get :file {:id (:file-id ctx)})))))) + +(t/deftest saves-every-commit-when-all-are-valid + (let [{:keys [page-id] :as ctx} (create-file) + out (update-file ctx {:changes-with-metadata [(commit (th/add-rect-change page-id (uuid/next))) + (commit (th/add-rect-change page-id (uuid/next)))]})] + (t/is (nil? (:error out))) + (t/is (nil? (-> out :result :rejected))) + (t/is (= 1 (:revn (th/db-get :file {:id (:file-id ctx)})))))) + +(t/deftest flat-changes-stay-all-or-nothing + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + out (update-file ctx {:changes [(th/add-rect-change page-id shape-a) + (break-parent page-id shape-a)]})] + (t/is (= :validation (th/ex-type (:error out)))) + (t/is (= 0 (:revn (th/db-get :file {:id (:file-id ctx)})))))) + +(t/deftest groups-without-commit-ids-stay-all-or-nothing + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + out (update-file ctx {:changes-with-metadata + [{:changes [(th/add-rect-change page-id shape-a)]} + {:changes [(break-parent page-id shape-a)]}]})] + (t/is (= :validation (th/ex-type (:error out)))) + (t/is (= 0 (:revn (th/db-get :file {:id (:file-id ctx)})))))) + +(t/deftest database-failures-fail-the-whole-request + (let [{:keys [page-id] :as ctx} (create-file) + conflict (conflicting-page-change) + process cpc/process-changes + out (with-redefs [cpc/process-changes + (fn [data changes & args] + ;; The database fails on every attempt that + ;; leaves the conflicting commit out: the + ;; retries, never the pass over every commit + (when (and (seq changes) + (not-any? #{conflict} changes)) + (throw (java.sql.SQLException. "connection lost"))) + (apply process data changes args))] + (update-file ctx {:changes-with-metadata + [(commit (th/add-rect-change page-id (uuid/next))) + (commit conflict)]}))] + (t/is (instance? java.sql.SQLException (:error out))) + (t/is (= 0 (:revn (th/db-get :file {:id (:file-id ctx)})))))) + +(defn- move-rect + [page-id id x] + {:type :mod-obj + :page-id page-id + :id id + :operations [{:type :set :attr :x :val x}]}) + +(defn- wait-for-record + "The first log record whose message hint is `hint`, waiting up to two + seconds for the asynchronous logger to write it." + [records hint] + (loop [tries 0] + (if-let [record (->> @records + (filter #(= hint (get-in % [::l/props :hint]))) + (first))] + record + (when (< tries 200) + (Thread/sleep 10) + (recur (inc tries)))))) + +(defn- update-file-logged + "Updates the file and returns the response in `:out` and, in `:rejected`, + the props of the log record of the rejection, if any." + [ctx params] + (let [records (atom []) + out (try + (add-watch l/log-record ::capture + (fn [_ _ _ record] (swap! records conj record))) + (update-file ctx params) + (finally + (remove-watch l/log-record ::capture)))] + {:out out + :rejected (some-> (wait-for-record records "update-file commit rejected") + ::l/props)})) + +(t/deftest bisects-the-commits-to-find-the-invalid-one + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + _ (update-file ctx {:changes [(th/add-rect-change page-id shape-a)]}) + commits (mapv (fn [i] + (if (= i 5) + (commit (break-parent page-id shape-a)) + (commit (move-rect page-id shape-a i)))) + (range 16)) + + {:keys [out rejected]} + (update-file-logged ctx {:revn 1 :changes-with-metadata commits})] + + (t/is (nil? (:error out))) + (t/is (= (mapv :commit-id (subvec commits 5)) + (-> out :result :rejected :commit-ids))) + (t/is (= (:commit-id (nth commits 5)) + (-> out :result :rejected :failed-commit-id))) + (t/is (= :referential-integrity (-> out :result :rejected :error :code))) + + (t/testing "the search takes log2 of the commits attempts" + (t/is (= 4 (:attempts rejected)))) + + (t/testing "the file holds the commits before the invalid one" + (let [objects (-> (get-file-data ctx) :pages-index (get page-id) :objects)] + (t/is (= uuid/zero (get-in objects [shape-a :parent-id]))) + (t/is (= 4 (get-in objects [shape-a :x]))))))) + +(t/deftest rejects-a-single-commit-without-a-retry + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + _ (update-file ctx {:changes [(th/add-rect-change page-id shape-a)]}) + commit1 (commit (break-parent page-id shape-a)) + + {:keys [out rejected]} + (update-file-logged ctx {:revn 1 :changes-with-metadata [commit1]})] + + (t/is (nil? (:error out))) + (t/is (= [(:commit-id commit1)] (-> out :result :rejected :commit-ids))) + (t/is (= :referential-integrity (-> out :result :rejected :error :code))) + (t/is (= 0 (:attempts rejected)) "no retry") + (t/is (= 1 (:revn (th/db-get :file {:id (:file-id ctx)})))))) + +(defn- rename-rect + [page-id id name] + {:type :mod-obj + :page-id page-id + :id id + :operations [{:type :set :attr :name :val name}]}) + +(t/deftest keeps-fifty-commits-before-the-invalid-one + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + _ (update-file ctx {:changes [(th/add-rect-change page-id shape-a)]}) + renames (mapv #(commit (rename-rect page-id shape-a (str "name " %))) (range 50)) + invalid (commit (break-parent page-id shape-a)) + + {:keys [out rejected]} + (update-file-logged ctx {:revn 1 :changes-with-metadata (conj renames invalid)})] + + (t/is (nil? (:error out))) + (t/is (= [(:commit-id invalid)] (-> out :result :rejected :commit-ids))) + + (t/testing "the file holds the fifty renames" + (let [objects (-> (get-file-data ctx) :pages-index (get page-id) :objects)] + (t/is (= "name 49" (get-in objects [shape-a :name]))) + (t/is (= uuid/zero (get-in objects [shape-a :parent-id]))))) + + (t/testing "the change log holds the renames in one revision" + (let [rows (get-file-changes ctx)] + (t/is (= 2 (count rows))) + (t/is (= (into [] (mapcat :changes) renames) + (:changes (last rows)))))) + + (t/testing "the search takes log2 of the commits attempts" + (t/is (= 51 (:commits rejected))) + (t/is (= 6 (:attempts rejected)))))) + +(t/deftest keeps-a-commit-that-repairs-an-earlier-one + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + shape-c (uuid/next) + _ (update-file ctx {:changes [(th/add-rect-change page-id shape-a)]}) + commit1 (commit (break-parent page-id shape-a)) + commit2 (commit {:type :mod-obj + :page-id page-id + :id shape-a + :operations [{:type :set :attr :parent-id :val uuid/zero}]}) + commit3 (commit (th/add-rect-change page-id shape-c)) + commit4 (commit (break-parent page-id shape-c)) + out (update-file ctx {:revn 1 :changes-with-metadata [commit1 commit2 commit3 commit4]}) + objects (-> (get-file-data ctx) :pages-index (get page-id) :objects)] + + (t/is (nil? (:error out))) + (t/is (= [(:commit-id commit4)] (-> out :result :rejected :commit-ids))) + (t/is (= uuid/zero (get-in objects [shape-a :parent-id]))) + (t/is (= uuid/zero (get-in objects [shape-c :parent-id]))))) + +(defn- debug-fault + "A change that asks the backend to fail with `fault` while applied." + [fault] + {:type :set-plugin-data + :object-type :file + :namespace :penpot-debug + :key (str "save-fault:" fault) + :value nil}) + +(defn- with-debug-faults + [f] + (with-redefs [cf/flags (conj cf/flags :debug-save-faults)] + (f))) + +(t/deftest a-debug-fault-rejects-its-commit + (let [{:keys [page-id] :as ctx} (create-file) + shape-a (uuid/next) + commit1 (commit (th/add-rect-change page-id shape-a)) + commit2 (commit (debug-fault "runtime")) + commit3 (commit (th/add-rect-change page-id (uuid/next))) + out (with-debug-faults + #(update-file ctx {:changes-with-metadata [commit1 commit2 commit3]})) + objects (-> (get-file-data ctx) :pages-index (get page-id) :objects)] + + (t/is (nil? (:error out))) + (t/is (= [(:commit-id commit2) (:commit-id commit3)] + (-> out :result :rejected :commit-ids))) + (t/is (= :internal (-> out :result :rejected :error :type))) + (t/is (contains? objects shape-a)))) + +(t/deftest a-debug-validation-fault-rejects-like-a-validation-error + (let [{:keys [page-id] :as ctx} (create-file) + out (with-debug-faults + #(update-file ctx {:changes-with-metadata + [(commit (th/add-rect-change page-id (uuid/next))) + (commit (debug-fault "validation"))]}))] + (t/is (= :validation (-> out :result :rejected :error :type))))) + +(t/deftest a-debug-infrastructure-fault-fails-the-whole-request + (let [{:keys [page-id] :as ctx} (create-file) + out (with-debug-faults + #(update-file ctx {:changes-with-metadata + [(commit (th/add-rect-change page-id (uuid/next))) + (commit (debug-fault "sql"))]}))] + (t/is (instance? java.sql.SQLException (:error out))) + (t/is (= 0 (:revn (th/db-get :file {:id (:file-id ctx)})))))) + +(t/deftest debug-faults-need-the-flag + (let [{:keys [page-id] :as ctx} (create-file) + out (update-file ctx {:changes-with-metadata + [(commit (th/add-rect-change page-id (uuid/next))) + (commit (debug-fault "runtime"))]})] + (t/is (nil? (:error out))) + (t/is (nil? (-> out :result :rejected))) + (t/is (= 1 (:revn (th/db-get :file {:id (:file-id ctx)})))) + (t/is (nil? (:plugin-data (get-file-data ctx)))))) + +(t/deftest a-rejection-without-an-error-type-reports-an-internal-error + (let [{:keys [page-id] :as ctx} (create-file) + out (with-debug-faults + #(update-file ctx {:changes-with-metadata + [(commit (th/add-rect-change page-id (uuid/next))) + (commit (debug-fault "ex-info"))]})) + error (-> out :result :rejected :error)] + (t/is (= :internal (:type error))) + (t/is (= :unexpected-error (:code error))) + (t/is (= "debug save fault" (:hint error))))) + +(defn- upload-image + "Uploads an image to the file `file-id` and returns its media object." + [profile-id file-id] + (let [out (th/multipart-command! + {::th/type :upload-file-media-object + ::rpc/profile-id profile-id + :file-id file-id + :is-local true + :name "image" + :content {:filename "sample.jpg" + :path (th/tempfile "backend_tests/test_files/sample.jpg") + :mtype "image/jpeg" + :size 312043}})] + (t/is (nil? (:error out))) + (:result out))) + +(defn- add-image-rect + "A rect filled with the image of `mobj`." + [page-id id mobj] + (assoc-in (th/add-rect-change page-id id) + [:obj :fills] + [{:fill-opacity 1 + :fill-image {:id (:id mobj) :width 100 :height 100 :mtype "image/jpeg"}}])) + +(t/deftest a-rejected-commit-adds-no-media-references + (let [{:keys [profile-id page-id file-id] :as ctx} (create-file) + other (th/create-file* 2 {:profile-id profile-id + :project-id (:default-project-id (th/db-get :profile {:id profile-id})) + :is-shared false}) + ;; An image of another file: referencing it copies the media + ;; object into this file + mobj (upload-image profile-id (:id other)) + shape-a (uuid/next) + shape-b (uuid/next) + commit1 (commit (th/add-rect-change page-id shape-a)) + commit2 (commit (add-image-rect page-id shape-b mobj) + (break-parent page-id shape-a)) + out (update-file ctx {:changes-with-metadata [commit1 commit2]})] + + (t/is (nil? (:error out))) + (t/is (= [(:commit-id commit2)] (-> out :result :rejected :commit-ids))) + + (t/testing "the rejected image adds no media object to the file" + (t/is (empty? (th/db-query :file-media-object {:file-id file-id})))) + + (t/testing "the same image in an accepted commit does" + (let [out (update-file ctx {:revn 1 + :changes-with-metadata [(commit (add-image-rect page-id shape-b mobj))]})] + (t/is (nil? (:error out))) + (t/is (nil? (-> out :result :rejected))) + (t/is (= 1 (count (th/db-query :file-media-object {:file-id file-id})))))))) diff --git a/backend/test/backend_tests/rpc_management_jobs_test.clj b/backend/test/backend_tests/rpc_management_jobs_test.clj new file mode 100644 index 0000000000..c228289528 --- /dev/null +++ b/backend/test/backend_tests/rpc_management_jobs_test.clj @@ -0,0 +1,450 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.rpc-management-jobs-test + (:require + [app.common.logging :as l] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.metrics :as-alias mtx] + [app.rpc :as-alias rpc] + [backend-tests.helpers :as th] + [clojure.test :as t])) + +(t/use-fixtures :once th/state-init) + +(defn- make-cfg [] + {::db/pool th/*pool* + ::mtx/metrics (get th/*system* :app.metrics/metrics)}) + +(defn- test-fixture [next] + (th/database-reset next)) + +(t/use-fixtures :each test-fixture) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; HELPERS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- mk-job + [{:keys [name status params scheduled-at] + :or {name "media-process" + status "new" + params {:x 1} + scheduled-at (ct/now)}}] + (let [id (uuid/next)] + (th/db-insert! :job {:id id + :name name + :tenant (cf/get :tenant) + :queue "media" + :params (db/json params) + :priority 100 + :max-retries 3 + :retry-num 0 + :status status + :scheduled-at scheduled-at + :created-at (ct/now) + :modified-at (ct/now)}) + id)) + +(defn- get-row + [id] + (let [row (th/db-get :job {:id id} + :status :result :error + :started-at :completed-at :modified-at)] + (reduce (fn [row key] + (update row key #(cond-> % (db/pgobject? %) db/decode-json-pgobject))) + row + [:result :error]))) + +(def ^:private test-error + {:type :internal + :code :boom + :hint "boom"}) + +(defn- get-progresss + "Progress events of a job, oldest first, with decoded payloads." + [id] + (->> (th/db-exec! ["SELECT payload FROM job_event + WHERE job_id = ? AND kind = 'progress' + ORDER BY created_at ASC, id ASC" + id]) + (mapv #(jobs/decode-progress (:payload %))))) + +(defn- count-job-events + [id] + (-> (th/db-exec! ["SELECT count(*) AS count FROM job_event WHERE job_id = ?" id]) + first + :count)) + +(defn- mgmt + [type params] + (th/management-command! (assoc params ::th/type type))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest claim-job-returns-run-with-name-and-params + (let [scheduled-at (ct/now) + job-id (mk-job {:scheduled-at scheduled-at + :params {:command :process :file-id (str (uuid/next))}}) + out (mgmt :claim-job {:job-id job-id + :scheduled-at scheduled-at})] + (t/is (nil? (:error out))) + (t/is (= {:action :run + :name "media-process" + :params {:command "process" + :file-id (get-in out [:result :params :file-id])}} + (:result out))) + (let [row (get-row job-id)] + (t/is (= "running" (:status row))) + (t/is (some? (:started-at row)))))) + +(t/deftest claim-job-skips-on-stale-scheduled-at + (let [job-id (mk-job {}) + out (mgmt :claim-job {:job-id job-id + :scheduled-at (ct/in-future {:minutes 5})})] + (t/is (nil? (:error out))) + (t/is (= {:action :skip} (:result out))) + (t/is (= "new" (:status (get-row job-id)))))) + +(t/deftest claim-job-skips-terminal-and-cancelled-rows + (let [completed-id (mk-job {:status "completed"}) + cancelled-id (mk-job {:status "cancelled"}) + aborted-id (mk-job {:status "aborted"})] + (t/is (= {:action :skip} + (:result (mgmt :claim-job {:job-id completed-id + :scheduled-at (ct/now)})))) + (t/is (= {:action :skip} + (:result (mgmt :claim-job {:job-id cancelled-id + :scheduled-at (ct/now)})))) + (t/is (= {:action :skip} + (:result (mgmt :claim-job {:job-id aborted-id + :scheduled-at (ct/now)})))) + (t/is (= "completed" (:status (get-row completed-id)))) + (t/is (= "cancelled" (:status (get-row cancelled-id)))) + (t/is (= "aborted" (:status (get-row aborted-id)))))) + +(t/deftest claim-job-skips-missing-row + (t/is (= {:action :skip} + (:result (mgmt :claim-job {:job-id (uuid/next) + :scheduled-at (ct/now)}))))) + +(t/deftest claim-job-validates-params + (let [out (mgmt :claim-job {:job-id (uuid/next)})] + (t/is (= :validation (th/ex-type (:error out)))))) + +(t/deftest jobs-methods-reject-a-key-they-do-not-declare + (doseq [type [:claim-job :report-job-progress :complete-job :fail-job]] + (let [out (mgmt type {::rpc/auth-key-id :nexus})] + (t/is (= :authorization (th/ex-type (:error out))) (name type)) + (t/is (= :shared-key-not-allowed (th/ex-code (:error out))) (name type))))) + +(t/deftest complete-job-rejects-a-key-it-does-not-declare-without-side-effects + (let [job-id (mk-job {}) + before (get-row job-id) + events-before (count-job-events job-id) + out (mgmt :complete-job {::rpc/auth-key-id :nexus + :job-id job-id + :result {:x 1}}) + after (get-row job-id)] + (t/is (= :authorization (th/ex-type (:error out)))) + (t/is (= :shared-key-not-allowed (th/ex-code (:error out)))) + (t/is (= (:status before) (:status after))) + (t/is (nil? (:completed-at after))) + (t/is (= events-before (count-job-events job-id))))) + +(t/deftest report-job-progress-appends-a-progress-event + (let [job-id (mk-job {}) + _ (jobs/claim (make-cfg) job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + out (mgmt :report-job-progress {:job-id job-id + :progress {:stage :pages + :counters {:pages {:current 50 :total 100}}}})] + (t/is (nil? (:error out))) + (t/is (= {:action :run} (:result out))) + (t/is (= [{:stage :pages :counters {:pages {:current 50 :total 100}}}] + (get-progresss job-id))) + (t/testing "the job row has no progress column anymore" + (t/is (not (contains? (get-row job-id) :progress)))))) + +(t/deftest report-job-progress-noop-on-terminal-row + (let [job-id (mk-job {:status "completed"}) + out (mgmt :report-job-progress {:job-id job-id + :progress {:stage :pages + :counters {:pages {:current 100 :total 100}}}})] + (t/is (nil? (:error out))) + (t/is (= {:action :skip} (:result out))) + (t/is (= [] (get-progresss job-id))))) + +(t/deftest report-job-progress-persists-rapid-reports + (let [job-id (mk-job {}) + _ (jobs/claim (make-cfg) job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + _ (mgmt :report-job-progress {:job-id job-id + :progress {:stage :pages + :counters {:pages {:current 3 :total 10}}}}) + _ (mgmt :report-job-progress {:job-id job-id + :progress {:stage :pages + :counters {:pages {:current 4 :total 10}}}})] + (t/testing "the second immediate report is not throttled away" + (t/is (= [{:stage :pages :counters {:pages {:current 3 :total 10}}} + {:stage :pages :counters {:pages {:current 4 :total 10}}}] + (get-progresss job-id)))))) + +(t/deftest report-job-progress-validates-the-payload + (let [job-id (mk-job {}) + _ (jobs/claim (make-cfg) job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at)))] + (t/testing "stage is mandatory" + (t/is (= :validation (th/ex-type (:error (mgmt :report-job-progress + {:job-id job-id + :progress {:counters {:work {:current 1}}}})))))) + (t/testing "no extra key is accepted" + (t/is (= :validation (th/ex-type (:error (mgmt :report-job-progress + {:job-id job-id + :progress {:stage :work + :message "boom"}})))))) + (t/is (= [] (get-progresss job-id))))) + +(t/deftest complete-job-marks-completed-with-result + (let [cfg (make-cfg) + job-id (mk-job {}) + _ (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + out (mgmt :complete-job {:job-id job-id + :result {:value 42}})] + (t/is (nil? (:error out))) + (t/is (= {:action :run} (:result out))) + (let [row (get-row job-id)] + (t/is (= "completed" (:status row))) + (t/is (= {:value 42} (:result row))) + (t/is (some? (:completed-at row)))))) + +(t/deftest complete-job-accepts-nested-result + (let [cfg (make-cfg) + job-id (mk-job {}) + _ (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + result {:value 42 :nested {:items [1 2 {:three 3}] :ok true}} + out (mgmt :complete-job {:job-id job-id :result result})] + (t/is (nil? (:error out))) + (let [row (get-row job-id)] + (t/is (= "completed" (:status row))) + (t/is (= result (:result row)))))) + +(t/deftest complete-job-without-result-stores-null + (let [cfg (make-cfg) + job-id (mk-job {}) + _ (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + out (mgmt :complete-job {:job-id job-id :result nil})] + (t/is (nil? (:error out))) + (let [row (get-row job-id)] + (t/is (= "completed" (:status row))) + (t/is (nil? (:result row)))))) + +(t/deftest complete-job-with-unserializable-result-stores-null + (let [cfg (make-cfg) + job-id (mk-job {}) + _ (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at)))] + (jobs/complete cfg :job-id job-id :result (Object.)) + (let [row (get-row job-id)] + (t/is (= "completed" (:status row))) + (t/is (nil? (:result row)))))) + +(t/deftest complete-job-with-unserializable-result-logs-job-name + (let [cfg (make-cfg) + job-id (mk-job {:name "media-process"}) + _ (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + captured (atom nil)] + (with-redefs [l/emit-log (fn [props _cause _ctx _logger _level _sync?] + (reset! captured (into {} @props)))] + (jobs/complete cfg :job-id job-id :result (Object.))) + (let [row (get-row job-id)] + (t/is (= "completed" (:status row))) + (t/is (nil? (:result row))) + (t/testing "the serialization warning carries the job name" + (t/is (= "media-process" (:job-name @captured))))))) + +(t/deftest complete-and-fail-clean-throttle-state + (let [cfg (make-cfg) + job-id1 (mk-job {}) + job-id2 (mk-job {})] + (jobs/claim cfg job-id1 (:scheduled-at (th/db-get :job {:id job-id1} :id :scheduled-at))) + (jobs/claim cfg job-id2 (:scheduled-at (th/db-get :job {:id job-id2} :id :scheduled-at))) + (jobs/heartbeat cfg :job-id job-id1) + (jobs/heartbeat cfg :job-id job-id2 :progress {:stage :work}) + (jobs/complete cfg :job-id job-id1) + (jobs/fail cfg job-id2 test-error) + (t/is (not (contains? @@#'jobs/heartbeats job-id1))) + (t/is (not (contains? @@#'jobs/progresses job-id2))))) + +(t/deftest complete-and-fail-respect-first-terminal-wins + (let [cfg (make-cfg) + running-id (mk-job {}) + orphan-id (mk-job {:status "aborted"}) + _ (jobs/claim cfg running-id + (:scheduled-at (th/db-get :job {:id running-id} :id :scheduled-at)))] + ;; a running job completes; a later complete/fail on the same row is a no-op + (t/is (= 1 (jobs/complete cfg :job-id running-id :result {:value 1}))) + (t/is (= 0 (jobs/complete cfg :job-id running-id :result {:value 2}))) + (t/is (= 0 (jobs/fail cfg running-id test-error))) + (t/is (= {:value 1} (:result (get-row running-id)))) + + ;; an orphan (aborted by the dispatcher) is never overwritten + (t/is (= 0 (jobs/complete cfg :job-id orphan-id :result {:value 3}))) + (t/is (= 0 (jobs/fail cfg orphan-id test-error))))) + +(t/deftest fail-job-marks-failed-with-error + (let [cfg (make-cfg) + job-id (mk-job {}) + _ (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + out (mgmt :fail-job {:job-id job-id + :error {:type :internal + :code "processing-error" + :hint "bad image"}})] + (t/is (nil? (:error out))) + (let [row (get-row job-id)] + (t/is (= "failed" (:status row))) + (t/is (= {:type "internal" + :code "processing-error" + :hint "bad image"} + (:error row)))))) + +(t/deftest fail-job-validates-params + (let [job-id (mk-job {})] + (t/is (= :validation (th/ex-type (:error (mgmt :fail-job {:job-id job-id}))))))) + +(t/deftest terminal-writers-report-skip-on-terminal-rows + (let [done-id (mk-job {:status "completed"})] + (t/testing "complete on a terminal row reports skip" + (let [out (mgmt :complete-job {:job-id done-id :result {:x 1}})] + (t/is (nil? (:error out))) + (t/is (= {:action :skip} (:result out))))) + (t/testing "fail on a terminal row reports skip" + (let [out (mgmt :fail-job {:job-id done-id + :error {:type :internal + :code "processing-error" + :hint "bad image"}})] + (t/is (nil? (:error out))) + (t/is (= {:action :skip} (:result out))))) + (t/testing "progress on a terminal row reports skip" + (let [out (mgmt :report-job-progress {:job-id done-id + :progress {:stage :pages :counters {:pages {:current 4 :total 10}}}})] + (t/is (nil? (:error out))) + (t/is (= {:action :skip} (:result out)))))) + (let [cfg (make-cfg) + job-id (mk-job {}) + _ (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at)))] + (t/testing "live paths report run" + (t/is (= {:action :run} + (:result (mgmt :report-job-progress {:job-id job-id + :progress {:stage :pages :counters {:pages {:current 4 :total 10}}}})))) + (t/is (= {:action :run} + (:result (mgmt :complete-job {:job-id job-id + :result {:x 1}}))))))) + +(t/deftest complete-job-associates-a-resource-id + (let [cfg (make-cfg) + resource-id (uuid/next)] + (th/db-insert! :storage-object {:id resource-id :backend "test"}) + (let [job-id (mk-job {})] + (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + (t/is (nil? (:error (mgmt :complete-job {:job-id job-id + :result {:v 1} + :resource-id resource-id})))) + (t/is (= resource-id (:resource-id (get-row job-id))))))) + +(t/deftest complete-job-refuses-a-second-resource-id + (let [cfg (make-cfg) + first (uuid/next) + second (uuid/next)] + (doseq [id [first second]] + (th/db-insert! :storage-object {:id id :backend "test"})) + (let [job-id (mk-job {})] + (th/db-update! :job {:resource-id first} {:id job-id}) + (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + (let [out (mgmt :complete-job {:job-id job-id + :result {:v 1} + :resource-id second})] + (t/is (= :validation (th/ex-type (:error out))))) + (t/testing "the job keeps the resource it had and is still running" + (let [row (get-row job-id)] + (t/is (= first (:resource-id row))) + (t/is (= "running" (:status row)))))))) + +(t/deftest complete-job-on-a-finished-job-skips-instead-of-refusing-the-resource + (let [cfg (make-cfg) + first (uuid/next) + second (uuid/next)] + (doseq [id [first second]] + (th/db-insert! :storage-object {:id id :backend "test"})) + (let [job-id (mk-job {})] + (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + (t/is (= {:action :run} + (:result (mgmt :complete-job {:job-id job-id + :result {:v 1} + :resource-id first})))) + + (t/testing "the job is already finished, so there is no resource left to refuse" + (let [out (mgmt :complete-job {:job-id job-id + :result {:v 2} + :resource-id second})] + (t/is (nil? (:error out))) + (t/is (= {:action :skip} (:result out))))) + + (t/testing "the row keeps the resource it was given and no event is added" + (let [row (get-row job-id)] + (t/is (= first (:resource-id row))) + (t/is (= "completed" (:status row))) + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM job_event + WHERE job_id = ? AND kind = 'end'" + job-id]))))))))) + +(t/deftest complete-job-validates-the-resource-id + (let [cfg (make-cfg) + job-id (mk-job {})] + (jobs/claim cfg job-id (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + (t/is (= :validation + (th/ex-type (:error (mgmt :complete-job {:job-id job-id + :result {:v 1} + :resource-id "not-a-uuid"}))))))) + +(t/deftest fail-job-validates-the-error-shape + (let [cfg (make-cfg) + run (fn [error] + (let [job-id (mk-job {})] + (jobs/claim cfg job-id + (:scheduled-at (th/db-get :job {:id job-id} :id :scheduled-at))) + [(mgmt :fail-job {:job-id job-id :error error}) job-id]))] + + (t/testing "hint is required" + (t/is (= :validation + (th/ex-type (:error (first (run {:type :internal + :code :processing-error}))))))) + + (t/testing "a string code over the wire decodes into the keyword" + (let [[out job-id] (run {:type :internal + :code "processing-error" + :hint "bad image"})] + (t/is (nil? (:error out))) + (t/is (= {:type :internal :code :processing-error :hint "bad image"} + (jobs/decode-job-error (:error (get-row job-id))))))) + + (t/testing "a keyword error is stored and decodes back untouched" + (let [[out job-id] (run {:type :internal + :code :processing-error + :hint "bad image"})] + (t/is (nil? (:error out))) + (t/is (= {:type :internal :code :processing-error :hint "bad image"} + (jobs/decode-job-error (:error (get-row job-id))))))) + + (t/testing "worker-defined extra details are kept" + (let [[out job-id] (run {:type :internal + :code :processing-error + :hint "bad image" + :attempt 3})] + (t/is (nil? (:error out))) + (t/is (= 3 (:attempt (jobs/decode-job-error (:error (get-row job-id)))))))))) diff --git a/backend/test/backend_tests/rpc_management_nitrate_test.clj b/backend/test/backend_tests/rpc_management_nitrate_test.clj index 5be56ece30..2bc29482c0 100644 --- a/backend/test/backend_tests/rpc_management_nitrate_test.clj +++ b/backend/test/backend_tests/rpc_management_nitrate_test.clj @@ -14,11 +14,12 @@ [app.db :as db] [app.email :as eml] [app.http :as-alias http] + [app.jobs :as jobs] [app.msgbus :as mbus] [app.nitrate :as nitrate] [app.rpc :as-alias rpc] + [app.rpc.commands.profile :as profile] [app.util.ssrf :as ssrf] - [app.worker :as wrk] [backend-tests.helpers :as th] [clojure.set :as set] [clojure.test :as t] @@ -51,9 +52,16 @@ (t/is (= :authentication (th/ex-type (:error out)))) (t/is (= :authentication-required (th/ex-code (:error out)))))) +(t/deftest authenticate-rejects-a-key-it-does-not-declare + (let [out (th/management-command! {::th/type :authenticate + ::rpc/auth-key-id :exporter})] + (t/is (not (th/success? out))) + (t/is (= :authorization (th/ex-type (:error out)))) + (t/is (= :shared-key-not-allowed (th/ex-code (:error out)))))) + (t/deftest create-and-update-organization-invitations-audit-props (let [owner-id-ref (atom nil)] - (with-mocks [email-mock {:target 'app.email/send! :return nil} + (with-mocks [email-mock {:target 'app.email/send-template :return nil} audit-mock {:target 'app.loggers.audit/submit :return nil} nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method params] @@ -102,7 +110,7 @@ (t/deftest invite-to-organization-rejects-non-owner (let [organization-summary-ref (atom nil)] - (with-mocks [email-mock {:target 'app.email/send! :return nil} + (with-mocks [email-mock {:target 'app.email/send-template :return nil} nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (when (= method :get-organization-summary) @@ -130,7 +138,7 @@ (t/is (not (:called? @email-mock))))))) (t/deftest invite-to-organization-rejects-unknown-organization - (with-mocks [email-mock {:target 'app.email/send! :return nil} + (with-mocks [email-mock {:target 'app.email/send-template :return nil} nitrate-mock {:target 'app.nitrate/call :return nil}] (let [profile (th/create-profile* 105 {:is-active true}) organization-id (uuid/random) @@ -149,7 +157,7 @@ (t/deftest invite-to-organization-uses-authoritative-branding (let [organization-summary-ref (atom nil)] - (with-mocks [email-mock {:target 'app.email/send! :return nil} + (with-mocks [email-mock {:target 'app.email/send-template :return nil} nitrate-mock {:target 'app.nitrate/call :return (fn [_cfg method _params] (when (= method :get-organization-summary) @@ -175,7 +183,7 @@ :logo "https://evil.example/logo.png" :avatar-bg-url "https://evil.example/avatar.svg" :sso-active false}}) - email-params (first (:call-args @email-mock)) + email-params (second (:call-args @email-mock)) organization (:organization email-params)] (t/is (th/success? out)) (t/is (= "Trusted Organization" (:name organization))) @@ -226,6 +234,167 @@ (t/is (= #{(:name owned-team)} (->> out :result (map :name) set)))))) +(t/deftest get-member-teams-returns-all-active-memberships + (with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}] + (let [profile (th/create-profile* 1 {:is-active true}) + other (th/create-profile* 2 {:is-active true}) + default-team (th/db-get :team {:id (:default-team-id profile)}) + owned-team (th/create-team* 1 {:profile-id (:id profile)}) + member-team (th/create-team* 2 {:profile-id (:id other)}) + _ (th/create-team-role* {:team-id (:id member-team) + :profile-id (:id profile) + :role :editor}) + deleted-team (th/create-team* 3 {:profile-id (:id profile)}) + _ (th/db-update! :team + {:deleted-at (ct/now)} + {:id (:id deleted-team)}) + out (th/management-command! {::th/type :get-member-teams + ::rpc/profile-id (:id profile)})] + (t/is (th/success? out)) + (let [teams-by-id (->> out :result (d/index-by :id))] + (t/is (= #{(:id default-team) (:id owned-team) (:id member-team)} + (set (keys teams-by-id)))) + (t/is (true? (get-in teams-by-id [(:id default-team) :is-default]))) + (t/is (false? (get-in teams-by-id [(:id owned-team) :is-default]))) + (t/is (false? (get-in teams-by-id [(:id member-team) :is-default]))))))) + +(t/deftest get-organization-member-teams-returns-roles-within-given-teams + (with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}] + (let [profile (th/create-profile* 1 {:is-active true}) + other (th/create-profile* 2 {:is-active true}) + owned-team (th/create-team* 1 {:profile-id (:id profile)}) + admin-team (th/create-team* 2 {:profile-id (:id other)}) + editor-team (th/create-team* 3 {:profile-id (:id other)}) + viewer-team (th/create-team* 4 {:profile-id (:id other)}) + foreign-team (th/create-team* 5 {:profile-id (:id other)}) + not-member (th/create-team* 6 {:profile-id (:id other)}) + deleted-team (th/create-team* 7 {:profile-id (:id profile)}) + _ (th/db-update! :team + {:deleted-at (ct/now)} + {:id (:id deleted-team)}) + _ (doseq [[team role] [[admin-team :admin] + [editor-team :editor] + [viewer-team :viewer] + [foreign-team :editor]]] + (th/create-team-role* {:team-id (:id team) + :profile-id (:id profile) + :role role})) + team-ids (mapv :id [owned-team admin-team editor-team viewer-team + not-member deleted-team + {:id (:default-team-id profile)}]) + out (th/management-command! {::th/type :get-organization-member-teams + ::rpc/profile-id (:id other) + :profile-id (:id profile) + :team-ids team-ids})] + (t/is (th/success? out)) + (t/is (= {(:id owned-team) :owner + (:id admin-team) :admin + (:id editor-team) :editor + (:id viewer-team) :viewer} + (->> out :result (map (juxt :id :role)) (into {})))) + (t/is (= (:name owned-team) + (->> out :result (d/seek #(= (:id owned-team) (:id %))) :name)))))) + +(t/deftest get-organization-member-teams-empty-team-ids + (with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}] + (let [profile (th/create-profile* 1 {:is-active true}) + out (th/management-command! {::th/type :get-organization-member-teams + ::rpc/profile-id (:id profile) + :profile-id (:id profile) + :team-ids []})] + (t/is (th/success? out)) + (t/is (= [] (:result out)))))) + +(t/deftest get-organization-team-roles-returns-owners-and-admins-only + (with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}] + (let [owner (th/create-profile* 1 {:is-active true}) + admin (th/create-profile* 2 {:is-active true}) + editor (th/create-profile* 3 {:is-active true}) + viewer (th/create-profile* 4 {:is-active true}) + deleted (th/create-profile* 5 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id owner)}) + deleted-team (th/create-team* 2 {:profile-id (:id owner)}) + _ (th/db-update! :team + {:deleted-at (ct/now)} + {:id (:id deleted-team)}) + _ (doseq [[profile role] [[admin :admin] + [editor :editor] + [viewer :viewer] + [deleted :admin]]] + (th/create-team-role* {:team-id (:id team) + :profile-id (:id profile) + :role role})) + _ (th/db-update! :profile + {:deleted-at (ct/now)} + {:id (:id deleted)}) + team-ids (mapv :id [team deleted-team {:id (:default-team-id owner)}]) + out (th/management-command! {::th/type :get-organization-team-roles + ::rpc/profile-id (:id owner) + :team-ids team-ids})] + (t/is (th/success? out)) + (t/is (= {(:id owner) :owner + (:id admin) :admin} + (->> out :result (map (juxt :profile-id :role)) (into {})))) + (t/is (= #{(:id team)} (into #{} (map :team-id) (:result out)))) + (t/is (= [(:name team)] (->> out :result (map :team-name) distinct)))))) + +(t/deftest get-organization-team-roles-empty-team-ids + (with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}] + (let [profile (th/create-profile* 1 {:is-active true}) + out (th/management-command! {::th/type :get-organization-team-roles + ::rpc/profile-id (:id profile) + :team-ids []})] + (t/is (th/success? out)) + (t/is (= [] (:result out)))))) + +(t/deftest update-profile-theme-updates-only-theme + (with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}] + (let [profile (th/create-profile* 1 {:is-active true + :fullname "Nitrate User" + :lang "es" + :theme "light"}) + _ (th/db-update! :profile + {:lang "es"} + {:id (:id profile)}) + out (th/management-command! {::th/type :update-profile-theme + ::rpc/profile-id (:id profile) + :theme "dark"}) + saved (-> (th/db-get :profile {:id (:id profile)}) + (profile/decode-row))] + (t/is (th/success? out)) + (t/is (= "dark" (:theme saved))) + (t/is (= "Nitrate User" (:fullname saved))) + (t/is (= "es" (:lang saved)))))) + +(t/deftest update-profile-theme-rejects-invalid-theme + (with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}] + (let [profile (th/create-profile* 1 {:is-active true}) + out (th/management-command! {::th/type :update-profile-theme + ::rpc/profile-id (:id profile) + :theme "invalid"}) + error (:error out)] + (t/is (th/ex-info? error)) + (t/is (th/ex-of-type? error :validation)) + (t/is (th/ex-of-code? error :params-validation))))) + +(t/deftest update-profile-props-merges-onboarding-props + (with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}] + (let [profile (th/create-profile* 1 {:is-active true}) + out (th/management-command! {::th/type :update-profile-props + ::rpc/profile-id (:id profile) + :props {:nitrate-onboarding-viewed true + :onboarding-questions-answered true + :onboarding-questions + {:role "developer" + :company-size "2-100"}}}) + saved (-> (th/db-get :profile {:id (:id profile)}) + (profile/decode-row))] + (t/is (th/success? out)) + (t/is (true? (get-in saved [:props :nitrate-onboarding-viewed]))) + (t/is (true? (get-in saved [:props :onboarding-questions-answered]))) + (t/is (= {:role "developer" :company-size "2-100"} + (get-in saved [:props :onboarding-questions])))))) + (t/deftest notify-team-change-publishes-event (let [team-id (uuid/random) organization-id (uuid/random) @@ -416,7 +585,7 @@ @organization-summary-ref nil))} ;; --- Worker mock: capture delete-task submission --- - wrk-mock {:target 'app.worker/submit! :return nil} + wrk-mock {:target 'app.jobs/submit :return nil} ;; --- Message bus mock: capture published events --- mbus-mock {:target 'app.msgbus/pub! :return nil}] @@ -503,6 +672,34 @@ (t/is (= :organization-change-sso (:type msg))) (t/is (= organization-id (:organization-id msg)))))) +(t/deftest notify-organization-sso-change-emits-token-revoked-when-updated-props + (with-mocks [audit-mock {:target 'app.loggers.audit/submit :return nil}] + (let [organization-id (uuid/random) + out (with-redefs [mbus/pub! (constantly nil)] + (th/management-command! {::th/type :notify-organization-sso-change + :organization-id organization-id + :updated-props true + :announce-activation false})) + revoked (->> (:call-args-list @audit-mock) + (map second) + (filter #(= "organization-sso-token-revoked" (:name %))))] + (t/is (th/success? out)) + (t/is (= 1 (count revoked))) + (t/is (= uuid/zero (:profile-id (first revoked)))) + (t/is (= {:organization-id organization-id} (:props (first revoked)))))) + + (with-mocks [audit-mock {:target 'app.loggers.audit/submit :return nil}] + (let [out (with-redefs [mbus/pub! (constantly nil)] + (th/management-command! {::th/type :notify-organization-sso-change + :organization-id (uuid/random) + :updated-props false + :announce-activation false})) + revoked (->> (:call-args-list @audit-mock) + (map second) + (filter #(= "organization-sso-token-revoked" (:name %))))] + (t/is (th/success? out)) + (t/is (empty? revoked))))) + (t/deftest notify-user-organizations-deletion-renames-or-deletes-teams-and-publishes-per-organization-events ;; --- Deferred owned-organizations: nil during setup, filled before RPC --- (let [owned-organizations-ref (atom nil)] @@ -515,7 +712,7 @@ :get-owned-organizations @owned-organizations-ref nil))} ;; --- Worker mock: capture delete-task submissions --- - wrk-mock {:target 'app.worker/submit! :return nil} + wrk-mock {:target 'app.jobs/submit :return nil} ;; --- Message bus mock: capture published events --- mbus-mock {:target 'app.msgbus/pub! :return nil}] @@ -669,6 +866,34 @@ (t/is (= :not-found (th/ex-type (:error ko-out)))) (t/is (= :profile-not-found (th/ex-code (:error ko-out))))))) +(t/deftest get-profiles-by-ids-existing-only-deduped-and-empty + (with-mocks [nitrate-mock {:target 'app.nitrate/call :return nil}] + (let [profile-a (th/create-profile* 1 {:is-active true + :fullname "Batch A"}) + profile-b (th/create-profile* 2 {:is-active true + :fullname "Batch B"}) + unknown (uuid/random) + params {::th/type :get-profiles-by-ids + ::rpc/profile-id (:id profile-a)} + out (th/management-command! (assoc params :ids [(:id profile-a) + (:id profile-b) + unknown])) + dup-out (th/management-command! (assoc params :ids [(:id profile-a) + (:id profile-a)])) + empty-out (th/management-command! (assoc params :ids []))] + (t/is (th/success? out)) + (t/is (= #{(:id profile-a) (:id profile-b)} + (set (map :id (:result out))))) + (t/is (= #{"Batch A" "Batch B"} + (set (map :name (:result out))))) + (t/is (every? #(contains? % :email) (:result out))) + + (t/is (th/success? dup-out)) + (t/is (= 1 (count (:result dup-out)))) + + (t/is (th/success? empty-out)) + (t/is (= [] (:result empty-out)))))) + (t/deftest get-organization-invitations-returns-valid-deduped-by-email ;; --- Deferred organization-summary: nil during setup, filled before RPC --- (let [organization-summary-ref (atom nil)] @@ -1762,8 +1987,8 @@ nil)) nil))} ;; --- Email mock: capture sent emails --- - email-mock {:target 'app.email/send! - :return (fn [params] (swap! sent conj params) nil)}] + email-mock {:target 'app.email/send-template + :return (fn [_cfg params] (swap! sent conj params) nil)}] ;; --- Setup: create profiles, team, organization-summary --- (let [owner (th/create-profile* 1 {:is-active true :fullname "Owner"}) @@ -1832,7 +2057,7 @@ emails)) (doseq [email-params @sent] (t/is (= organization-name (:organization-name email-params))) - (t/is (= eml/organization-setup-sso (::eml/factory email-params))))))))) + (t/is (= "organization-setup-sso" (::eml/template email-params))))))))) (t/deftest notify-organization-sso-change-skips-email-when-not-active (let [sent (atom []) @@ -1840,7 +2065,7 @@ :organization-id (uuid/random) :updated-props false :announce-activation false}] - (with-redefs [eml/send! (fn [params] (swap! sent conj params))] + (with-redefs [eml/send-template (fn [_cfg params] (swap! sent conj params))] (th/management-command! params)) (t/is (empty? @sent)))) @@ -2042,34 +2267,34 @@ (t/deftest send-renewal-email-falls-back-to-profile-fullname-when-name-is-nil ;; `nil` user-name means "no override": the RPC must look up the ;; account owner's real name instead of sending a blank greeting. - (with-mocks [email-mock {:target 'app.email/send! :return nil} + (with-mocks [email-mock {:target 'app.email/send-template :return nil} nitrate-mock {:target 'app.nitrate/call :return nil}] (let [profile (th/create-profile* 1 {:is-active true :fullname "Nitrate User"}) out (th/management-command! (send-renewal-email-params profile nil))] (t/is (th/success? out)) - (let [[params] (:call-args @email-mock)] + (let [[_cfg params] (:call-args @email-mock)] (t/is (= "Nitrate User" (:user-name params))))))) (t/deftest send-renewal-email-keeps-explicit-empty-name ;; An explicit "" means the caller deliberately wants no name shown ;; and must not be replaced by the profile's fullname. - (with-mocks [email-mock {:target 'app.email/send! :return nil} + (with-mocks [email-mock {:target 'app.email/send-template :return nil} nitrate-mock {:target 'app.nitrate/call :return nil}] (let [profile (th/create-profile* 1 {:is-active true :fullname "Nitrate User"}) out (th/management-command! (send-renewal-email-params profile ""))] (t/is (th/success? out)) - (let [[params] (:call-args @email-mock)] + (let [[_cfg params] (:call-args @email-mock)] (t/is (= "" (:user-name params))))))) (t/deftest send-renewal-email-treats-blank-name-as-empty ;; A blank name is trimmed and follows the same path as "": no name ;; is shown and the profile's fullname is not used. - (with-mocks [email-mock {:target 'app.email/send! :return nil} + (with-mocks [email-mock {:target 'app.email/send-template :return nil} nitrate-mock {:target 'app.nitrate/call :return nil}] (let [profile (th/create-profile* 1 {:is-active true :fullname "Nitrate User"}) out (th/management-command! (send-renewal-email-params profile " "))] (t/is (th/success? out)) - (let [[params] (:call-args @email-mock)] + (let [[_cfg params] (:call-args @email-mock)] (t/is (= "" (:user-name params))))))) (t/deftest upload-organization-logo-rejects-forged-content diff --git a/backend/test/backend_tests/rpc_management_shared_keys_test.clj b/backend/test/backend_tests/rpc_management_shared_keys_test.clj new file mode 100644 index 0000000000..ea78696293 --- /dev/null +++ b/backend/test/backend_tests/rpc_management_shared_keys_test.clj @@ -0,0 +1,49 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.rpc-management-shared-keys-test + "Covers the shared-key scope declared by every management RPC method." + (:require + [app.rpc :as-alias rpc] + [backend-tests.helpers :as th] + [clojure.test :as t])) + +(t/use-fixtures :once (t/compose-fixtures + (partial th/init-config [:enable-admin-console]) + th/init-system)) + +(def ^:private expected-shared-keys + {"app.rpc.management.exporter" #{:exporter :media-processor} + "app.rpc.management.files" #{:nexus} + "app.rpc.management.jobs" #{:exporter} + "app.rpc.management.nitrate" #{:admin-console}}) + +(t/deftest every-management-method-declares-its-shared-keys + (let [methods (get th/*system* :app.rpc/management-methods) + seen (into #{} (map (comp :ns first val)) methods)] + (t/is (seq methods)) + (t/is (= (set (keys expected-shared-keys)) seen) + "every management namespace must be scanned") + (doseq [[method-name [mdata _]] methods] + (let [ns-name (:ns mdata) + expected (get expected-shared-keys ns-name)] + (t/is (some? expected) + (str "unexpected management namespace for " method-name ": " ns-name)) + (t/is (= expected (::rpc/allowed-shared-keys mdata)) + (str "wrong ::rpc/allowed-shared-keys for " method-name " in " ns-name)))))) + +(t/deftest management-command-uses-the-key-from-the-request-metadata + (let [out (th/management-command! + (with-meta {::th/type :upload-tempfile} + {:app.http/request {:app.http/auth-key-id :nexus}}))] + (t/is (= :authorization (th/ex-type (:error out)))) + (t/is (= :shared-key-not-allowed (th/ex-code (:error out)))))) + +(t/deftest management-command-keeps-an-explicit-nil-key + (let [out (th/management-command! {::th/type :upload-tempfile + ::rpc/auth-key-id nil})] + (t/is (= :authorization (th/ex-type (:error out)))) + (t/is (= :shared-key-not-allowed (th/ex-code (:error out)))))) diff --git a/backend/test/backend_tests/rpc_management_test.clj b/backend/test/backend_tests/rpc_management_test.clj index a55dcf95a4..eec3aaabb9 100644 --- a/backend/test/backend_tests/rpc_management_test.clj +++ b/backend/test/backend_tests/rpc_management_test.clj @@ -82,6 +82,29 @@ (t/is (= :validation (th/ex-type (:error out)))) (t/is (= :media-type-not-allowed (th/ex-code (:error out)))))) +(t/deftest upload-tempfile-rejects-a-key-it-does-not-declare + (let [out (th/management-command! {::th/type :upload-tempfile + ::rpc/auth-key-id :nexus})] + (t/is (some? (:error out))) + (t/is (= :authorization (th/ex-type (:error out)))) + (t/is (= :shared-key-not-allowed (th/ex-code (:error out)))))) + +(t/deftest upload-tempfile-accepts-both-declared-keys + (let [profile (th/create-profile* 1 {:is-active true})] + (doseq [key-id [:exporter :media-processor]] + (let [path (fs/create-tempfile :dir "/tmp/penpot" + :prefix (str "test-upload-" (name key-id) "-")) + _ (io/write* path "content") + out (th/multipart-management-command! + {::th/type :upload-tempfile + ::rpc/auth-key-id key-id + ::rpc/profile-id (:id profile) + :content {:filename "export.png" + :path path + :mtype "image/png" + :size 7}})] + (t/is (nil? (:error out)) (name key-id)))))) + (t/deftest duplicate-file (let [storage (-> (:app.storage/storage th/*system*) (configure-storage-backend)) diff --git a/backend/test/backend_tests/rpc_nitrate_test.clj b/backend/test/backend_tests/rpc_nitrate_test.clj index 978a91ca19..abbfc679fc 100644 --- a/backend/test/backend_tests/rpc_nitrate_test.clj +++ b/backend/test/backend_tests/rpc_nitrate_test.clj @@ -1197,7 +1197,7 @@ :sso-active? true :set-team-params set-team-params}) teams/initialize-user-in-organization (fn [& _] nil) - eml/send! (fn [params] (swap! sent conj params))] + eml/send-template (fn [_cfg params] (swap! sent conj params))] (let [out (th/command! {::th/type :add-team-to-organization ::rpc/profile-id (:id owner) :team-id (:id team) @@ -1214,7 +1214,7 @@ (t/is (= #{"member302@example.com"} emails)) (doseq [email-params @sent] (t/is (= organization-name (:organization-name email-params))) - (t/is (= eml/organization-setup-sso (::eml/factory email-params))))))) + (t/is (= "organization-setup-sso" (::eml/template email-params))))))) (t/deftest add-team-to-organization-deletes-external-invitations-for-unregistered-users (let [owner (th/create-profile* 305 {:is-active true @@ -1328,7 +1328,7 @@ :team-id (:id team) :sso-active? false}) teams/initialize-user-in-organization (fn [& _] nil) - eml/send! (fn [params] (swap! sent conj params))] + eml/send-template (fn [_cfg params] (swap! sent conj params))] (let [out (th/command! {::th/type :add-team-to-organization ::rpc/profile-id (:id owner) :team-id (:id team) @@ -1336,6 +1336,181 @@ (t/is (th/success? out)) (t/is (empty? @sent)))))) +;; The move-teams tests below simulate a second session: Nitrate answers +;; with the permissions as they are at the moment of the call, as if the +;; organization owner had changed them after the user opened the modal. + +(defn- move-team-nitrate-mock + [{:keys [owner-id team-id source-organization perms-by-organization-id calls]}] + (fn [_cfg method params] + (swap! calls conj method) + (case method + :get-organization-membership {:is-member true :organization-id (:organization-id params)} + :get-organization-members [owner-id] + :get-team-organization {:organization source-organization} + :get-organization-permissions (get perms-by-organization-id (:organization-id params)) + :set-team-organization {:id team-id} + :get-organization-sso {:active false} + :get-organization-summary {:id (:organization-id params) :teams [{:id team-id}]} + nil))) + +(t/deftest remove-team-from-organization-follows-move-teams + (doseq [[i move-teams allowed?] [[401 "always" true] + [402 "myOrganizations" false] + [403 "never" false]]] + (t/testing move-teams + (let [owner (th/create-profile* i {:is-active true}) + team (th/create-team* i {:profile-id (:id owner)}) + organization-id (uuid/random) + calls (atom [])] + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call (move-team-nitrate-mock + {:owner-id (:id owner) + :team-id (:id team) + :perms-by-organization-id + {organization-id {:owner-id (uuid/random) + :permissions {:move-teams move-teams}}} + :calls calls})] + (let [out (th/command! {::th/type :remove-team-from-organization + ::rpc/profile-id (:id owner) + :team-id (:id team) + :organization-id organization-id + :organization-name "Org"})] + (if allowed? + (t/is (th/success? out)) + (t/is (= :not-allowed (th/ex-code (:error out))))) + (t/is (= allowed? + (contains? (set @calls) :remove-team-from-organization))))))))) + +(t/deftest add-team-to-organization-move-follows-move-teams + (doseq [[i desc move-teams same-owner? requester-owns-organization? allowed?] + [[411 "myOrganizations, same owner" "myOrganizations" true false true] + [412 "myOrganizations, other owner" "myOrganizations" false false false] + [413 "never" "never" true false false] + [414 "never, organization owner" "never" true true false]]] + (t/testing desc + (let [owner (th/create-profile* i {:is-active true}) + team (th/create-team* i {:profile-id (:id owner)}) + source-id (uuid/random) + target-id (uuid/random) + source-owner-id (if requester-owns-organization? (:id owner) (uuid/random)) + target-owner-id (if same-owner? source-owner-id (uuid/random)) + calls (atom [])] + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call (move-team-nitrate-mock + {:owner-id (:id owner) + :team-id (:id team) + :source-organization {:id source-id} + :perms-by-organization-id + {source-id {:owner-id source-owner-id + :permissions {:create-teams "any" + :move-teams move-teams}} + target-id {:owner-id target-owner-id + :permissions {:create-teams "any" + :move-teams "always"}}} + :calls calls}) + teams/initialize-user-in-organization (fn [& _] nil)] + (let [out (th/command! {::th/type :add-team-to-organization + ::rpc/profile-id (:id owner) + :team-id (:id team) + :organization-id target-id})] + (if allowed? + (t/is (th/success? out)) + (t/is (= :not-allowed (th/ex-code (:error out))))) + (t/is (= allowed? + (contains? (set @calls) :set-team-organization))))))))) + +(t/deftest remove-team-from-organization-follows-move-team-restriction + (doseq [[i restrictions-met allowed?] [[431 {:move-teams false} false] + [432 {:move-teams true} true]]] + (t/testing (str "move-teams restriction met=" (:move-teams restrictions-met)) + (let [owner (th/create-profile* i {:is-active true}) + team (th/create-team* i {:profile-id (:id owner)}) + organization-id (uuid/random) + perms-params (atom nil) + calls (atom [])] + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call + (fn [_cfg method params] + (swap! calls conj method) + (case method + :get-organization-membership {:is-member true :organization-id (:organization-id params)} + :get-organization-members [(:id owner)] + :get-team-organization {:organization nil} + :get-organization-permissions + (do + (reset! perms-params params) + {:owner-id (uuid/random) + :permissions {:move-teams "always"} + :restrictions-met restrictions-met}) + :remove-team-from-organization nil + nil))] + (let [out (th/command! {::th/type :remove-team-from-organization + ::rpc/profile-id (:id owner) + :team-id (:id team) + :organization-id organization-id + :organization-name "Org"})] + (if allowed? + (t/is (th/success? out)) + (t/is (= :not-allowed (th/ex-code (:error out))))) + ;; the caller's profile-id travels to the permissions lookup + (t/is (= (:id owner) (::rpc/profile-id @perms-params))) + (t/is (= allowed? + (contains? (set @calls) :remove-team-from-organization))))))))) + +(t/deftest add-team-to-organization-follows-move-and-create-restrictions + (doseq [[i desc source-met target-met allowed?] + [[441 "source move restriction not met" {:move-teams false} {:create-teams true} false] + [442 "target create restriction not met" {:move-teams true} {:create-teams false} false] + [443 "both met" {:move-teams true} {:create-teams true} true]]] + (t/testing desc + (let [owner (th/create-profile* i {:is-active true}) + team (th/create-team* i {:profile-id (:id owner)}) + source-id (uuid/random) + target-id (uuid/random) + org-owner-id (uuid/random) + perms-params (atom []) + calls (atom [])] + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call + (fn [_cfg method params] + (swap! calls conj method) + (case method + :get-organization-membership {:is-member true :organization-id (:organization-id params)} + :get-organization-members [(:id owner)] + :get-team-organization + (do + (swap! perms-params conj params) + {:organization {:id source-id}}) + :get-organization-permissions + (do + (swap! perms-params conj params) + (cond + (= (:organization-id params) source-id) + {:owner-id org-owner-id + :permissions {:create-teams "any" :move-teams "always"} + :restrictions-met source-met} + (= (:organization-id params) target-id) + {:owner-id org-owner-id + :permissions {:create-teams "any" :move-teams "always"} + :restrictions-met target-met})) + :set-team-organization {:id (:id team)} + :get-organization-sso {:active false} + :get-organization-summary {:id (:organization-id params) :teams [{:id (:id team)}]} + nil)) + teams/initialize-user-in-organization (fn [& _] nil)] + (let [out (th/command! {::th/type :add-team-to-organization + ::rpc/profile-id (:id owner) + :team-id (:id team) + :organization-id target-id})] + (if allowed? + (t/is (th/success? out)) + (t/is (= :not-allowed (th/ex-code (:error out))))) + ;; every gating lookup carries the caller's profile-id + (t/is (every? #(= (:id owner) (::rpc/profile-id %)) @perms-params)) + (t/is (= allowed? + (contains? (set @calls) :set-team-organization))))))))) + (t/deftest get-nitrate-activation-code-request (let [profile (th/create-profile* 1 {:is-active true}) nitrate-id "nitrate-instance-1" diff --git a/backend/test/backend_tests/rpc_plugins_test.clj b/backend/test/backend_tests/rpc_plugins_test.clj index 8c57931844..8d6be84dc6 100644 --- a/backend/test/backend_tests/rpc_plugins_test.clj +++ b/backend/test/backend_tests/rpc_plugins_test.clj @@ -47,6 +47,17 @@ (t/is (= [plugin-id-1] (:ids plugins))) (t/is (= valid-plugin (get-in plugins [:data plugin-id-1])))))) +(t/deftest add-profile-plugin-accepts-global-plugins + (let [profile (th/create-profile* 1) + plugin (assoc valid-plugin + :scope "global" + :permissions #{"content:read" "allow:global"}) + out (th/command! {::th/type :add-profile-plugin + ::rpc/profile-id (:id profile) + :plugin plugin})] + (t/is (nil? (:error out))) + (t/is (= plugin (:result out))))) + (t/deftest add-profile-plugin-rejects-invalid-permissions (let [profile (th/create-profile* 1) plugin (assoc valid-plugin :permissions #{"content:read" "admin:delete"}) diff --git a/backend/test/backend_tests/rpc_profile_test.clj b/backend/test/backend_tests/rpc_profile_test.clj index d2bc861ba0..2b4a3469c1 100644 --- a/backend/test/backend_tests/rpc_profile_test.clj +++ b/backend/test/backend_tests/rpc_profile_test.clj @@ -190,7 +190,7 @@ (t/is (nil? (:error out))) (t/is (= 1 (count (:result out))))) - (th/run-pending-tasks!) + (th/run-pending-jobs) (let [row (th/db-get :team {:id (:default-team-id prof)} @@ -357,7 +357,7 @@ (t/is (nil? (:error out))) (t/is (= 1 (count (:result out))))) - (th/run-pending-tasks!) + (th/run-pending-jobs) ;; execute permanent deletion task (let [result (th/run-task! :objects-gc {:min-age 0})] @@ -393,7 +393,7 @@ (t/is (nil? (:result out))) (t/is (nil? (:error out)))) - (th/run-pending-tasks!) + (th/run-pending-jobs) (let [rows (th/db-exec! ["select id,name,deleted_at from team where deleted_at is not null"])] (t/is (= 3 (count rows)))) @@ -508,8 +508,41 @@ (t/is (= "utma" (:penpot/utm-campaign props))) (t/is (= "mtma" (:penpot/mtm-campaign props))))))) +(defn- prepare-register-token + [email] + (-> (th/command! {::th/type :prepare-register-profile + :email email + :fullname "foobar" + :password "Foobar12!"}) + (get-in [:result :token]))) + +(t/deftest register-profile-defaults-to-wasm-renderer + (let [token (prepare-register-token "user@example.com") + result (th/command-raw! {::th/type :register-profile + :token token}) + props (:app.loggers.audit/replace-props (meta result))] + (t/is (= :wasm (:renderer-default props))) + + (let [profile (-> (th/db-get :profile {:email "user@example.com"}) + (profile/decode-row))] + (t/is (= :wasm (get-in profile [:props :renderer])))))) + +(t/deftest register-profile-retry-has-no-renderer-default + (with-redefs [cf/flags (conj cf/flags :email-verification)] + (th/create-profile* 1 {:is-active false}) + (let [token (prepare-register-token "profile1.test@nodomain.com") + result (th/command-raw! {::th/type :register-profile + :token token}) + props (:app.loggers.audit/replace-props (meta result))] + (t/is (map? props)) + (t/is (not (contains? props :renderer-default)))))) + +(t/deftest create-profile-keeps-given-renderer + (let [profile (th/create-profile* 1 {:props {:renderer :svg}})] + (t/is (= :svg (get-in profile [:props :renderer]))))) + (t/deftest prepare-register-and-register-profile-2 - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [current-token (atom nil)] ;; PREPARE REGISTER (let [data {::th/type :prepare-register-profile @@ -563,7 +596,7 @@ (t/is (= 1 (:call-count @mock)))))))) (t/deftest prepare-register-and-register-profile-3 - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [current-token (atom nil)] ;; PREPARE REGISTER (let [data {::th/type :prepare-register-profile @@ -617,7 +650,7 @@ ;; When disable-email-verification is set and the profile is inactive ;; (e.g. created before the flag was set), re-registering should be ;; rejected with :email-already-exists. - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (with-redefs [app.config/flags #{:registration :login-with-password}] (let [current-token (atom nil)] ;; PREPARE REGISTER: first attempt (no profile exists yet) @@ -711,7 +744,7 @@ (t/deftest prepare-register-with-invitation-and-disabled-registration (with-redefs [app.config/flags #{:login-with-password :email-verification}] - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [email "invited@example.com" fixture (create-invitation-fixture email (ct/in-future "48h")) itoken (create-test-invitation-token fixture email) @@ -852,7 +885,7 @@ ;; verify-email mail with the invitation token EMBEDDED into the ;; verify-email JWE (so the team-invitation flow can resume after ;; the user clicks the email link). - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [itoken (tokens/generate th/*system* {:iss :team-invitation :exp (ct/in-future "48h") @@ -1197,7 +1230,7 @@ "prepare-register must not embed existing profile id of an anonymous caller")))) (t/deftest register-profile-with-invitation-must-not-take-over-existing-account - (with-mocks [_mock {:target 'app.email/send! :return nil}] + (with-mocks [_mock {:target 'app.email/send-template :return nil}] (let [;; Victim profile exists but is not yet active (e.g. registered ;; but has not clicked the verification link). This is the ;; remaining attack surface after fix 1b: `prepare-register` @@ -1305,7 +1338,7 @@ (t/is (true? (:is-active reloaded)))))) (t/deftest email-change-request - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile (th/create-profile* 1) pool (:app.db/pool th/*system*) data {::th/type :request-email-change @@ -1345,7 +1378,7 @@ (t/deftest email-change-request-without-smtp - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (with-redefs [app.config/flags #{}] (let [profile (th/create-profile* 1) pool (:app.db/pool th/*system*) @@ -1361,7 +1394,7 @@ (t/deftest request-profile-recovery - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile1 (th/create-profile* 1 {:is-active false}) profile2 (th/create-profile* 2 {:is-active true}) pool (:app.db/pool th/*system*) @@ -1419,7 +1452,7 @@ (t/deftest update-profile-password - (with-mocks [_ {:target 'app.email/send! :return nil}] + (with-mocks [_ {:target 'app.email/send-template :return nil}] (let [profile (th/create-profile* 1) data {::th/type :update-profile-password ::rpc/profile-id (:id profile) @@ -1431,7 +1464,7 @@ (t/deftest update-profile-password-bad-old-password - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile (th/create-profile* 1) data {::th/type :update-profile-password ::rpc/profile-id (:id profile) @@ -1718,7 +1751,7 @@ (t/is (th/ex-of-code? (:error out) :weak-password)))) (t/deftest update-profile-password-sends-notification - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile (th/create-profile* 1) data {::th/type :update-profile-password ::rpc/profile-id (:id profile) @@ -1728,14 +1761,14 @@ (t/is (nil? (:error out))) (t/is (nil? (:result out))) (t/is (= 1 (:call-count @mock))) - (let [{:keys [::eml/factory :to :name]} (first (:call-args-list @mock))] - (t/is (= eml/password-changed factory)) + (let [[_cfg {:keys [::eml/template :to :name]}] (first (:call-args-list @mock))] + (t/is (= "password-changed" template)) (t/is (= (:email profile) to)) (t/is (= (:fullname profile) name)))))) (t/deftest update-profile-password-sends-notification-for-first-password - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile (th/create-profile* 1 {:password "!"}) data {::th/type :update-profile-password ::rpc/profile-id (:id profile) @@ -1744,14 +1777,14 @@ (t/is (nil? (:error out))) (t/is (nil? (:result out))) (t/is (= 1 (:call-count @mock))) - (let [{:keys [::eml/factory :to :name]} (first (:call-args-list @mock))] - (t/is (= eml/password-changed factory)) + (let [[_cfg {:keys [::eml/template :to :name]}] (first (:call-args-list @mock))] + (t/is (= "password-changed" template)) (t/is (= (:email profile) to)) (t/is (= (:fullname profile) name)))))) (t/deftest recover-profile-sends-notification - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile (th/create-profile* 1) token (tokens/generate th/*system* {:iss :password-recovery @@ -1763,8 +1796,8 @@ out (th/command! data)] (t/is (nil? (:error out))) (t/is (= 1 (:call-count @mock))) - (let [{:keys [::eml/factory :to :name]} (first (:call-args-list @mock))] - (t/is (= eml/password-changed factory)) + (let [[_cfg {:keys [::eml/template :to :name]}] (first (:call-args-list @mock))] + (t/is (= "password-changed" template)) (t/is (= (:email profile) to)) (t/is (= (:fullname profile) name)))))) diff --git a/backend/test/backend_tests/rpc_quotes_test.clj b/backend/test/backend_tests/rpc_quotes_test.clj index 03e0e05389..cd49f85042 100644 --- a/backend/test/backend_tests/rpc_quotes_test.clj +++ b/backend/test/backend_tests/rpc_quotes_test.clj @@ -6,9 +6,12 @@ (ns backend-tests.rpc-quotes-test (:require + [app.common.time :as ct] [app.common.uuid :as uuid] + [app.config :as cf] [app.db :as db] [app.http :as http] + [app.jobs :as jobs] [app.rpc :as-alias rpc] [app.rpc.cond :as cond] [app.rpc.quotes :as-alias quotes] @@ -565,3 +568,79 @@ (t/is (true? (:result out))) (check-snapshot-ok profile file "version4"))))) + +(t/deftest binfile-jobs-per-profile-quote + (with-mocks [mock {:target 'app.config/get + :return (th/config-get-mock + {:quotes-export-jobs-per-profile 2 + :quotes-import-jobs-per-profile 1})}] + + (let [profile (th/create-profile* 1) + other (th/create-profile* 2) + + insert! (fn [profile-id name status & {:keys [tenant]}] + (th/db-insert! :job {:id (uuid/next) + :name name + :tenant (or tenant (cf/get :tenant)) + :queue "binfile" + :params (db/json {}) + :priority 100 + :max-retries 0 + :retry-num 0 + :status status + :profile-id profile-id + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)})) + + check! (fn check! + ([id profile-id] (check! th/*system* id profile-id)) + ([cfg id profile-id] + (quotes/check! cfg {::quotes/id id + ::quotes/profile-id profile-id}))) + + error (fn [& args] + (try (apply check! args) nil (catch Throwable cause cause)))] + + (t/testing "a profile without jobs is under the quote" + (t/is (nil? (check! ::quotes/export-jobs-per-profile (:id profile))))) + + (t/testing "the jobs of another profile do not count" + (insert! (:id other) "export-binfile" "new") + (insert! (:id profile) "export-binfile" "new") + (t/is (nil? (check! ::quotes/export-jobs-per-profile (:id profile))))) + + (t/testing "the jobs of another tenant do not count either" + (insert! (:id profile) "export-binfile" "new" :tenant "another-tenant") + (t/is (nil? (check! ::quotes/export-jobs-per-profile (:id profile))))) + + (t/testing "a job that already reached a terminal state does not count" + (insert! (:id profile) "export-binfile" "completed") + (insert! (:id profile) "export-binfile" "failed") + (insert! (:id profile) "export-binfile" "cancelled") + (t/is (nil? (check! ::quotes/export-jobs-per-profile (:id profile))))) + + (t/testing "the job that would go over the limit is rejected" + (insert! (:id profile) "export-binfile" "running") + (let [cause (error ::quotes/export-jobs-per-profile (:id profile))] + (t/is (some? cause)) + (t/is (= :restriction (:type (ex-data cause)))) + (t/is (= :max-quote-reached (:code (ex-data cause)))) + (t/is (= "export-jobs-per-profile" (:target (ex-data cause)))))) + + (t/testing "the families are independent" + (t/is (nil? (check! ::quotes/import-jobs-per-profile (:id profile)))) + (insert! (:id profile) "import-binfile" "new") + (t/is (some? (error ::quotes/import-jobs-per-profile (:id profile))))) + + (t/testing "a new name of the family is covered by the registry" + (let [future (th/create-profile* 3) + defs (assoc (::jobs/defs th/*system*) + :export-assets {::jobs/name :export-assets + ::jobs/family :export}) + cfg (assoc th/*system* ::jobs/defs defs)] + (insert! (:id future) "export-assets" "new") + (t/is (nil? (check! cfg ::quotes/export-jobs-per-profile (:id future)))) + (insert! (:id future) "export-assets" "new") + (t/is (some? (error cfg ::quotes/export-jobs-per-profile (:id future))))))))) + diff --git a/backend/test/backend_tests/rpc_team_test.clj b/backend/test/backend_tests/rpc_team_test.clj index 10638cee1d..277228ffcd 100644 --- a/backend/test/backend_tests/rpc_team_test.clj +++ b/backend/test/backend_tests/rpc_team_test.clj @@ -15,6 +15,7 @@ [app.db :as db] [app.email.blacklist :as email.blacklist] [app.http :as http] + [app.loggers.audit :as-alias audit] [app.nitrate :as nitrate] [app.rpc :as-alias rpc] [app.rpc.commands.teams :as teams] @@ -30,7 +31,7 @@ (t/use-fixtures :each th/database-reset) (t/deftest create-team-invitations - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile1 (th/create-profile* 1 {:is-active true}) profile2 (th/create-profile* 2 {:is-active true}) profile3 (th/create-profile* 3 {:is-active true :is-muted true}) @@ -109,7 +110,7 @@ (t/is (= :member-is-muted (:code edata)))))))) (t/deftest create-and-update-team-invitations-include-organization-props - (with-mocks [email-mock {:target 'app.email/send! :return nil} + (with-mocks [email-mock {:target 'app.email/send-template :return nil} audit-mock {:target 'app.loggers.audit/submit :return nil}] (let [owner (th/create-profile* 101 {:is-active true}) invitee (th/create-profile* 102 {:is-active true}) @@ -169,8 +170,122 @@ (t/is (false? (get-in create-plain [:props :adds-invitee-to-organization]))) (t/is (false? (get-in create-plain [:props :invitee-already-organization-member]))))))) +(t/deftest create-team-invitations-follows-send-invitations-restriction + (with-mocks [email-mock {:target 'app.email/send :return nil} + audit-mock {:target 'app.loggers.audit/submit :return nil}] + (let [org-owner (th/create-profile* 420 {:is-active true}) + admin (th/create-profile* 421 {:is-active true}) + invitee (th/create-profile* 422 {:is-active true}) + team (th/create-team* 420 {:profile-id (:id org-owner)}) + _ (th/create-team-role* {:team-id (:id team) + :profile-id (:id admin) + :role :admin}) + organization-id (uuid/random) + team-org-params (atom nil) + make-organization + (fn [restrictions-met] + {:id organization-id + :name "Acme" + :slug "acme" + :owner-id (:id org-owner) + :avatar-bg-url "https://example.com/avatar.svg" + :permissions {:send-invitations "ownersAndAdmins" + :new-team-members "anyone"} + :restrictions-met restrictions-met}) + make-nitrate-call + (fn [restrictions-met] + (fn [_cfg method params] + (case method + :get-team-organization + (do + (reset! team-org-params params) + (if (= (:team-id params) (:id team)) + {:organization (make-organization restrictions-met) :is-your-penpot false} + {:organization nil :is-your-penpot false})) + + :get-organization-members + [(:id invitee)] + + nil))) + invite! + (fn [] + (th/command! {::th/type :create-team-invitations + ::rpc/profile-id (:id admin) + :team-id (:id team) + :role :editor + :emails [(:email invitee)]}))] + + ;; Restriction not met: a team admin (not the org owner) is denied. + (with-redefs [cf/flags (conj cf/flags :admin-console :email-verification) + nitrate/call (make-nitrate-call {:send-invitations false})] + (let [out (invite!)] + (t/is (not (th/success? out))) + (t/is (= :insufficient-permissions (th/ex-code (:error out)))) + ;; the caller's profile-id travels to Nitrate on the team lookup + (t/is (= (:id admin) (::rpc/profile-id @team-org-params))))) + + ;; Restriction met: the same team admin can invite. + (with-redefs [cf/flags (conj cf/flags :admin-console :email-verification) + nitrate/call (make-nitrate-call {:send-invitations true})] + (t/is (th/success? (invite!))))))) + +(t/deftest create-team-invitations-fails-closed-when-organization-lookup-fails + (with-mocks [email-mock {:target 'app.email/send :return nil} + audit-mock {:target 'app.loggers.audit/submit :return nil}] + (let [owner (th/create-profile* 430 {:is-active true}) + admin (th/create-profile* 431 {:is-active true}) + invitee (th/create-profile* 432 {:is-active true}) + team (th/create-team* 430 {:profile-id (:id owner)}) + _ (th/create-team-role* {:team-id (:id team) + :profile-id (:id admin) + :role :admin}) + make-nitrate-call + (fn [get-team-organization] + (fn [_cfg method params] + (case method + :get-team-organization (get-team-organization params) + nil))) + invite! + (fn [] + (th/command! {::th/type :create-team-invitations + ::rpc/profile-id (:id admin) + :team-id (:id team) + :role :editor + :emails [(:email invitee)]}))] + + ;; A 4xx other than 404 must not look like a team without organization. + (with-redefs [cf/flags (conj cf/flags :admin-console :email-verification) + nitrate/call (make-nitrate-call + (fn [_] + (throw (ex-info "forbidden" + {:type :nitrate-http-error + :status 403}))))] + (let [out (invite!)] + (t/is (not (th/success? out))) + (t/is (= :nitrate-http-error (th/ex-type (:error out)))))) + + ;; An invalid response body (the client returns nil) is rejected too. + (with-redefs [cf/flags (conj cf/flags :admin-console :email-verification) + nitrate/call (make-nitrate-call (constantly nil))] + (let [out (invite!)] + (t/is (not (th/success? out))) + (t/is (= :nitrate-unavailable (th/ex-type (:error out)))))) + + ;; A 404 confirms the team has no organization: team-level rules apply. + (let [lookups (atom [])] + (with-redefs [cf/flags (conj cf/flags :admin-console :email-verification) + nitrate/call (make-nitrate-call + (fn [params] + (swap! lookups conj params) + (throw (ex-info "not found" + {:type :nitrate-http-error + :status 404}))))] + (t/is (th/success? (invite!))) + ;; one strict lookup serves every organization rule + (t/is (= [true] (mapv :throw-on-error? @lookups)))))))) + (t/deftest create-team-invitations-blacklisted-domain - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile1 (th/create-profile* 1 {:is-active true}) team (th/create-team* 1 {:profile-id (:id profile1)}) data {::th/type :create-team-invitations @@ -210,7 +325,7 @@ (t/is (= 1 (:call-count @mock)))))))) (t/deftest create-team-invitations-with-request-access - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile1 (th/create-profile* 1 {:is-active true}) requester (th/create-profile* 2 {:is-active true :email "requester@example.com"}) @@ -247,7 +362,7 @@ (t/deftest create-team-invitations-with-request-access-2 - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile1 (th/create-profile* 1 {:is-active true}) requester (th/create-profile* 2 {:is-active true :email "requester@example.com"}) @@ -310,7 +425,7 @@ (t/deftest invitation-tokens - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile1 (th/create-profile* 1 {:is-active true}) profile2 (th/create-profile* 2 {:is-active true}) @@ -526,7 +641,8 @@ (t/is (th/success? out)) (t/is (not (contains? (:result out) :organization-invitation-audit))))) - (let [event (organization-event)] + (let [event (organization-event) + from-event (first (emitted-events "accept-organization-invitation-from"))] (t/is (= organization-id (get-in event [:props :organization-id]))) (t/is (= (:id invitee) (get-in event [:props :profile-id]))) (t/is (= (:id inviter) (get-in event [:props :invited-by]))) @@ -540,11 +656,39 @@ (t/is (not (contains? (:props event) :invitation-origin))) (t/is (not (contains? (:context event) :event-origin))) (t/is (= 1 (count (emitted-events "accept-organization-invitation")))) + (t/is (= 1 (count (emitted-events "accept-organization-invitation-from")))) + (t/is (= (:id inviter) (:profile-id from-event))) + (t/is (= (:props event) (:props from-event))) (t/is (not-any? #(contains? #{"accept-team-invitation" "accept-team-invitation-from"} (:name (second %))) (:call-args-list @audit-mock)))) + ;; Legacy org invitation with no :created-by → invitee event only, no -from. + (th/reset-mock! audit-mock) + (db/insert! (:app.db/pool th/*system*) + :team-invitation + {:org-id organization-id + :email-to (:email invitee) + :role "editor" + :valid-until (ct/in-future "48h")}) + + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call + (fn [_cfg method _params] + (case method + :get-organization-membership {:organization-id organization-id + :is-member false} + :get-organization-members [(:id inviter) (uuid/random) (uuid/random)] + nil)) + teams/initialize-user-in-organization + (fn [& _] default-team-id)] + (let [out (verify! direct-token)] + (t/is (th/success? out)))) + + (t/is (= 1 (count (emitted-events "accept-organization-invitation")))) + (t/is (empty? (emitted-events "accept-organization-invitation-from"))) + (th/reset-mock! audit-mock) (db/insert! (:app.db/pool th/*system*) :team-invitation @@ -568,8 +712,9 @@ (t/is (th/success? out)) (t/is (not (contains? (:result out) :organization-invitation-audit))))) - (let [events (mapv second (:call-args-list @audit-mock)) - event (organization-event)] + (let [events (mapv second (:call-args-list @audit-mock)) + event (organization-event) + org-from-event (first (emitted-events "accept-organization-invitation-from"))] (t/is (some #(= "accept-team-invitation" (:name %)) events)) (t/is (some #(= "accept-team-invitation-from" (:name %)) events)) (t/is (= (:id team) (get-in event [:props :team-id]))) @@ -582,7 +727,10 @@ (t/is (= 5 (get-in event [:props :organization-member-count-before]))) (t/is (not (contains? (:props event) :invitation-origin))) (t/is (not (contains? (:context event) :event-origin))) - (t/is (= 1 (count (emitted-events "accept-organization-invitation"))))) + (t/is (= 1 (count (emitted-events "accept-organization-invitation")))) + (t/is (= 1 (count (emitted-events "accept-organization-invitation-from")))) + (t/is (= (:id inviter) (:profile-id org-from-event))) + (t/is (= (:props event) (:props org-from-event)))) (let [from-event (first (emitted-events "accept-team-invitation-from"))] (t/is (= (:id team) (get-in from-event [:props :team-id]))) @@ -622,7 +770,7 @@ (t/is (not-any? #(= "accept-organization-invitation" (:name %)) events)))))) (t/deftest create-team-invitations-with-email-verification-disabled - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile1 (th/create-profile* 1 {:is-active true}) profile2 (th/create-profile* 2 {:is-active true}) profile3 (th/create-profile* 3 {:is-active true :is-muted true}) @@ -796,6 +944,41 @@ :organization))))))) +(t/deftest get-teams-propagates-profile-id-and-restrictions-met + (let [profile (th/create-profile* 1 {:is-active true}) + organization-team (th/create-team* 1 {:profile-id (:id profile)}) + organization-id (uuid/random) + calls (atom []) + restrictions-met {:create-teams true :move-teams false :send-invitations true} + organization {:id organization-id + :name "Acme" + :slug "acme" + :owner-id (:id profile) + :avatar-bg-url "https://example.com/avatar.svg" + :restrictions-met restrictions-met} + nitrate-call (fn [_cfg method params] + (swap! calls conj [method params]) + [{:id (:id organization-team) + :is-your-penpot false + :organization organization}]) + params {::th/type :get-teams + ::rpc/profile-id (:id profile)}] + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call nitrate-call] + (let [out (th/command! params) + teams (:result out) + team (first (filter #(= (:id organization-team) (:id %)) teams))] + (t/is (th/success? out)) + ;; The batched lookup carries the caller's profile-id so Nitrate can + ;; answer :restrictions-met for this profile. + (t/is (= :get-teams-organizations (ffirst @calls))) + (t/is (= (:id profile) (-> @calls first second ::rpc/profile-id))) + ;; :restrictions-met flows through apply-organization into the nested + ;; :organization map (organization->team-keys). + (t/is (= restrictions-met + (get-in team [:organization :restrictions-met]))))))) + + (t/deftest get-teams-rejects-invalid-organization-batch-response (let [profile (th/create-profile* 1 {:is-active true}) calls (atom []) @@ -853,7 +1036,7 @@ (t/is (= 1 (count result))) (t/is (= (:default-team-id profile1) (get-in result [0 :id]))))) - (th/run-pending-tasks!) + (th/run-pending-jobs) ;; run permanent deletion (should be noop) (let [result (th/run-task! :objects-gc {})] @@ -921,7 +1104,7 @@ #_(th/print-result! out) (t/is (nil? (:error out)))) - (th/run-pending-tasks!) + (th/run-pending-jobs) (let [rows (th/db-exec! ["select * from team where id = ?" (:id team)])] (t/is (= 1 (count rows))) @@ -931,8 +1114,40 @@ (let [result (th/run-task! :objects-gc {})] (t/is (= 7 (:processed result))))))) +(t/deftest delete-team-audit-includes-organization-id + ;; Assert result metadata directly: wrap-audit is inactive in the + ;; default test flags, so mocking audit/submit would never see this. + (let [owner (th/create-profile* 1 {:is-active true}) + team (th/create-team* 1 {:profile-id (:id owner)}) + organization-id (uuid/next) + organization {:id organization-id + :name "Acme" + :slug "acme" + :owner-id (:id owner) + :avatar-bg-url "https://example.com/avatar.svg" + :permissions {:delete-teams "onlyOwners"}}] + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call + (fn [_cfg method params] + (case method + :get-team-organization + (when (= (:id team) (:team-id params)) + {:id (:id team) + :is-your-penpot false + :organization organization}) + :delete-team nil + nil))] + (let [result (db/tx-run! th/*system* + (fn [cfg] + (teams/delete-team cfg {:team-id (:id team) + :profile-id (:id owner)}))) + props (::audit/props (meta result))] + (t/is (= organization-id (:organization-id props))) + (t/is (= (:id team) (:team-id props))) + (t/is (= (:name team) (:team-name props))))))) + (t/deftest create-team-access-request - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [owner (th/create-profile* 1 {:is-active true :email "owner@bar.com"}) requester (th/create-profile* 3 {:is-active true :email "requester@bar.com"}) team (th/create-team* 1 {:profile-id (:id owner)}) @@ -979,7 +1194,7 @@ (t/deftest create-team-access-request-owner-muted - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [owner (th/create-profile* 1 {:is-active true :is-muted true :email "owner@bar.com"}) requester (th/create-profile* 2 {:is-active true :email "requester@bar.com"}) team (th/create-team* 1 {:profile-id (:id owner)}) @@ -998,7 +1213,7 @@ (t/deftest create-team-access-request-requester-muted - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [owner (th/create-profile* 1 {:is-active true :email "owner@bar.com"}) requester (th/create-profile* 2 {:is-active true :is-muted true :email "requester@bar.com"}) team (th/create-team* 1 {:profile-id (:id owner)}) @@ -1024,7 +1239,7 @@ (t/deftest create-team-access-request-owner-bounce - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [owner (th/create-profile* 1 {:is-active true :email "owner@bar.com"}) requester (th/create-profile* 2 {:is-active true :email "requester@bar.com"}) team (th/create-team* 1 {:profile-id (:id owner)}) @@ -1052,7 +1267,7 @@ (t/is (= "private" (:email edata))))))) (t/deftest create-team-access-request-requester-bounce - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [owner (th/create-profile* 1 {:is-active true :email "owner@bar.com"}) requester (th/create-profile* 2 {:is-active true :email "requester@bar.com"}) team (th/create-team* 1 {:profile-id (:id owner)}) @@ -1109,7 +1324,7 @@ (t/is (th/success? out))))) (t/deftest create-team-invitations-email-cooldown - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [profile1 (th/create-profile* 1 {:is-active true}) team (th/create-team* 1 {:profile-id (:id profile1)}) @@ -1240,10 +1455,66 @@ (t/is (uuid? (:id team))) (t/is (= "Test Team" (:name team))))))))) +(t/deftest create-team-in-organization-follows-attribute-restriction + (with-mocks [audit-mock {:target 'app.loggers.audit/submit :return nil}] + (let [org-owner (th/create-profile* 410 {:is-active true}) + member (th/create-profile* 411 {:is-active true}) + organization-id (uuid/random) + perms-params (atom nil) + make-nitrate-call + (fn [restrictions-met] + (fn [_cfg method p] + (case method + :get-organization-membership + {:organization-id organization-id :is-member true} + + :get-organization-permissions + (do + (reset! perms-params p) + {:owner-id (:id org-owner) + :permissions {:create-teams "any"} + :restrictions-met restrictions-met}) + + :set-team-organization + (let [team-id (:team-id p)] + {:id team-id + :name "Test Team" + :organization-id organization-id + :default-project-id (uuid/random)}) + + nil))) + create! + (fn [profile-id] + (th/command! {::th/type :create-team + ::rpc/profile-id profile-id + :name "Test Team" + :organization-id organization-id}))] + + ;; A non-owner member is denied when the create-teams restriction is not met + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call (make-nitrate-call {:create-teams false})] + (let [out (create! (:id member))] + (t/is (not (th/success? out))) + (t/is (= :not-allowed (th/ex-code (:error out)))) + ;; the caller's profile-id travels to Nitrate so it can evaluate the restriction + (t/is (= (:id member) (::rpc/profile-id @perms-params))))) + + ;; The same member is allowed when the restriction is met + (th/reset-mock! audit-mock) + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call (make-nitrate-call {:create-teams true})] + (t/is (th/success? (create! (:id member))))) + + ;; The organization owner keeps the create-teams bypass even when not met + (th/reset-mock! audit-mock) + (with-redefs [cf/flags (conj cf/flags :admin-console) + nitrate/call (make-nitrate-call {:create-teams false})] + (t/is (th/success? (create! (:id org-owner)))))))) + ;; --- T7-F-01: Role ceiling in team invitations --- (t/deftest admin-cannot-create-invitation-with-owner-role - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [owner (th/create-profile* 1 {:is-active true}) admin (th/create-profile* 2 {:is-active true}) team (th/create-team* 1 {:profile-id (:id owner)})] @@ -1267,7 +1538,7 @@ (t/is (= 0 (:call-count @mock))))))) (t/deftest admin-cannot-create-invitation-with-owner-role-invitations-format - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [owner (th/create-profile* 1 {:is-active true}) admin (th/create-profile* 2 {:is-active true}) team (th/create-team* 1 {:profile-id (:id owner)})] @@ -1290,7 +1561,7 @@ (t/is (= 0 (:call-count @mock))))))) (t/deftest admin-cannot-update-invitation-role-to-owner - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [owner (th/create-profile* 1 {:is-active true}) admin (th/create-profile* 2 {:is-active true}) team (th/create-team* 1 {:profile-id (:id owner)})] @@ -1324,7 +1595,7 @@ (t/is (th/ex-of-code? (:error out) :cant-promote-to-owner)))))) (t/deftest owner-can-create-invitation-with-owner-role - (with-mocks [mock {:target 'app.email/send! :return nil}] + (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [owner (th/create-profile* 1 {:is-active true}) team (th/create-team* 1 {:profile-id (:id owner)})] @@ -1921,7 +2192,7 @@ ;; The team is created by an internal call, so both the command event and ;; the manual event must carry its id for a projection to key it. (with-mocks [audit-mock {:target 'app.loggers.audit/submit :return nil} - email-mock {:target 'app.email/send! :return nil}] + email-mock {:target 'app.email/send-template :return nil}] (with-redefs [cf/flags (conj cf/flags :audit-log)] (let [profile (th/create-profile* 1 {:is-active true}) out (th/command-raw! {::th/type :create-team-with-invitations diff --git a/backend/test/backend_tests/rpc_test.clj b/backend/test/backend_tests/rpc_test.clj index 233ffdd8e2..0be65dd983 100644 --- a/backend/test/backend_tests/rpc_test.clj +++ b/backend/test/backend_tests/rpc_test.clj @@ -7,6 +7,7 @@ (ns backend-tests.rpc-test (:require [app.common.exceptions :as ex] + [app.common.logging :as l] [app.common.schema :as sm] [app.common.time :as ct] [app.common.uuid :as uuid] @@ -902,6 +903,113 @@ (t/is (some? ex)) (t/is (th/ex-of-type? ex :not-found)))) +;; --- TOKEN TYPE RESTRICTIONS + +(t/deftest mcp-token-authentication + (let [pid (uuid/custom 1 1) + method (fn [mdata] + (rpc/make-rpc-handler + {:test-method [mdata (partial (#'rpc/wrap-authentication + nil (fn [_ params] params) mdata) + nil)]})) + handler (method {::rpc/auth-type :token ::rpc/token-type "mcp"})] + (doseq [extras [{::session/profile-id pid} + {::actoken/profile-id pid} + {::actoken/profile-id pid ::actoken/type "personal"}]] + (t/is (th/ex-of-code? (call-ex handler {:extras extras}) :token-auth-required))) + (let [extras {::actoken/profile-id pid ::actoken/type "mcp"}] + (t/is (= "mcp" (::rpc/token-type (call-body handler {:extras extras})))) + (t/is (th/ex-of-code? (call-ex (method {}) {:extras extras}) :token-auth-required))) + (t/is (= pid (::rpc/profile-id + (call-body (method {}) {:extras {::actoken/profile-id pid + ::actoken/type "personal"}})))))) + +;; --- SHARED KEY AUTHORIZATION + +(defn- call-shared-key-wrapper + [mdata params] + (let [wrapped (#'rpc/wrap-shared-key-authorization + nil (fn [_ params] params) mdata)] + (wrapped nil params))) + +(t/deftest shared-key-authorization-allows-declared-keys + (let [mdata {::rpc/allowed-shared-keys #{:exporter :media-processor}}] + (t/is (= :exporter + (::rpc/auth-key-id + (call-shared-key-wrapper mdata {::rpc/auth-key-id :exporter})))) + (t/is (= :media-processor + (::rpc/auth-key-id + (call-shared-key-wrapper mdata {::rpc/auth-key-id :media-processor})))))) + +(t/deftest shared-key-authorization-rejects-other-key + (let [ex (try (call-shared-key-wrapper {::rpc/allowed-shared-keys #{:exporter}} + {::rpc/auth-key-id :nexus}) + (catch Exception e e))] + (t/is (th/ex-of-type? ex :authorization)) + (t/is (th/ex-of-code? ex :shared-key-not-allowed)))) + +(t/deftest shared-key-authorization-rejects-missing-key + (let [ex (try (call-shared-key-wrapper {::rpc/allowed-shared-keys #{:exporter}} {}) + (catch Exception e e))] + (t/is (th/ex-of-type? ex :authorization)) + (t/is (th/ex-of-code? ex :shared-key-not-allowed)))) + +(t/deftest shared-key-authorization-without-metadata-allows-any-key + (t/is (= :nexus + (::rpc/auth-key-id + (call-shared-key-wrapper {} {::rpc/auth-key-id :nexus})))) + (t/is (nil? (::rpc/auth-key-id (call-shared-key-wrapper {} {}))))) + +(t/deftest shared-key-authorization-empty-set-denies-all + (let [ex (try (call-shared-key-wrapper {::rpc/allowed-shared-keys #{}} + {::rpc/auth-key-id :exporter}) + (catch Exception e e))] + (t/is (th/ex-of-type? ex :authorization)) + (t/is (th/ex-of-code? ex :shared-key-not-allowed)))) + +(t/deftest shared-key-authorization-logs-the-denied-key + (let [records (atom []) + watch (fn [_ _ _ record] (swap! records conj record))] + (add-watch l/log-record ::shared-key-denied watch) + (try + (try (call-shared-key-wrapper {::rpc/allowed-shared-keys #{:exporter}} + {::rpc/auth-key-id :nexus + ::rpc/handler-name "test-method"}) + (catch Exception _)) + (let [deadline (+ (System/currentTimeMillis) 2000) + logged? (fn [] + (some #(and (= :warn (::l/level %)) + (= "shared key is not allowed for this method" + (get (::l/props %) :hint)) + (= :nexus (get (::l/props %) :key-id)) + (= "test-method" (get (::l/props %) :handler-name))) + @records))] + (loop [] + (when-not (or (logged?) + (>= (System/currentTimeMillis) deadline)) + (Thread/sleep 10) + (recur))) + (t/is (some? (logged?)))) + (finally + (remove-watch l/log-record ::shared-key-denied))))) + +(t/deftest shared-key-authorization-is-applied-to-rpc-requests + (let [mdata {::rpc/allowed-shared-keys #{:exporter}} + wrapped (partial (#'rpc/wrap-shared-key-authorization + nil (fn [_ params] params) mdata) + nil) + handler (make-handler {:shared-key-test [mdata wrapped]})] + (t/is (= :exporter + (::rpc/auth-key-id + (call-body handler + {:path-params {:method-name "shared-key-test"} + :extras {::http/auth-key-id :exporter}})))) + (t/is (th/ex-of-code? + (call-ex handler + {:path-params {:method-name "shared-key-test"} + :extras {::http/auth-key-id :nexus}}) + :shared-key-not-allowed)))) + ;; --- PARAMS VALIDATION: SERVER AUTH CONTEXT VS CLIENT PARAMS (t/deftest client-qualified-keys-do-not-override-server-auth-context @@ -915,7 +1023,8 @@ request {:params {:name "valid" :app.rpc/profile-id attacker-pid :app.rpc/auth-type :token - :app.rpc/token-perms #{"admin"}}} + :app.rpc/token-perms #{"admin"} + :app.rpc/token-type "mcp"}} params (with-meta {::rpc/profile-id server-pid ::rpc/auth-type :session} {::http/request request})] @@ -923,6 +1032,7 @@ (t/is (= server-pid (::rpc/profile-id @captured))) (t/is (= :session (::rpc/auth-type @captured))) (t/is (not (contains? @captured ::rpc/token-perms))) + (t/is (not (contains? @captured ::rpc/token-type))) (t/is (= "valid" (:name @captured))))) ;; --- RESPONSE: NIL BODY WITH CUSTOM HEADERS diff --git a/backend/test/backend_tests/srepl_main_test.clj b/backend/test/backend_tests/srepl_main_test.clj index 3b44e7352b..9cbf5a39af 100644 --- a/backend/test/backend_tests/srepl_main_test.clj +++ b/backend/test/backend_tests/srepl_main_test.clj @@ -7,9 +7,9 @@ (ns backend-tests.srepl-main-test (:require [app.common.time :as ct] + [app.jobs :as jobs] [app.srepl.main :as srepl] [app.system :as sys] - [app.worker :as wrk] [backend-tests.helpers :as th] [clojure.test :as t] [mockery.core :refer [with-mocks]])) @@ -20,19 +20,19 @@ ;; --- HELPERS (defn- capture-submissions! - "Runs `f` with `wrk/submit!` replaced by a collector, and returns the option + "Runs `f` with `jobs/submit` replaced by a collector, and returns the option maps it was called with, in order." [f] (let [submitted (atom [])] - (with-redefs [wrk/submit! (fn [opts] + (with-redefs [jobs/submit (fn [_cfg opts] (swap! submitted conj opts) - (get-in opts [::wrk/params :id]))] + (get-in opts [::jobs/params :id]))] (f)) @submitted)) (defn- params-of [submitted] - (mapv ::wrk/params submitted)) + (mapv ::jobs/params submitted)) (defn- create-deleted-profile! [i deleted-at] @@ -60,7 +60,7 @@ (t/is (thrown? clojure.lang.ExceptionInfo (srepl/parse-emails 42)))) -(t/deftest cascade-enqueues-one-delete-object-task-per-deleted-row +(t/deftest cascade-enqueues-one-delete-object-job-per-deleted-row (let [deleted (create-deleted-profile! 991 (ct/in-past {:days 30})) _ (th/create-profile* 992) submitted (capture-submissions! @@ -70,19 +70,19 @@ (t/testing "only the soft-deleted row is processed" (t/is (= 1 (count submitted)))) - (t/testing "the enqueued task is a delete-object for that row" + (t/testing "the enqueued job is a delete-object for that row" (let [opts (first submitted)] - (t/is (= :delete-object (::wrk/task opts))) + (t/is (= :delete-object (::jobs/name opts))) (t/is (= {:object :profile :id (:id deleted)} - (select-keys (::wrk/params opts) [:object :id]))))) + (select-keys (::jobs/params opts) [:object :id]))))) (t/testing "the date is the row's own, not now" - (t/is (ct/is-before? (:deleted-at (::wrk/params (first submitted))) + (t/is (ct/is-before? (:deleted-at (::jobs/params (first submitted))) (ct/in-past {:days 29})))) (t/testing "label is the row id and dedupe is on" - (t/is (= (str (:id deleted)) (::wrk/label (first submitted)))) - (t/is (true? (::wrk/dedupe (first submitted))))))) + (t/is (= (str (:id deleted)) (::jobs/label (first submitted)))) + (t/is (true? (::jobs/dedupe (first submitted))))))) (t/deftest cascade-honours-limit-and-ids (let [p1 (create-deleted-profile! 993 (ct/in-past {:days 10})) @@ -105,7 +105,7 @@ (t/deftest cascade-dry-run-counts-without-enqueueing (let [_ (create-deleted-profile! 996 (ct/in-past {:days 10})) submitted (atom []) - result (with-redefs [wrk/submit! (fn [opts] (swap! submitted conj opts))] + result (with-redefs [jobs/submit (fn [_cfg opts] (swap! submitted conj opts))] (#'srepl/submit-deleted-cascade th/*system* {:table "profile" :object :profile :dry-run? true}))] @@ -157,7 +157,7 @@ ;; props is what makes the row attributable. Both insert and cascade are ;; mocked, so this has no side effects. (with-mocks [audit-mock {:target 'app.loggers.audit/insert :return nil} - wrk-mock {:target 'app.worker/invoke! :return nil}] + wrk-mock {:target 'app.jobs/invoke :return nil}] (let [profile (th/create-profile* 1 {:is-active true})] (srepl/delete-profile! (str (:id profile))) (let [events (->> (:call-args-list @audit-mock) @@ -173,7 +173,7 @@ (t/deftest delete-team-emits-identifiable-audit-event (with-mocks [audit-mock {:target 'app.loggers.audit/insert :return nil} - wrk-mock {:target 'app.worker/invoke! :return nil}] + wrk-mock {:target 'app.jobs/invoke :return nil}] (let [profile (th/create-profile* 1 {:is-active true}) team (th/create-team* 95 {:profile-id (:id profile)})] (srepl/delete-team! (str (:id team))) @@ -191,7 +191,7 @@ ;; SREPL has no actor, so the event profile stays zero; the subject id in ;; props is what makes the row attributable. (with-mocks [audit-mock {:target 'app.loggers.audit/insert :return nil} - wrk-mock {:target 'app.worker/invoke! :return nil}] + wrk-mock {:target 'app.jobs/invoke :return nil}] (with-redefs [sys/system th/*system*] (let [profile (th/create-profile* 1 {:is-active true})] (srepl/restore-profile! (str (:id profile))) diff --git a/backend/test/backend_tests/storage_metadata_test.clj b/backend/test/backend_tests/storage_metadata_test.clj index 4efb4c80df..c622bdae4b 100644 --- a/backend/test/backend_tests/storage_metadata_test.clj +++ b/backend/test/backend_tests/storage_metadata_test.clj @@ -259,7 +259,8 @@ (t/deftest metadata-buckets-is-the-canonical-set (t/is (= #{"file-media-object" "team-font-variant" "file-object-thumbnail" "file-thumbnail" "profile" "organization" "tempfile" - "upload-session" "file-data" "file-data-fragment" "file-change"} + "upload-session" "job-resource" "file-data" "file-data-fragment" + "file-change"} stsch/metadata-buckets))) (t/deftest decode-metadata-returns-nil-for-nil diff --git a/backend/test/backend_tests/storage_test.clj b/backend/test/backend_tests/storage_test.clj index 6257d3759c..3787db7c1f 100644 --- a/backend/test/backend_tests/storage_test.clj +++ b/backend/test/backend_tests/storage_test.clj @@ -16,6 +16,7 @@ [app.rpc :as-alias rpc] [app.storage :as sto] [app.storage.fs :as-alias sto.fs] + [app.storage.gc-deleted :as gcd] [app.storage.gc-touched :as gc-touched] [app.storage.impl :as impl] [app.storage.s3 :as-alias sto.s3] @@ -113,6 +114,44 @@ :content-type "text/plain"})] (t/is (not= (:id object1) (:id object2))))) +(t/deftest job-resource-objects-require-an-owner + (let [storage (-> (:app.storage/storage th/*system*) + (configure-storage-backend)) + content (sto/content "content")] + + (t/testing "an object of this bucket cannot be stored without its owner" + (t/is (thrown? Throwable + (sto/put-object! storage {::sto/content content + :bucket sto/job-resource-bucket + :content-type "text/plain"})))) + + (t/testing "and storing it with an owner still works" + (t/is (some? (sto/put-object! storage {::sto/content content + :bucket sto/job-resource-bucket + :profile-id (uuid/next) + :content-type "text/plain"})))))) + +(t/deftest job-resource-objects-are-not-deduplicated + ;; An object in this bucket belongs to one profile: sharing a blob + ;; between two owners would serve one user's artifact to another. + (let [storage (-> (:app.storage/storage th/*system*) + (configure-storage-backend)) + content (-> (sto/content "content") + (sto/wrap-with-hash "same-hash")) + object1 (sto/put-object! storage {::sto/content content + ::sto/deduplicate? true + ::sto/touched-at (ct/now) + :bucket sto/job-resource-bucket + :profile-id (uuid/next) + :content-type "text/plain"}) + object2 (sto/put-object! storage {::sto/content content + ::sto/deduplicate? true + ::sto/touched-at (ct/now) + :bucket sto/job-resource-bucket + :profile-id (uuid/next) + :content-type "text/plain"})] + (t/is (not= (:id object1) (:id object2))))) + (t/deftest put-and-retrieve-expired-object (let [storage (-> (:app.storage/storage th/*system*) (configure-storage-backend)) @@ -478,7 +517,7 @@ (t/is (= 0 (:freeze res))) (t/is (= 0 (:delete res))))) - ;; processed immediately with skip-delay + ;; processed immediately with skip-delay, bypassing min-age (binding [ct/*clock* (ct/fixed-clock now)] (let [res (th/run-task! :storage-gc-touched {:skip-delay true})] (t/is (= 0 (:freeze res))) @@ -642,6 +681,35 @@ (let [res (th/run-task! :storage-gc-deleted {})] (t/is (= 1 (:deleted res)))))) +(t/deftest storage-gc-deleted-commits-each-chunk-separately + (let [storage (-> (:app.storage/storage th/*system*) + (configure-storage-backend)) + ;; more than one chunk (chunk-size is 25); distinct contents so + ;; the storage layer does not deduplicate them into one row + ids (doall (for [i (range 26)] + (:id (sto/put-object! storage + {::sto/content (sto/content (str "content" i)) + :content-type "text/plain"}))))] + (t/is (= 26 (count ids))) + (th/db-exec! ["update storage_object set deleted_at = ?" (ct/now)]) + + (let [orig @#'gcd/process-chunk-deletion + calls (atom 0)] + (alter-var-root #'gcd/process-chunk-deletion + (constantly (fn [& args] + (when (= 2 (swap! calls inc)) + (throw (ex-info "boom" {}))) + (apply orig args)))) + (try + (let [result (th/run-task! :storage-gc-deleted {})] + (t/testing "the fault of the second chunk is skipped, the run finishes" + (t/is (= 25 (:deleted result))))) + + (t/testing "first chunk committed before the fault" + (t/is (= 1 (:cnt (th/db-exec-one! ["SELECT count(*) AS cnt FROM storage_object WHERE deleted_at IS NOT NULL"]))))) + (finally + (alter-var-root #'gcd/process-chunk-deletion (constantly orig))))))) + (t/deftest objects-gc-task-skip-delay (let [storage (-> (:app.storage/storage th/*system*) (configure-storage-backend)) @@ -679,9 +747,10 @@ (let [res (th/run-task! :objects-gc {})] (t/is (= 0 (:processed res)))) - ;; with skip-delay it is processed immediately + ;; with skip-delay the future deleted row IS processed immediately (let [res (th/run-task! :objects-gc {:skip-delay true})] - (t/is (= 1 (:processed res))))))) + (t/is (= 1 (:processed res))) + (t/is (nil? (th/db-get :file-media-object {:id (:id result-1)}))))))) (t/deftest put-object-write-failure-leaves-pending-row (let [storage (-> (:app.storage/storage th/*system*) @@ -1381,3 +1450,69 @@ (t/is (= "boom" (ex-message (ex-cause ex))))) ;; one initial attempt plus max-retries (t/is (= 4 (:call-count @mock))))) + +(t/deftest touched-gc-job-resource-bucket + (let [storage (-> (:app.storage/storage th/*system*) + (configure-storage-backend)) + job-id (uuid/next) + object (sto/put-object! storage {::sto/content (sto/content "content") + :content-type "text/plain" + :profile-id (uuid/next) + :bucket sto/job-resource-bucket})] + + ;; the object is created with the job-resource bucket in metadata + (t/is (= sto/job-resource-bucket (:bucket (meta object)))) + + ;; a live job row referencing the object keeps it frozen + (th/db-update! :storage-object {:touched-at (ct/now)} {:id (:id object)}) + (th/db-insert! :job {:id job-id + :name "test-job" + :tenant (cf/get :tenant) + :queue "default" + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status "running" + :resource-id (:id object) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + + (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:hours 3}))] + (th/run-task! :storage-gc-touched {}))] + (t/is (= 1 (:freeze res))) + (t/is (= 0 (:delete res))) + (t/is (nil? (:touched-at (th/db-get :storage-object {:id (:id object)} + :id :touched-at)))) + (t/is (nil? (:deleted-at (th/db-get :storage-object {:id (:id object)} + :id :deleted-at))))) + + ;; once the referencing row is gone, the touched object becomes + ;; eligible for deletion (the job table is deletion-protected) + (th/db-force-delete :job {:id job-id}) + (th/db-update! :storage-object {:touched-at (ct/now)} {:id (:id object)}) + + (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:hours 3}))] + (th/run-task! :storage-gc-touched {}))] + (t/is (= 0 (:freeze res))) + (t/is (= 1 (:delete res))) + (t/is (nil? (:touched-at (th/db-get :storage-object {:id (:id object)} + :id :touched-at)))) + (t/is (some? (:deleted-at (th/db-get :storage-object + {:id (:id object)} + {::db/remove-deleted false}))))) + + ;; a touched object with no referencing job row at all is deleted too + (let [object (sto/put-object! storage {::sto/content (sto/content "content") + :content-type "text/plain" + :profile-id (uuid/next) + :bucket sto/job-resource-bucket})] + (th/db-update! :storage-object {:touched-at (ct/now)} {:id (:id object)}) + (let [res (binding [ct/*clock* (ct/fixed-clock (ct/in-future {:hours 3}))] + (th/run-task! :storage-gc-touched {}))] + (t/is (= 0 (:freeze res))) + (t/is (>= (:delete res) 1)) + (t/is (some? (:deleted-at (th/db-get :storage-object + {:id (:id object)} + {::db/remove-deleted false})))))))) diff --git a/backend/test/backend_tests/tasks/binfile_jobs_integration_test.clj b/backend/test/backend_tests/tasks/binfile_jobs_integration_test.clj new file mode 100644 index 0000000000..07a959aa1a --- /dev/null +++ b/backend/test/backend_tests/tasks/binfile_jobs_integration_test.clj @@ -0,0 +1,270 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.tasks.binfile-jobs-integration-test + "The whole chain of a binfile job, without a full end-to-end: the + command creates the job, the dispatcher sends it to its own queue, the + runner of that queue executes it and the artifact it produced (or + consumed) ends up where the ledger says." + (:require + [app.binfile.common :as bfc] + [app.binfile.jobs :as bfj] + [app.binfile.v3 :as v3] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.metrics :as-alias mtx] + [app.msgbus :as-alias mbus] + [app.redis :as rds] + [app.rpc :as-alias rpc] + [app.storage :as sto] + [app.storage.tmp :as tmp] + [app.worker :as wrk] + [app.worker.dispatcher :as wdisp] + [app.worker.runner :as wrkr] + [backend-tests.helpers :as th] + [clojure.java.io :as jio] + [clojure.test :as t] + [datoteka.io :as io])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each (th/serial th/database-reset th/clean-storage)) + +(def ^:private fixture + "A real penpot export, the same fixture the binfile tests read." + "backend_tests/test_files/svg-attrs-camel-case.penpot") + +(def ^:private fast-timeout + "A runner of an empty queue must not make the test wait for the default + timeout of the production loop." + (ct/duration {:millis 200})) + +(defn- get-storage + [] + (assoc (:app.storage/storage th/*system*) ::sto/backend :fs)) + +(defn- import-fixture! + "A file in the default project of the profile, imported from the + fixture through the core." + [profile] + (-> th/*system* + (assoc ::bfc/project-id (:default-project-id profile)) + (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) + (assoc ::bfc/input (-> fixture io/resource jio/file)) + (v3/import-files!))) + +(defn- upload-chunked! + "Upload the fixture as a single chunk and return the session id." + [profile] + (let [source (-> fixture io/resource jio/file) + session-id (-> (th/command! {::th/type :create-upload-session + ::rpc/profile-id (:id profile) + :total-chunks 1}) + :result :session-id) + out (th/multipart-command! {::th/type :upload-chunk + ::rpc/profile-id (:id profile) + :session-id session-id + :index 0 + :content {:filename "package.penpot" + :path (th/tempfile fixture) + :mtype "application/zip" + :size (.length source)}})] + (t/is (nil? (:error out))) + session-id)) + +(defn- dispatcher-cfg + [] + {::db/pool th/*pool* + ::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics) + ::wrk/tenant (cf/get :tenant) + ::wdisp/lease (cf/get-jobs-lease) + ::wdisp/timeout (ct/duration "10s")}) + +(defn- runner-cfg + "The cfg the runner loop needs, with the real job-defs of the system: + the handler closes over the component of its own job-def." + [queue] + {::db/pool th/*pool* + ::rds/conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)}) + ::jobs/defs (::jobs/defs th/*system*) + ::mtx/metrics (get th/*system* :app.metrics/metrics) + ::mbus/msgbus (get th/*system* :app.msgbus/msgbus) + ::wrkr/id "test-runner" + ::wrkr/queue queue + ::wrk/tenant (cf/get :tenant) + ::wrkr/timeout fast-timeout}) + +(defn- run-one + [cfg] + (@#'wrkr/run-worker-loop cfg)) + +(defn- job-row + [job-id] + (-> (th/db-get :job {:id job-id}) + (update :result #(cond-> % (db/pgobject? %) db/decode-json-pgobject)) + (update :error jobs/decode-job-error))) + +(defn- project-files + [project-id] + (th/db-exec! ["SELECT id FROM file WHERE project_id = ? AND deleted_at IS NULL" + project-id])) + +(defn- download + "The artifact as a local file, as a client would fetch it." + [resource-id] + (let [object (sto/get-object (get-storage) resource-id) + path (tmp/tempfile* :suffix ".penpot")] + (with-open [out (io/output-stream path)] + (io/copy (sto/get-object-data (get-storage) object) out)) + path)) + +(defn- create-export-binfile-job + [profile file-ids] + (th/command! {::th/type :create-export-binfile-job + ::rpc/profile-id (:id profile) + :params {:file-ids file-ids + :export-type :detach-libraries}})) + +(defn- create-import-binfile-job + [profile project-id] + (th/command! {::th/type :create-import-binfile-job + ::rpc/profile-id (:id profile) + :params {:project-id project-id + :name "imported"} + :upload-id (upload-chunked! profile)})) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest the-export-job-runs-to-completion-in-its-own-queue + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + out (create-export-binfile-job profile #{file-id}) + job-id (:id (:result out))] + + (t/is (th/success? out)) + + (wdisp/run-batch (dispatcher-cfg)) + (t/testing "the dispatcher schedules it, and it waits for its queue" + (t/is (= "scheduled" (:status (jobs/get-job th/*system* job-id))))) + + (t/testing "a runner of another queue does not take it" + (run-one (runner-cfg "default")) + (t/is (= "scheduled" (:status (jobs/get-job th/*system* job-id))))) + + (t/testing "the runner of its queue does" + (run-one (runner-cfg "binfile")) + (t/is (= "completed" (:status (jobs/get-job th/*system* job-id))))) + + (let [row (job-row job-id) + job (:result row)] + (t/testing "the runner associated the artifact of the completion envelope" + (t/is (some? (:resource-id row)))) + + (t/testing "the result of the job is the descriptor the handler built" + (t/is (= "export.penpot" (:filename job))) + (t/is (= "application/zip" (:mtype job))) + (t/is (pos? (:size job))) + (t/is (some? (:resource-uri job)))) + + (t/testing "and the artifact is the package of those files" + (t/is (= [file-id] + (mapv :id (:files (v3/get-manifest th/*system* + (download (:resource-id row)))))))) + + (t/testing "the artifact belongs to the profile of the job" + (let [object (sto/get-object (get-storage) (:resource-id row))] + (t/is (= sto/job-resource-bucket (:bucket (meta object)))) + (t/is (= (:id profile) (:profile-id (meta object)))))) + + (t/testing "the runner reached the terminal state through the row" + (t/is (= "completed" (:status row))) + (t/is (some? (:completed-at row))))))) + +(t/deftest the-import-job-runs-to-completion-in-its-own-queue + (let [profile (th/create-profile* 1) + project-id (:default-project-id profile) + out (create-import-binfile-job profile project-id) + job-id (:id (:result out)) + ;; read before the run: the handler drops the reference as soon as + ;; it releases the package + input-id (:resource-id (th/db-get :job {:id job-id}))] + + (t/is (th/success? out)) + + (wdisp/run-batch (dispatcher-cfg)) + (run-one (runner-cfg "binfile")) + + (let [row (job-row job-id) + job (:result row)] + (t/testing "the job ends completed" + (t/is (= "completed" (:status row)))) + + (t/testing "the files of the package are in the destination" + ;; the result is JSON: its ids come back as text, the rows as uuids + (t/is (= (set (map str (:file-ids job))) + (set (map (comp str :id) (project-files project-id)))))) + + (t/testing "the result carries the files and their resolution" + (t/is (pos? (count (:file-ids job)))) + (t/is (= "imported" (:name job)))) + + (t/testing "the package it consumed is released" + (t/is (some? (:deleted-at (th/db-get :storage-object + {:id input-id} + {::db/remove-deleted false}))))) + + (t/testing "and the job no longer points at it" + (t/is (nil? (:resource-id row))))))) + +(t/deftest the-adapter-reports-progress-through-heartbeat + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (uuid/next) + calls (atom [])] + + (th/db-insert! :job {:id job-id + :name "export-binfile" + :tenant (cf/get :tenant) + :queue "binfile" + :params (db/json {}) + :priority 100 + :max-retries 0 + :retry-num 0 + :status "running" + :profile-id (:id profile) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + + (with-redefs [jobs/heartbeat (fn [_cfg & {:as options}] + (swap! calls conj options) + 1)] + (let [cfg (bfc/export-cfg th/*system* + {:file-ids #{file-id} + :export-type :detach-libraries + :output (tmp/tempfile* :suffix ".penpot")}) + context (jobs/make-context (jobs/get-job th/*system* job-id))] + (bfj/check-active cfg context) + (bfj/run cfg context v3/export-files!))) + + (let [reports (map :progress @calls)] + + (t/testing "the adapter reports progress through heartbeat" + (t/is (pos? (count reports)))) + + (t/testing "with the payload of the job contract, and nothing else" + (t/is (every? keyword? (map :stage reports))) + (t/is (every? #(<= (count (keys %)) 2) reports))) + + (t/testing "about the job of the context, not one the caller named" + (t/is (every? #(= job-id (:job-id %)) @calls)))))) diff --git a/backend/test/backend_tests/tasks/export_binfile_test.clj b/backend/test/backend_tests/tasks/export_binfile_test.clj new file mode 100644 index 0000000000..40a67a549a --- /dev/null +++ b/backend/test/backend_tests/tasks/export_binfile_test.clj @@ -0,0 +1,170 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.tasks.export-binfile-test + "The `:export-binfile` job: what it produces, who may ask for it and + what it leaves behind when it fails." + (:require + [app.binfile.common :as bfc] + [app.binfile.v3 :as v3] + [app.common.exceptions :as ex] + [app.jobs :as jobs] + [app.storage :as sto] + [app.storage.tmp :as tmp] + [backend-tests.helpers :as th] + [clojure.java.io :as jio] + [clojure.test :as t] + [cuerdas.core :as str] + [datoteka.io :as io])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each (th/serial th/database-reset th/clean-storage)) + +(def ^:private fixture + "A real penpot export, the same fixture the binfile tests read." + "backend_tests/test_files/svg-attrs-camel-case.penpot") + +(defn- get-storage + [] + (assoc (:app.storage/storage th/*system*) ::sto/backend :fs)) + +(defn- import-fixture! + "A file in the default project of the profile, imported from the + fixture through the core." + [profile] + (-> th/*system* + (assoc ::bfc/project-id (:default-project-id profile)) + (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) + (assoc ::bfc/input (-> fixture io/resource jio/file)) + (v3/import-files!))) + +(defn- run-export + "Run the export handler of a job, as the runner does." + [job-id params] + (jobs/invoke (-> th/*system* + (assoc ::jobs/name :export-binfile) + (assoc ::jobs/params params) + (assoc ::jobs/context + (jobs/make-context (jobs/get-job th/*system* job-id)))))) + +(defn- count-storage-objects + [] + (count (th/db-exec! ["SELECT id FROM storage_object"]))) + +(defn- temp-files + "The temporary files the export leaves in the tmp directory." + [] + (seq (.list (jio/file tmp/default-tmp-dir)))) + +(defn- download + "The artifact as a local file, as a client would fetch it." + [resource-id] + (let [object (sto/get-object (get-storage) resource-id) + path (tmp/tempfile* :suffix ".penpot")] + (with-open [out (io/output-stream path)] + (io/copy (sto/get-object-data (get-storage) object) out)) + path)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest the-export-handler-stores-the-artifact-of-its-job + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (th/create-job* :export-binfile {:profile-id (:id profile)}) + result (run-export job-id {:file-ids #{file-id} + :export-type :detach-libraries}) + object (sto/get-object (get-storage) (:resource-id result))] + + (t/testing "the handler answers with the completion envelope" + (t/is (uuid? (:resource-id result))) + (t/is (= "export.penpot" (get-in result [:result :filename]))) + (t/is (= "application/zip" (get-in result [:result :mtype]))) + (t/is (pos? (get-in result [:result :size]))) + (t/is (str/includes? (get-in result [:result :resource-uri]) + (str (:resource-id result))))) + + (t/testing "the artifact is a job resource owned by the profile of the job" + (t/is (= sto/job-resource-bucket (:bucket (meta object)))) + (t/is (= (:id profile) (:profile-id (meta object))))) + + (t/testing "it is touched, so a crash before the completion leaves it reclaimable" + (t/is (some? (:touched-at (th/db-get :storage-object + {:id (:resource-id result)}))))) + + (t/testing "the package is the one the core produces for those files" + (let [manifest (v3/get-manifest th/*system* (download (:resource-id result)))] + (t/is (= [file-id] (mapv :id (:files manifest)))))) + + (t/testing "the completion of the job associates the artifact" + (jobs/complete th/*system* + :job-id job-id + :result (:result result) + :resource-id (:resource-id result)) + (t/is (= (:resource-id result) + (:resource-id (jobs/get-job th/*system* job-id))))))) + +(t/deftest the-export-handler-revalidates-the-read-permission + (let [owner (th/create-profile* 1) + other (th/create-profile* 2) + file-id (first (:file-ids (import-fixture! owner))) + job-id (th/create-job* :export-binfile {:profile-id (:id other)}) + before (count-storage-objects)] + + (t/testing "a job whose owner cannot read a file does not export it" + (t/is (thrown? Throwable + (run-export job-id {:file-ids #{file-id} + :export-type :detach-libraries})))) + + (t/testing "and nothing was stored for it" + (t/is (= before (count-storage-objects))) + (t/is (nil? (:resource-id (jobs/get-job th/*system* job-id))))) + + (t/testing "the temporary file is not left behind either" + (t/is (empty? (temp-files)))))) + +(t/deftest the-export-handler-leaves-nothing-when-the-storage-fails + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (th/create-job* :export-binfile {:profile-id (:id profile)}) + before (count-storage-objects)] + + (with-redefs [sto/put-object! (fn [& _] + (ex/raise :type :internal + :code :boom + :hint "storage is down"))] + (t/is (thrown? Throwable + (run-export job-id {:file-ids #{file-id} + :export-type :detach-libraries})))) + + (t/testing "the job never comes to own an artifact" + (t/is (nil? (:resource-id (jobs/get-job th/*system* job-id))))) + + (t/testing "no artifact was stored" + (t/is (= before (count-storage-objects)))) + + (t/testing "and the temporary file of the export is gone" + (t/is (empty? (temp-files)))))) + +(t/deftest the-export-handler-leaves-nothing-behind-when-the-job-is-cancelled + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + job-id (th/create-job* :export-binfile {:profile-id (:id profile)}) + before (count-storage-objects)] + + (th/db-update! :job {:status "cancelled"} {:id job-id}) + + (t/is (thrown? Throwable + (run-export job-id {:file-ids #{file-id} + :export-type :detach-libraries}))) + + (t/testing "no artifact was stored" + (t/is (= before (count-storage-objects)))) + + (t/testing "and the temporary file of the export was cleaned up" + (t/is (empty? (temp-files)))))) diff --git a/backend/test/backend_tests/tasks/import_binfile_test.clj b/backend/test/backend_tests/tasks/import_binfile_test.clj new file mode 100644 index 0000000000..e82d462df3 --- /dev/null +++ b/backend/test/backend_tests/tasks/import_binfile_test.clj @@ -0,0 +1,267 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.tasks.import-binfile-test + "The `:import-binfile` job: what it imports, who may ask for it and what + it leaves behind when it fails." + (:require + [app.binfile.common :as bfc] + [app.binfile.v1 :as v1] + [app.binfile.v3 :as v3] + [app.common.uuid :as uuid] + [app.db :as db] + [app.jobs :as jobs] + [app.jobs.storage :as js] + [app.storage :as sto] + [app.storage.tmp :as tmp] + [backend-tests.helpers :as th] + [clojure.java.io :as jio] + [clojure.test :as t] + [datoteka.io :as io])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each (th/serial th/database-reset th/clean-storage)) + +(def ^:private fixture + "A real penpot export, the same fixture the binfile tests read." + "backend_tests/test_files/svg-attrs-camel-case.penpot") + +(defn- fixture-path + [] + (-> fixture io/resource jio/file)) + +(defn- import-fixture! + "A file in the default project of the profile, imported from the + fixture through the core." + [profile] + (-> th/*system* + (assoc ::bfc/project-id (:default-project-id profile)) + (assoc ::bfc/profile-id (:id profile)) + (assoc ::bfc/team-id (:default-team-id profile)) + (assoc ::bfc/input (fixture-path)) + (v3/import-files!))) + +(defn- export-to-v1! + "A version 1 package of a file, the format the old exporter writes." + [file-id] + (let [output (tmp/tempfile* :suffix ".penpot")] + (v1/export-files! (-> th/*system* + (assoc ::bfc/file-ids #{file-id}) + (assoc ::bfc/include-libraries false) + (assoc ::bfc/embed-assets false)) + output) + output)) + +(defn- stage-package! + "The package a job consumes, as its resource." + [profile path] + (js/put-resource th/*system* (:id profile) + {:content (sto/content path) + :filename "package.penpot" + :mtype "application/zip"})) + +(defn- run-import + "Run the import handler of a job, as the runner does." + [job-id params] + (jobs/invoke (-> th/*system* + (assoc ::jobs/name :import-binfile) + (assoc ::jobs/params params) + (assoc ::jobs/context + (jobs/make-context (jobs/get-job th/*system* job-id)))))) + +(defn- project-files + [project-id] + (th/db-exec! ["SELECT id FROM file WHERE project_id = ? AND deleted_at IS NULL" + project-id])) + +(defn- input-row + [resource-id] + (th/db-get :storage-object {:id resource-id} {::db/remove-deleted false})) + +(defn- temp-files + [] + (seq (.list (jio/file tmp/default-tmp-dir)))) + +(defn- project-modified-at + [project-id] + (:modified-at (th/db-get :project {:id project-id}))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest the-import-handler-imports-the-package-of-its-job + (let [profile (th/create-profile* 1) + staged (stage-package! profile (fixture-path)) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + result (run-import job-id {:project-id (:default-project-id profile) + :name "imported" + :version 3})] + + (t/testing "the handler answers with the files it created" + (t/is (= 1 (count (:file-ids result)))) + (t/is (= "imported" (:name result))) + (t/is (= 3 (:version result))) + (t/testing "and the file is in the destination project" + (t/is (= (set (:file-ids result)) + (set (map :id (project-files (:default-project-id profile)))))))) + + (t/testing "the package of the job is released once the job is terminal" + (t/is (some? (:deleted-at (input-row (:resource-id staged)))))) + + (t/testing "and its temporary copy is gone" + (t/is (empty? (temp-files)))))) + +(t/deftest the-import-marks-the-destination-project-as-modified + (let [profile (th/create-profile* 1) + project-id (:default-project-id profile) + staged (stage-package! profile (fixture-path)) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + before (project-modified-at project-id)] + + (run-import job-id {:project-id project-id + :name "imported" + :version 3}) + + (t/testing "the project the import wrote into is not left looking untouched" + (t/is (pos? (- (inst-ms (project-modified-at project-id)) + (inst-ms before))))))) + +(t/deftest the-import-handler-imports-a-version-1-package + (let [profile (th/create-profile* 1) + file-id (first (:file-ids (import-fixture! profile))) + staged (stage-package! profile (export-to-v1! file-id)) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + result (run-import job-id {:project-id (:default-project-id profile) + :name "imported-v1" + :version 1})] + + (t/is (= 1 (count (:file-ids result)))) + (t/is (= 1 (:version result))))) + +(t/deftest the-import-handler-revalidates-the-edit-permission + (let [owner (th/create-profile* 1) + other (th/create-profile* 2) + staged (stage-package! other (fixture-path)) + ;; the package belongs to `other`, but the destination project + ;; belongs to `owner`: an import there is not allowed + job-id (th/create-job* :import-binfile {:profile-id (:id other) + :resource-id (:resource-id staged)}) + before (project-files (:default-project-id owner))] + + (t/testing "a job that cannot edit the destination does not import it" + (t/is (thrown? Throwable + (run-import job-id {:project-id (:default-project-id owner) + :name "imported" + :version 3})))) + + (t/testing "nothing was written in the destination" + (t/is (= before (project-files (:default-project-id owner))))) + + (t/testing "the package is released anyway: the job is terminal" + (t/is (some? (:deleted-at (input-row (:resource-id staged)))))))) + +(t/deftest a-job-that-was-already-cancelled-never-imports + (let [profile (th/create-profile* 1) + staged (stage-package! profile (fixture-path)) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + before (project-files (:default-project-id profile))] + + (th/db-update! :job {:status "cancelled"} {:id job-id}) + + (t/testing "a cancelled job does not import anything" + (t/is (thrown? Throwable + (run-import job-id {:project-id (:default-project-id profile) + :name "imported" + :version 3}))) + (t/is (= before (project-files (:default-project-id profile))))) + + (t/testing "and the package is released" + (t/is (some? (:deleted-at (input-row (:resource-id staged)))))))) + +(t/deftest an-import-cancelled-while-it-runs-leaves-nothing-behind + (let [profile (th/create-profile* 1) + staged (stage-package! profile (fixture-path)) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + before (project-files (:default-project-id profile))] + + ;; the user cancels while the import is running: the first progress + ;; report is a good moment for it, because the core is already + ;; writing by then + (with-redefs [jobs/heartbeat (fn [& _] + (th/db-update! :job {:status "cancelled"} {:id job-id}) + 1)] + (t/is (thrown? Throwable + (run-import job-id {:project-id (:default-project-id profile) + :name "imported" + :version 3})))) + + (t/testing "the import is rolled back: the destination keeps no files" + (t/is (= before (project-files (:default-project-id profile))))) + + (t/testing "and the package is released anyway" + (t/is (some? (:deleted-at (input-row (:resource-id staged)))))))) + +(t/deftest a-package-that-is-not-a-penpot-file-fails-without-writing + (let [profile (th/create-profile* 1) + staged (stage-package! profile (fixture-path)) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + before (project-files (:default-project-id profile))] + + ;; the bytes of the resource are replaced by something that is not a + ;; package at all: the core must fail instead of importing garbage + (sto/del-object! (:app.storage/storage th/*system*) (:resource-id staged)) + (let [broken (js/put-resource th/*system* (:id profile) + {:content (sto/content "not a penpot package") + :filename "package.penpot" + :mtype "application/zip"})] + (th/db-update! :job {:resource-id (:resource-id broken)} {:id job-id}) + + (t/is (thrown? Throwable + (run-import job-id {:project-id (:default-project-id profile) + :name "imported" + :version 3}))) + + (t/testing "nothing was imported" + (t/is (= before (project-files (:default-project-id profile))))) + + (t/testing "and the broken package is released too" + (t/is (some? (:deleted-at (input-row (:resource-id broken))))))))) + +(t/deftest a-result-that-does-not-conform-leaves-nothing-behind + (let [profile (th/create-profile* 1) + project-id (:default-project-id profile) + staged (stage-package! profile (fixture-path)) + job-id (th/create-job* :import-binfile {:profile-id (:id profile) + :resource-id (:resource-id staged)}) + before (project-files project-id)] + + ;; the core does its work inside the transaction and answers with a result + ;; the schema of the task does not accept: the check runs before the + ;; transaction commits, so the work is rolled back with it + (with-redefs [v3/import-files! + (fn [cfg] + (th/create-file* cfg 7 {:profile-id (:id profile) + :project-id project-id + :is-shared false + :name "half imported"}) + {:file-ids [(uuid/next)] + :resolution "not-a-map"})] + + (t/testing "the handler refuses the result" + (t/is (thrown-with-msg? Exception #"invalid result of an import job" + (run-import job-id {:project-id project-id + :name "imported" + :version 3})))) + + (t/testing "and the import is rolled back: the destination keeps no files" + (t/is (= before (project-files project-id))))))) diff --git a/backend/test/backend_tests/tasks_telemetry_test.clj b/backend/test/backend_tests/tasks_telemetry_test.clj index 79f29f335b..b7b9d0c2ed 100644 --- a/backend/test/backend_tests/tasks_telemetry_test.clj +++ b/backend/test/backend_tests/tasks_telemetry_test.clj @@ -11,6 +11,7 @@ [app.config :as cf] [app.db :as db] [app.loggers.audit :as audit] + [app.loggers.audit.consumer-task :as at] [app.tasks.telemetry :as telemetry] [app.util.json :as json] [backend-tests.helpers :as th] @@ -827,6 +828,79 @@ (t/is (not (contains? (:props result) :active))) (t/is (not (contains? (:props result) :label))))) +(t/deftest test-filter-telemetry-props-subscription-creation-keeps-safe-fields + (let [ftp (ns-resolve 'app.loggers.audit 'filter-telemetry-props) + organization-id (uuid/next) + props {:type "nitrate:enterprise" + :deployment "saas" + :quantity 5 + :subscription-mode "paid" + :organization-id organization-id + :email "owner@example.com" + :unsafe-label "should-be-stripped"} + expected {:type "nitrate:enterprise" + :deployment "saas" + :quantity 5 + :subscription-mode "paid" + :organization-id organization-id}] + (doseq [source ["backend" "frontend"] + name ["create-subscription" "create-trial-subscription"]] + (t/testing (str source " " name) + (let [result (ftp {:source source + :name name + :type "action" + :props props})] + (t/is (= expected (:props result)))))))) + +(t/deftest test-filter-telemetry-props-subscription-update-keeps-safe-fields + (let [ftp (ns-resolve 'app.loggers.audit 'filter-telemetry-props) + props {:type "nitrate:enterprise" + :deployment "selfhost" + :previous-status "trialing" + :status "active" + :previous-cancel-at-period-end false + :cancel-at-period-end true + :previous-cancel-at nil + :cancel-at "2026-12-01T00:00:00.000Z" + :changed-fields ["status" "cancel-at-period-end" "cancel-at"] + :email "owner@example.com" + :unsafe-label "should-be-stripped"} + result (ftp {:source "backend" + :name "update-subscription" + :type "action" + :props props})] + (t/is (= (dissoc props :email :unsafe-label) + (:props result))))) + +(t/deftest test-filter-telemetry-props-subscription-summary-keeps-safe-fields + (let [ftp (ns-resolve 'app.loggers.audit 'filter-telemetry-props) + props {:type "nitrate:enterprise" + :deployment "saas" + :instance-id "00000000-0000-4000-8000-000000000099" + :snapshot-date "2026-11-05" + :trialing-count 1 + :active-count 2 + :active-cancel-scheduled-count 3 + :past-due-count 4 + :canceled-count 5 + :unpaid-count 6 + :incomplete-count 7 + :incomplete-expired-count 8 + :paused-count 9 + :other-count 10 + :total-count 55 + :sync-succeeded 54 + :sync-failed 1 + :snapshot-complete false + :email "owner@example.com" + :unsafe-label "should-be-stripped"} + result (ftp {:source "backend" + :name "subscription-summary" + :type "action" + :props props})] + (t/is (= (dissoc props :email :unsafe-label) + (:props result))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SEND-EVENT-BATCH PAYLOAD STRUCTURE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -999,3 +1073,45 @@ (th/run-task! :telemetry {:send? true :enabled? true}) (t/is (= 1 @send-count)) ;; still 1, not 2 (t/is (= 0 (count-telemetry-rows))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PENDING-QUEUE REGRESSION (telemetry deletes) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- insert-pending-pair! + "Queue the row for both consumers (from the consumer constants, not + literals): the delete paths must face pending-ful rows." + [row-id] + (doseq [consumer [at/consumer-nexus at/consumer-admin-console]] + (th/db-exec! ["INSERT INTO audit_log_pending (audit_log_id, consumer) + VALUES (?::uuid, ?::smallint) + ON CONFLICT DO NOTHING" + row-id consumer]))) + +(defn- count-pendings [] + (-> (th/db-exec-one! ["SELECT count(*) AS cnt FROM audit_log_pending"]) + :cnt + long)) + +(t/deftest test-delete-sent-events-removes-pendings-first + ;; Regression: telemetry rows carry pendings for both consumers, so + ;; deleting the row must delete its pendings first, or the FK + ;; rejects the delete and the batch is sent again next tick. + (let [row (insert-telemetry-row! "navigate")] + (insert-pending-pair! (:id row)) + (t/is (= 2 (count-pendings))) + (db/tx-run! th/*pool* + (fn [{:keys [::db/conn]}] + (@#'telemetry/delete-sent-events conn [(:id row)]))) + (t/is (= 0 (count-telemetry-rows))) + (t/is (= 0 (count-pendings))))) + +(t/deftest test-gc-events-removes-pendings-first + ;; Same guarantee for the >7-days purge. + (let [past (ct/in-past {:days 8}) + row (insert-telemetry-row! "navigate" {:created-at past :tracked-at past})] + (insert-pending-pair! (:id row)) + (t/is (= 2 (count-pendings))) + (@#'telemetry/gc-events {::db/conn th/*pool*}) + (t/is (= 0 (count-telemetry-rows))) + (t/is (= 0 (count-pendings))))) diff --git a/backend/test/backend_tests/util_events_test.clj b/backend/test/backend_tests/util_events_test.clj new file mode 100644 index 0000000000..9c75d32699 --- /dev/null +++ b/backend/test/backend_tests/util_events_test.clj @@ -0,0 +1,77 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns backend-tests.util-events-test + "The events subsystem: a tap delivers to whatever sink is bound, a + function inline on the same thread or a channel for a listener of its + own, and untracked runs pay nothing." + (:require + [app.util.events :as events] + [clojure.test :as t] + [promesa.exec :as px] + [promesa.exec.csp :as sp])) + +(t/deftest tap-outside-a-run-is-a-no-op + (t/testing "a tap with no sink bound does nothing" + (t/is (nil? (events/tap :one 1))))) + +(t/deftest tap-calls-a-function-sink-inline + (let [seen (atom [])] + (binding [events/*sink* (fn [event] (swap! seen conj event))] + (t/testing "every tap reaches the function, in order" + (t/is (nil? (events/tap :one 1))) + (t/is (nil? (events/tap :two 2))) + (t/is (= [[:one 1] [:two 2]] @seen)))))) + +(t/deftest tap-puts-on-a-channel-sink + (let [channel (sp/chan :buf 4)] + (try + (binding [events/*sink* channel] + (t/testing "a tap puts the event on the channel" + (t/is (nil? (events/tap :one 1))) + (t/is (= [:one 1] (sp/take! channel))))) + (finally + (sp/close! channel))))) + +(t/deftest an-exception-from-a-function-sink-propagates + (let [cause (ex-info "the job is no longer active" + {:type :interrupt :code :job-interrupted})] + (binding [events/*sink* (fn [_event] (throw cause))] + (t/testing "the tap raises the very exception the sink threw" + (t/is (identical? cause + (try (events/tap :one 1) nil + (catch Throwable raised raised)))))))) + +(t/deftest spawn-listener-consumes-until-the-channel-closes + (let [seen (atom []) + ended (promise) + channel (sp/chan :buf 4) + listener (events/spawn-listener + channel + (fn [event] (swap! seen conj event)) + (fn [] (deliver ended true)))] + (sp/put! channel [:one 1]) + (sp/put! channel [:two 2]) + (sp/close! channel) + (px/await! listener) + (t/testing "the listener saw every event, in order" + (t/is (= [[:one 1] [:two 2]] @seen))) + (t/testing "and the close hook ran" + (t/is (true? (deref ended 1000 false)))))) + +(t/deftest spawn-listener-stops-on-a-consumer-failure + (let [seen (atom []) + channel (sp/chan :buf 4) + listener (events/spawn-listener + channel + (fn [_event] (throw (ex-info "boom" {}))) + (fn [] nil))] + (sp/put! channel [:one 1]) + (sp/put! channel [:two 2]) + (px/await! listener) + (t/testing "the failure stops the listener, so nothing is consumed" + (t/is (= [] @seen))) + (sp/close! channel))) diff --git a/backend/test/backend_tests/util_pointer_map_test.clj b/backend/test/backend_tests/util_pointer_map_test.clj index ce39715c4b..2b0d98babb 100644 --- a/backend/test/backend_tests/util_pointer_map_test.clj +++ b/backend/test/backend_tests/util_pointer_map_test.clj @@ -117,5 +117,20 @@ (t/is (not (contains? obj1 :b))) (t/is (= 1 (get obj1 :a))) (t/is (= nil (get obj1 :b))) - (t/is (= ::empty (get obj1 :b ::empty))))))) + (t/is (= ::empty (get obj1 :b ::empty)))))) + + (t/testing "with-meta" + (binding [pmap/*tracked* (atom {}) + pmap/*load-fn* (constantly {})] + (let [obj1 (pmap/create uuid/zero {}) + obj2 (with-meta obj1 {:foo :bar}) + obj3 (with-meta obj1 {})] + ;; A metadata change is a new pointer: fresh id and tracked, so + ;; the fragment row is never rewritten under the same id. + (t/is (pmap/modified? obj2)) + (t/is (not= (pmap/get-id obj1) (pmap/get-id obj2))) + (t/is (contains? @pmap/*tracked* (pmap/get-id obj2))) + ;; The same metadata keeps the pointer untouched. + (t/is (not (pmap/modified? obj3))) + (t/is (= (pmap/get-id obj1) (pmap/get-id obj3))))))) diff --git a/backend/test/backend_tests/worker_cron_test.clj b/backend/test/backend_tests/worker_cron_test.clj new file mode 100644 index 0000000000..e0797ab4b0 --- /dev/null +++ b/backend/test/backend_tests/worker_cron_test.clj @@ -0,0 +1,249 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.worker-cron-test + (:require + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.util.cron :as ucron] + [app.worker.cron :as cron] + [backend-tests.helpers :as th] + [clojure.string :as str] + [clojure.test :as t] + [integrant.core :as ig])) + +(t/use-fixtures :once th/state-init) +(t/use-fixtures :each th/database-reset) + +(defn- get-job + [cfg job-id] + (db/get* cfg :job {:id job-id})) + +(defn- count-jobs + [cfg & {:keys [name tenant queue label]}] + (let [conditions (concat + (when name ["name = ?"]) + (when tenant ["tenant = ?"]) + (when queue ["queue = ?"]) + (when label ["label = ?"])) + sql (str "SELECT count(*) AS n FROM job" + (when (seq conditions) + (str " WHERE " (str/join " AND " conditions)))) + params (concat + (when name [name]) + (when tenant [tenant]) + (when queue [queue]) + (when label [label]))] + (-> (db/exec-one! cfg (into [sql] params)) + :n))) + +(defn- make-cfg + [] + (-> th/*system* + (assoc ::jobs/defs (get th/*system* :app.jobs/defs)) + (assoc ::db/pool (get th/*system* ::db/pool)))) + +(defn- get-cron-entries + [] + [{:id :session-gc-no-props + :job :session-gc + :cron "0 0 * * *"} + {:id :jobs-gc-with-props + :job :jobs-gc + :cron "0 0 * * *" + :props {:min-age 3600000}}]) + +(t/deftest cron-component-init-registers-and-schedules-entries + (let [defs (get th/*system* :app.jobs/defs) + entries [{:id :test-cron-init + :job :session-gc + :cron (ucron/cron "0 0 0 * * ?")}] + inst (ig/init-key :app.worker/cron + {:app.worker/entries entries + ::jobs/defs defs + ::db/pool th/*pool*})] + (try + (t/testing "init schedules futures" + (t/is (pos? (count @inst)))) + (t/testing "scheduled_task row was upserted" + (t/is (some? (th/db-get :scheduled-task {:id "test-cron-init"})))) + (finally + (ig/halt-key! :app.worker/cron inst))))) + +(t/deftest cron-submits-job-with-empty-params-when-entry-has-no-props + (let [cfg (make-cfg) + entries (get-cron-entries) + entry (first entries)] + + ;; Submit a cron job for an entry without :props + (let [job-id (cron/submit-cron-job cfg entry)] + ;; Job should be created + (t/is (some? job-id)) + + ;; Job should exist in DB + (let [row (get-job cfg job-id)] + (t/is (some? row)) + (t/is (= "session-gc" (:name row))) + (t/is (= "cron" (:queue row))) + (t/is (= (cf/get :tenant) (:tenant row))) + (t/is (= "session-gc-no-props" (:label row))) + (t/is (nil? (:profile-id row))) + + ;; Props should be empty map after decoding + (let [props (db/decode-json-pgobject (:params row))] + (t/is (= {} props))))))) + +(t/deftest cron-submits-job-with-props-when-entry-declares-them + (let [cfg (make-cfg) + entries (get-cron-entries) + entry (second entries)] + + ;; Submit a cron job for an entry with :props + (let [job-id (cron/submit-cron-job cfg entry)] + ;; Job should be created + (t/is (some? job-id)) + + ;; Job should exist in DB with the props + (let [row (get-job cfg job-id)] + (t/is (some? row)) + (t/is (= "jobs-gc" (:name row))) + (t/is (= "jobs-gc-with-props" (:label row))) + ;; Props should be decoded to the original map + (let [props (db/decode-json-pgobject (:params row))] + (t/is (= {:min-age 3600000} props))))))) + +(t/deftest cron-tick-submits-only-when-no-active-instance + (let [cfg (make-cfg) + ;; the entry keeps the canonical keyword task; only the id is a + ;; string, as normalized by the cron component init. The cron + ;; expression must be parsed: the tick reschedules itself in + ;; `finally` + entry {:id "session-gc-no-overlap" + :job :session-gc + :cron (ucron/cron "0 0 0 * * ?")}] + + ;; the tick claims this row; without it the tick silently skips + (th/db-insert! :scheduled-task {:id "session-gc-no-overlap" + :cron-expr "0 0 * * *"}) + + ;; execute-cron-task runs on a plain thread; join waits for the tick + (let [tick! (fn [] (.join ^Thread (#'cron/execute-cron-task cfg entry)))] + ;; first tick submits exactly one job + (tick!) + (t/is (= 1 (count-jobs cfg :name "session-gc" + :label "session-gc-no-overlap"))) + + ;; second tick with the first still active submits nothing + (tick!) + (t/is (= 1 (count-jobs cfg :name "session-gc" + :label "session-gc-no-overlap"))) + + ;; once the first reaches terminal, the next tick submits again + (th/db-update! :job {:status "completed"} {:label "session-gc-no-overlap"}) + (tick!) + (t/is (= 2 (count-jobs cfg :name "session-gc" + :label "session-gc-no-overlap")))))) + +(t/deftest cron-job-created-on-cron-queue-with-null-profile-id + (let [cfg (make-cfg) + entries (get-cron-entries) + entry (first entries)] + + (let [job-id (cron/submit-cron-job cfg entry) + row (get-job cfg job-id)] + + ;; the queue column is bare; the tenant lives in its own column + (t/is (= "cron" (:queue row))) + (t/is (= (cf/get :tenant) (:tenant row))) + + ;; Verify profile_id is NULL (system job) + (t/is (nil? (:profile-id row))) + + ;; Verify label is the entry id as string (without colon) + (t/is (= "session-gc-no-props" (:label row)))))) + +(t/deftest cron-tick-is-not-blocked-by-another-tenants-active-job + (let [cfg (make-cfg) + entry {:id "session-gc-shared-db" + :job :session-gc + :cron (ucron/cron "0 0 0 * * ?")}] + + (th/db-insert! :scheduled-task {:id "session-gc-shared-db" + :cron-expr "0 0 * * *"}) + ;; An active job with the same name and label, owned by another + ;; instance that shares this database. It runs in another tenant's + ;; queue, so it is none of this instance's business. + (th/db-insert! :job {:id (uuid/next) + :name "session-gc" + :tenant "other-tenant" + :queue "cron" + :label "session-gc-shared-db" + :status "running" + :params (db/json {}) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + + (.join ^Thread (#'cron/execute-cron-task cfg entry)) + + (t/testing "this tenant still submits its own job" + (t/is (= 1 (count-jobs cfg :name "session-gc" + :tenant (cf/get :tenant) + :label "session-gc-shared-db")))) + (t/testing "and the other tenant's row is untouched" + (t/is (= "running" (:status (th/db-get :job {:name "session-gc" + :tenant "other-tenant"}))))))) + +(t/deftest cron-tick-is-not-blocked-by-active-job-on-another-queue + (let [cfg (make-cfg) + entry {:id "session-gc-other-queue" + :job :session-gc + :cron (ucron/cron "0 0 0 * * ?")}] + + (th/db-insert! :scheduled-task {:id "session-gc-other-queue" + :cron-expr "0 0 * * *"}) + ;; A live job with the same name, label and tenant, but sitting on + ;; another queue. The cron no-overlap check is qualified by queue, + ;; so it is none of this tick's business. + (th/db-insert! :job {:id (uuid/next) + :name "session-gc" + :tenant (cf/get :tenant) + :queue "default" + :label "session-gc-other-queue" + :status "running" + :params (db/json {}) + :scheduled-at (ct/now) + :created-at (ct/now) + :modified-at (ct/now)}) + + (.join ^Thread (#'cron/execute-cron-task cfg entry)) + + (t/testing "the tick still submits its own job on the cron queue" + (t/is (= 1 (count-jobs cfg :name "session-gc" + :queue "cron" + :label "session-gc-other-queue")))) + (t/testing "and the row on the other queue is untouched" + (t/is (= "running" (:status (th/db-get :job {:name "session-gc" + :queue "default" + :label "session-gc-other-queue"}))))))) + +(t/deftest cron-submit-skips-when-active-instance-on-cron-queue + (let [cfg (make-cfg) + entry (first (get-cron-entries))] + + (let [job-id (cron/submit-cron-job cfg entry)] + (t/is (some? job-id)) + + (t/testing "a second submit while the first is live returns nil" + (t/is (nil? (cron/submit-cron-job cfg entry)))) + + (t/testing "no second row was inserted" + (t/is (= 1 (count-jobs cfg :name "session-gc" + :queue "cron" + :label "session-gc-no-props"))))))) diff --git a/backend/test/backend_tests/worker_dispatcher_test.clj b/backend/test/backend_tests/worker_dispatcher_test.clj new file mode 100644 index 0000000000..c498214beb --- /dev/null +++ b/backend/test/backend_tests/worker_dispatcher_test.clj @@ -0,0 +1,327 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.worker-dispatcher-test + (:require + [app.common.json :as json] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.main :as main] + [app.metrics :as-alias mtx] + [app.metrics.definition :as-alias mdef] + [app.redis :as rds] + [app.worker :as wrk] + [app.worker.dispatcher :as wdisp] + [backend-tests.helpers :as th] + [clojure.string :as str] + [clojure.test :as t] + [integrant.core :as ig]) + (:import + io.prometheus.client.Collector$MetricFamilySamples + io.prometheus.client.Collector$MetricFamilySamples$Sample + io.prometheus.client.Counter + io.prometheus.client.Counter$Child)) + +(t/use-fixtures :once th/state-init) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; HELPERS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- mk-cfg + [] + {::db/pool th/*pool* + ::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics) + ::wrk/tenant (cf/get :tenant) + ::batch-size 100 + ::lease (cf/get-jobs-lease) + ::timeout (ct/duration "10s")}) + +(defn- make-metrics [] + (ig/init-key :app.metrics/metrics + {:default (select-keys main/default-metrics + [:jobs-dispatcher-timing])})) + +(defn- make-orphan-metrics [] + (ig/init-key :app.metrics/metrics + {:default (select-keys main/default-metrics + [:jobs-orphaned :jobs-total-timing])})) + +(defn- counter-value [metrics id labels] + (let [collector (mtx/get-collector metrics id) + instance (::mdef/instance collector) + child (.labels ^Counter instance (into-array String labels))] + (.get ^Counter$Child child))) + +(defn- histogram-sample-count [metrics labels] + (->> (enumeration-seq + (.metricFamilySamples ^io.prometheus.client.CollectorRegistry + (mtx/get-registry metrics))) + (mapcat (fn [^Collector$MetricFamilySamples family] + (.samples family))) + (filter (fn [^Collector$MetricFamilySamples$Sample sample] + (and (str/ends-with? (.-name sample) "_count") + (= labels (vec (.-labelValues sample)))))) + (map (fn [^Collector$MetricFamilySamples$Sample sample] + (long (.-value sample)))) + (reduce + 0))) + +(defn- mk-job + [{:keys [name queue status scheduled-at modified-at tenant] + :or {name "test-job" + queue "test" + tenant (cf/get :tenant) + status "new" + scheduled-at (ct/now) + modified-at (ct/now)}}] + (let [id (uuid/next)] + (th/db-insert! :job {:id id + :name name + :tenant tenant + :queue queue + :params (db/json {}) + :priority 100 + :max-retries 3 + :retry-num 0 + :status status + :scheduled-at scheduled-at + :created-at (ct/now) + :modified-at modified-at}) + id)) + +(defn- get-row + [id] + (-> (th/db-get :job {:id id} :status :modified-at :scheduled-at :error) + (update :error jobs/decode-job-error) + (assoc :events (->> (th/db-exec! ["SELECT kind, payload FROM job_event + WHERE job_id = ? ORDER BY id" id]) + (mapv (fn [{:keys [kind payload]}] + {:kind kind + :payload (db/decode-json-pgobject payload)})))))) + +(defn- queue-key + [queue-name] + (wrk/queue-key (cf/get :tenant) queue-name)) + +(defn- test-fixture [next] + (th/database-reset + (fn [] + ;; clear the redis hand-off list so tests don't see stale payloads + (let [conn (rds/connect (mk-cfg))] + (try + (rds/del conn (queue-key "test")) + (finally + (rds/close conn))) + (next))))) + +(t/use-fixtures :each test-fixture) + +(defn- drain-queue + [queue-name] + (let [conn (rds/connect (mk-cfg))] + (try + (let [cmd (.-cmd conn) + res (.lrange ^io.lettuce.core.api.sync.RedisCommands + cmd (queue-key queue-name) 0 -1)] + (vec res)) + (finally + (rds/del conn (queue-key queue-name)) + (rds/close conn))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest dispatcher-claims-due-jobs-and-pushes-json-payload + (let [cfg (mk-cfg) + id (mk-job {}) + _ (wdisp/run-batch cfg) + row (get-row id) + key (queue-key "test")] + + (t/testing "claimed job is marked scheduled" + (t/is (= "scheduled" (:status row)))) + + (t/testing "payload is plain JSON [uuid, iso-8601]" + (let [payloads (drain-queue "test")] + (t/is (= 1 (count payloads))) + (let [[job-id scheduled-at :as payload] (json/decode (first payloads))] + (t/is (string? job-id)) + (t/is (= (str id) job-id)) + (t/is (string? scheduled-at)) + (t/testing "round-trip: inst-ms are equal after ct/inst" + (let [expected (:scheduled-at row) + actual (ct/inst scheduled-at)] + (t/is (= (inst-ms expected) (inst-ms actual)))))))))) + +(t/deftest dispatcher-claims-retry-jobs-too + (let [cfg (mk-cfg) + id (mk-job {:status "retry"})] + (wdisp/run-batch cfg) + (t/is (= "scheduled" (:status (get-row id)))))) + +(t/deftest dispatcher-does-not-claim-future-or-terminal-jobs + (let [cfg (mk-cfg) + future (mk-job {:scheduled-at (ct/plus (ct/now) + (ct/duration {:minutes 10}))}) + running (mk-job {:status "running"})] + (wdisp/run-batch cfg) + (t/is (= "new" (:status (get-row future)))) + (t/is (= "running" (:status (get-row running)))))) + +(t/deftest dispatcher-reschedules-lost-scheduled-jobs + (let [cfg (mk-cfg) + lost-id (mk-job {:status "scheduled" + :scheduled-at (ct/minus (ct/now) + (ct/duration {:minutes 6}))}) + fresh-id (mk-job {:status "scheduled"})] + (wdisp/run-batch cfg) + (let [lost (get-row lost-id) + fresh (get-row fresh-id)] + ;; the lost job is rescheduled to 'new' and claimed again in the + ;; same batch, so it ends as 'scheduled' with a recent scheduled_at + (t/is (= "scheduled" (:status lost))) + (t/is (> (inst-ms (:scheduled-at lost)) + (inst-ms (ct/minus (ct/now) + (ct/duration {:minutes 5}))))) + (t/testing "the rescheduled job was pushed again to the queue" + (t/is (= 1 (count (drain-queue "test")))) + (t/is (= "scheduled" (:status fresh))))))) + +(t/deftest dispatcher-marks-stale-running-jobs-as-aborted-by-lease + (let [cfg (mk-cfg) + stale (mk-job {:status "running" + :modified-at (ct/minus (ct/now) + (ct/plus (cf/get-jobs-lease) + (ct/duration {:minutes 1})))}) + fresh (mk-job {:status "running"})] + (wdisp/run-batch cfg) + (let [stale-row (get-row stale)] + (t/is (= "aborted" (:status stale-row))) + (t/testing "the error is the structured one, decoded back to keywords" + (t/is (= jobs/orphan-error (:error stale-row)))) + (t/testing "the sweep stores the end event with the aborted outcome" + (t/is (= [{:kind "end" :payload {:outcome "aborted"}}] + (:events stale-row)))) + (t/testing "an aborted job is terminal: it is never retried nor reclaimed" + (t/is (zero? (jobs/retry-job cfg + (:id stale-row) + 1 (ct/now) jobs/orphan-error :backoff))))) + + (t/testing "recent running job is not touched" + (t/is (= "running" (:status (get-row fresh))))))) + +(t/deftest dispatcher-orphan-sweep-records-orphan-and-terminal-metrics + (let [metrics (make-orphan-metrics) + cfg (assoc (mk-cfg) ::mtx/metrics metrics) + _ (mk-job {:status "running" + :modified-at (ct/minus (ct/now) + (ct/plus (cf/get-jobs-lease) + (ct/duration {:minutes 1})))})] + (wdisp/run-batch cfg) + (t/is (= 1.0 (counter-value metrics :jobs-orphaned ["test"]))) + (t/is (= 1 (histogram-sample-count metrics ["test-job" "test" "aborted"]))))) + +(t/deftest dispatcher-batch-without-pending-jobs-signals-wait + (let [cfg (mk-cfg)] + (t/is (= ::wdisp/wait (wdisp/run-batch cfg))))) + +(t/deftest dispatcher-failed-mark-leaves-no-orphan-payload + ;; Mark runs before push: when the mark fails, the push never runs, so + ;; a rolled-back batch must leave neither a marked row nor a payload. + (let [cfg (assoc (mk-cfg) ::wdisp/timeout (ct/duration {:millis 10})) + id (mk-job {}) + orig @#'wdisp/mark-as-scheduled + calls (atom 0)] + (alter-var-root #'wdisp/mark-as-scheduled + (constantly (fn [& args] + (when (= 1 (swap! calls inc)) + (throw (ex-info "boom" {}))) + (apply orig args)))) + (try + (wdisp/run-batch cfg) + (t/testing "failed mark rolls back with no payload pushed" + (t/is (= "new" (:status (get-row id)))) + (t/is (empty? (drain-queue "test")))) + (finally + (alter-var-root #'wdisp/mark-as-scheduled (constantly orig)))) + (t/testing "next batch delivers exactly once" + (wdisp/run-batch cfg) + (t/is (= "scheduled" (:status (get-row id)))) + (t/is (= 1 (count (drain-queue "test"))))))) + +(t/deftest dispatcher-records-one-redis-failure + (let [metrics (make-metrics) + cfg (assoc (mk-cfg) ::mtx/metrics metrics + ::wdisp/timeout (ct/duration {:millis 10})) + _ (mk-job {})] + (with-redefs [rds/rpush (fn [& _] (throw (ex-info "redis down" {})))] + (wdisp/run-batch cfg)) + (t/is (= 1 (histogram-sample-count metrics ["execution" "failed"]))) + (t/is (= 0 (histogram-sample-count metrics ["dispatch" "failed"]))))) + +(t/deftest dispatcher-push-failure-rolls-back-without-throwing + (let [cfg (assoc (mk-cfg) ::wdisp/timeout (ct/duration {:millis 10})) + id (mk-job {}) + orig @#'rds/rpush] + (alter-var-root #'rds/rpush + (constantly (fn [& _] (throw (ex-info "redis down" {}))))) + (try + ;; run-batch catches the failure (sleep path) instead of throwing; + ;; a throw would error this test by itself + (wdisp/run-batch cfg) + (t/testing "failed push rolls back the batch" + (t/is (= "new" (:status (get-row id)))) + (t/is (empty? (drain-queue "test")))) + (finally + (alter-var-root #'rds/rpush (constantly orig)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TENANT ISOLATION +;; A batch is this tenant's: it must not read, claim or kill the rows of +;; another instance sharing the same database. + +(t/deftest dispatcher-does-not-claim-another-tenants-jobs + (let [cfg (mk-cfg) + foreign (mk-job {:tenant "other-tenant"})] + (wdisp/run-batch cfg) + (t/testing "the other tenant's job is not claimed" + (t/is (= "new" (:status (get-row foreign))))) + (t/testing "and nothing was pushed to this tenant's Redis list" + (t/is (empty? (drain-queue "test")))))) + +(t/deftest dispatcher-does-not-reschedule-another-tenants-lost-jobs + (let [cfg (mk-cfg) + foreign (mk-job {:status "scheduled" + :tenant "other-tenant" + :scheduled-at (ct/minus (ct/now) + (ct/duration {:minutes 6}))})] + (wdisp/run-batch cfg) + (t/testing "the other tenant's scheduled job keeps its status and its date" + (let [row (get-row foreign)] + (t/is (= "scheduled" (:status row))) + (t/is (< (inst-ms (:scheduled-at row)) + (inst-ms (ct/minus (ct/now) + (ct/duration {:minutes 5}))))))))) + +(t/deftest dispatcher-does-not-orphan-another-tenants-running-jobs + (let [cfg (mk-cfg) + ;; older than any lease: this tenant's sweep would kill it + foreign (mk-job {:status "running" + :tenant "other-tenant" + :modified-at (ct/minus (ct/now) + (ct/plus (cf/get-jobs-lease) + (ct/duration {:minutes 1})))})] + (wdisp/run-batch cfg) + (t/testing "the other tenant's running job is still running" + (let [row (get-row foreign)] + (t/is (= "running" (:status row))) + (t/is (nil? (:error row))) + (t/is (= [] (:events row))))))) diff --git a/backend/test/backend_tests/worker_runner_test.clj b/backend/test/backend_tests/worker_runner_test.clj new file mode 100644 index 0000000000..2df99415a3 --- /dev/null +++ b/backend/test/backend_tests/worker_runner_test.clj @@ -0,0 +1,567 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.worker-runner-test + (:require + [app.common.json :as json] + [app.common.schema :as sm] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.main :as main] + [app.metrics :as-alias mtx] + [app.metrics.definition :as-alias mdef] + [app.redis :as rds] + [app.worker :as wrk] + [app.worker.runner :as wrkr] + [backend-tests.helpers :as th] + [clojure.string :as str] + [clojure.test :as t] + [integrant.core :as ig]) + (:import + io.prometheus.client.Collector$MetricFamilySamples + io.prometheus.client.Collector$MetricFamilySamples$Sample)) + +(t/use-fixtures :once th/state-init) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; HELPERS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def received (atom [])) + +(def received-contexts (atom [])) + +(defn echo-handler + "Handler implementation: the context comes from the job-def wrapper and + the params are what the runner decoded from the row." + [cfg context params] + (swap! received conj params) + (swap! received-contexts conj context) + params) + +(def schema:echo-params + [:map + [:object :keyword] + [:deleted-at ::ct/inst] + [:id ::sm/uuid]]) + +(defn- echo-job-def + "Job-def with the standard wrapper: [context params], with the component + deps closed over at init time." + [] + {::jobs/name :echo-runner + ::jobs/schema schema:echo-params + ::jobs/handler (fn [context params] + (echo-handler nil context params)) + ::jobs/decoder (sm/decoder schema:echo-params sm/json-transformer) + ::jobs/validator (sm/validator schema:echo-params)}) + +(defn- get-defs + [] + {:echo-runner (echo-job-def)}) + +(defn- mk-cfg + [{:keys [defs] + :or {defs (get-defs)}}] + {::db/pool th/*pool* + ::rds/conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)}) + ::jobs/defs defs + ::mtx/metrics (get th/*system* :app.metrics/metrics) + ::wrkr/id "test-runner" + ::wrkr/queue "test" + ::wrk/tenant (cf/get :tenant) + ::wrkr/timeout (ct/duration "5s")}) + +(defn- make-metrics [] + (ig/init-key :app.metrics/metrics + {:default (select-keys main/default-metrics + [:jobs-queue-wait-timing + :jobs-execution-timing + :jobs-total-timing + :tasks-timing])})) + +(defn- histogram-sample-count [metrics id labels] + (->> (enumeration-seq + (.metricFamilySamples ^io.prometheus.client.CollectorRegistry + (mtx/get-registry metrics))) + (filter (fn [^Collector$MetricFamilySamples family] + (= (.-name family) (::mdef/name (main/default-metrics id))))) + (mapcat (fn [^Collector$MetricFamilySamples family] + (.samples family))) + (filter (fn [^Collector$MetricFamilySamples$Sample sample] + (and (str/ends-with? (.-name sample) "_count") + (= labels (vec (.-labelValues sample)))))) + (map (fn [^Collector$MetricFamilySamples$Sample sample] + (long (.-value sample)))) + (reduce + 0))) + +(defn- queue-key + [] + (wrk/queue-key (cf/get :tenant) "test")) + +(defn- clear-queue + [] + (let [conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)})] + (try + (rds/del conn (queue-key)) + (finally + (rds/close conn))))) + +(defn- test-fixture [next] + (th/database-reset + (fn [] + (reset! received []) + (reset! received-contexts []) + (clear-queue) + (next)))) + +(t/use-fixtures :each test-fixture) + +(defn- mk-job + [{:keys [name status scheduled-at params max-retries label] + :or {name "echo-runner" + status "new" + ;; Valid echo params by default: direct inserts bypass the + ;; submit! contract, and the runner validates decoded params. + params {:object :snapshot + :deleted-at (ct/now) + :id (uuid/next)} + max-retries 3}}] + (let [id (uuid/next)] + (th/db-insert! :job {:id id + :name name + :tenant (cf/get :tenant) + :label label + :queue "test" + :params (db/json params) + :priority 100 + :max-retries max-retries + :retry-num 0 + :status status + :scheduled-at scheduled-at + :created-at (ct/now) + :modified-at (ct/now)}) + id)) + +(defn- push-payload + [job-id scheduled-at] + (let [conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)})] + (try + (rds/rpush conn (queue-key) [(json/encode [(str job-id) + (ct/format-inst scheduled-at)])]) + (finally + (rds/close conn))))) + +(defn- get-row + [id] + (-> (th/db-get :job {:id id}) + (as-> row + (-> row + (update :error jobs/decode-job-error) + (update :result #(cond-> % (db/pgobject? %) db/decode-json-pgobject)))))) + +(defn- get-events + [id] + (->> (th/db-exec! ["SELECT kind, payload FROM job_event + WHERE job_id = ? ORDER BY id ASC" id]) + (mapv (fn [{:keys [kind payload]}] + {:kind kind :payload (db/decode-json-pgobject payload)})))) + +(defn- run-one + [cfg] + (@#'wrkr/run-worker-loop cfg)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TESTS +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest runner-executes-handler-with-decoded-typed-params + (let [params {:object :snapshot + :deleted-at (ct/now) + :id (uuid/next)} + scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at :params params})] + + (push-payload job-id scheduled-at) + (run-one (mk-cfg {})) + + (t/testing "handler received the decoded params" + (t/is (= 1 (count @received))) + (let [params' (first @received)] + (t/is (keyword? (:object params'))) + (t/is (= :snapshot (:object params'))) + (t/is (uuid? (:id params'))) + (t/is (ct/inst? (:deleted-at params'))))) + + (t/testing "job row is completed with the handler result" + (let [row (get-row job-id)] + (t/is (= "completed" (:status row))) + (t/is (some? (:started-at row))) + (t/is (some? (:completed-at row))) + (t/is (nil? (:error row))))))) + +(t/deftest runner-builds-the-handler-context-from-the-row + (let [params {:object :snapshot + :deleted-at (ct/now) + :id (uuid/next)} + scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at + :params params + :label "runner-label"})] + + (push-payload job-id scheduled-at) + (run-one (mk-cfg {})) + + (t/testing "the handler receives exactly the five context keys" + (t/is (= 1 (count @received-contexts))) + (let [context (first @received-contexts)] + (t/is (= #{:id :name :label :resource-id :profile-id} (set (keys context)))) + (t/is (= job-id (:id context))) + (t/is (= "echo-runner" (:name context))) + (t/is (= "runner-label" (:label context))) + (t/is (nil? (:resource-id context))) + (t/is (nil? (:profile-id context)) "an internal job has no owner") + + (t/testing "and nothing of the row leaks into it" + (t/is (not (contains? context :params))) + (t/is (not (contains? context :status))) + (t/is (not (contains? context :result))) + (t/is (not (contains? context :retry-num))) + (t/is (not (contains? context :max-retries))) + (t/is (not (contains? context :attempt))) + (t/is (not (contains? context :queue)) + "the tenant-prefixed queue is a routing detail, not context")))))) + +(t/deftest runner-records-queue-wait-execution-and-total + (let [metrics (make-metrics) + cfg (assoc (mk-cfg {}) ::mtx/metrics metrics) + at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at at})] + (push-payload job-id at) + (run-one cfg) + (t/is (= 1 (histogram-sample-count metrics + :jobs-queue-wait-timing + ["echo-runner" "test"]))) + (t/is (= 1 (histogram-sample-count metrics + :jobs-execution-timing + ["echo-runner" "test"]))) + (t/is (= 1 (histogram-sample-count metrics + :jobs-total-timing + ["echo-runner" "test" "completed"]))) + (t/testing "the legacy histogram is still emitted, with its raw name label" + (t/is (= 1 (histogram-sample-count metrics + :tasks-timing + ["echo-runner"])))))) + +(t/deftest runner-skips-cancelled-jobs + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at}) + _ (th/db-update! :job {:status "cancelled"} {:id job-id})] + + (push-payload job-id scheduled-at) + (run-one (mk-cfg {})) + + (t/testing "conditional claim found 0 rows: state untouched" + (let [row (get-row job-id)] + (t/is (= "cancelled" (:status row))) + (t/is (nil? (:started-at row))) + (t/is (nil? (:completed-at row))))) + (t/testing "handler never invoked" + (t/is (empty? @received))))) + +(t/deftest runner-retry-with-backoff-respects-max-retries + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at :max-retries 1}) + defs {:echo-runner + (assoc (echo-job-def) + ::jobs/handler + (fn [_context _params] + (throw (ex-info "transient failure" + {:type ::wrk/retry + :delay (ct/duration {:millis 1000})}))))}] + (push-payload job-id scheduled-at) + + (t/testing "first retry attempt" + (run-one (mk-cfg {:defs defs})) + (let [row (get-row job-id)] + (t/is (= "retry" (:status row))) + (t/is (= 1 (:retry-num row))) + (t/is (= {:type :retry + :code :failed + :hint "transient failure" + :ex-type "retry"} (:error row))) + (t/testing "scheduled_at respects the backoff delay" + (t/is (> (inst-ms (:scheduled-at row)) (inst-ms (ct/now))))))) + + (t/testing "second attempt exhausts max-retries and fails" + (push-payload job-id (:scheduled-at (get-row job-id))) + (run-one (mk-cfg {:defs defs})) + (let [row (get-row job-id)] + (t/is (= "failed" (:status row))) + (t/is (= 1 (:retry-num row))) + (t/is (= {:type :retry + :code :failed + :hint "transient failure" + :ex-type "retry"} (:error row))))))) + +(t/deftest runner-retry-with-millis-delay-schedules-backoff + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at}) + defs {:echo-runner + (assoc (echo-job-def) + ::jobs/handler + (fn [_context _params] + (throw (ex-info "transient failure" + {:type ::wrk/retry + ;; plain Long literal, not a Duration + :delay 5000}))))}] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {:defs defs})) + (let [row (get-row job-id)] + (t/testing "Long millis delay is honored (int? covers Long)" + (t/is (= "retry" (:status row))) + (t/is (= 1 (:retry-num row))) + (t/testing "scheduled_at is ~5s in the future" + (let [delta (- (inst-ms (:scheduled-at row)) (inst-ms (ct/now)))] + (t/is (> delta 4000)) + (t/is (< delta 6000)))))))) + +(t/deftest runner-noop-retry-at-zero-schedules-base-delay + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at}) + defs {:echo-runner + (assoc (echo-job-def) + ::jobs/handler + (fn [_context _params] + (throw (ex-info "noop" + {:type ::wrk/retry + :strategy ::wrk/noop + :delay 5000}))))}] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {:defs defs})) + (let [row (get-row job-id)] + (t/testing "noop retry schedules instead of NPEing on take 0" + (t/is (= "retry" (:status row))) + (t/is (= 0 (:retry-num row))) + (t/testing "scheduled_at is ~5s in the future" + (let [delta (- (inst-ms (:scheduled-at row)) (inst-ms (ct/now)))] + (t/is (> delta 4000)) + (t/is (< delta 6000)))))))) + +(t/deftest runner-unhandled-exception-fails-when-no-retries-left + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at :max-retries 0}) + defs {:echo-runner + (assoc (echo-job-def) + ::jobs/handler + (fn [_context _params] (throw (ex-info "fatal" {}))))}] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {:defs defs})) + (let [row (get-row job-id)] + (t/is (= "failed" (:status row))) + (t/is (= {:type :internal + :code :failed + :hint "fatal" + :ex-type "ex-info"} (:error row)))))) + +(t/deftest runner-terminal-write-does-not-overwrite-orphan-failure + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at}) + defs {:echo-runner + (assoc (echo-job-def) + ::jobs/handler + (fn [_context _params] + ;; simulate the dispatcher marking the + ;; running job as aborted while the handler + ;; is executing + (th/db-update! :job + {:status "aborted" + :error (db/json jobs/orphan-error)} + {:id jobs/*job-id*}) + :ok))}] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {:defs defs})) + + (let [row (get-row job-id)] + (t/testing "the orphan abort is preserved (first-terminal-wins)" + (t/is (= "aborted" (:status row))) + (t/is (= jobs/orphan-error (:error row))) + (t/is (nil? (:completed-at row))))))) + +(t/deftest runner-claim-requires-current-scheduled-at + (let [at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:status "scheduled" :scheduled-at at}) + stale (ct/minus at (ct/duration {:minutes 6}))] + (t/testing "stale scheduled_at claims nothing" + (t/is (zero? (#'wrkr/claim-job (mk-cfg {}) job-id stale))) + (t/is (= "scheduled" (:status (get-row job-id))))) + (t/testing "current scheduled_at claims the row" + (t/is (= 1 (#'wrkr/claim-job (mk-cfg {}) job-id at))) + (t/is (= "running" (:status (get-row job-id))))))) + +(t/deftest runner-skips-stale-payload-after-reschedule + (let [stale-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at stale-at}) + fresh-at (ct/plus stale-at (ct/duration {:minutes 6}))] + ;; dispatcher reschedules: new scheduled_at + fresh payload + (th/db-update! :job {:status "scheduled" :scheduled-at fresh-at} {:id job-id}) + + (t/testing "stale payload is skipped, handler never invoked" + (push-payload job-id stale-at) + (run-one (mk-cfg {})) + (t/is (empty? @received)) + (let [row (get-row job-id)] + (t/is (= "scheduled" (:status row))) + (t/is (nil? (:started-at row))))) + + (t/testing "fresh payload still executes" + (push-payload job-id fresh-at) + (run-one (mk-cfg {})) + (t/is (= 1 (count @received))) + (t/is (= "completed" (:status (get-row job-id))))))) + +(t/deftest runner-unknown-job-name-fails-fast + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:name "no-such-job" + :scheduled-at scheduled-at + :max-retries 3})] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {})) + (let [row (get-row job-id)] + (t/testing "no retries burned on a permanently-unknown name" + (t/is (= "failed" (:status row))) + (t/is (= 0 (:retry-num row))) + (t/is (nil? (:completed-at row))) + (t/is (= "not-found" (:ex-type (:error row)))))))) + +(t/deftest runner-schema-violation-fails-fast-without-retry + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at + :max-retries 3 + :params {:object :snapshot + :deleted-at (ct/now) + :id "not-a-uuid"}})] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {})) + (let [row (get-row job-id)] + (t/testing "permanent schema violation fails fast" + (t/is (= "failed" (:status row))) + (t/is (= 0 (:retry-num row))) + (t/is (nil? (:completed-at row))) + (t/is (= "assertion" (:ex-type (:error row)))))))) + +(t/deftest invoke-executes-handlers-in-process-with-decoded-params + (let [raw-params {:object "snapshot" + :deleted-at "2026-01-01T00:00:00Z" + :id (str (uuid/next))}] + (reset! received []) + (let [result (jobs/invoke (merge (mk-cfg {}) + {::jobs/name :echo-runner + ::jobs/params raw-params}))] + (t/is (= 1 (count @received))) + (let [params' (first @received)] + (t/is (keyword? (:object params'))) + (t/is (= :snapshot (:object params'))) + (t/is (uuid? (:id params'))) + (t/is (ct/inst? (:deleted-at params')))) + (t/testing "invoke! returns the handler result" + (t/is (= (first @received) result)))))) + +(t/deftest runner-skips-malformed-payloads + (let [conn (rds/connect {::rds/client (get th/*system* :app.redis/client) + ::mtx/metrics (get th/*system* :app.metrics/metrics)})] + (try + (rds/rpush conn (queue-key) ["not-json" + (json/encode ["not-a-uuid" "not-an-inst"])]) + (finally + (rds/close conn)))) + (t/testing "non-JSON payload is skipped, handler never invoked" + (run-one (mk-cfg {})) + (t/is (empty? @received))) + (t/testing "JSON payload with wrong shape is skipped too" + (run-one (mk-cfg {})) + (t/is (empty? @received)))) + +(t/deftest runner-reads-the-reserved-completion-envelope + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + resource-id (uuid/next) + defs {:echo-runner + (assoc (echo-job-def) + ::jobs/handler + (fn [_context _params] + {:result {:business 1} + :resource-id resource-id}))}] + (th/db-insert! :storage-object {:id resource-id :backend "test"}) + (let [job-id (mk-job {:scheduled-at scheduled-at})] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {:defs defs})) + + (let [row (get-row job-id)] + (t/testing "the envelope splits: business result in result, resource on the row" + (t/is (= "completed" (:status row))) + (t/is (= {:business 1} (:result row))) + (t/is (= resource-id (:resource-id row)))) + + (t/testing "and the end event is still a plain outcome" + (t/is (= [{:kind "end" :payload {:outcome "completed"}}] + (drop 1 (get-events job-id))))))))) + +(t/deftest runner-records-the-lifecycle-events-of-a-successful-job + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at})] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {})) + (t/is (= [{:kind "start" :payload {:attempt 1}} + {:kind "end" :payload {:outcome "completed"}}] + (get-events job-id))))) + +(t/deftest runner-records-a-retry-event-before-the-next-attempt + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at :max-retries 3}) + defs {:echo-runner + (assoc (echo-job-def) + ::jobs/handler + (fn [_context _params] + (throw (ex-info "transient" {:type ::wrk/retry + :delay (ct/duration {:millis 1})}))))}] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {:defs defs})) + (t/is (= [{:kind "start" :payload {:attempt 1}} + {:kind "retry" :payload {:attempt 2 :reason "backoff"}}] + (get-events job-id))))) + +(t/deftest runner-records-a-noop-retry-as-the-same-attempt + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at :max-retries 3}) + defs {:echo-runner + (assoc (echo-job-def) + ::jobs/handler + (fn [_context _params] + (throw (ex-info "transient" {:type ::wrk/retry + :delay (ct/duration {:millis 1}) + :strategy ::wrk/noop}))))}] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {:defs defs})) + (t/testing "a noop consumed no budget, so the attempt does not move" + (t/is (= [{:kind "start" :payload {:attempt 1}} + {:kind "retry" :payload {:attempt 1 :reason "noop"}}] + (get-events job-id))) + (t/is (= 0 (:retry-num (get-row job-id))))))) + +(t/deftest deleting-a-job-deletes-its-events + (let [scheduled-at (ct/truncate (ct/now) :millisecond) + job-id (mk-job {:scheduled-at scheduled-at})] + (push-payload job-id scheduled-at) + (run-one (mk-cfg {})) + (t/is (= 2 (count (get-events job-id)))) + (th/db-force-delete :job {:id job-id}) + (t/is (= [] (get-events job-id))))) diff --git a/backend/test/backend_tests/worker_test.clj b/backend/test/backend_tests/worker_test.clj new file mode 100644 index 0000000000..02f525820d --- /dev/null +++ b/backend/test/backend_tests/worker_test.clj @@ -0,0 +1,68 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns backend-tests.worker-test + (:require + [app.common.schema :as sm] + [app.config :as cf] + [app.db :as db] + [app.jobs :as jobs] + [app.main :as main] + [app.metrics :as-alias mtx] + [app.msgbus :as mbus] + [app.worker :as wrk] + [app.worker.cron :as wcron] + [app.worker.dispatcher :as wdisp] + [app.worker.runner :as wrkr] + [clojure.test :as t])) + +;; The dispatcher's push and the runner's pop agree on the Redis list +;; only through this helper, so the test helpers use it too. That +;; keeps producer and consumer in sync by construction, but it also +;; means no other test would catch a malformed shape. This test pins +;; the physical format instead. + +(t/deftest queue-key-pins-the-physical-redis-shape + (t/testing "tenant and queue compose the documented key" + (t/is (= "penpot.worker.queue:acme:default" + (wrk/queue-key "acme" :default)))) + (t/testing "a string queue gives the same key as a keyword one" + (t/is (= "penpot.worker.queue:acme:default" + (wrk/queue-key "acme" "default")))) + (t/testing "hyphens travel through untouched" + (t/is (= "penpot.worker.queue:my-tenant:daily-cleanup" + (wrk/queue-key "my-tenant" :daily-cleanup))))) + +;; Booting the backend asserts these integrant keys, and assert-key +;; compiles the schema first: a ref with no registered schema crashes +;; the boot with :malli.core/invalid-schema instead of asserting. The +;; component schemas lean on the shared registry (db pool, metrics, +;; redis client, msgbus, job defs), so the registry dependencies must +;; be loaded for them to compile, like the full system does. +(t/deftest worker-component-schemas-resolve-every-ref + (t/testing "cron, dispatcher and runner schemas compile" + (t/is (sm/schema? (sm/schema ::mbus/msgbus))) + (doseq [s [@#'wcron/schema:params + @#'wdisp/schema:dispatcher + @#'wrkr/schema:params]] + (t/is (sm/schema? (sm/schema s)))))) + +(t/deftest the-binfile-runner-is-wired + (let [cfg (get main/worker-config [:app.main/binfile ::wrk/runner])] + + (t/is (some? cfg)) + + (t/testing "the heavy work of a user job does not share the default runner" + (t/is (= :binfile (::wrk/queue cfg))) + (t/is (= (cf/get :worker-binfile-parallelism 1) (::wrk/parallelism cfg)))) + + (t/testing "a job of a profile cannot store its events without a msgbus" + (t/is (contains? cfg ::mbus/msgbus))) + + (t/testing "and the runner resolves jobs, metrics and the pool" + (t/is (contains? cfg ::jobs/defs)) + (t/is (contains? cfg ::mtx/metrics)) + (t/is (contains? cfg ::db/pool))))) diff --git a/common/package.json b/common/package.json index 2569c0fb8c..c4c8c89efe 100644 --- a/common/package.json +++ b/common/package.json @@ -4,7 +4,7 @@ "license": "MPL-2.0", "author": "Kaleidos INC Sucursal en España SL", "private": true, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "type": "module", "repository": { "type": "git", diff --git a/common/pnpm-lock.yaml b/common/pnpm-lock.yaml index cd80e3a302..92e614fc48 100644 --- a/common/pnpm-lock.yaml +++ b/common/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' diff --git a/common/src/app/common/features.cljc b/common/src/app/common/features.cljc index e038356b4a..281ed4c9bb 100644 --- a/common/src/app/common/features.cljc +++ b/common/src/app/common/features.cljc @@ -133,6 +133,7 @@ :feature-text-editor-v2 "text-editor/v2" :feature-text-editor-v2-html-paste "text-editor/v2-html-paste" :feature-text-editor-wasm "text-editor-wasm/v1" + :feature-text-editor-wasm-html-paste "text-editor-wasm/v1-html-paste" :feature-render-wasm "render-wasm/v1" :feature-variants "variants/v1" :feature-token-input "tokens/numeric-input" diff --git a/common/src/app/common/files/builder.cljc b/common/src/app/common/files/builder.cljc index 2e02166a93..d9b86ce36d 100644 --- a/common/src/app/common/files/builder.cljc +++ b/common/src/app/common/files/builder.cljc @@ -414,7 +414,14 @@ (defn add-shape [state params] (let [obj (-> params - (d/update-when :svg-attrs csvg/attrs->props) + (d/update-when :svg-attrs csvg/attrs->kebab-props) + (d/update-when :svg-defs + (fn [defs] + (if (map? defs) + (d/mapm (fn [_ node] (csvg/normalize-def-node node)) defs) + defs))) + (cond-> (= :svg-raw (:type params)) + (d/update-when :content csvg/kebabize-content-node)) (types.shape/setup-shape) (assign-shape-name state))] (-> state diff --git a/common/src/app/common/files/changes.cljc b/common/src/app/common/files/changes.cljc index 8e4bbb67d9..b2b0e6de60 100644 --- a/common/src/app/common/files/changes.cljc +++ b/common/src/app/common/files/changes.cljc @@ -524,6 +524,59 @@ (let [result (reduce #(or (process-change %1 %2) %1) data items)] (reduce process-touched-change result @*touched-changes*))))) +(defn- get-frame-descendant-ids + "Ids below `id` whose `:frame-id` a move rewrites; the walk stops at + boards, as `update-frame-id` in `:mov-objects` does." + [objects id] + (let [shape (get objects id)] + (when-not (cfh/frame-shape? shape) + (mapcat #(cons % (get-frame-descendant-ids objects %)) + (get shape :shapes))))) + +(defn- get-change-candidate-ids + [old-objects new-objects change] + (case (:type change) + :add-obj + (let [parent-id (or (:parent-id change) (:frame-id change))] + [(:id change) + (if (contains? new-objects parent-id) parent-id uuid/zero)]) + + (:mod-obj :fix-obj) + [(:id change)] + + :del-obj + (let [id (:id change) + target (get old-objects id)] + (when (some? target) + (into [id (or (:parent-id target) (:frame-id target))] + (cfh/get-children-ids old-objects id)))) + + :mov-objects + (let [shapes (:shapes change)] + (-> [(:parent-id change)] + (into shapes) + (into (keep #(dm/get-in old-objects [% :parent-id])) shapes) + (into (mapcat #(get-frame-descendant-ids new-objects %)) shapes))) + + :reg-objects + (let [shapes (:shapes change)] + (into (vec shapes) (mapcat #(cfh/get-parent-ids new-objects %)) shapes)) + + :reorder-children + [(:parent-id change)] + + nil)) + +(defn get-candidate-object-ids + "Ids on `page-id` that `changes` may have added, edited or removed; + it may hold unchanged ids but never misses a changed one." + [old-objects new-objects page-id changes] + (into #{} + (comp (filter #(= page-id (:page-id %))) + (mapcat #(get-change-candidate-ids old-objects new-objects %)) + (remove nil?)) + changes)) + ;; --- Comment Threads (defmethod process-change :set-comment-thread-position @@ -653,6 +706,7 @@ [objects {:keys [parent-id shapes allow-altering-copies] :as change}] (if-let [old-shapes (dm/get-in objects [parent-id :shapes])] ;; Component sync owns copy child ordering. + ;; The worker replays this on trimmed shapes: see `app.worker.trim`. (if (and (not allow-altering-copies) (ctk/in-component-copy? (get objects parent-id))) objects @@ -736,6 +790,7 @@ (empty? children) group + ;; The worker replays this on trimmed shapes: see `app.worker.trim`. (= :bool (:type group)) (path/update-bool-shape group objects) @@ -764,6 +819,7 @@ ;; Avoid placing a shape as a direct or indirect child of itself, or ;; inside its main component if it's in a copy, or inside a copy, or ;; from a copy + ;; The worker replays this on trimmed shapes: see `app.worker.trim`. (is-valid-move? [objects shape-id] (let [invalid-targets (calculate-invalid-targets objects shape-id) shape (get objects shape-id)] diff --git a/common/src/app/common/files/changes_builder.cljc b/common/src/app/common/files/changes_builder.cljc index 22f7369a19..94f50fccef 100644 --- a/common/src/app/common/files/changes_builder.cljc +++ b/common/src/app/common/files/changes_builder.cljc @@ -187,7 +187,8 @@ (assert (check-changes changes) "expected valid changes") - (if-let [file-data (::file-data (meta changes))] + (if-let [file-data (and (not (::skip-local? (meta changes))) + (::file-data (meta changes)))] (let [library-data (::library-data (meta changes)) index (::applied-changes-count (meta changes)) redo-changes (:redo-changes changes) @@ -206,6 +207,11 @@ ::applied-changes-count (count redo-changes))) changes)) +(defn skip-local + "Stop applying changes to the builder objects." + [changes skip?] + (vary-meta changes assoc ::skip-local? skip?)) + (defn- without-changes-local "Append changes through `f` without applying them to the mounted page's working state." diff --git a/common/src/app/common/files/helpers.cljc b/common/src/app/common/files/helpers.cljc index 2c8ff25600..2b83fa75c5 100644 --- a/common/src/app/common/files/helpers.cljc +++ b/common/src/app/common/files/helpers.cljc @@ -162,14 +162,15 @@ ([objects id {:keys [ignore-children-fn] ;;ignore-children-fn should receive a shape and return a boolean :or {ignore-children-fn (constantly false)}}] - (letfn [(get-children-ids-rec [id processed] - (when-not (contains? processed id) - (when-let [shapes (as-> (get objects id) $ - (:shapes $) - (remove ignore-children-fn $) - (some-> $ vec))] - (into shapes (mapcat #(get-children-ids-rec % (conj processed id))) shapes))))] - (get-children-ids-rec id #{})))) + (letfn [(collect [result id processed] + (if (contains? processed id) + result + (let [shapes (into [] (remove ignore-children-fn) (dm/get-in objects [id :shapes])) + processed (conj processed id)] + (reduce #(collect %1 %2 processed) + (reduce conj! result shapes) + shapes))))] + (persistent! (collect (transient []) id #{}))))) (defn get-children-ids-with-self [objects id] diff --git a/common/src/app/common/files/migrations.cljc b/common/src/app/common/files/migrations.cljc index 6a82e463ef..7033bfcb7d 100644 --- a/common/src/app/common/files/migrations.cljc +++ b/common/src/app/common/files/migrations.cljc @@ -88,7 +88,9 @@ (-> file (assoc :data data) - (update :migrations set/union diff) + ;; `:migrations` is an ordered set everywhere; keep it that way + ;; even if a caller hands a plain set. + (update :migrations #(into (d/ordered-set) (set/union % diff))) (vary-meta assoc ::migrated (not-empty diff))))) (defn generate-migrations-from-version @@ -1827,21 +1829,6 @@ {})] (cfcp/sync-component-id-with-ref-shape data libraries))) -(defmethod migrate-data "0021-fix-shape-svg-attrs" - [data _] - (some-> cfeat/*new* (swap! conj "fdata/shape-data-type")) - (letfn [(update-object [object] - (-> object - (d/update-when :svg-attrs csvg/attrs->props) - (d/update-when :svg-viewbox grc/make-rect))) - - (update-container [container] - (d/update-when container :objects d/update-vals update-object))] - - (-> data - (update :pages-index d/update-vals update-container) - (d/update-when :components d/update-vals update-container)))) - ;; Re-run the 0019 and 0020 fixers after normalizing :component-root. ;; Migrations 0019 and 0020 missed shapes with an explicit :component-root ;; false because subcopy-head? expects nil. Normalize first, then re-run. @@ -2335,6 +2322,52 @@ (update :pages-index d/update-vals update-container) (d/update-when :components d/update-vals update-container))))) +(defmethod migrate-data "0035-fix-shape-svg-attrs-and-defs" + ;; Stored svg attribute keys are kebab-case (issue #11947): the v3 reader + ;; already outputs kebab and birth normalizes any outside spelling, so + ;; export/import converges to the same state. Old birth and the cleaner + ;; era stored camelCase React prop names instead, which the reader + ;; kebab-ized on import beyond repair. Convert once, with the same + ;; birth transforms: `:svg-attrs` and `:svg-defs` node attrs go through + ;; the whitelist+kebab transform (unknown attrs drop, same as birth), + ;; while svg-raw `:content` attrs are kebab-ized spelling-only (unknown + ;; keys kept: the sidebar reads keys outside the whitelist). Kebab + ;; input passes through unchanged, so re-running is safe. This replaces + ;; "0021-fix-shape-svg-attrs" (id withdrawn from `available-migrations` + ;; below): files that recorded it migrate again through this one. + ;; Node tags in defs and svg-raw content are stored as keywords; files + ;; that hold them as strings get them converted here, since the closed + ;; node schemas reject strings. `:svg-transform` held as a plain map + ;; becomes a matrix, because the svg defs renderer multiplies it as one. + ;; Non-lowercase XML ids stay lossy: the reader lowercases them before + ;; anything runs. + [data _] + (some-> cfeat/*new* (swap! conj "fdata/shape-data-type")) + (letfn [(migrate-defs [defs] + (if (map? defs) + (d/mapm (fn [_ node] (csvg/normalize-def-node node)) defs) + defs)) + + (migrate-shape [shape] + (-> shape + (d/update-when :svg-attrs + (fn [attrs] + (if (map? attrs) + (csvg/attrs->kebab-props attrs) + attrs))) + (d/update-when :svg-defs migrate-defs) + (d/update-when :svg-viewbox grc/make-rect) + (d/update-when :svg-transform gmt/coerce) + (cond-> (= :svg-raw (:type shape)) + (d/update-when :content csvg/kebabize-content-node)))) + + (migrate-container [container] + (d/update-when container :objects d/update-vals migrate-shape))] + + (-> data + (update :pages-index d/update-vals migrate-container) + (d/update-when :components d/update-vals migrate-container)))) + (def available-migrations (into (d/ordered-set) ["legacy-2" @@ -2413,7 +2446,6 @@ "0018-remove-unneeded-objects-from-components" "0019-fix-missing-swap-slots" "0020-sync-component-id-with-near-main" - "0021-fix-shape-svg-attrs" "0022-normalize-component-root-and-resync" "0023-repair-token-themes-with-inexistent-sets" "0024b-fix-stroke-cap-placement" @@ -2426,4 +2458,5 @@ "0031-migrate-stroke-width-token-attr" "0032-fix-children-not-in-parent" "0033-normalize-main-instance-names" - "0034-fix-path-content-offset"])) + "0034-fix-path-content-offset" + "0035-fix-shape-svg-attrs-and-defs"])) diff --git a/common/src/app/common/files/page_diff.cljc b/common/src/app/common/files/page_diff.cljc index 32279b746e..3a86f20706 100644 --- a/common/src/app/common/files/page_diff.cljc +++ b/common/src/app/common/files/page_diff.cljc @@ -12,7 +12,9 @@ [clojure.set :as set])) (defn calculate-page-diff - [old-page page check-attrs] + "Only the objects in `object-ids` are compared; guides are always + compared in full." + [old-page page check-attrs object-ids] (let [old-objects (get old-page :objects) old-guides (or (get old-page :guides) []) @@ -83,8 +85,7 @@ changed-object-ids (into #{} (filter changed-object?) - (set/union (set (keys old-objects)) - (set (keys new-objects)))) + object-ids) changed-guides-ids (into #{} diff --git a/common/src/app/common/files/repair.cljc b/common/src/app/common/files/repair.cljc index f0a52b788c..f0a3e80f1b 100644 --- a/common/src/app/common/files/repair.cljc +++ b/common/src/app/common/files/repair.cljc @@ -281,9 +281,26 @@ (pcb/with-file-data file-data) (pcb/update-shapes [(:id shape)] repair-shape)))) +(defn- find-lost-main + "Return the main of the local copy root `root-shape` when its component's + `:main-instance-id` points to a missing shape and the main is still on + the component's page; nil otherwise." + [file-data root-shape] + (when (= (:component-file root-shape) (:id file-data)) + (let [component (ctkl/get-component file-data (:component-id root-shape))] + (when (and (some? component) + (nil? (ctf/get-component-root file-data component))) + (d/seek #(and (ctk/main-instance? %) + (= (:component-id %) (:id component)) + (= (:component-file %) (:id file-data))) + (ctn/shapes-seq (ctf/get-component-page file-data component))))))) + (defmethod repair-error :ref-shape-not-found [_ {:keys [shape page-id] :as error} file-data libraries] - (let [matching-shape (let [page (ctpl/get-page file-data page-id) + (let [lost-main (let [page (ctpl/get-page file-data page-id)] + (find-lost-main file-data (ctn/get-component-shape (:objects page) shape))) + + matching-shape (let [page (ctpl/get-page file-data page-id) root-shape (ctn/get-component-shape (:objects page) shape) component-file (if (= (:component-file root-shape) (:id file-data)) file-data @@ -321,13 +338,24 @@ (log/debug :hint " -> detach shape" :shape-id (:id shape)) (ctk/detach-shape shape))] - ;; If the shape still refers to the remote component, try to find the corresponding near one + ;; If the copy's component has lost track of its main, point it back to the main. Else, if + ;; the shape still refers to the remote component, try to find the corresponding near one ;; and link to it. If not, detach the shape. (log/debug :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id) - (if (some? matching-shape) + (cond + (some? lost-main) + (do + (log/debug :hint " -> assign main-instance-id" :main-instance-id (:id lost-main)) + (-> (pcb/empty-changes nil page-id) + (pcb/with-library-data file-data) + (pcb/update-component (:component-id lost-main) #(assoc % :main-instance-id (:id lost-main))))) + + (some? matching-shape) (-> (pcb/empty-changes nil page-id) (pcb/with-file-data file-data) (pcb/update-shapes [(:id shape)] reassign-shape)) + + :else (let [page (ctpl/get-page file-data page-id) shape-ids (cfh/get-children-ids-with-self (:objects page) (:id shape))] (-> (pcb/empty-changes nil page-id) diff --git a/common/src/app/common/files/shapes_builder.cljc b/common/src/app/common/files/shapes_builder.cljc index 9577fb528d..ad2d90f610 100644 --- a/common/src/app/common/files/shapes_builder.cljc +++ b/common/src/app/common/files/shapes_builder.cljc @@ -122,7 +122,11 @@ - The referencing gradient's attributes override the base ones - If the referencing gradient has stops, they replace the base stops - Returns the defs map with all gradient href references resolved." + Returns the defs map with all gradient href references resolved. + + NOTE: reads raw parser keys (`:xlink:href` with colon, + `:gradientTransform` camelCase). Runs before birth normalization; + do not switch to kebab." [defs] (letfn [(resolve-gradient [gradient-id gradient-node defs visited] (if (contains? visited gradient-id) @@ -220,7 +224,9 @@ (csvg/extract-defs)) ;; Resolve gradient href references in all defs before processing shapes - def-nodes (resolve-gradient-href def-nodes) + ;; then normalize them to the stored kebab convention + def-nodes (d/mapm (fn [_ node] (csvg/normalize-def-node node)) + (resolve-gradient-href def-nodes)) ;; In penpot groups have the size of their children. To ;; respect the imported svg size and empty space let's create @@ -269,8 +275,9 @@ (defn create-raw-svg [name frame-id {:keys [x y width height offset-x offset-y defs] :as svg-data} {:keys [attrs] :as data}] - (let [props (csvg/attrs->props attrs) - vbox (grc/make-rect offset-x offset-y width height)] + (let [props (csvg/attrs->kebab-props attrs) + content (csvg/kebabize-content-node data) + vbox (grc/make-rect offset-x offset-y width height)] (cts/setup-shape {:type :svg-raw :name name @@ -279,7 +286,7 @@ :height height :x x :y y - :content data + :content content :svg-attrs props :svg-viewbox vbox :svg-defs defs}))) @@ -288,7 +295,7 @@ [id frame-id parent-id {:keys [name x y width height offset-x offset-y attrs defs] :as svg-data}] (let [props (-> (dissoc attrs :viewBox :view-box :xmlns) (d/without-keys csvg/inheritable-props) - (csvg/attrs->props))] + (csvg/attrs->kebab-props))] (cts/setup-shape {:id id :type :group @@ -325,7 +332,7 @@ (let [transform (csvg/parse-transform (:transform attrs)) attrs (-> attrs (d/without-keys csvg/inheritable-props) - (csvg/attrs->props)) + (csvg/attrs->kebab-props)) vbox (grc/make-rect offset-x offset-y width height)] (cts/setup-shape {:type :group @@ -365,7 +372,7 @@ points (grc/rect->points selrect) origin (gpt/negate (gpt/point svg-data)) attrs (-> (dissoc attrs :d :transform) - (csvg/attrs->props))] + (csvg/attrs->kebab-props))] (-> (cts/setup-shape {:type :path :name name @@ -434,7 +441,7 @@ (update :y - (:y origin))) props (-> (dissoc attrs :x :y :width :height :rx :ry :transform) - (csvg/attrs->props)) + (csvg/attrs->kebab-props)) radius-attrs (parse-radius-attrs attrs)] (cts/setup-shape @@ -480,7 +487,7 @@ (* 2 rx) (* 2 ry)) props (-> (dissoc attrs :cx :cy :r :rx :ry :transform) - (csvg/attrs->props))] + (csvg/attrs->kebab-props))] (cts/setup-shape (-> (calculate-rect-metadata rect transform) @@ -510,7 +517,7 @@ (update :x - (:x origin)) (update :y - (:y origin))) props (-> (dissoc attrs :x :y :width :height :href :xlink:href) - (csvg/attrs->props))] + (csvg/attrs->kebab-props))] (when (some? image-data) (cts/setup-shape @@ -545,17 +552,17 @@ ;; Only create an opacity if the color is set. Otherwise can create problems down the line (and (or (clr/color-string? color-attr) (clr/color-string? color-style)) - (dm/get-in shape [:svg-attrs :fillOpacity])) - (-> (update :svg-attrs dissoc :fillOpacity) - (update-in [:svg-attrs :style] dissoc :fillOpacity) - (assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :fillOpacity]) + (dm/get-in shape [:svg-attrs :fill-opacity])) + (-> (update :svg-attrs dissoc :fill-opacity) + (update-in [:svg-attrs :style] dissoc :fill-opacity) + (assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :fill-opacity]) (d/parse-double 1)))) (and (or (clr/color-string? color-attr) (clr/color-string? color-style)) - (dm/get-in shape [:svg-attrs :style :fillOpacity])) - (-> (update-in [:svg-attrs :style] dissoc :fillOpacity) - (update :svg-attrs dissoc :fillOpacity) - (assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fillOpacity]) + (dm/get-in shape [:svg-attrs :style :fill-opacity])) + (-> (update-in [:svg-attrs :style] dissoc :fill-opacity) + (update :svg-attrs dissoc :fill-opacity) + (assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fill-opacity]) (d/parse-double 1))))))) (defn- setup-stroke @@ -573,30 +580,30 @@ opacity (when (some? color) (d/parse-double - (or (:strokeOpacity attrs) - (:strokeOpacity style)) + (or (:stroke-opacity attrs) + (:stroke-opacity style)) 1)) width (when (some? color) (d/parse-double - (or (:strokeWidth attrs) - (:strokeWidth style)) + (or (:stroke-width attrs) + (:stroke-width style)) 1)) - linecap (or (get attrs :strokeLinecap) - (get style :strokeLinecap)) + linecap (or (get attrs :stroke-linecap) + (get style :stroke-linecap)) linecap (some-> linecap str/trim keyword) attrs (-> attrs (cond-> (some? color) - (dissoc :stroke :strokeWidth :strokeOpacity)) + (dissoc :stroke :stroke-width :stroke-opacity)) (update :style (fn [style] (-> style (cond-> (some? color) - (dissoc :stroke :strokeWidth :strokeOpacity))))) + (dissoc :stroke :stroke-width :stroke-opacity))))) (d/without-nils))] (cond-> (assoc shape :svg-attrs attrs) @@ -629,13 +636,13 @@ (assoc :opacity (-> (dm/get-in shape [:svg-attrs :style :opacity]) (d/parse-double 1)))) - (dm/get-in shape [:svg-attrs :mixBlendMode]) - (-> (update :svg-attrs dissoc :mixBlendMode) - (assoc :blend-mode (-> (dm/get-in shape [:svg-attrs :mixBlendMode]) assert-valid-blend-mode))) + (dm/get-in shape [:svg-attrs :mix-blend-mode]) + (-> (update :svg-attrs dissoc :mix-blend-mode) + (assoc :blend-mode (-> (dm/get-in shape [:svg-attrs :mix-blend-mode]) assert-valid-blend-mode))) - (dm/get-in shape [:svg-attrs :style :mixBlendMode]) - (-> (update-in [:svg-attrs :style] dissoc :mixBlendMode) - (assoc :blend-mode (-> (dm/get-in shape [:svg-attrs :style :mixBlendMode]) assert-valid-blend-mode))))) + (dm/get-in shape [:svg-attrs :style :mix-blend-mode]) + (-> (update-in [:svg-attrs :style] dissoc :mix-blend-mode) + (assoc :blend-mode (-> (dm/get-in shape [:svg-attrs :style :mix-blend-mode]) assert-valid-blend-mode))))) (defn setup-other [shape] (cond-> shape diff --git a/common/src/app/common/files/validate.cljc b/common/src/app/common/files/validate.cljc index 6654e28508..78f300b738 100644 --- a/common/src/app/common/files/validate.cljc +++ b/common/src/app/common/files/validate.cljc @@ -1030,6 +1030,58 @@ [file] (update file :data ctf/check-file-data)) +(def ^:private file-level-change-types + "Change types that leave every page as it was." + #{:del-page :mov-page + :add-color :mod-color :del-color + :add-media :mod-media :del-media + :add-component :mod-component :del-component :restore-component :purge-component + :add-typography :mod-typography :del-typography + :set-tokens-lib :set-token :set-token-set :set-token-theme :set-tokens-status + :rename-token-set-group :move-token-set :move-token-set-group + :set-base-font-size :set-tokens-source}) + +(defn- touched-page-ids + "Ids of the pages `changes` modify, or nil if a change has a type not + known here." + [changes] + (reduce (fn [page-ids {:keys [type page-id id page object-type object-id]}] + (case type + (:add-obj :mod-obj :del-obj :fix-obj :mov-objects :reorder-children :reg-objects + :set-guide :set-flow :set-default-grid :set-comment-thread-position) + (cond-> page-ids (some? page-id) (conj page-id)) + + :add-page + (conj page-ids (or id (:id page))) + + :mod-page + (conj page-ids id) + + :set-plugin-data + (case object-type + :page (conj page-ids object-id) + :shape (cond-> page-ids (some? page-id) (conj page-id)) + page-ids) + + (if (contains? file-level-change-types type) + page-ids + (reduced nil)))) + #{} + changes)) + +(defn validate-file-schema-affected! + "Like `validate-file-schema!` but checks, of the pages, only those that + `changes` modify; the rest of the file data is checked whole. If a change + has a type not known here, checks the whole file. Returns `file`." + [file changes] + (if-let [page-ids (touched-page-ids changes)] + (do + (-> (:data file) + (update :pages-index select-keys page-ids) + (ctf/check-file-data)) + file) + (validate-file-schema! file))) + (defn validate-file! "Validate full referential integrity and semantic coherence on file data. diff --git a/common/src/app/common/flags.cljc b/common/src/app/common/flags.cljc index 26f1d54390..202b41b1d1 100644 --- a/common/src/app/common/flags.cljc +++ b/common/src/app/common/flags.cljc @@ -60,8 +60,8 @@ (def varia "Rest of the flags" #{:audit-log - :audit-log-archive - :audit-log-gc + ;; Enables shipping audit_log chunks to Nexus. + :nexus :audit-log-logger :auto-file-snapshot ;; enables the `/api/doc` endpoint that lists all the rpc methods available. @@ -96,6 +96,10 @@ :file-validation ;; Reports the referential integrity validation errors internally. :soft-file-validation + ;; Disabled by default; never enable it in production. Lets a save + ;; carry a marker that makes the backend fail it with a chosen error + ;; (frontend `debug.failNextSave`). + :debug-save-faults ;; TODO: deprecate this flag and consolidate the code :frontend-svgo ;; TODO: deprecate this flag and consolidate the code diff --git a/common/src/app/common/geom/matrix.cljc b/common/src/app/common/geom/matrix.cljc index 9e7239f01d..ab28e019ed 100644 --- a/common/src/app/common/geom/matrix.cljc +++ b/common/src/app/common/geom/matrix.cljc @@ -60,6 +60,19 @@ ([a b c d e f] (pos->Matrix a b c d e f))) +(defn coerce + "Returns `v` as a Matrix. Matrices pass through; a plain map with + numeric `:a` to `:f` (the form legacy and JSON-decoded data keep) is + turned into one. Anything else is returned unchanged." + [v] + (if (and (map? v) (not (matrix? v))) + (let [{:keys [a b c d e f]} v] + (if (and (number? a) (number? b) (number? c) + (number? d) (number? e) (number? f)) + (matrix a b c d e f) + v)) + v)) + (def ^:private schema:matrix-attrs [:map {:title "MatrixAttrs"} [:a ::sm/safe-double] diff --git a/common/src/app/common/logic/libraries.cljc b/common/src/app/common/logic/libraries.cljc index 3afa807ead..fee7e718c2 100644 --- a/common/src/app/common/logic/libraries.cljc +++ b/common/src/app/common/logic/libraries.cljc @@ -1448,6 +1448,12 @@ container {:type :reg-objects :shapes all-parents})) + ;; Conj reg before the deletions, so undo recalculates the parents + ;; after removing the shapes. + changes' (update changes' :undo-changes conj (make-change + container + {:type :reg-objects + :shapes all-parents})) changes' (reduce del-obj-change changes' new-shapes)] (if (and (ctk/touched-group? parent-shape :shapes-group) omit-touched?) @@ -1554,6 +1560,12 @@ changes' (update changes' :redo-changes conj {:type :reg-objects :component-id (:id component) :shapes all-parents}) + ;; Conj reg before the other undo changes, so undo recalculates the + ;; parents after removing the shapes. + changes' (update changes' :undo-changes conj (make-change + component-container + {:type :reg-objects + :shapes all-parents})) changes' (reduce mod-obj-change changes' updated-shapes) changes' (reduce del-obj-change changes' new-shapes)] @@ -1601,6 +1613,12 @@ changes' (-> (reduce add-redo-change changes ids) (update :redo-changes conj (make-change + container + {:type :reg-objects + :shapes (vec parents)})) + ;; Conj reg before the additions, so undo recalculates + ;; the parents after restoring the shapes. + (update :undo-changes conj (make-change container {:type :reg-objects :shapes (vec parents)}))) @@ -1803,15 +1821,19 @@ container {:type :reg-objects :shapes all-parents})) - (update :undo-changes conj (make-change - container - {:type :mod-obj - :id (:id dest-shape) - :operations (vec uoperations)})) - (update :undo-changes concat [(make-change - container - {:type :reg-objects - :shapes all-parents})]) + ;; Conj reg before mod, so undo restores the shape and then its + ;; parents. Do not append with concat: it nests one lazy seq per + ;; shape and overflows the stack on big syncs. + (update :undo-changes conj + (make-change + container + {:type :reg-objects + :shapes all-parents}) + (make-change + container + {:type :mod-obj + :id (:id dest-shape) + :operations (vec uoperations)})) (pcb/apply-changes-local)))) @@ -2842,11 +2864,24 @@ page {:id (:id file) :data file} - libraries)))] + libraries))) + + ;; The attributes of `keep-props-values` get their value restored in the + ;; new shape. Keep too the touched groups they belong to, so the override + ;; stays marked as such (otherwise a later sync would overwrite it). + kept-touched-groups + (->> (keys keep-props-values) + (keep #(ctk/resolve-sync-group (:type new-shape) %)) + (filter #(contains? (:touched shape) %)) + (into #{})) + + new-shape (update new-shape :touched (fnil into #{}) kept-touched-groups)] [new-shape (-> changes ;; Restore the properties - (pcb/update-shapes [(:id new-shape)] #(d/patch-object % keep-props-values)) + (pcb/update-shapes [(:id new-shape)] + #(d/patch-object % keep-props-values) + {:ignore-touched true}) ;; Touched attribute has already been set above ;; We need to set the same index as the original shape (pcb/change-parent (:parent-id shape) [new-shape] index {:allow-altering-copies true @@ -3100,12 +3135,12 @@ changes)) (defn generate-duplicate-shape-change - ([changes objects page unames update-unames! ids ids-map obj delta level-delta libraries library-data file-id] - (generate-duplicate-shape-change changes objects page unames update-unames! ids ids-map obj delta level-delta libraries library-data file-id (:frame-id obj) (:parent-id obj) false false true nil)) - ([changes objects page unames update-unames! ids ids-map obj delta level-delta libraries library-data file-id variant-props] - (generate-duplicate-shape-change changes objects page unames update-unames! ids ids-map obj delta level-delta libraries library-data file-id (:frame-id obj) (:parent-id obj) false false true variant-props)) + ([changes objects page ids ids-map obj delta level-delta libraries library-data file-id] + (generate-duplicate-shape-change changes objects page ids ids-map obj delta level-delta libraries library-data file-id (:frame-id obj) (:parent-id obj) false false true nil)) + ([changes objects page ids ids-map obj delta level-delta libraries library-data file-id variant-props] + (generate-duplicate-shape-change changes objects page ids ids-map obj delta level-delta libraries library-data file-id (:frame-id obj) (:parent-id obj) false false true variant-props)) - ([changes objects page unames update-unames! ids ids-map obj delta level-delta libraries library-data file-id frame-id parent-id duplicating-component? child? remove-swap-slot? variant-props] + ([changes objects page ids ids-map obj delta level-delta libraries library-data file-id frame-id parent-id duplicating-component? child? remove-swap-slot? variant-props] (cond (nil? obj) changes @@ -3218,8 +3253,6 @@ (generate-duplicate-shape-change changes (:objects page') page - unames - update-unames! ids ids-map child @@ -3246,8 +3279,6 @@ move to the desired position, and recalculate parents and frames as needed." [changes all-objects page ids delta libraries library-data file-id & {:keys [variant-props alt-duplication?]}] (let [shapes (map (d/getf all-objects) ids) - unames (volatile! (cfh/get-used-names (:objects page))) - update-unames! (fn [new-name] (vswap! unames conj new-name)) all-ids (reduce #(into %1 (cons %2 (cfh/get-children-ids all-objects %2))) (d/ordered-set) ids) ;; We need ids-map for remapping the grid layout. But when duplicating the guides @@ -3277,8 +3308,6 @@ (reduce #(generate-duplicate-shape-change %1 all-objects page - unames - update-unames! ids ids-map %2 diff --git a/common/src/app/common/render_wasm/api/props.cljs b/common/src/app/common/render_wasm/api/props.cljs index c8f71571e5..5a5be8563f 100644 --- a/common/src/app/common/render_wasm/api/props.cljs +++ b/common/src/app/common/render_wasm/api/props.cljs @@ -74,9 +74,7 @@ ;; to its default dash pattern when no override is stored. dash (or (:stroke-dash stroke) -1) gap (or (:stroke-gap stroke) -1) - offset (mem/alloc types.fills.impl/FILL-U8-SIZE) - heap (mem/get-heap-u8) - dview (js/DataView. (.-buffer heap))] + offset (mem/alloc types.fills.impl/FILL-U8-SIZE)] (case align :inner (h/call wasm/internal-module "_add_shape_inner_stroke" width style cap-start cap-end dash gap) :outer (h/call wasm/internal-module "_add_shape_outer_stroke" width style cap-start cap-end dash gap) @@ -94,17 +92,17 @@ (cond (some? gradient) - (do (types.fills.impl/write-gradient-fill offset dview opacity gradient) + (do (types.fills.impl/write-gradient-fill offset (mem/get-data-view) opacity gradient) (h/call wasm/internal-module "_add_shape_stroke_fill") nil) (some? image) - (do (types.fills.impl/write-image-fill offset dview opacity image) + (do (types.fills.impl/write-image-fill offset (mem/get-data-view) opacity image) (h/call wasm/internal-module "_add_shape_stroke_fill") (get image :id)) (some? color) - (do (types.fills.impl/write-solid-fill offset dview opacity color) + (do (types.fills.impl/write-solid-fill offset (mem/get-data-view) opacity color) (h/call wasm/internal-module "_add_shape_stroke_fill") nil)))))) strokes)) @@ -157,9 +155,9 @@ (defn set-shape-svg-attrs [attrs] (let [style (:style attrs) - fill-rule (-> (or (:fillRule style) (:fillRule attrs)) sr/translate-fill-rule) - stroke-linecap (-> (or (:strokeLinecap style) (:strokeLinecap attrs)) sr/translate-stroke-linecap) - stroke-linejoin (-> (or (:strokeLinejoin style) (:strokeLinejoin attrs)) sr/translate-stroke-linejoin) + fill-rule (-> (or (:fill-rule style) (:fill-rule attrs)) sr/translate-fill-rule) + stroke-linecap (-> (or (:stroke-linecap style) (:stroke-linecap attrs)) sr/translate-stroke-linecap) + stroke-linejoin (-> (or (:stroke-linejoin style) (:stroke-linejoin attrs)) sr/translate-stroke-linejoin) fill-none (= "none" (or (:fill style) (:fill attrs)))] (h/call wasm/internal-module "_set_shape_svg_attrs" fill-rule stroke-linecap stroke-linejoin fill-none))) @@ -179,15 +177,14 @@ buffer (js/Uint8Array. padded-size)] (path/write-to content (.-buffer buffer) 0) (h/call wasm/internal-module "_start_shape_path_buffer") - (let [heapu32 (mem/get-heap-u32)] - (loop [offset 0] - (when (< offset padded-size) - (let [end (min padded-size (+ offset (* chunk-size 4))) - chunk (.subarray buffer offset end) - chunk-u32 (js/Uint32Array. chunk.buffer chunk.byteOffset (quot (.-length chunk) 4)) - offset-size (.-length chunk-u32) - heap-offset (mem/alloc->offset-32 (* 4 offset-size))] - (.set heapu32 chunk-u32 heap-offset) - (h/call wasm/internal-module "_set_shape_path_chunk_buffer") - (recur end))))) + (loop [offset 0] + (when (< offset padded-size) + (let [end (min padded-size (+ offset (* chunk-size 4))) + chunk (.subarray buffer offset end) + chunk-u32 (js/Uint32Array. chunk.buffer chunk.byteOffset (quot (.-length chunk) 4)) + offset-size (.-length chunk-u32) + heap-offset (mem/alloc->offset-32 (* 4 offset-size))] + (.set (mem/get-heap-u32) chunk-u32 heap-offset) + (h/call wasm/internal-module "_set_shape_path_chunk_buffer") + (recur end)))) (h/call wasm/internal-module "_set_shape_path_buffer"))) diff --git a/common/src/app/common/render_wasm/api/select.cljs b/common/src/app/common/render_wasm/api/select.cljs new file mode 100644 index 0000000000..f461c06302 --- /dev/null +++ b/common/src/app/common/render_wasm/api/select.cljs @@ -0,0 +1,28 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.common.render-wasm.api.select + "WASM cursor selection. + Note that setters in `app.common.render-wasm.api.props` assume the caller + selected first." + (:require + [app.common.render-wasm.helpers :as h] + [app.common.render-wasm.wasm :as wasm] + [app.common.uuid :as uuid])) + +(defn use-shape + "Makes the shape with `id` current in WASM so subsequent setters apply to it. + This is a no-op when there is no WASM context." + [id] + ;; Use `wasm/live?` (not `initialized?`) so context-restore reload can + ;; select shapes while `reloading?` still blocks external app callers. + (when (wasm/live?) + (let [buffer (uuid/get-u32 id)] + (h/call wasm/internal-module "_use_shape" + (aget buffer 0) + (aget buffer 1) + (aget buffer 2) + (aget buffer 3))))) diff --git a/common/src/app/common/render_wasm/api/upload.cljs b/common/src/app/common/render_wasm/api/upload.cljs index f7a26712bc..8f53145b05 100644 --- a/common/src/app/common/render_wasm/api/upload.cljs +++ b/common/src/app/common/render_wasm/api/upload.cljs @@ -205,17 +205,29 @@ (buf/write-i32 dview (+ offset 36) (d/nilv z-index 0)) (+ offset 40))) +(defn- coerce-fills + "Binary fills, or nil when there are none." + [fills] + (when-not (or (nil? fills) (and (vector? fills) (empty? fills))) + (types.fills/coerce fills))) + +(defn- fills-byte-size + [fills] + (if (some? fills) (types.fills/get-byte-size fills) 4)) + (defn- write-fills-section! "Write fills in the same layout as `_set_shape_fills`: [u8 n][u8;3 pad][n × FILL-U8-SIZE]. Returns next offset." [dview offset fills] - (let [fills (types.fills/coerce (or fills [])) - byte-size (types.fills/get-byte-size fills) - ;; write-to expects a Uint32Array heap + u32 element offset - heap-u32 (js/Uint32Array. (.-buffer dview)) - u32-off (quot offset 4)] - (types.fills/write-to fills heap-u32 u32-off) - (+ offset byte-size))) + (if (some? fills) + ;; write-to expects a Uint32Array heap + u32 element offset + (let [heap-u32 (js/Uint32Array. (.-buffer dview) + (.-byteOffset dview) + (quot (.-byteLength dview) 4))] + (types.fills/write-to fills heap-u32 (quot offset 4)) + (+ offset (types.fills/get-byte-size fills))) + (do (buf/write-u32 dview offset 0) + (+ offset 4)))) (defn- write-stroke-fill! [dview offset stroke] @@ -288,15 +300,16 @@ (defn write-shape-payload! "Serialize one shape's structural payload into `dview` starting at `offset` (payload only — no length prefix). Returns the offset after the payload. + `fills` and `strokes` come from `prepare-record`. Options: - `:include-layout?` — when true, emit FLEX + LAYOUT-ITEM (workspace cold load). - `:include-fills-strokes?` — when true, emit FILLS + STROKES sections." - [dview offset shape {:keys [include-layout? include-fills-strokes?] - :or {include-layout? false - include-fills-strokes? false}}] + [dview offset shape children fills strokes + {:keys [include-layout? include-fills-strokes?] + :or {include-layout? false + include-fills-strokes? false}}] (let [shape-type (dm/get-prop shape :type) - children (into [] (filter uuid?) (get shape :shapes)) blur (get shape :blur) bg-blur (get shape :background-blur) shadows (or (get shape :shadow) []) @@ -305,7 +318,6 @@ grow-type (when (= shape-type :text) (get shape :grow-type)) flex? (and include-layout? (ctl/flex-layout? shape)) layout-item? include-layout? - strokes (when include-fills-strokes? (visible-strokes shape)) mask (cond-> 0 true (bit-or SECTION-CHILDREN) @@ -376,7 +388,7 @@ offset (cond-> offset include-fills-strokes? - (as-> o (write-fills-section! dview o (get shape :fills)))) + (as-> o (write-fills-section! dview o fills))) offset (cond-> offset include-fills-strokes? @@ -384,18 +396,14 @@ offset)) (defn- payload-byte-size - [shape {:keys [include-layout? include-fills-strokes?] - :or {include-layout? false include-fills-strokes? false}}] - (let [children (into [] (filter uuid?) (get shape :shapes)) - shadows (or (get shape :shadow) []) + [shape children fills strokes {:keys [include-layout? include-fills-strokes?] + :or {include-layout? false include-fills-strokes? false}}] + (let [shadows (or (get shape :shadow) []) shape-type (dm/get-prop shape :type) blur (get shape :blur) bg-blur (get shape :background-blur) flex? (and include-layout? (ctl/flex-layout? shape)) - fills-size (if include-fills-strokes? - (types.fills/get-byte-size (types.fills/coerce (or (get shape :fills) []))) - 0) - strokes (when include-fills-strokes? (visible-strokes shape)) + fills-size (if include-fills-strokes? (fills-byte-size fills) 0) strokes-size (if include-fills-strokes? (+ 4 (* (count strokes) (+ STROKE-HEADER-U8-SIZE types.fills.impl/FILL-U8-SIZE))) @@ -414,18 +422,35 @@ fills-size strokes-size))) -(defn- encode-shape-record - "Returns a Uint8Array: [u32 payload_len][payload]." - [shape opts] - (let [capacity (+ 4 (payload-byte-size shape opts)) - buffer (js/ArrayBuffer. capacity) - dview (js/DataView. buffer) - end (write-shape-payload! dview 4 shape opts) - payload-len (- end 4)] - (assert (= end capacity) - (str "upload record size mismatch: wrote " end " expected " capacity)) - (buf/write-u32 dview 0 payload-len) - (js/Uint8Array. buffer 0 end))) +(defn- prepare-record + "Everything a record needs, computed once: children, binary fills, visible + strokes and payload size." + [shape {:keys [include-fills-strokes?] :as opts}] + (let [children (into [] (filter uuid?) (get shape :shapes)) + fills (when include-fills-strokes? (coerce-fills (get shape :fills))) + strokes (when include-fills-strokes? (visible-strokes shape))] + #js {:shape shape + :children children + :fills fills + :strokes strokes + :size (payload-byte-size shape children fills strokes opts)})) + +(defn- write-record! + "Writes [u32 payload_len][payload] at `offset`. Returns the next offset." + [dview offset ^js record opts] + (let [size (.-size record) + start (+ offset 4) + end (write-shape-payload! dview start + (.-shape record) + (.-children record) + (.-fills record) + (.-strokes record) + opts)] + (when (not= end (+ start size)) + (throw (ex-info "upload record size mismatch" + {:wrote (- end start) :expected size}))) + (buf/write-u32 dview offset size) + end)) (defn flush-shapes-batch! "Upload `shapes` as one `_set_shapes_batch` call. @@ -433,17 +458,20 @@ `:include-fills-strokes?`)." [shapes opts] (when (and (wasm/live?) (seq shapes)) - (let [records (mapv #(encode-shape-record % opts) shapes) - total (reduce (fn [acc ^js u8] (+ acc (.-byteLength u8))) 4 records) + (let [records (mapv #(prepare-record % opts) shapes) + total (reduce (fn [acc ^js record] (+ acc 4 (.-size record))) 4 records) offset (mem/alloc total) heap (mem/get-heap-u8) - dview (js/DataView. (.-buffer heap))] - (buf/write-u32 dview offset (count records)) - (reduce (fn [o ^js u8] - (.set heap u8 o) - (+ o (.-byteLength u8))) - (+ offset 4) - records) + dview (js/DataView. (.-buffer heap) offset total)] + (try + (.fill heap 0 offset (+ offset total)) + (buf/write-u32 dview 0 (count records)) + (reduce (fn [o record] (write-record! dview o record opts)) + 4 + records) + (catch :default cause + (mem/free) + (throw cause))) (h/call wasm/internal-module "_set_shapes_batch") nil))) diff --git a/common/src/app/common/render_wasm/serialize_shape.cljs b/common/src/app/common/render_wasm/serialize_shape.cljs index 07ce3697ab..b97fc8599a 100644 --- a/common/src/app/common/render_wasm/serialize_shape.cljs +++ b/common/src/app/common/render_wasm/serialize_shape.cljs @@ -25,6 +25,7 @@ dispatching per changed key through the same underlying `props` setters." (:require [app.common.render-wasm.api.props :as props] + [app.common.render-wasm.api.select :as wselect] [app.common.render-wasm.api.upload :as upload] [app.common.render-wasm.svg-derived :as svg-derived])) @@ -47,7 +48,7 @@ (when (and (contains? #{:path :bool} type) (some? (:content shape))) (props/set-shape-path-content (:content shape))))) -(defn serialize-shape! +(defn serialize-shape "Applies every host-independent WASM property of `shape`." [shape] (let [shape (svg-derived/apply-svg-derived shape)] @@ -58,19 +59,19 @@ shape)) -(defn serialize-shapes-batch! +(defn serialize-shapes-batch "Structural batch upload plus per-shape svg-attrs/path tail. Derives via svg-derived, uploads one `_set_shapes_batch` with `opts`, - then selects each shape needing svg-attrs/path content and applies it. - Host text/grid/image sequencing stays in callers. Returns the prepared - vector for the downstream host-attrs loop." - [shapes opts select-fn] + then selects each shape needing svg-attrs/path content via `use-shape` + and applies the tail. Host text/grid/image sequencing stays in callers. + Returns the prepared vector for the downstream host-attrs loop." + [shapes opts] (let [prepared (mapv svg-derived/apply-svg-derived shapes)] (when (seq prepared) (upload/flush-shapes-batch! prepared opts) (doseq [shape prepared] (when (needs-shape-tail? shape) - (select-fn (:id shape)) + (wselect/use-shape (:id shape)) (write-shape-tail! shape)))) prepared)) diff --git a/common/src/app/common/render_wasm/svg_derived.cljs b/common/src/app/common/render_wasm/svg_derived.cljs index f93a07bdf3..7391807a4a 100644 --- a/common/src/app/common/render_wasm/svg_derived.cljs +++ b/common/src/app/common/render_wasm/svg_derived.cljs @@ -221,10 +221,19 @@ (let [result (update combined :content #(or % []))] result)))))))) +(defn- style-map + "Stop `:style` as a map, whatever its spelling: pre-migration files + carry it as a CSS string, stored (post-0034) files as a kebab map." + [style] + (cond + (string? style) (csvg/parse-style style) + (map? style) style + :else nil)) + (defn- parse-gradient-stop [stop-node] (let [attrs (normalize-attrs (:attrs stop-node)) - style (some-> (get attrs :style) csvg/parse-style) + style (some-> (get attrs :style) style-map) color-value (or (get attrs :stop-color) (get attrs :stopcolor) (get style :stop-color) @@ -356,9 +365,9 @@ attr-fill (parse-svg-fill shape (dm/get-in shape [:svg-attrs :fill])) {:keys [type value]} (or style-fill attr-fill)] (when (some? type) - (let [opacity (or (some-> (dm/get-in shape [:svg-attrs :style :fillOpacity]) + (let [opacity (or (some-> (dm/get-in shape [:svg-attrs :style :fill-opacity]) (d/parse-double 1)) - (some-> (dm/get-in shape [:svg-attrs :fillOpacity]) + (some-> (dm/get-in shape [:svg-attrs :fill-opacity]) (d/parse-double 1))) base-fill (case type :color {:fill-color value} @@ -402,8 +411,8 @@ (when gaussian-blur {:id (uuid/next) :type :layer-blur - ;; For layer blur the value matches stdDeviation directly - :value (-> (dm/get-in gaussian-blur [:attrs :stdDeviation]) + ;; For layer blur the value matches std-deviation directly + :value (-> (dm/get-in gaussian-blur [:attrs :std-deviation]) (d/parse-double 0)) :hidden false})) @@ -470,7 +479,7 @@ (let [blur-elem (find-filter-element filter-content :feGaussianBlur)] (drop-shadow (d/parse-double (filter-attr offset-elem :dx) 0) (d/parse-double (filter-attr offset-elem :dy) 0) - (d/parse-double (filter-attr blur-elem :stddeviation) 0) + (d/parse-double (filter-attr blur-elem :std-deviation :stddeviation) 0) (or (shadow-color filter-content) {:color clr/black :opacity 1}))))) @@ -479,7 +488,7 @@ [elem] (drop-shadow (d/parse-double (filter-attr elem :dx) 2) (d/parse-double (filter-attr elem :dy) 2) - (d/parse-double (filter-attr elem :stddeviation) 2) + (d/parse-double (filter-attr elem :std-deviation :stddeviation) 2) (flood->shadow-color elem))) (defn apply-svg-filters diff --git a/common/src/app/common/render_wasm/text_content.cljs b/common/src/app/common/render_wasm/text_content.cljs index 1ffbebb054..c98c00b4c4 100644 --- a/common/src/app/common/render_wasm/text_content.cljs +++ b/common/src/app/common/render_wasm/text_content.cljs @@ -190,9 +190,9 @@ text-buffer (encode-text text) text-size (mem/size text-buffer) total-size (+ 4 metadata-size text-size) + offset (mem/alloc total-size) heapu8 (mem/get-heap-u8) - dview (mem/get-data-view) - offset (mem/alloc total-size)] + dview (mem/get-data-view)] (-> offset (mem/write-u32 dview num-spans) (write-paragraph dview paragraph) diff --git a/common/src/app/common/render_wasm/wasm.cljs b/common/src/app/common/render_wasm/wasm.cljs index 81b21ca5d7..5726534a3a 100644 --- a/common/src/app/common/render_wasm/wasm.cljs +++ b/common/src/app/common/render_wasm/wasm.cljs @@ -6,8 +6,15 @@ (ns app.common.render-wasm.wasm) +;; Types of frames in the rendering pipeline +(def ^:const FRAME_TYPE_NONE 0) ;; This type should never leak +(def ^:const FRAME_TYPE_PARTIAL 1) ;; A frame needs more _render calls to end +(def ^:const FRAME_TYPE_FULL 2) ;; A frame is done +(def ^:const FRAME_TYPE_VIEWPORT_READY 3) ;; Viewport presented; interest tiles may still be pending +(def ^:const RENDER_FLAG_SYNC_TILES 4) ;; Rebuild tile index without ending fast mode (pan/zoom pause) + (defonce internal-frame-id nil) -(defonce internal-frame-type 0) +(defonce internal-frame-type FRAME_TYPE_NONE) (defonce internal-module #js {}) ;; Reference to the HTML canvas element. diff --git a/common/src/app/common/svg.cljc b/common/src/app/common/svg.cljc index e51cad731e..15a3bff12d 100644 --- a/common/src/app/common/svg.cljc +++ b/common/src/app/common/svg.cljc @@ -179,6 +179,7 @@ :horiz-adv-x :horiz-origin-x :horiz-origin-y + :href :id :ideographic :in @@ -379,6 +380,13 @@ :writing-mode :mask-type}) +(def penpot-extra-attrs + "Non-spec attrs Penpot deliberately keeps because exporters emit them + and Penpot readers need them. Seeded with `:stroke-style`, read by the + svg-raw sidebar. Only add an entry with a reader that needs it plus a + test; this set is not a catch-all for unknown attrs." + #{:stroke-style}) + (def inheritable-props #{:style :clip-rule @@ -520,11 +528,45 @@ (let [xf (map prop-key)] (-> #{} (into xf svg-attrs) - (into xf svg-presentation-attrs)))) + (into xf svg-presentation-attrs) + (into xf penpot-extra-attrs)))) + +(def penpot-extra-prop-keys + "Camel render key -> source spelling for `penpot-extra-attrs`. + `attrs->props` keeps these keys in source spelling (instead of + camelizing them) so React passes them to the DOM silently, with no + unknown-prop warning, and export writes them back as emitted." + (into {} (map (fn [k] [(prop-key k) k])) penpot-extra-attrs)) + +(def svg-stored-attr-keys + "Kebab-case keys storable in `:svg-attrs` and `:svg-defs` node + `:attrs`: every `svg-props` whitelist key in stored spelling, sorted + for stable schema derivation. Birth (`attrs->kebab-props`) can only + produce these keys, so the shape schema derived from them cannot + drift from birth." + (sort-by name (map (comp keyword str/kebab name) svg-props))) + +(def stored-attr-display-names + "Stored kebab key -> spec/source spelling, for user-facing labels + (the svg-attrs menu shows these). Kebab storage matches the spec for + most keys (`:stroke-width` shows `stroke-width`), but spec-camel keys + do not (`:class-name` shows `class`, `:view-box` shows `viewBox`)." + (into {} + (map (fn [k] [(keyword (str/kebab (name (prop-key k)))) (name k)])) + (concat svg-attrs svg-presentation-attrs penpot-extra-attrs))) + +(defn stored-attr-display-name + "User-facing label for a stored kebab attr key. Unknown keys fall + back to their name." + [k] + (get stored-attr-display-names k (name k))) ;; Defaults for some tags per spec https://www.w3.org/TR/SVG11/single-page.html ;; they are basically the defaults that can be percents and we need to replace because ;; otherwise won't work as expected in the workspace +;; NOTE: camelCase keys on purpose. These defaults run on the raw +;; parser output before birth normalizes stored keys to kebab-case; +;; do not "fix" them to kebab. (def svg-tag-defaults (let [filter-default {:units :filterUnits :default "objectBoundingBox" @@ -605,29 +647,70 @@ ([attrs whitelist?] (reduce-kv (fn [res k v] - (let [k (prop-key k)] + (let [ck (prop-key k)] (cond - (nil? k) + (nil? ck) res (nil? v) res - (= k :style) + (= ck :style) (let [v (if (string? v) (parse-style v) v) v (not-empty (attrs->props v false))] (if v - (assoc res k v) + (assoc res ck v) res)) :else - (if (or (not whitelist?) (contains? svg-props k)) - (let [v (if (string? v) (str/trim v) v)] - (assoc res k v)) + (if (or (not whitelist?) (contains? svg-props ck)) + (let [v (if (string? v) (str/trim v) v) + ;; Extra attrs keep their source spelling so + ;; React passes them to the DOM silently. + out-k (get penpot-extra-prop-keys ck ck)] + (assoc res out-k v)) res)))) {} attrs))) +(defn attrs->kebab-props + "Like `attrs->props` (same whitelist, trim and `:style` parsing) but + with kebab-case keys, the stored convention for `:svg-attrs` and + `:svg-defs` node attrs. Kebab is a fixed point: running it twice + returns the same map." + [attrs] + (-> attrs + (attrs->props) + (d/kebab-keys))) + +(defn- normalize-node-tree + "Walks a svg node tree applying `attrs-fn` to every `:attrs` map. Tags + are stored as keywords in their source spelling (`:clipPath`); string + tags found in stored files are turned into keywords, and nil `:attrs` + into an empty map." + [attrs-fn node] + (if-not (map? node) + node + (-> node + (d/update-when :tag keyword) + (d/update-when :attrs #(attrs-fn (or % {}))) + (d/update-when :content #(mapv (partial normalize-node-tree attrs-fn) %))))) + +(defn normalize-def-node + "Normalizes a `:svg-defs` node to the stored convention: keyword tags, + and `:attrs` through `attrs->kebab-props` (whitelist + kebab), + recursively through nested `:content`. Ids and values are left + untouched." + [node] + (normalize-node-tree attrs->kebab-props node)) + +(defn kebabize-content-node + "Normalizes an svg-raw `:content` tree: keyword tags, and `:attrs` keys + kebab-ized in spelling only. Unknown keys are kept because the sidebar + reads keys outside the whitelist (e.g. `:stroke-style`)." + [node] + (normalize-node-tree d/kebab-keys node)) + (defn update-attr-ids "Replaces the ids inside a property" [attrs replace-fn] @@ -885,7 +968,7 @@ ;; Don't inherit a property that is already in the style attribute inherit-style (-> (:style group-attrs) (d/without-keys (keys attrs))) - inheritable-props (->> inheritable-props (remove #(contains? (:styles attrs) %))) + inheritable-props (->> inheritable-props (remove #(contains? (:style attrs) %))) group-attrs (-> group-attrs (assoc :style inherit-style)) attrs (-> (select-keys group-attrs inheritable-props) @@ -925,7 +1008,10 @@ (defn fix-default-values "Gives values to some SVG elements which defaults won't work when - imported into the platform" + imported into the platform. + + NOTE: reads raw parser keys (camelCase). Runs before birth + normalization; do not switch to kebab." [svg-data] (let [add-defaults (fn [{:keys [tag attrs] :as node}] @@ -950,7 +1036,10 @@ (mth/sqrt 2))) (defn fix-percents - "Changes percents to a value according to the size of the svg imported" + "Changes percents to a value according to the size of the svg imported. + + NOTE: reads raw parser keys (camelCase). Runs before birth + normalization; do not switch to kebab." [svg-data] ;; https://www.w3.org/TR/SVG11/single-page.html#coords-Units (let [viewbox {:x (:offset-x svg-data) @@ -1023,7 +1112,10 @@ (map-nodes fix-percent-values svg-data)))) -(defn collect-images [svg-data] +(defn collect-images + "NOTE: reads raw parser keys (`:xlink:href` with colon). Runs before + birth normalization; do not switch to kebab." + [svg-data] (let [redfn (fn [acc {:keys [tag attrs]}] (cond-> acc (= :image tag) diff --git a/common/src/app/common/test_helpers/compositions.cljc b/common/src/app/common/test_helpers/compositions.cljc index e2efb1a170..dcc51a2fc5 100644 --- a/common/src/app/common/test_helpers/compositions.cljc +++ b/common/src/app/common/test_helpers/compositions.cljc @@ -18,6 +18,7 @@ [app.common.test-helpers.files :as thf] [app.common.test-helpers.ids-map :as thi] [app.common.test-helpers.shapes :as ths] + [app.common.types.component :as ctk] [app.common.types.container :as ctn] [app.common.types.shape :as cts] [app.common.types.text :as txt])) @@ -394,6 +395,8 @@ orig-shapes (when keep-touched? (cfh/get-children-with-self (:objects page) (:id shape))) + keep-props-values (select-keys shape ctk/swap-keep-attrs) + [new-shape _all-parents changes] (cll/generate-component-swap (pcb/empty-changes) (:objects page) @@ -405,7 +408,7 @@ :id) 0 nil - {} + keep-props-values (true? keep-touched?)) [changes _] (if keep-touched? diff --git a/common/src/app/common/transit.cljc b/common/src/app/common/transit.cljc index dc3e37b380..5e205144ba 100644 --- a/common/src/app/common/transit.cljc +++ b/common/src/app/common/transit.cljc @@ -99,6 +99,48 @@ ;; --- HANDLERS +#?(:cljs + (defn- keyword-reader + "Return a reader that gives the same keyword instance for each name." + [] + (let [cache (js/Map.)] + (fn [value] + (or (.get cache value) + (let [kw (keyword value)] + (.set cache value kw) + kw)))))) + +#?(:cljs + (def ^:private uuid-cache-size + "Twice the ids of a very large page (~60k shapes), so decoding one + page does not rotate the cache." + 131072)) + +#?(:cljs (def ^:private recent-uuids (js/Map.))) +#?(:cljs (def ^:private previous-uuids (js/Map.))) + +#?(:cljs + (defn clear-uuid-cache + "Forget the cached uuids, so the ids of a closed file can be freed." + [] + (set! recent-uuids (js/Map.)) + (set! previous-uuids (js/Map.)))) + +#?(:cljs + (defn- read-uuid + "Give the same uuid instance for each value. Entries live in two + generations: when the recent one is full it becomes the previous one + and the older one is dropped, so the cache stays bounded." + [value] + (or (.get recent-uuids value) + (let [id (or (.get previous-uuids value) + (parse-uuid value))] + (when (>= (.-size recent-uuids) uuid-cache-size) + (set! previous-uuids recent-uuids) + (set! recent-uuids (js/Map.))) + (.set recent-uuids value id) + id)))) + (add-handlers! #?@(:clj [{:id "file" @@ -117,7 +159,11 @@ #?(:cljs {:id "u" - :rfn parse-uuid}) + :rfn read-uuid}) + + #?(:cljs + {:id ":" + :rfn (keyword-reader)}) {:id "ordered-map" :class #?(:clj LinkedMap :cljs lkm/LinkedMap) diff --git a/common/src/app/common/types/container.cljc b/common/src/app/common/types/container.cljc index d790999db0..849467d7f4 100644 --- a/common/src/app/common/types/container.cljc +++ b/common/src/app/common/types/container.cljc @@ -324,7 +324,6 @@ delta (gpt/subtract position orig-pos) objects (:objects page) - unames (volatile! (cfh/get-used-names objects)) component-children (d/index-by :id (cfh/get-children-with-self objects (:id component-shape))) @@ -353,9 +352,6 @@ (let [new-name (:name new-shape) root? (ctk/instance-root? original-shape)] - (when root? - (vswap! unames conj new-name)) - (vswap! ids-map assoc (:id original-shape) (:id new-shape)) (cond-> new-shape diff --git a/common/src/app/common/types/fills/impl.cljc b/common/src/app/common/types/fills/impl.cljc index 2f8ca65a6f..453b95d6b3 100644 --- a/common/src/app/common/types/fills/impl.cljc +++ b/common/src/app/common/types/fills/impl.cljc @@ -59,6 +59,27 @@ result (bit-or result n)] result)) +(defn encode-mtype + "Encode an image media type as the 16-bit code shared with render-wasm." + [mtype] + (case mtype + "image/jpeg" 0x01 + "image/png" 0x02 + "image/gif" 0x03 + "image/webp" 0x04 + "image/svg+xml" 0x05 + 0x00)) + +(defn decode-mtype + [code] + (case code + 0x01 "image/jpeg" + 0x02 "image/png" + 0x03 "image/gif" + 0x04 "image/webp" + 0x05 "image/svg+xml" + nil)) + (defn- get-color-hex [n] (let [n (bit-and n 0x00ffffff) @@ -127,12 +148,13 @@ transform (get image :transform) has-transform? (some? transform) transform-flag (if has-transform? 0x02 0x00) - flags (bit-or keep-aspect-ratio transform-flag)] + flags (bit-or keep-aspect-ratio transform-flag) + mtype (encode-mtype (get image :mtype))] (buf/write-byte buffer (+ offset 0) 0x03) (buf/write-uuid buffer (+ offset 4) image-id) (buf/write-byte buffer (+ offset 20) alpha) (buf/write-byte buffer (+ offset 21) flags) - (buf/write-short buffer (+ offset 22) 0) ;; 2-byte padding (reserved for future use) + (buf/write-short buffer (+ offset 22) mtype) (buf/write-int buffer (+ offset 24) image-width) (buf/write-int buffer (+ offset 28) image-height) (if has-transform? @@ -155,13 +177,7 @@ mtype (dm/get-in fill [:fill-image :mtype])] (when mtype - (let [val (case mtype - "image/jpeg" 0x01 - "image/png" 0x02 - "image/gif" 0x03 - "image/webp" 0x04 - "image/svg+xml" 0x05)] - (buf/write-short buffer (+ offset 2) val))) + (buf/write-short buffer (+ offset 2) (encode-mtype mtype))) (if (and (some? ref-file) (some? ref-id)) @@ -235,13 +251,7 @@ :y (buf/read-float dbuffer (+ doffset 36)) :width (buf/read-float dbuffer (+ doffset 40)) :height (buf/read-float dbuffer (+ doffset 44))}) - mtype (buf/read-short mbuffer (+ moffset 2)) - mtype (case mtype - 0x01 "image/jpeg" - 0x02 "image/png" - 0x03 "image/gif" - 0x04 "image/webp" - 0x05 "image/svg+xml")] + mtype (decode-mtype (buf/read-short mbuffer (+ moffset 2)))] {:fill-opacity opacity :fill-image (cond-> {:id id :width width diff --git a/common/src/app/common/types/objects_map.cljc b/common/src/app/common/types/objects_map.cljc index 77289a2b1e..1b4e17ce4c 100644 --- a/common/src/app/common/types/objects_map.cljc +++ b/common/src/app/common/types/objects_map.cljc @@ -206,7 +206,7 @@ c/IWithMeta (-with-meta [this new-meta] - (if (identical? new-meta meta) + (if (identical? new-meta metadata) this (ObjectsMap. new-meta cache diff --git a/common/src/app/common/types/organization.cljc b/common/src/app/common/types/organization.cljc index f601cf2971..82428eab16 100644 --- a/common/src/app/common/types/organization.cljc +++ b/common/src/app/common/types/organization.cljc @@ -19,11 +19,13 @@ [:logo-id {:optional true} [:maybe ::sm/uuid]] [:expired-license {:optional true} [:maybe :boolean]] [:sso-active {:optional true} [:maybe :boolean]] + [:restrictions-met {:optional true} [:maybe [:map-of :keyword :boolean]]] [:permissions {:optional true} [:maybe [:map [:create-teams {:optional true} [:maybe [:enum "any" "onlyMe"]]] [:delete-teams {:optional true} [:maybe [:enum "onlyMe" "onlyOwners"]]] [:move-teams {:optional true} [:maybe [:enum "always" "myOrganizations" "never"]]] + [:send-invitations {:optional true} [:maybe [:enum "ownersAndAdmins" "owners"]]] [:new-team-members {:optional true} [:maybe [:enum "anyone" "members"]]]]]]]) @@ -35,7 +37,7 @@ (def organization->team-keys "Organization field keys to include in the nested :organization map." - [:id :name :custom-photo :slug :avatar-bg-url :owner-id :expired-license :permissions :sso-active]) + [:id :name :custom-photo :slug :avatar-bg-url :owner-id :expired-license :permissions :sso-active :restrictions-met]) (defn apply-organization "Updates a team map with organization fields in a nested :organization map. @@ -85,9 +87,9 @@ :new-team-members "anyone"}) (defn- can-create-team? - [{:keys [is-organization-owner? permission-value]}] + [{:keys [is-organization-owner? permission-value restrictions-met?]}] (or is-organization-owner? - (= permission-value "any"))) + (and (= permission-value "any") (boolean restrictions-met?)))) (defn- can-delete-team? [{:keys [is-organization-owner? permission-value team-perms]}] @@ -100,27 +102,42 @@ :else false)) (defn- can-move-team? - [{:keys [permission-value target-organization-same-owner?]}] + "Checks the source organization's `:move-teams` value. The check that + the user owns the team happens elsewhere. + + - \"always\": the team can move to any organization, or be taken out + of the organization. + - \"myOrganizations\": the team can move only to an organization with + the same owner, and cannot be taken out. A person has one Enterprise + subscription, so the same owner means the same subscription. + - \"never\": only the Admin Console can move teams. Nobody moves them + from Penpot, not even the organization owner. + + An attribute restriction (when present) narrows \"always\" and + \"myOrganizations\"; it never widens them and never affects \"never\"." + [{:keys [permission-value target-organization-same-owner? restrictions-met?]}] (cond (= permission-value "never") false (= permission-value "always") - true + (boolean restrictions-met?) (= permission-value "myOrganizations") - (true? target-organization-same-owner?) + (and (true? target-organization-same-owner?) (boolean restrictions-met?)) :else false)) (defn- can-invite-to-team? - [{:keys [permission-value team-perms]}] - (cond - (= permission-value "ownersAndAdmins") - (or (boolean (:is-owner team-perms)) - (boolean (:is-admin team-perms))) + [{:keys [permission-value team-perms restrictions-met?]}] + (and + (cond + (= permission-value "ownersAndAdmins") + (or (boolean (:is-owner team-perms)) + (boolean (:is-admin team-perms))) - (= permission-value "owners") - (boolean (:is-owner team-perms)) + (= permission-value "owners") + (boolean (:is-owner team-perms)) - :else false)) + :else false) + (boolean restrictions-met?))) (defn- can-add-anybody-to-team? [{:keys [permission-value]}] @@ -153,13 +170,18 @@ (get action-rules action) permissions (normalize-organization-permissions organization-perms) is-organization-owner? (owner? organization-perms profile-id) - permission-value (get permissions permission-key)] + permission-value (get permissions permission-key) + ;; Missing key means "met": an old Nitrate that does not send + ;; :restrictions-met keeps today's behaviour (fail-open on the key, + ;; the option rules above still fail-closed on the action). + restrictions-met? (get-in organization-perms [:restrictions-met permission-key] true)] (cond (nil? rule) false :else (boolean (check-fn {:is-organization-owner? is-organization-owner? :permission-value permission-value :team-perms team-perms - :target-organization-same-owner? target-organization-same-owner?}))))) + :target-organization-same-owner? target-organization-same-owner? + :restrictions-met? restrictions-met?}))))) (defn can-send-invitations? "Returns true when the user can send invitations to a team. @@ -168,8 +190,7 @@ [{:keys [organization profile-id team-permissions]}] (if (and (contains? flags/*current* :admin-console) organization) (allowed? :send-invitations - {:organization-perms {:owner-id (:owner-id organization) - :permissions (:permissions organization)} + {:organization-perms organization :profile-id profile-id :team-perms team-permissions}) (or (boolean (:is-owner team-permissions)) diff --git a/common/src/app/common/types/plugins.cljc b/common/src/app/common/types/plugins.cljc index 035be0efb3..3b32ccbaed 100644 --- a/common/src/app/common/types/plugins.cljc +++ b/common/src/app/common/types/plugins.cljc @@ -35,11 +35,15 @@ "clipboard:read" "clipboard:write" "user:read" "allow:downloads" - "allow:localstorage"}) + "allow:localstorage" + "allow:global" + "manage:projects" + "manage:teams" + "manage:delete"}) (def schema:permissions "Schema for plugin permissions - a set of valid permission strings." - [:set {:gen/max 11} (into [:enum] (sort valid-permissions))]) + [:set {:gen/max 15} (into [:enum] (sort valid-permissions))]) (def max-plugins "Maximum number of plugins a profile can hold." @@ -58,6 +62,7 @@ [:map [:plugin-id :string] [:version {:optional true} :int] + [:scope {:optional true} [:enum "workspace" "global"]] [:name [:string {:max (:name registry-entry-max-lengths)}]] [:description {:optional true} [:string {:max (:description registry-entry-max-lengths)}]] [:host [:string {:max (:host registry-entry-max-lengths)}]] diff --git a/common/src/app/common/types/shape.cljc b/common/src/app/common/types/shape.cljc index 80f2e608f6..b7bf5f2961 100644 --- a/common/src/app/common/types/shape.cljc +++ b/common/src/app/common/types/shape.cljc @@ -17,6 +17,7 @@ [app.common.record :as cr] [app.common.schema :as sm] [app.common.schema.generators :as sg] + [app.common.svg :as csvg] [app.common.transit :as t] [app.common.types.color :as clr] [app.common.types.fills :refer [schema:fills fill->color]] @@ -186,8 +187,82 @@ [:width ::sm/safe-number] [:height ::sm/safe-number]]) +(def ^:private schema:svg-scalar-gen + "Small generation-only scalar for svg attr values. Validation stays + wide (`:string`, any `::sm/number`, `:boolean`), but the default + `::sm/number` generator emits unbounded doubles whose own precision + pass overflows float: bound everything explicitly here." + (sg/one-of + (sg/word-string) + (sg/small-int :min -1000 :max 1000) + (sg/small-double :min -1000 :max 1000) + (sg/elements [true false]))) + +(def ^:private schema:svg-scalar + "A scalar svg attr value: trimmed strings or numbers/booleans + straight from programmatic svg data." + [:or {:gen/gen schema:svg-scalar-gen} + :string ::sm/number :boolean]) + +(def schema:svg-attrs + "Closed `:svg-attrs`: every key birth can store, derived from + `csvg/svg-stored-attr-keys` (the whitelist in stored spelling), so + birth and validation cannot drift apart. Values are the scalars birth + stores (trimmed strings, or numbers/booleans straight from + programmatic svg data), except `:style`, which birth parses into a + map. `:tag` stays a keyword: switching it to string would touch ~22 + comparison sites and 7 tag sets for zero behavior gain." + (into [:map {:title "SvgAttrs" :closed true}] + (map (fn [k] + [k {:optional true} + (if (= k :style) + [:map-of :keyword schema:svg-scalar] + schema:svg-scalar)])) + csvg/svg-stored-attr-keys)) + +(def ^:private schema:svg-shallow-node-gen + "Bounded generator for the svg node schemas below: one shallow node. + The schemas are recursive, and unbounded recursive generation grows + monster trees that OOM around ~200 sampled shapes. Hand-written tests + cover deep nesting; generation only needs small valid nodes." + (sg/one-of + (sg/elements [{:tag :g :attrs {}} + {:tag :g :attrs {} :content []} + {:tag :g :attrs {} :content ["text"]} + {:tag :rect :attrs {:x "0"} :content []}]))) + +(def schema:svg-def-node + "A `:svg-defs` node: keyword tag, whitelisted attrs, recursive content + that may interleave text. Closed: birth and migration normalize def + nodes to exactly these keys, and turn string tags into keywords." + [:schema {:registry + {::svg-def-node + [:map {:closed true + :gen/gen schema:svg-shallow-node-gen} + [:tag :keyword] + [:attrs schema:svg-attrs] + [:content {:optional true} + [:vector [:or :string [:ref ::svg-def-node]]]]]}} + [:ref ::svg-def-node]]) + +(def schema:svg-content-node + "An svg-raw `:content` tree node: keyword tag, spelling-only + kebab-ized attrs (open: the sidebar reads keys outside the whitelist, + such as `:stroke-style`), recursive content that may interleave text." + [:schema {:registry + {::svg-content-node + [:map {:gen/gen schema:svg-shallow-node-gen} + [:tag :keyword] + [:attrs {:optional true} :map] + [:content {:optional true} + [:vector [:or :string [:ref ::svg-content-node]]]]]}} + [:ref ::svg-content-node]]) + (def schema:shape-generic-attrs - [:map {:title "ShapeGenericAttrs"} + [:map {:title "ShapeGenericAttrs" + ;; Generated shapes must not carry svg provenance: the closed + ;; svg schemas above would fill them with random attrs. + :gen/fmap (fn [m] (dissoc m :svg-attrs :svg-defs))} [:page-id {:optional true} ::sm/uuid] [:component-id {:optional true} ::sm/uuid] [:component-file {:optional true} ::sm/uuid] @@ -264,15 +339,19 @@ ;; it, and `setup-shape` drops it when a caller passes nil. [:hide-in-viewer {:optional true} :boolean] - ;; The SVG provenance an import leaves on a shape. Typed `:map` rather than - ;; more precisely on purpose: legacy files hold `svg-transform` as a plain - ;; `{:a … :f}` map rather than a `::gmt/matrix` record, and `svg-viewbox` as - ;; either a `::grc/rect` record or a plain map, so a tighter schema here - ;; would reject files that are otherwise valid. The graph *column* types are - ;; tightened separately, where a wrong guess costs a column rather than a - ;; rejected file (`app.graph.schema.contract/type-overrides`). - [:svg-attrs {:optional true} :map] - [:svg-defs {:optional true} :map] + ;; The SVG provenance an import leaves on a shape. `:svg-attrs` is + ;; closed over the whitelist in stored spelling (see + ;; `schema:svg-attrs`): legacy files hold `svg-transform` as a plain + ;; `{:a … :f}` map rather than a `::gmt/matrix` record, and + ;; `svg-viewbox` as either a `::grc/rect` record or a plain map, so a + ;; tighter schema on those two would reject files that are otherwise + ;; valid. The graph *column* types are tightened separately, where a + ;; wrong guess costs a column rather than a rejected file + ;; (`app.graph.schema.contract/type-overrides`). Outer `:svg-defs` + ;; keys are id strings at birth and keywords after JSON decode, so + ;; both are accepted; node tags decode back to keywords. + [:svg-attrs {:optional true} schema:svg-attrs] + [:svg-defs {:optional true} [:map-of [:or :string :keyword] schema:svg-def-node]] [:svg-transform {:optional true} :map] [:svg-viewbox {:optional true} :map]]) @@ -347,7 +426,9 @@ ;; string itself: `hi` becomes one svg-raw for the element ;; and another for `"hi"`. `app.common.files.shapes-builder/parse-svg-element` ;; carries a FIXME about exactly that. Both forms are legal and stored. - [:content {:optional true} [:or :map :string]]]) + ;; Element nodes follow `schema:svg-content-node` (keyword tags, attrs + ;; kebab-ized in spelling only). + [:content {:optional true} [:or schema:svg-content-node :string]]]) (def schema:image-attrs [:map {:title "ImageAttrs"} diff --git a/common/src/app/common/types/shape_tree.cljc b/common/src/app/common/types/shape_tree.cljc index 0080aae00d..05cb0fc40f 100644 --- a/common/src/app/common/types/shape_tree.cljc +++ b/common/src/app/common/types/shape_tree.cljc @@ -368,11 +368,17 @@ (defn start-page-index [objects] - (with-meta objects {::index-frames (get-frames (with-meta objects nil))})) + (with-meta objects {::index-frames (into [] (get-frames objects {:ignore-index? true}))})) (defn update-page-index + "Lazy, so pages that are not open are not decoded. The index reads + from the returned objects, so decoded shapes are shared with them." [objects] - (with-meta objects {::index-frames (get-frames (with-meta objects nil))})) + (let [self (volatile! nil) + objects (with-meta objects + {::index-frames (lazy-seq (get-frames @self {:ignore-index? true}))})] + (vreset! self objects) + objects)) (defn update-object-indices [file page-id] diff --git a/common/src/app/common/types/token.cljc b/common/src/app/common/types/token.cljc index fd7a96e55f..704fc55083 100644 --- a/common/src/app/common/types/token.cljc +++ b/common/src/app/common/types/token.cljc @@ -664,6 +664,14 @@ (when (contains? text-decoration-values normalized-value) normalized-value))) +(def text-case-values #{"none" "uppercase" "lowercase" "capitalize"}) + +(defn number-out-of-bounds? + "Whether a resolved numeric token value is outside the safe integer + range." + [n] + (or (>= n sm/max-safe-int) (<= n sm/min-safe-int))) + (def font-weight-aliases {"100" #{"thin" "hairline"}, "200" #{"ultra light" "extralight" "extraleicht" "extra-light" "ultra-light" "ultralight" "extra light"}, diff --git a/common/test/common_tests/files/page_diff_test.cljc b/common/test/common_tests/files/page_diff_test.cljc new file mode 100644 index 0000000000..1b8b97bc9f --- /dev/null +++ b/common/test/common_tests/files/page_diff_test.cljc @@ -0,0 +1,54 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns common-tests.files.page-diff-test + (:require + [app.common.files.page-diff :as diff] + [app.common.types.shape :as cts] + [app.common.uuid :as uuid] + [clojure.test :as t])) + +(def ^:private check-attrs [:frame-id :x :y :width :height :hidden :selrect :grids]) + +(def ^:private rect-a-id (uuid/custom 50 1)) +(def ^:private rect-b-id (uuid/custom 50 2)) +(def ^:private rect-c-id (uuid/custom 50 3)) +(def ^:private guide-id (uuid/custom 50 4)) + +(defn- make-rect + [id x] + (cts/setup-shape {:id id :type :rect :name "rect" + :frame-id uuid/zero :parent-id uuid/zero + :x x :y 0 :width 10 :height 10})) + +(def ^:private old-page + {:id (uuid/custom 50 10) + :objects {rect-a-id (make-rect rect-a-id 0) + rect-b-id (make-rect rect-b-id 0)}}) + +(def ^:private new-page + {:id (uuid/custom 50 10) + :objects {rect-a-id (make-rect rect-a-id 50) + rect-b-id (make-rect rect-b-id 50) + rect-c-id (make-rect rect-c-id 0)} + :guides {guide-id {:id guide-id :axis :x :position 10}}}) + +(t/deftest diff-with-every-id-reports-every-change + (let [result (diff/calculate-page-diff old-page new-page check-attrs + #{rect-a-id rect-b-id rect-c-id})] + (t/is (= #{rect-a-id rect-b-id} + (into #{} (map (comp :id second)) (:updated-shapes result)))) + (t/is (= [rect-c-id] (mapv :id (:new-shapes result)))) + (t/is (= [guide-id] (mapv :id (:new-guides result)))))) + +(t/deftest diff-ignores-objects-outside-the-given-ids + (let [result (diff/calculate-page-diff old-page new-page check-attrs #{rect-a-id})] + (t/is (= [rect-a-id] (mapv (comp :id second) (:updated-shapes result)))) + (t/is (= [] (:new-shapes result))))) + +(t/deftest diff-always-compares-every-guide + (let [result (diff/calculate-page-diff old-page new-page check-attrs #{})] + (t/is (= [guide-id] (mapv :id (:new-guides result)))))) diff --git a/common/test/common_tests/files/repair_test.cljc b/common/test/common_tests/files/repair_test.cljc index f6f24821ce..ab7c32bc6e 100644 --- a/common/test/common_tests/files/repair_test.cljc +++ b/common/test/common_tests/files/repair_test.cljc @@ -546,3 +546,74 @@ ;; First iteration used the seed; errors-fn runs only after the repair. (t/is (= 1 @calls))))) + +(defn- lost-main-file + "A component whose main is on page1 and its copy on page2, with the + component's `:main-instance-id` pointing to a missing shape." + [] + (-> (thf/sample-file :file1 :page-label :page1) + (tho/add-simple-component :component1 :root1 :child1) + (thf/add-sample-page :page2) + (thc/instantiate-component :component1 :copy1) + (thc/update-component :component1 :main-instance-id (uuid/next)))) + +(defn- edit-copy-changes + [] + [{:type :mod-obj + :page-id (thi/id :page2) + :id (thi/id :copy1) + :operations [{:type :set :attr :name :val "renamed"}]}]) + +(defn- repair-scoped + [file changes] + (cfr/repair-file-until-valid + file {} + (fn [file applied] + (cfv/validate-file-affected file {} (into applied changes))))) + +(t/deftest repair-ref-shape-not-found-fixes-lost-main-instance-id + (t/testing "a repair scoped to the copy's page points the component back + to its main and keeps the copy linked" + (let [file (lost-main-file) + changes (edit-copy-changes) + errors (cfv/validate-file-affected file {} changes) + result (repair-scoped file changes) + file' (:file result) + copy1' (ths/get-shape file' :copy1 :page-label :page2) + component' (thc/get-component file' :component1)] + + (t/is (= #{:ref-shape-not-found} (into #{} (map :code) errors))) + (t/is (nil? (:errors result))) + (t/is (nil? (cfv/validate-file file' {}))) + (t/is (= (thi/id :root1) (:main-instance-id component'))) + (t/is (= (thi/id :component1) (:component-id copy1'))) + (t/is (= (thi/id :file1) (:component-file copy1'))) + (t/is (= (thi/id :root1) (:shape-ref copy1'))) + (t/is (true? (:component-root copy1')))))) + +(t/deftest repair-whole-file-keeps-copy-of-lost-main + (t/testing "a whole-file repair fixes the component and keeps the copy linked" + (let [file (lost-main-file) + result (cfr/repair-file-until-valid + file {} + (fn [file _] (cfv/validate-file file {}))) + file' (:file result) + copy1' (ths/get-shape file' :copy1 :page-label :page2)] + + (t/is (nil? (:errors result))) + (t/is (nil? (cfv/validate-file file' {}))) + (t/is (= (thi/id :root1) (:main-instance-id (thc/get-component file' :component1)))) + (t/is (= (thi/id :root1) (:shape-ref copy1'))) + (t/is (= (thi/id :component1) (:component-id copy1')))))) + +(t/deftest repair-ref-shape-not-found-detaches-copy-of-missing-main + (t/testing "a copy is detached when no shape on the component's page is its main" + (let [file (-> (lost-main-file) + (ths/update-shape :root1 :main-instance nil :page-label :page1)) + changes (edit-copy-changes) + result (repair-scoped file changes) + copy1' (ths/get-shape (:file result) :copy1 :page-label :page2)] + + (t/is (nil? (:errors result))) + (t/is (nil? (:component-id copy1'))) + (t/is (nil? (:shape-ref copy1')))))) diff --git a/common/test/common_tests/files/shapes_builder_test.cljc b/common/test/common_tests/files/shapes_builder_test.cljc index 614ff7cf77..0c9418ffde 100644 --- a/common/test/common_tests/files/shapes_builder_test.cljc +++ b/common/test/common_tests/files/shapes_builder_test.cljc @@ -7,6 +7,8 @@ (ns common-tests.files.shapes-builder-test (:require [app.common.files.shapes-builder :as sb] + [app.common.svg :as csvg] + [app.common.types.color :as clr] [clojure.test :as t])) ;; Regression for https://github.com/penpot/penpot/issues/7869. @@ -78,8 +80,8 @@ (doseq [shape path-shapes] (let [stroke (first (:strokes shape))] ;; svg-attrs must carry stroke-linecap and stroke-linejoin for the renderers - (t/is (= "round" (get-in shape [:svg-attrs :strokeLinecap]))) - (t/is (= "round" (get-in shape [:svg-attrs :strokeLinejoin]))) + (t/is (= "round" (get-in shape [:svg-attrs :stroke-linecap]))) + (t/is (= "round" (get-in shape [:svg-attrs :stroke-linejoin]))) ;; stroke-cap-start/end must be inside the stroke entry, not at shape level (t/is (= :round (:stroke-cap-start stroke)) @@ -91,3 +93,234 @@ ;; stroke-style is not set at import time (nil means default solid rendering) (t/is (nil? (:stroke-style stroke))))))) + +;; Kebab storage convention for https://github.com/penpot/penpot/issues/11947. +;; Stored attribute keys (`:svg-attrs`, `:svg-defs` node `:attrs`, +;; `:content` `:attrs`) are always kebab-case: birth normalizes any +;; outside spelling, so export/import converges to the same state. + +(t/deftest attrs->kebab-props-whitelists-trims-and-kebabizes + (let [props (csvg/attrs->kebab-props {:fillRule "evenodd" + :stroke-width " 2 " + :viewBox "0 0 10 10" + :stdDeviation "3" + :xlink:href "#g" + :href "#g" + :stroke-style "dotted" + :style "fill:red;stroke-width:2" + :data-foo "drop-me"})] + (t/is (= "evenodd" (:fill-rule props))) + (t/is (= "2" (:stroke-width props)) "values are trimmed") + (t/is (= "0 0 10 10" (:view-box props))) + (t/is (= "3" (:std-deviation props))) + (t/is (= "#g" (:xlink-href props)) "colon is lost through the camel roundtrip") + (t/is (= "#g" (:href props))) + (t/is (= "dotted" (:stroke-style props)) "penpot-extra-attrs survive") + (t/is (= {:fill "red" :stroke-width "2"} (:style props)) "style maps are kebab too") + (t/is (not (contains? props :data-foo)) "unknown attrs are dropped") + (t/is (not (contains? props :fillRule)) "no camel keys survive") + (t/is (= props (csvg/attrs->kebab-props props)) "kebab is a fixed point"))) + +(t/deftest attrs->props-keeps-extra-attrs-in-source-spelling + ;; Extra attrs skip camelization so React passes them to the DOM + ;; silently (lowercase + hyphenated: no unknown-prop warning). + (t/is (= {:stroke-style "dotted" :fillRule "evenodd"} + (csvg/attrs->props {:stroke-style "dotted" :fillRule "evenodd"}))) + (t/is (= {:stroke-style "dotted"} + (:style (csvg/attrs->props {:style "stroke-style:dotted"})))) + (t/is (= {:stroke-style "dotted"} + (:style (csvg/attrs->kebab-props {:style "stroke-style:dotted"}))) + "birth agrees: kebab stays kebab")) + +(t/deftest collect-images-resolves-href-and-xlink-href + ;; Both href spellings resolve: plain `:href` (SVG2 style, kept by the + ;; whitelist so gradients and images survive birth) and `:xlink:href`. + (let [svg-data {:tag :svg + :attrs {} + :content [{:tag :image + :attrs {:xlink:href "#a" :width "10" :height "20"} + :content []} + {:tag :image + :attrs {:href "#b" :width "30" :height "40"} + :content []}]}] + (t/is (= [{:href "#a" :width 10 :height 20} + {:href "#b" :width 30 :height 40}] + (csvg/collect-images svg-data)))) + (t/is (= {:href "#b"} (csvg/attrs->kebab-props {:href "#b"})) + "plain href survives the whitelist")) + +(t/deftest birth-stores-svg-attrs-as-kebab + (let [svg-data {:name "kebab" + :tag :svg + :attrs {:width "100" :height "100" :viewBox "0 0 100 100"} + :content [{:tag :rect + :attrs {:x "10" :y "10" :width "80" :height "80" + :fill "red" + :fillRule "evenodd" + :stroke-width "2" + :stroke-style "dotted" + :data-foo "drop-me"} + :content []}]} + [_ children] (sb/create-svg-shapes svg-data {:x 0 :y 0} {} nil nil #{} false) + ;; NOTE: `create-svg-shapes` prepends a hidden background rect, skip it + rect (first (filter #(and (= :rect (:type %)) (not (:hidden %))) children))] + (t/is (some? rect)) + (let [attrs (:svg-attrs rect)] + (t/is (= "evenodd" (:fill-rule attrs))) + (t/is (= "2" (:stroke-width attrs))) + (t/is (= "dotted" (:stroke-style attrs)) "penpot-extra-attrs survive birth") + (t/is (not (contains? attrs :data-foo)) "unknown attrs are dropped") + (t/is (not (contains? attrs :fillRule)) "no camel keys survive")))) + +(t/deftest birth-stores-def-node-attrs-as-kebab + (let [svg-data {:name "defs" + :tag :svg + :attrs {:width "100" :height "100" :viewBox "0 0 100 100"} + :content [{:tag :linearGradient + :attrs {:id "MyGrad" + :gradientUnits "userSpaceOnUse" + :gradientTransform "rotate(45)" + :xlink:href "#missing" + :data-foo "drop-me"} + :content [{:tag :stop + :attrs {:offset "0" + :stop-color "#ffffff" + :style "stop-opacity:0.5"} + :content []}]} + {:tag :filter + :attrs {:id "blur1" :filterUnits "userSpaceOnUse"} + :content [{:tag :feGaussianBlur + :attrs {:stdDeviation "5"} + :content []}]} + {:tag :rect + :attrs {:x "10" :y "10" :width "80" :height "80" + :fill "url(#MyGrad)"} + :content []}]} + [root _] (sb/create-svg-shapes svg-data {:x 0 :y 0} {} nil nil #{} false) + defs (:svg-defs root) + grad (get defs "MyGrad") + blur (get defs "blur1")] + (t/is (contains? defs "MyGrad") "outer def id strings are byte-identical") + (t/is (= :linearGradient (:tag grad)) "tags are untouched") + (t/is (= "MyGrad" (get-in grad [:attrs :id])) ":id survives") + (t/is (= "userSpaceOnUse" (get-in grad [:attrs :gradient-units]))) + (t/is (= "rotate(45)" (get-in grad [:attrs :gradient-transform]))) + (t/is (= "#missing" (get-in grad [:attrs :xlink-href]))) + (t/is (not (contains? (:attrs grad) :data-foo)) "unknown def attrs are dropped") + (t/is (not (contains? (:attrs grad) :gradientUnits)) "no camel keys survive") + (t/is (= "0.5" (get-in grad [:content 0 :attrs :stop-opacity])) "nested stops are normalized") + (t/is (= "#ffffff" (get-in grad [:content 0 :attrs :stop-color]))) + (t/is (= "userSpaceOnUse" (get-in blur [:attrs :filter-units]))) + (t/is (= "5" (get-in blur [:content 0 :attrs :std-deviation]))))) + +(t/deftest birth-kebabizes-content-attrs-without-filtering + (let [svg-data {:name "raw" + :tag :svg + :attrs {:width "100" :height "100" :viewBox "0 0 100 100"} + :content [{:tag :text + :attrs {:x "10" :y "20" + :stroke-style "dotted" + :strokeWidth "3" + :data-foo "keep-me"} + :content ["hello"]}]} + [_ children] (sb/create-svg-shapes svg-data {:x 0 :y 0} {} nil nil #{} false) + raw (first (filter #(= :svg-raw (:type %)) children))] + (t/is (some? raw)) + (let [content (:content raw)] + (t/is (= :text (:tag content)) "tags are untouched") + (t/is (= "dotted" (get-in content [:attrs :stroke-style])) "the sidebar reads this key") + (t/is (= "3" (get-in content [:attrs :stroke-width])) "camel is kebab-ized") + (t/is (= "keep-me" (get-in content [:attrs :data-foo])) "unknown content keys survive") + (t/is (= ["hello"] (:content content)) "values are untouched")))) + +(t/deftest birth-setup-fill-reads-kebab-attrs + (let [svg-data {:name "fill" + :tag :svg + :attrs {:width "100" :height "100" :viewBox "0 0 100 100"} + :content [{:tag :rect + :attrs {:x "10" :y "10" :width "80" :height "80" + :fill "#ff0000" + :fill-opacity "0.5"} + :content []} + {:tag :rect + :attrs {:x "10" :y "50" :width "80" :height "40" + :style "fill:#00ff00;fill-opacity:0.25"} + :content []}]} + [_ children] (sb/create-svg-shapes svg-data {:x 0 :y 0} {} nil nil #{} false) + ;; NOTE: `create-svg-shapes` prepends a hidden background rect, skip it + [r1 r2] (filter #(and (= :rect (:type %)) (not (:hidden %))) children) + f1 (first (:fills r1)) + f2 (first (:fills r2))] + (t/is (some? (:fill-color f1))) + (t/is (= 0.5 (:fill-opacity f1))) + (t/is (nil? (get-in r1 [:svg-attrs :fill])) "consumed keys are removed") + (t/is (nil? (get-in r1 [:svg-attrs :fill-opacity]))) + (t/is (some? (:fill-color f2)) "style-form fills work too") + (t/is (= 0.25 (:fill-opacity f2))) + (t/is (nil? (get-in r2 [:svg-attrs :style :fill]))) + (t/is (nil? (get-in r2 [:svg-attrs :style :fill-opacity]))))) + +(t/deftest birth-setup-stroke-reads-kebab-attrs + (let [svg-data {:name "stroke" + :tag :svg + :attrs {:width "100" :height "100" :viewBox "0 0 100 100"} + :content [{:tag :path + :attrs {:d "M0 0L10 10" + :fill "none" + :stroke "#ff0000" + :stroke-width "3" + :stroke-opacity "0.5" + :stroke-linecap "round"} + :content []}]} + [_ children] (sb/create-svg-shapes svg-data {:x 0 :y 0} {} nil nil #{} false) + path (first (filter #(= :path (:type %)) children)) + stroke (first (:strokes path))] + (t/is (some? path)) + (t/is (some? (:stroke-color stroke))) + (t/is (= 3.0 (:stroke-width stroke))) + (t/is (= 0.5 (:stroke-opacity stroke))) + (t/is (= :round (:stroke-cap-start stroke))) + (t/is (= :round (:stroke-cap-end stroke))) + (t/is (= "round" (get-in path [:svg-attrs :stroke-linecap])) "linecap stays for renderers") + (t/is (nil? (get-in path [:svg-attrs :stroke-width])) "consumed keys are removed") + (t/is (nil? (get-in path [:svg-attrs :stroke-opacity]))))) + +(t/deftest birth-setup-opacity-reads-kebab-blend-mode + ;; Regression for PR #12006 review: `setup-opacity` still looked up + ;; `:mixBlendMode` (camel) after birth started storing kebab keys, so + ;; `mix-blend-mode` survived in `:svg-attrs` and `:blend-mode` was + ;; never set. Birth stores the style map kebab-ized. + (let [svg-data {:name "blend" + :tag :svg + :attrs {:width "100" :height "100" :viewBox "0 0 100 100"} + :content [{:tag :rect + :attrs {:x "10" :y "10" :width "80" :height "80" + :style "mix-blend-mode: multiply"} + :content []}]} + [_ children] (sb/create-svg-shapes svg-data {:x 0 :y 0} {} nil nil #{} false) + rect (first (filter #(and (= :rect (:type %)) (not (:hidden %))) children))] + (t/is (some? rect)) + (t/is (= :multiply (:blend-mode rect))) + (t/is (nil? (get-in rect [:svg-attrs :style :mix-blend-mode])) "consumed keys are removed"))) + +(t/deftest birth-child-style-beats-inherited-group-attr + ;; Regression: `inherit-attributes` filtered on `(:styles attrs)` + ;; (plural, always nil) instead of `(:style attrs)`, so a group attr + ;; leaked next to the child's own style and `setup-stroke` preferred + ;; the group color. Per CSS the child's inline style wins. + (let [svg-data {:name "inherit" + :tag :svg + :attrs {:width "100" :height "100" :viewBox "0 0 100 100"} + :content [{:tag :g + :attrs {:stroke "#ff0000"} + :content [{:tag :rect + :attrs {:x "10" :y "10" :width "80" :height "80" + :fill "none" + :style "stroke:#0000ff"} + :content []}]}]} + [_ children] (sb/create-svg-shapes svg-data {:x 0 :y 0} {} nil nil #{} false) + rect (first (filter #(and (= :rect (:type %)) (not (:hidden %))) children)) + stroke (first (:strokes rect))] + (t/is (some? rect)) + (t/is (= (clr/parse "#0000ff") (:stroke-color stroke)) "child style beats group attr") + (t/is (nil? (get-in rect [:svg-attrs :stroke])) "group attr is not inherited over child style"))) diff --git a/common/test/common_tests/files/validate_test.cljc b/common/test/common_tests/files/validate_test.cljc index 48fd3e3f07..d36743ee24 100644 --- a/common/test/common_tests/files/validate_test.cljc +++ b/common/test/common_tests/files/validate_test.cljc @@ -10,6 +10,7 @@ - validate-file-affected – returns nil or list of errors - validate-file-affected! – same but raises on non-empty errors + - validate-file-schema-affected! – schema check of the touched pages The tests verify the scoping logic implemented by extract-affected-ids (tested indirectly) by injecting controlled broken states into specific @@ -50,6 +51,15 @@ (let [root (ctst/get-shape page uuid/zero)] (ctst/set-shape page (update root :shapes conj missing-id))))))))) +(defn- inject-invalid-page + "Give the page identified by `page-label` a name that is not a string, + so the page fails the file schema check." + [file page-label] + (ctf/update-file-data + file + (fn [file-data] + (ctpl/update-page file-data (thi/id page-label) #(assoc % :name 42))))) + (defn- inject-broken-component "Add a deleted component with `:objects nil` to the file. @@ -447,3 +457,62 @@ (t/is (= :validation (:type data))) (t/is (= :referential-integrity (:code data))) (t/is (seq (:details data))))))))) + +;; ---------------------------------------------------------------- +;; 11. validate-file-schema-affected! – schema of the touched pages +;; ---------------------------------------------------------------- + +(defn- schema-error? + [file changes] + (try + (cfv/validate-file-schema-affected! file changes) + false + (catch #?(:clj clojure.lang.ExceptionInfo :cljs cljs.core.ExceptionInfo) _ + true))) + +(t/deftest validate-file-schema-affected!-returns-the-file + (let [file (thf/sample-file :file1 :page-label :page1) + changes [{:type :mod-obj :page-id (thi/id :page1) :id uuid/zero :operations []}]] + (t/is (= file (cfv/validate-file-schema-affected! file changes))))) + +(t/deftest validate-file-schema-affected!-checks-touched-pages + (let [file (-> (thf/sample-file :file1 :page-label :page1) + (thf/add-sample-page :page2) + (inject-invalid-page :page2))] + + (t/testing "a shape change on the invalid page raises" + (t/is (schema-error? file [{:type :mod-obj :page-id (thi/id :page2) :id uuid/zero :operations []}]))) + + (t/testing "mod-page on the invalid page raises" + (t/is (schema-error? file [{:type :mod-page :id (thi/id :page2) :name "page2"}]))) + + (t/testing "page plugin data on the invalid page raises" + (t/is (schema-error? file [{:type :set-plugin-data :object-type :page :object-id (thi/id :page2) + :namespace "ns" :key "k" :value "v"}]))) + + (t/testing "a guide on the invalid page raises" + (t/is (schema-error? file [{:type :set-guide :page-id (thi/id :page2) :id (uuid/next) :params nil}]))))) + +(t/deftest validate-file-schema-affected!-skips-untouched-pages + (let [file (-> (thf/sample-file :file1 :page-label :page1) + (thf/add-sample-page :page2) + (inject-invalid-page :page2))] + + (t/testing "a shape change on another page does not check the invalid page" + (t/is (not (schema-error? file [{:type :mod-obj :page-id (thi/id :page1) :id uuid/zero :operations []}])))) + + (t/testing "a file level change does not check the invalid page" + (t/is (not (schema-error? file [{:type :del-color :id (uuid/next)}])))))) + +(t/deftest validate-file-schema-affected!-checks-the-rest-of-the-file + (let [color-id (uuid/next) + file (-> (thf/sample-file :file1 :page-label :page1) + (ctf/update-file-data #(assoc-in % [:colors color-id] {:id color-id :name 42})))] + (t/is (schema-error? file [{:type :mod-obj :page-id (thi/id :page1) :id uuid/zero :operations []}])))) + +(t/deftest validate-file-schema-affected!-checks-everything-for-unknown-changes + (let [file (-> (thf/sample-file :file1 :page-label :page1) + (thf/add-sample-page :page2) + (inject-invalid-page :page2))] + (t/is (schema-error? file [{:type :mod-obj :page-id (thi/id :page1) :id uuid/zero :operations []} + {:type :some-future-change}])))) diff --git a/common/test/common_tests/files_builder_test.cljc b/common/test/common_tests/files_builder_test.cljc index 23891a583b..db0242ea93 100644 --- a/common/test/common_tests/files_builder_test.cljc +++ b/common/test/common_tests/files_builder_test.cljc @@ -18,6 +18,39 @@ :stroke-color color :stroke-opacity 1}]) +(t/deftest add-shape-normalizes-svg-defs-and-content-to-kebab + ;; Defense in depth: generic producers that bypass `shapes-builder` + ;; still land on the stored kebab convention for defs and content. + (let [file-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next) + state (-> (fb/create-state) + (fb/add-file {:id file-id :name "Test file"}) + (fb/add-page {:id page-id :name "Page 1"}) + (fb/add-shape {:id shape-id + :type :svg-raw + :name "raw" + :x 0 + :y 0 + :width 10 + :height 10 + :content {:tag :g + :attrs {:strokeWidth "2" + :data-foo "keep-me"} + :content []} + :svg-attrs {:fillRule "evenodd"} + :svg-defs {"g1" {:tag :linearGradient + :attrs {:id "g1" + :gradientUnits "userSpaceOnUse"} + :content []}}})) + shape (fb/get-shape state shape-id) + node (get (:svg-defs shape) "g1")] + (t/is (= "evenodd" (get-in shape [:svg-attrs :fill-rule]))) + (t/is (= "userSpaceOnUse" (get-in node [:attrs :gradient-units]))) + (t/is (= "2" (get-in shape [:content :attrs :stroke-width]))) + (t/is (= "keep-me" (get-in shape [:content :attrs :data-foo])) + "unknown content keys survive"))) + (t/deftest add-bool-uses-difference-head-style (let [file-id (uuid/next) page-id (uuid/next) diff --git a/common/test/common_tests/files_changes_test.cljc b/common/test/common_tests/files_changes_test.cljc index 935ff092fb..3cf8d4f93b 100644 --- a/common/test/common_tests/files_changes_test.cljc +++ b/common/test/common_tests/files_changes_test.cljc @@ -22,6 +22,7 @@ [app.common.types.typography :as ctt] [app.common.uuid :as uuid] [clojure.datafy :refer [datafy]] + [clojure.set :as set] [clojure.test :as t] [clojure.walk :as walk] [common-tests.types.shape-decode-encode-test :refer [json-roundtrip]])) @@ -1165,3 +1166,138 @@ (nil? (get-in result2 [:pages-index page-id :default-grids]))))) {:num 1000}))) + +;; --- Candidate object ids + +(defn- add-shape-change + [page-id [id parent-id frame-id type]] + {:type :add-obj :id id :page-id page-id + :frame-id frame-id :parent-id parent-id + :obj (cts/setup-shape + {:id id :type type :name (name type) + :frame-id frame-id :parent-id parent-id + :x 0 :y 0 :width 10 :height 10})}) + +(defn- make-tree-data + "A board with a group of two rects, a rect at the root, and two nested + groups around a rect." + [page-id ids] + (ch/process-changes + (make-file-data (uuid/custom 40 99) page-id) + (mapv (partial add-shape-change page-id) + [[(ids :board) uuid/zero uuid/zero :frame] + [(ids :group) (ids :board) (ids :board) :group] + [(ids :rect-a) (ids :group) (ids :board) :rect] + [(ids :rect-b) (ids :group) (ids :board) :rect] + [(ids :rect-c) uuid/zero uuid/zero :rect] + [(ids :outer) uuid/zero uuid/zero :group] + [(ids :inner) (ids :outer) uuid/zero :group] + [(ids :rect-d) (ids :inner) uuid/zero :rect]]))) + +(def ^:private tree-ids + (zipmap [:board :group :rect-a :rect-b :rect-c :outer :inner :rect-d :new] + (map #(uuid/custom 40 %) (range 1 10)))) + +(defn- get-changed-ids + [old-objects new-objects] + (into #{} + (filter #(not= (get old-objects %) (get new-objects %))) + (set/union (set (keys old-objects)) (set (keys new-objects))))) + +(defn- apply-and-get-candidates + "Applies `changes`, checks that the candidates cover every changed id, + and returns the candidates." + [data page-id changes] + (let [old-objects (get-in data [:pages-index page-id :objects]) + result (ch/process-changes data changes) + new-objects (get-in result [:pages-index page-id :objects]) + candidates (ch/get-candidate-object-ids old-objects new-objects page-id changes)] + (t/is (set/subset? (get-changed-ids old-objects new-objects) candidates) + "Every changed object must be a candidate") + candidates)) + +(t/deftest candidate-ids-of-added-shapes + (let [page-id (uuid/custom 40 100) + ids tree-ids + data (make-tree-data page-id ids)] + (t/testing "A shape added at the root gives the shape and the root" + (t/is (= #{(ids :new) uuid/zero} + (apply-and-get-candidates + data page-id + [(add-shape-change page-id [(ids :new) uuid/zero uuid/zero :rect])])))) + (t/testing "A shape added inside a board gives the shape and the board" + (t/is (= #{(ids :new) (ids :board)} + (apply-and-get-candidates + data page-id + [(add-shape-change page-id [(ids :new) (ids :board) (ids :board) :rect])])))))) + +(t/deftest candidate-ids-of-modified-shape + (let [page-id (uuid/custom 40 100) + ids tree-ids + data (make-tree-data page-id ids)] + (t/is (= #{(ids :rect-a)} + (apply-and-get-candidates + data page-id + [{:type :mod-obj :id (ids :rect-a) :page-id page-id + :operations [{:type :set :attr :name :val "renamed"}]}]))))) + +(t/deftest candidate-ids-of-deleted-group + (let [page-id (uuid/custom 40 100) + ids tree-ids + data (make-tree-data page-id ids) + candidates (apply-and-get-candidates + data page-id + [{:type :del-obj :id (ids :group) :page-id page-id}])] + (t/is (set/subset? #{(ids :group) (ids :rect-a) (ids :rect-b) (ids :board)} + candidates)))) + +(t/deftest candidate-ids-of-moved-group + (let [page-id (uuid/custom 40 100) + ids tree-ids + data (make-tree-data page-id ids) + candidates (apply-and-get-candidates + data page-id + [{:type :mov-objects :page-id page-id + :parent-id (ids :board) :shapes [(ids :outer)]}])] + (t/is (set/subset? #{(ids :outer) (ids :inner) (ids :rect-d) (ids :board) uuid/zero} + candidates)))) + +(t/deftest candidate-ids-of-registered-objects + (let [page-id (uuid/custom 40 100) + ids tree-ids + data (make-tree-data page-id ids) + rect (get-in data [:pages-index page-id :objects (ids :rect-d)]) + moved (gsh/move rect (gpt/point 100 0)) + data (ch/process-changes + data + [{:type :mod-obj :id (ids :rect-d) :page-id page-id + :operations (mapv (fn [[attr val]] {:type :set :attr attr :val val}) + (select-keys moved [:x :y :selrect :points]))}])] + (t/is (set/subset? #{(ids :inner) (ids :outer)} + (apply-and-get-candidates + data page-id + [{:type :reg-objects :page-id page-id :shapes [(ids :rect-d)]}]))))) + +(t/deftest candidate-ids-of-reordered-children + (let [page-id (uuid/custom 40 100) + ids tree-ids + data (make-tree-data page-id ids)] + (t/is (= #{(ids :group)} + (apply-and-get-candidates + data page-id + [{:type :reorder-children :page-id page-id + :parent-id (ids :group) :shapes [(ids :rect-b) (ids :rect-a)]}]))))) + +(t/deftest candidate-ids-ignore-other-pages + (let [page-id (uuid/custom 40 100) + ids tree-ids + data (make-tree-data page-id ids)] + (t/is (= #{} + (ch/get-candidate-object-ids + (get-in data [:pages-index page-id :objects]) + (get-in data [:pages-index page-id :objects]) + page-id + [{:type :mod-obj :id (ids :rect-a) :page-id (uuid/custom 40 101) + :operations [{:type :set :attr :name :val "renamed"}]} + {:type :set-plugin-data :object-type :shape :object-id (ids :rect-a) + :page-id page-id :namespace "ns" :key "k" :value "v"}]))))) diff --git a/common/test/common_tests/files_migrations_0035_test.cljc b/common/test/common_tests/files_migrations_0035_test.cljc new file mode 100644 index 0000000000..8dd421d614 --- /dev/null +++ b/common/test/common_tests/files_migrations_0035_test.cljc @@ -0,0 +1,319 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns common-tests.files-migrations-0035-test + (:require + [app.common.files.migrations :as cfm] + [app.common.geom.matrix :as gmt] + [app.common.geom.rect :as grc] + [app.common.types.file :as ctf] + [app.common.types.shape :as cts] + [app.common.uuid :as uuid] + [clojure.test :as t])) + +;; 0035-fix-shape-svg-attrs-and-defs +;; Stored svg attribute keys are kebab-case (issue #11947). Old birth and +;; the cleaner era stored camelCase React prop names, which the v3 reader +;; kebab-ized on import beyond repair. This migration converts once, with +;; the same birth transforms: `:svg-attrs` and `:svg-defs` node attrs go +;; through the whitelist+kebab transform, svg-raw `:content` attrs are +;; kebab-ized spelling-only. It replaces "0021-fix-shape-svg-attrs" (id +;; withdrawn): files that recorded it migrate again through this one. + +(def ^:private migration-id + "0035-fix-shape-svg-attrs-and-defs") + +(defn- make-shape + [shape-id shape] + (assoc shape :id shape-id)) + +(defn- camel-shape + [shape-id] + (make-shape shape-id + {:type :rect + :svg-attrs {:fillRule "evenodd" + :stroke-width "2" + :stroke-style "dotted" + :data-foo "drop-me"}})) + +(defn- camel-defs + [] + {"g1" {:tag :linearGradient + :attrs {:id "g1" + :gradientUnits "userSpaceOnUse" + :xlink:href "#base" + :data-x "drop-me"} + :content [{:tag :stop + :attrs {:offset "0" + :stopColor "#ffffff"} + :content []}]}}) + +(t/deftest migration-0035-converts-camel-svg-attrs-to-kebab-in-pages + (let [shape-id (uuid/next) + page-id (uuid/next) + data {:pages-index + {page-id + {:objects + {shape-id (camel-shape shape-id)}}}} + data' (cfm/migrate-data data migration-id) + attrs (get-in data' [:pages-index page-id :objects shape-id :svg-attrs])] + (t/is (= "evenodd" (:fill-rule attrs))) + (t/is (= "2" (:stroke-width attrs))) + (t/is (= "dotted" (:stroke-style attrs)) "penpot-extra-attrs survive") + (t/is (not (contains? attrs :data-foo)) "unknown attrs are dropped") + (t/is (not (contains? attrs :fillRule)) "no camel keys survive"))) + +(t/deftest migration-0035-converts-camel-svg-attrs-to-kebab-in-components + (let [shape-id (uuid/next) + component-id (uuid/next) + data {:components + {component-id + {:objects + {shape-id (camel-shape shape-id)}}}} + data' (cfm/migrate-data data migration-id) + attrs (get-in data' [:components component-id :objects shape-id :svg-attrs])] + (t/is (= "evenodd" (:fill-rule attrs))) + (t/is (not (contains? attrs :fillRule)) "no camel keys survive"))) + +(t/deftest migration-0035-normalizes-def-node-attrs + (let [shape-id (uuid/next) + page-id (uuid/next) + data {:pages-index + {page-id + {:objects + {shape-id (make-shape shape-id + {:type :rect + :svg-defs (camel-defs)})}}}} + data' (cfm/migrate-data data migration-id) + grad (get-in data' [:pages-index page-id :objects shape-id :svg-defs "g1"])] + (t/is (= :linearGradient (:tag grad)) "tags are untouched") + (t/is (= "g1" (get-in grad [:attrs :id])) ":id survives") + (t/is (= "userSpaceOnUse" (get-in grad [:attrs :gradient-units]))) + (t/is (= "#base" (get-in grad [:attrs :xlink-href]))) + (t/is (not (contains? (:attrs grad) :data-x)) "unknown def attrs are dropped") + (t/is (= "#ffffff" (get-in grad [:content 0 :attrs :stop-color])) + "nested stops are normalized"))) + +(t/deftest migration-0035-kebabizes-content-spelling-only + (let [shape-id (uuid/next) + page-id (uuid/next) + raw-id (uuid/next) + data {:pages-index + {page-id + {:objects + {shape-id (make-shape shape-id + {:type :path + :content [{:marker "untouched"}]}) + raw-id (make-shape raw-id + {:type :svg-raw + :content {:tag :g + :attrs {:strokeWidth "2" + :data-foo "keep-me"} + :content [{:tag :path + :attrs {:strokeLinecap "round"} + :content []}]}})}}}} + data' (cfm/migrate-data data migration-id) + path (get-in data' [:pages-index page-id :objects shape-id]) + raw (get-in data' [:pages-index page-id :objects raw-id]) + content (:content raw)] + (t/is (= [{:marker "untouched"}] (:content path)) + "non-svg-raw content is untouched") + (t/is (= "2" (get-in content [:attrs :stroke-width]))) + (t/is (= "keep-me" (get-in content [:attrs :data-foo])) + "unknown content keys survive") + (t/is (= "round" (get-in content [:content 0 :attrs :stroke-linecap]))))) + +(defn- string-tag-defs + [] + {:a {:tag "mask" + :attrs {:id "a" :maskUnits "userSpaceOnUse"} + :content [{:tag "rect" + :attrs {:width "10" :height "10" :fill "#FFF"} + :content []}]}}) + +(defn- string-tag-content + [] + {:tag "g" + :attrs {} + :content [{:tag "text" + :attrs {:fontSize "12"} + :content ["Hi"]} + {:tag "style" + :attrs nil + :content [".a{fill:red}"]}]}) + +(t/deftest migration-0035-keywordizes-string-tags + (let [shape-id (uuid/next) + raw-id (uuid/next) + page-id (uuid/next) + data {:pages-index + {page-id + {:objects + {shape-id (make-shape shape-id + {:type :rect + :svg-defs (string-tag-defs)}) + raw-id (make-shape raw-id + {:type :svg-raw + :content (string-tag-content)})}}}} + data' (cfm/migrate-data data migration-id) + mask (get-in data' [:pages-index page-id :objects shape-id :svg-defs :a]) + content (get-in data' [:pages-index page-id :objects raw-id :content])] + (t/is (= :mask (:tag mask))) + (t/is (= :rect (get-in mask [:content 0 :tag])) "nested def tags too") + (t/is (= "userSpaceOnUse" (get-in mask [:attrs :mask-units]))) + (t/is (= :g (:tag content))) + (t/is (= :text (get-in content [:content 0 :tag])) "nested content tags too") + (t/is (= ["Hi"] (get-in content [:content 0 :content]))) + (t/is (= {} (get-in content [:content 1 :attrs])) "nil attrs become an empty map") + (t/is (= data' (cfm/migrate-data data' migration-id)) "re-running is stable"))) + +(t/deftest migration-0035-string-tag-file-passes-file-validation + ;; `migrate-file` validates the whole file data after migrating: a file + ;; with string node tags or nil node attrs must come out valid, or it + ;; cannot be opened. + (let [rect-id (uuid/next) + raw-id (uuid/next) + file (ctf/make-file {:name "String svg tags"}) + page-id (first (get-in file [:data :pages])) + rect (-> (cts/setup-shape {:id rect-id :type :rect :name "rect"}) + (assoc :svg-defs (string-tag-defs))) + raw (-> (cts/setup-shape {:id raw-id :type :svg-raw :name "raw"}) + (assoc :content (string-tag-content))) + file (-> file + (assoc :migrations (disj cfm/available-migrations migration-id)) + (update-in [:data :pages-index page-id :objects] assoc rect-id rect raw-id raw) + (update-in [:data :pages-index page-id :objects uuid/zero :shapes] conj rect-id raw-id)) + file' (cfm/migrate-file file {})] + (t/is (= :mask (get-in file' [:data :pages-index page-id :objects rect-id :svg-defs :a :tag]))) + (t/is (= :g (get-in file' [:data :pages-index page-id :objects raw-id :content :tag]))))) + +(t/deftest migration-0035-turns-plain-map-svg-transform-into-matrix + ;; The svg defs renderer multiplies `:svg-transform` as a matrix; a + ;; plain map there renders as `matrix(NaN, ...)`. + (let [plain-id (uuid/next) + matrix-id (uuid/next) + page-id (uuid/next) + data {:pages-index + {page-id + {:objects + {plain-id (make-shape plain-id + {:type :path + :svg-transform {:a 1.0 :b 0.0 :c 0.0 :d 1.0 :e 10.0 :f 20.0}}) + matrix-id (make-shape matrix-id + {:type :path + :svg-transform (gmt/matrix 2 0 0 2 0 0)})}}}} + data' (cfm/migrate-data data migration-id) + plain' (get-in data' [:pages-index page-id :objects plain-id :svg-transform]) + matrix' (get-in data' [:pages-index page-id :objects matrix-id :svg-transform])] + (t/is (gmt/matrix? plain')) + (t/is (= (gmt/matrix 1 0 0 1 10 20) plain')) + (t/is (= (gmt/matrix 2 0 0 2 0 0) matrix') "matrices are left as they are") + (t/is (= data' (cfm/migrate-data data' migration-id)) "re-running is stable"))) + +(t/deftest migration-0035-leaves-kebab-files-equal + (let [shape-id (uuid/next) + page-id (uuid/next) + data {:pages-index + {page-id + {:objects + {shape-id (make-shape shape-id + {:type :rect + :svg-attrs {:fill-rule "evenodd" + :stroke-width "2"} + :svg-defs {"g1" {:tag :linearGradient + :attrs {:id "g1" + :gradient-units "userSpaceOnUse"} + :content []}}})}}}} + data' (cfm/migrate-data data migration-id)] + (t/is (= data data') "kebab is a fixed point"))) + +(t/deftest migration-0035-is-idempotent + (let [shape-id (uuid/next) + page-id (uuid/next) + data {:pages-index + {page-id + {:objects + {shape-id (camel-shape shape-id)}}}} + data' (cfm/migrate-data data migration-id)] + (t/is (= data' (cfm/migrate-data data' migration-id))))) + +(t/deftest migration-0035-normalizes-flat-svg-viewbox-to-rect + ;; Carried over from the withdrawn 0021: legacy files hold + ;; `:svg-viewbox` as a plain map instead of a rect record. + (let [shape-id (uuid/next) + page-id (uuid/next) + data {:pages-index + {page-id + {:objects + {shape-id (make-shape shape-id + {:type :rect + :svg-viewbox {:x 1 :y 2 :width 3 :height 4}})}}}} + data' (cfm/migrate-data data migration-id) + viewbox (get-in data' [:pages-index page-id :objects shape-id :svg-viewbox])] + (t/is (grc/rect? viewbox) "flat viewbox becomes a rect record") + (t/is (= 1 (:x viewbox))) + (t/is (= 2 (:y viewbox))) + (t/is (= 3 (:width viewbox))) + (t/is (= 4 (:height viewbox))) + (t/is (= data' (cfm/migrate-data data' migration-id)) + "re-running is stable"))) + +(t/deftest migration-0035-passes-non-maps-through + (let [shape-id (uuid/next) + page-id (uuid/next) + data {:pages-index + {page-id + {:objects + {shape-id (make-shape shape-id + {:type :rect + :svg-attrs "junk" + :svg-defs "junk"})}}}} + data' (cfm/migrate-data data migration-id) + shape (get-in data' [:pages-index page-id :objects shape-id])] + (t/is (= "junk" (:svg-attrs shape)) "non-maps reach validation untouched") + (t/is (= "junk" (:svg-defs shape))))) + +(t/deftest migration-0035-junk-viewbox-becomes-default-rect + ;; Unlike `:svg-attrs`/`:svg-defs` (passed through to validation), + ;; `:svg-viewbox` goes through `make-rect`, which does not throw on + ;; junk: it falls back to the default rect. Inherited from 0021. + (let [shape-id (uuid/next) + page-id (uuid/next) + data {:pages-index + {page-id + {:objects + {shape-id (make-shape shape-id + {:type :rect + :svg-viewbox "junk"})}}}} + data' (cfm/migrate-data data migration-id) + viewbox (get-in data' [:pages-index page-id :objects shape-id :svg-viewbox])] + (t/is (grc/rect? viewbox) "junk viewbox becomes a rect, not an error") + (t/is (= 0 (:x viewbox))) + (t/is (= 0.01 (:width viewbox))))) + +(t/deftest migration-0035-runs-through-file-migration + ;; A file that recorded the withdrawn "0021-fix-shape-svg-attrs" (and + ;; everything else current) still needs migration: 0021 left + ;; `available-migrations`, which re-arms it for 0035. + (let [shape-id (uuid/next) + file (ctf/make-file {:name "Legacy camel svg attrs"}) + page-id (first (get-in file [:data :pages])) + shape (-> (cts/setup-shape {:id shape-id :type :rect}) + (assoc :svg-attrs {:fillRule "evenodd"})) + file (-> file + (assoc :migrations (-> cfm/available-migrations + (disj migration-id) + (conj "0021-fix-shape-svg-attrs"))) + (assoc-in [:data :pages-index page-id :objects shape-id] shape)) + file' (cfm/migrate-file file {})] + (t/is (cfm/need-migration? file) "new migration detected") + (t/is (not (cfm/need-migration? file')) "new migration recorded") + (t/is (contains? (:migrations file') migration-id) "migration id persisted") + (t/is (not (contains? cfm/available-migrations "0021-fix-shape-svg-attrs")) + "the withdrawn 0021 id stays out: its camel-restoring transform must never run again") + (t/is (= "evenodd" + (get-in file' [:data :pages-index page-id :objects shape-id :svg-attrs :fill-rule])) + "migration repaired file data before schema validation"))) diff --git a/common/test/common_tests/geom_test.cljc b/common/test/common_tests/geom_test.cljc index 98e80f7302..feb37550dd 100644 --- a/common/test/common_tests/geom_test.cljc +++ b/common/test/common_tests/geom_test.cljc @@ -126,3 +126,13 @@ (let [m (gmt/matrix 1 0 0 1 0 0) enc (sm/encode gmt/schema:matrix m (sm/string-transformer))] (t/is (= 5 (count (filter #(= \, %) enc))))))) + +(t/deftest matrix-coerce-test + (t/is (= (gmt/matrix 1 0 0 1 10 20) + (gmt/coerce {:a 1.0 :b 0.0 :c 0.0 :d 1.0 :e 10.0 :f 20.0})) + "a plain map becomes a matrix") + (t/is (gmt/matrix? (gmt/coerce {:a 1 :b 0 :c 0 :d 1 :e 0 :f 0}))) + (let [m (gmt/matrix 2 0 0 2 0 0)] + (t/is (identical? m (gmt/coerce m)) "matrices pass through")) + (t/is (= {:a 1 :b 0} (gmt/coerce {:a 1 :b 0})) "incomplete maps are left as they are") + (t/is (nil? (gmt/coerce nil)))) diff --git a/common/test/common_tests/logic/comp_sync_layout_items_test.cljc b/common/test/common_tests/logic/comp_sync_layout_items_test.cljc new file mode 100644 index 0000000000..0daeb4c77c --- /dev/null +++ b/common/test/common_tests/logic/comp_sync_layout_items_test.cljc @@ -0,0 +1,228 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns common-tests.logic.comp-sync-layout-items-test + (:require + [app.common.files.changes-builder :as pcb] + [app.common.logic.libraries :as cll] + [app.common.test-helpers.components :as thc] + [app.common.test-helpers.compositions :as tho] + [app.common.test-helpers.files :as thf] + [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.shapes :as ths] + [clojure.set :as set] + [clojure.test :as t])) + +(t/use-fixtures :each thi/test-fixture) + +;; ---- Value sets + +;; `:layout-item-h-sizing` and `:layout-item-v-sizing` may be absent, or take +;; one of these values. Each test sets both attributes to the same value. +;; +;; :fix -> valid for a board with flex and for any direct child of a flex board. +;; :auto -> valid for a board with flex. +;; :fill -> valid for a direct child of a flex board. +;; +;; A board with flex that is a direct child of another flex board (:comp1, +;; :board1) accepts every value. A plain shape that is a direct child (:rect1) +;; accepts :fix and :fill. :container-main is a top-level flex board, so it +;; accepts :fix and :auto but not :fill. +(def ^:private sizing-values-container + #{nil :fix :auto}) + +(def ^:private sizing-values-child + #{nil :fix :fill}) + +(def ^:private sizing-values-both + (set/union sizing-values-container sizing-values-child)) + +;; Cases for the touched tests: [copy-value main-before main-after]. +;; `main-before` differs from `copy-value`, so setting the copy activates the +;; sync group in :touched. `main-after` is a value the root copy would receive +;; if it were not touched. +(def ^:private root-touched-cases + [[nil :fill :auto] + [:fix :fill :auto] + [:auto :fill :fix] + [:fill :auto :fix]]) + +(def ^:private child-touched-cases + [[nil :fill :fill] + [:fix :fill :fill] + [:fill nil :fix]]) + +;; ---- Setup + +(defn- add-flex-frame + [file label & {:keys [parent-label]}] + (tho/add-frame file label (cond-> {:layout :flex :layout-flex-dir :row} + (some? parent-label) (assoc :parent-label parent-label)))) + +(defn- set-sizing + "Sets both sizing attrs of the shape. A nil value removes them." + [file label value] + (-> file + (tho/change-shape + label + (fn [shape] + (cond-> shape + (some? value) (assoc :layout-item-h-sizing value :layout-item-v-sizing value) + (nil? value) (dissoc :layout-item-h-sizing :layout-item-v-sizing)))))) + +(defn- set-sizing-untouched + "Sets both sizing attrs of the shape without marking it as touched." + [file label value] + (tho/change-shape + file label + (fn [shape] + (cond-> shape + (some? value) (assoc :layout-item-h-sizing value :layout-item-v-sizing value) + (nil? value) (dissoc :layout-item-h-sizing :layout-item-v-sizing))) + :ignore-touched true)) + +(defn- setup-file + "Builds this tree (all boards are flex): + + :container-main + :comp1 # component main (component :comp1-component) + :board1 + :rect1 + :container-copy + :copy1 #--> component :comp1-component + :copy-board1 + :copy-rect1 + + The h/v sizing attrs of :container-main, :comp1 and :board1 are set to the + given values. The copy is created afterwards, without explicit sizing attrs." + [comp-value board-value rect-value] + (let [file (-> (thf/sample-file :file1) + (add-flex-frame :container-main) + (add-flex-frame :comp1 :parent-label :container-main) + (add-flex-frame :board1 :parent-label :comp1) + (tho/add-rect :rect1 :parent-label :board1) + (set-sizing :comp1 comp-value) + (set-sizing :board1 board-value) + (set-sizing :rect1 rect-value) + (thc/make-component :comp1-component :comp1) + (add-flex-frame :container-copy) + (thc/instantiate-component :comp1-component :copy1 + :parent-label :container-copy + :children-labels [:copy-board1]))] + (thi/set-id! :copy-rect1 (first (:shapes (ths/get-shape file :copy-board1)))) + file)) + +;; ---- Actions + +(defn- sync-component + "Propagates the main component changes to its copies." + [file] + (let [file-id (:id file) + component-id (:id (thc/get-component file :comp1-component))] + (thf/apply-changes + file + (cll/generate-sync-file-changes (pcb/empty-changes) + nil + :components + file-id + component-id + file-id + {file-id file} + file-id)))) + +(defn- sizing + [file label] + (let [shape (ths/get-shape file label)] + [(:layout-item-h-sizing shape) (:layout-item-v-sizing shape)])) + +(defn- touched + [file label] + (:touched (ths/get-shape file label))) + +(defn- normalize-sizing + "`:fix` is the default sizing, so it is equivalent to the attribute being + absent. Both forms are accepted in the checks." + [value] + (if (= value :fix) nil value)) + +(defn- check-sizing + [file label expected] + (t/is (= (mapv normalize-sizing [expected expected]) + (mapv normalize-sizing (sizing file label))) + (str label " must be " (pr-str expected)))) + +(defn- expected-root-copy-sizing + "Sizing the copy of the component root is expected to have after a sync. + The root copy only receives `:auto` and `:fix`. `:fill` is never propagated + from the root, and neither is the absence of the attribute, so in those + cases the copy keeps the value it had before the propagation." + [comp-value previous] + (case comp-value + :auto :auto + :fix :fix + previous)) + +(defn- run-propagation + "Changes the main shapes to every valid value and checks that the value + reaches the copies." + [file] + (let [initial (first (sizing file :copy1))] + (doseq [comp-value sizing-values-both + board-value sizing-values-both + rect-value sizing-values-child] + (let [file' (-> file + (set-sizing :comp1 comp-value) + (set-sizing :board1 board-value) + (set-sizing :rect1 rect-value) + (sync-component))] + (check-sizing file' :copy1 (expected-root-copy-sizing comp-value initial)) + (check-sizing file' :copy-board1 board-value) + (check-sizing file' :copy-rect1 rect-value))) + file)) + +(defn- run-touched + "Overrides the sizing in the copy with every valid value (which activates the + sync groups in :touched) and then changes the main. The main values must not + be propagated to the copy this time." + [file] + (doseq [[label main-label cases] + [[:copy1 :comp1 root-touched-cases] + [:copy-board1 :board1 root-touched-cases] + [:copy-rect1 :rect1 child-touched-cases]] + [value before after] cases] + (let [file' (-> file + ;; Align the copy with the main, then override it with a + ;; different value so the override activates :touched. + (set-sizing main-label before) + (set-sizing-untouched label before) + (set-sizing label value)) + _ (t/is (not= (normalize-sizing value) + (normalize-sizing (first (sizing file' main-label)))) + (str label " value must differ from " main-label)) + _ (t/is (= #{:layout-item-h-sizing :layout-item-v-sizing} (touched file' label)) + (str label " must be touched after setting " (pr-str value))) + ;; Changing the main must not reach the copy. + file'' (-> file' + (set-sizing main-label after) + (sync-component))] + (check-sizing file'' label value)))) + +(defn- run-case + [comp-value board-value rect-value] + (-> (setup-file comp-value board-value rect-value) + (run-propagation) + (run-touched))) + +;; ---- Tests + +(t/deftest test-sync-layout-item-sizing + (doseq [comp-value sizing-values-both + board-value sizing-values-both + rect-value sizing-values-child] + (t/testing (str "Testing case comp1=" comp-value + ", board1=" board-value + ", rect1=" rect-value) + (run-case comp-value board-value rect-value)))) diff --git a/common/test/common_tests/logic/comp_sync_test.cljc b/common/test/common_tests/logic/comp_sync_test.cljc index a929c7a616..02cd219779 100644 --- a/common/test/common_tests/logic/comp_sync_test.cljc +++ b/common/test/common_tests/logic/comp_sync_test.cljc @@ -18,6 +18,7 @@ [app.common.test-helpers.ids-map :as thi] [app.common.test-helpers.shapes :as ths] [app.common.types.component :as ctk] + [app.common.types.container :as ctn] [app.common.types.shape-tree :as ctst] [clojure.test :as t])) @@ -539,3 +540,258 @@ ;; A position-only change in the main component must not propagate to copies ;; and therefore must produce no redo-changes. (t/is (empty? (:redo-changes sync-changes))))) + +(defn- sync-file-changes + [file] + (cll/generate-sync-file-changes (pcb/empty-changes) + nil + :components + (:id file) + (thi/id :component1) + (:id file) + {(:id file) file} + (:id file))) + +(t/deftest test-sync-many-updated-shapes + ;; The undo changes of a sync used to grow one lazy level per updated + ;; shape (#12576). Overflowing the stack needs thousands of shapes, which + ;; is too slow here; test-update-attr-changes-many-calls covers that. + (let [;; ==== Setup + n 500 + child-labels (mapv #(keyword (str "main-child-" %)) (range n)) + file (-> (thf/sample-file :file1) + (tho/add-component-with-many-children-and-copy + :component1 + :main-root + child-labels + :copy-root + :main-child-params-list (repeat n {:fills (ths/sample-fills-color + :fill-color "#abcdef")}))) + page (thf/current-page file) + + ;; ==== Action + changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) + (into #{} (map thi/id) child-labels) + (fn [shape] + (assoc shape :fills (ths/sample-fills-color :fill-color "#fabada"))) + (:objects page) + {}) + updated-file (thf/apply-changes file changes1) + changes2 (sync-file-changes updated-file) + file' (thf/apply-changes updated-file changes2) + file'' (thf/apply-undo-changes file' changes2) + + ;; ==== Get + copy-fill (fn [file] + (let [copy-root (ths/get-shape file :copy-root)] + (into #{} + (map #(-> (ths/get-shape-by-id file %) :fills first :fill-color)) + (:shapes copy-root))))] + + ;; ==== Check + (t/is (= #{"#fabada"} (copy-fill file'))) + (t/is (= #{"#abcdef"} (copy-fill file''))))) + +(t/deftest test-update-attr-changes-many-calls + ;; Regression: each call nested the undo list in one more lazy concat, so + ;; walking it after many calls overflowed the stack (#12576). + (let [;; ==== Setup + file (-> (thf/sample-file :file1) + (tho/add-rect :rect1)) + page (thf/current-page file) + rect (ths/get-shape file :rect1) + roperations [{:type :set :attr :opacity :val 0.5 :ignore-touched true}] + uoperations (list {:type :set :attr :opacity :val 1 :ignore-touched true}) + + ;; ==== Action + changes (reduce (fn [changes _] + (#'cll/add-update-attr-changes changes rect page roperations uoperations)) + (pcb/empty-changes) + (range 50000)) + undo (:undo-changes changes)] + + ;; ==== Check + (t/is (= 100000 (count undo))) + ;; Undo restores the shape first and then recalculates its parents. + (t/is (= [:mod-obj :reg-objects] (map :type (take 2 undo)))))) + +(defn- geometry + [shape] + ;; Compare as doubles: a recalculated selrect has doubles where the + ;; original one may have longs. + {:selrect (update-vals (select-keys (:selrect shape) [:x :y :width :height]) double) + :points (mapv #(update-vals (select-keys % [:x :y]) double) (:points shape))}) + +(defn- add-group-component-with-copy + [file] + ;; Generated shape tree: + ;; :main-root [:name Frame1] # [Component :component1] + ;; :main-group [:name Group1] + ;; :main-child1 [:name Rect1] + ;; :main-child2 [:name Rect2] + ;; + ;; :copy-root [:name Frame1] #--> [Component :component1] :main-root + ;; :copy-group [:name Group1] ---> :main-group + ;; :copy-child1 [:name Rect1] ---> :main-child1 + ;; :copy-child2 [:name Rect2] ---> :main-child2 + (-> file + (tho/add-frame :main-root :x 0 :y 0 :width 100 :height 100) + (tho/add-group :main-group :parent-label :main-root :x 10 :y 10 :width 30 :height 10) + (ths/add-sample-shape :main-child1 :parent-label :main-group :name "Rect1" + :x 10 :y 10 :width 10 :height 10 + :fills (ths/sample-fills-color :fill-color "#abcdef")) + (ths/add-sample-shape :main-child2 :parent-label :main-group :name "Rect2" + :x 30 :y 10 :width 10 :height 10 + :fills (ths/sample-fills-color :fill-color "#abcdef")) + (thc/make-component :component1 :main-root) + (thc/instantiate-component :component1 :copy-root + :children-labels [:copy-group :copy-child1 :copy-child2]))) + +(defn- touch-copy-group-geometry + ;; With its geometry touched, the sync does not copy the group's own + ;; geometry from the main, so only the parents recalculation decides it. + [file] + (reduce #(ths/update-shape %1 %2 :touched #{:geometry-group}) + file + [:copy-group :copy-child1 :copy-child2])) + +(defn- update-fill + [file label color] + (let [page (thf/current-page file) + changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) + #{(thi/id label)} + #(assoc % :fills (ths/sample-fills-color :fill-color color)) + (:objects page) + {})] + (thf/apply-changes file changes))) + +(t/deftest test-sync-undo-restores-group-geometry + (let [;; ==== Setup + file (add-group-component-with-copy (thf/sample-file :file1)) + page (thf/current-page file) + copy-group (ths/get-shape file :copy-group) + + ;; ==== Action + changes1 (-> (pcb/empty-changes nil (:id page)) + (pcb/with-objects (:objects page)) + (cls/generate-update-shapes #{(thi/id :main-child2)} + #(gsh/absolute-move % (gpt/point 60 60)) + (:objects page) + {}) + (pcb/resize-parents [(thi/id :main-child2)])) + updated-file (thf/apply-changes file changes1) + changes2 (sync-file-changes updated-file) + file' (thf/apply-changes updated-file changes2) + file'' (thf/apply-undo-changes file' changes2) + + ;; ==== Get + copy-group' (ths/get-shape file' :copy-group) + copy-group'' (ths/get-shape file'' :copy-group)] + + ;; ==== Check + (t/is (not= (geometry copy-group) (geometry copy-group'))) + (t/is (= (geometry copy-group) (geometry copy-group''))))) + +(t/deftest test-sync-undo-group-after-add-and-update + (let [;; ==== Setup + file (-> (thf/sample-file :file1) + (add-group-component-with-copy) + (touch-copy-group-geometry) + (ths/add-sample-shape :free-shape :x 60 :y 60 :width 10 :height 10)) + page (thf/current-page file) + copy-group (ths/get-shape file :copy-group) + + ;; ==== Action + ;; The new shape goes first in the group, so the sync handles it before + ;; the updated sibling. + changes1 (cls/generate-relocate (-> (pcb/empty-changes nil) + (pcb/with-page-id (:id page)) + (pcb/with-objects (:objects page))) + (thi/id :main-group) + 0 + #{(thi/id :free-shape)}) + updated-file (-> (thf/apply-changes file changes1) + (update-fill :main-child1 "#fabada")) + changes2 (sync-file-changes updated-file) + file' (thf/apply-changes updated-file changes2) + file'' (thf/apply-undo-changes file' changes2) + + ;; ==== Get + copy-group' (ths/get-shape file' :copy-group) + copy-group'' (ths/get-shape file'' :copy-group)] + + ;; ==== Check + (t/is (= 3 (count (:shapes copy-group')))) + (t/is (not= (geometry copy-group) (geometry copy-group'))) + (t/is (= (:shapes copy-group) (:shapes copy-group''))) + (t/is (= (geometry copy-group) (geometry copy-group''))))) + +(t/deftest test-sync-undo-group-after-remove-and-update + (let [;; ==== Setup + file (-> (thf/sample-file :file1) + (add-group-component-with-copy) + (touch-copy-group-geometry)) + page (thf/current-page file) + copy-group (ths/get-shape file :copy-group) + + ;; ==== Action + ;; The removed shape is the first one in the group, so the sync + ;; handles it before the updated sibling. Remove it only from the + ;; main, as a library update does, so that the sync removes it from + ;; the copy. + changes1 (-> (pcb/empty-changes nil (:id page)) + (pcb/with-objects (:objects page)) + (pcb/remove-objects #{(thi/id :main-child1)})) + updated-file (-> (thf/apply-changes file changes1) + (update-fill :main-child2 "#fabada")) + changes2 (sync-file-changes updated-file) + file' (thf/apply-changes updated-file changes2) + file'' (thf/apply-undo-changes file' changes2) + + ;; ==== Get + copy-group' (ths/get-shape file' :copy-group) + copy-group'' (ths/get-shape file'' :copy-group)] + + ;; ==== Check + (t/is (= 1 (count (:shapes copy-group')))) + (t/is (not= (geometry copy-group) (geometry copy-group'))) + (t/is (= (:shapes copy-group) (:shapes copy-group''))) + (t/is (= (geometry copy-group) (geometry copy-group''))))) + +(t/deftest test-sync-inverse-undo-group-after-add-and-update + (let [;; ==== Setup + ;; Penpot does not let users move shapes into a copy, but files can + ;; still hold copies with shapes the main lacks, so build the data + ;; directly. The new shape goes first in the copy group, so the + ;; inverse sync handles it before the updated sibling. + file (-> (thf/sample-file :file1) + (add-group-component-with-copy) + (ths/add-sample-shape :free-shape :parent-label :copy-group + :x 160 :y 160 :width 10 :height 10) + (ths/update-shape :copy-group :shapes [(thi/id :free-shape) + (thi/id :copy-child1) + (thi/id :copy-child2)])) + main-group (ths/get-shape file :main-group) + + ;; ==== Action + updated-file (update-fill file :copy-child1 "#fabada") + page (thf/current-page updated-file) + container (ctn/make-container page :page) + changes (-> (pcb/empty-changes) + (pcb/with-container container) + (cll/generate-sync-shape-inverse (:data updated-file) + {(:id updated-file) updated-file} + container + (thi/id :copy-root))) + file' (thf/apply-changes updated-file changes) + file'' (thf/apply-undo-changes file' changes) + + ;; ==== Get + main-group' (ths/get-shape file' :main-group) + main-group'' (ths/get-shape file'' :main-group)] + + ;; ==== Check + (t/is (= 3 (count (:shapes main-group')))) + (t/is (not= (geometry main-group) (geometry main-group'))) + (t/is (= (:shapes main-group) (:shapes main-group''))) + (t/is (= (geometry main-group) (geometry main-group''))))) diff --git a/common/test/common_tests/logic/variants_switch_test.cljc b/common/test/common_tests/logic/variants_switch_test.cljc index acaa4faf06..46effffac4 100644 --- a/common/test/common_tests/logic/variants_switch_test.cljc +++ b/common/test/common_tests/logic/variants_switch_test.cljc @@ -21,6 +21,8 @@ [app.common.test-helpers.variants :as thv] [app.common.types.component :as ctk] [app.common.types.modifiers :as ctm] + [clojure.set :as set] + [clojure.string :as string] [clojure.test :as t])) (t/use-fixtures :each thi/test-fixture) @@ -1818,6 +1820,202 @@ ;; Verify layout-item-h-sizing is still nil after switch (t/is (nil? (:layout-item-h-sizing rect02'))))) +;; ============================================================ +;; LAYOUT ITEM SIZING - TOUCHED GROUPS ON THE COPY ROOT +;; ============================================================ + +(def ^:private layout-item-attrs + ;; Every layout-child attribute that is kept when swapping a component. + (into #{} (filter #(string/starts-with? (name %) "layout-item-")) ctk/swap-keep-attrs)) + +(def ^:private layout-item-main-values + ;; Values on the component masters; the copy overrides every one of them. + {:layout-item-margin {:m1 1 :m2 1 :m3 1 :m4 1} + :layout-item-margin-type :simple + :layout-item-h-sizing :fix + :layout-item-v-sizing :fix + :layout-item-max-h 100 + :layout-item-min-h 10 + :layout-item-max-w 100 + :layout-item-min-w 10 + :layout-item-absolute false + :layout-item-z-index 0 + :layout-item-align-self :center}) + +(def ^:private layout-item-overrides + ;; User overrides on the copy root, all different from the masters. + {:layout-item-margin {:m1 10 :m2 10 :m3 10 :m4 10} + :layout-item-margin-type :multiple + :layout-item-h-sizing :fill + :layout-item-v-sizing :fill + :layout-item-max-h 500 + :layout-item-min-h 20 + :layout-item-max-w 500 + :layout-item-min-w 20 + :layout-item-absolute true + :layout-item-z-index 5 + :layout-item-align-self :start}) + +(def ^:private layout-item-override-groups + ;; The touched groups of all the overrides attributes. + [:layout-item-margin + :layout-item-h-sizing + :layout-item-v-sizing + :layout-item-max-h + :layout-item-min-h + :layout-item-max-w + :layout-item-min-w + :layout-item-absolute + :layout-item-z-index + :layout-item-align-self]) + +(defn- check-layout-item-attrs-kept + [shape label] + (doseq [[attr value] layout-item-overrides] + (t/is (= value (get shape attr)) + (str label ": value of " attr))) + (doseq [group layout-item-override-groups] + (t/is (contains? (:touched shape) group) + (str label ": touched group " group))) + (t/is (= (count layout-item-override-groups) (count (:touched shape))) + (str label ": number of touched groups"))) + +(def ^:private layout-item-attrs-partial + (set/difference layout-item-attrs #{:layout-item-margin-type + :layout-item-v-sizing + :layout-item-min-h + :layout-item-min-w + :layout-item-z-index})) + +(def ^:private layout-item-partial-overrides + ;; User overrides on the copy root, not all of them. + {:layout-item-margin {:m1 10 :m2 10 :m3 10 :m4 10} + :layout-item-h-sizing :fill + :layout-item-max-h 500 + :layout-item-max-w 500 + :layout-item-absolute true + :layout-item-align-self :start}) + +(def ^:private layout-item-partial-override-groups + ;; The touched groups of all the overrides attributes. + [:layout-item-margin + :layout-item-h-sizing + :layout-item-max-h + :layout-item-max-w + :layout-item-absolute + :layout-item-align-self]) + +(defn- check-layout-item-attrs-kept-partial + [shape label] + (doseq [[attr value] layout-item-partial-overrides] + (t/is (= value (get shape attr)) + (str label ": value of " attr))) + (doseq [group layout-item-partial-override-groups] + (t/is (contains? (:touched shape) group) + (str label ": touched group " group))) + (t/is (= (count layout-item-partial-override-groups) (count (:touched shape))) + (str label ": number of touched groups"))) + +(t/deftest test-switch-preserves-touched-layout-item-attrs-on-root + ;; Every layout-item override on the copy ROOT must survive a variant switch: + ;; `generate-new-shape-for-swap` restores the value from `keep-props-values`, + ;; so it must restore the touched group the value belongs to as well. Before + ;; the fix the groups were dropped, leaving the overrides unprotected. + (let [;; ==== Setup + file (-> (thf/sample-file :file1) + (thv/add-variant :v01 :c01 :m01 :c02 :m02 + {:variant1-params layout-item-main-values + :variant2-params layout-item-main-values}) + (thc/instantiate-component :c01 :copy01)) + file (tho/change-shape file :copy01 #(merge % layout-item-overrides)) + copy01 (ths/get-shape file :copy01) + + ;; ==== Action + file' (tho/swap-component-in-shape file :copy01 :c02 {:keep-touched? true}) + copy01' (ths/get-shape file' :copy01)] + + (t/is (= layout-item-attrs (set (keys layout-item-overrides)))) + (check-layout-item-attrs-kept copy01 "before switch") + ;; The values and their touched groups must both survive the switch + (check-layout-item-attrs-kept copy01' "after switch"))) + +(t/deftest test-switch-preserves-touched-layout-item-attrs-on-root-partial + ;; Same as above but with only some of the layout-item attributes overridden. The rest are left untouched. + (let [;; ==== Setup + file (-> (thf/sample-file :file1) + (thv/add-variant :v01 :c01 :m01 :c02 :m02 + {:variant1-params layout-item-main-values + :variant2-params layout-item-main-values}) + (thc/instantiate-component :c01 :copy01)) + file (tho/change-shape file :copy01 #(merge % layout-item-partial-overrides)) + copy01 (ths/get-shape file :copy01) + + ;; ==== Action + file' (tho/swap-component-in-shape file :copy01 :c02 {:keep-touched? true}) + copy01' (ths/get-shape file' :copy01)] + + (t/is (= layout-item-attrs-partial (set (keys layout-item-partial-overrides)))) + (check-layout-item-attrs-kept-partial copy01 "before switch") + ;; The values and their touched groups must both survive the switch + (check-layout-item-attrs-kept-partial copy01' "after switch"))) + +(t/deftest test-switch-preserved-layout-item-attrs-survive-main-sync + ;; End-to-end consequence of the bug: after the switch, editing the new master + ;; must not reset the copy. `update-flex-child-copy-attrs` overwrites the + ;; layout-item sizing whose group is not touched, so the preserved groups are + ;; what keep the overrides alive across later syncs. + (let [;; ==== Setup + file (-> (thf/sample-file :file1) + (thv/add-variant :v01 :c01 :m01 :c02 :m02 + {:variant1-params (assoc layout-item-main-values + :layout :flex :layout-flex-dir :row) + :variant2-params (assoc layout-item-main-values + :layout :flex :layout-flex-dir :row)}) + (thc/instantiate-component :c01 :copy01)) + file (tho/change-shape file :copy01 #(merge % layout-item-overrides)) + copy01 (ths/get-shape file :copy01) + + ;; ==== Action + file' (tho/swap-component-in-shape file :copy01 :c02 {:keep-touched? true}) + copy01' (ths/get-shape file' :copy01) + file'' (-> file' + (tho/change-shape :m02 #(assoc % :r1 10)) + (tho/propagate-component-changes :c02)) + copy01'' (ths/get-shape file'' :copy01)] + + (t/is (= layout-item-attrs (set (keys layout-item-overrides)))) + (check-layout-item-attrs-kept copy01 "before switch") + (check-layout-item-attrs-kept copy01' "after switch") + ;; The later sync must not overwrite the protected overrides + (check-layout-item-attrs-kept copy01'' "after main sync"))) + +(t/deftest test-switch-preserved-layout-item-attrs-survive-main-sync-partial + ;; Same as above but with only some of the layout-item attributes overridden. The rest are left untouched. + (let [;; ==== Setup + file (-> (thf/sample-file :file1) + (thv/add-variant :v01 :c01 :m01 :c02 :m02 + {:variant1-params (assoc layout-item-main-values + :layout :flex :layout-flex-dir :row) + :variant2-params (assoc layout-item-main-values + :layout :flex :layout-flex-dir :row)}) + (thc/instantiate-component :c01 :copy01)) + file (tho/change-shape file :copy01 #(merge % layout-item-partial-overrides)) + copy01 (ths/get-shape file :copy01) + + ;; ==== Action + file' (tho/swap-component-in-shape file :copy01 :c02 {:keep-touched? true}) + copy01' (ths/get-shape file' :copy01) + file'' (-> file' + (tho/change-shape :m02 #(assoc % :r1 10)) + (tho/propagate-component-changes :c02)) + copy01'' (ths/get-shape file'' :copy01)] + + (t/is (= layout-item-attrs-partial (set (keys layout-item-partial-overrides)))) + (check-layout-item-attrs-kept-partial copy01 "before switch") + (check-layout-item-attrs-kept-partial copy01' "after switch") + ;; The later sync must not overwrite the protected overrides + (check-layout-item-attrs-kept-partial copy01'' "after main sync"))) + ;; ============================================================ ;; LAYOUT ITEM SIZING - VERTICAL (fix, auto, fill, none) ;; ============================================================ diff --git a/common/test/common_tests/runner.cljc b/common/test/common_tests/runner.cljc index 9a774d271d..f650081321 100644 --- a/common/test/common_tests/runner.cljc +++ b/common/test/common_tests/runner.cljc @@ -23,9 +23,11 @@ [common-tests.files-migrations-0025-test] [common-tests.files-migrations-0026-test] [common-tests.files-migrations-0034-test] + [common-tests.files-migrations-0035-test] [common-tests.files-migrations-test] [common-tests.files.comp-processors-test] [common-tests.files.helpers-test] + [common-tests.files.page-diff-test] [common-tests.files.repair-test] [common-tests.files.shapes-builder-test] [common-tests.files.tokens-test] @@ -64,6 +66,7 @@ [common-tests.logic.comp-main-edit-breaks-copy-slots-test] [common-tests.logic.comp-remove-swap-slots-test] [common-tests.logic.comp-reset-test] + [common-tests.logic.comp-sync-layout-items-test] [common-tests.logic.comp-sync-test] [common-tests.logic.comp-touched-test] [common-tests.logic.copying-and-duplicating-test] @@ -108,6 +111,7 @@ [common-tests.types.shape-decode-encode-test] [common-tests.types.shape-interactions-test] [common-tests.types.shape-layout-test] + [common-tests.types.shape-test] [common-tests.types.stroke-test] [common-tests.types.text-test] [common-tests.types.token-test] @@ -132,9 +136,11 @@ 'common-tests.files-migrations-0025-test 'common-tests.files-migrations-0026-test 'common-tests.files-migrations-0034-test + 'common-tests.files-migrations-0035-test 'common-tests.files-migrations-test 'common-tests.files.comp-processors-test 'common-tests.files.helpers-test + 'common-tests.files.page-diff-test 'common-tests.files.repair-test 'common-tests.files.shapes-builder-test 'common-tests.files.tokens-test @@ -173,6 +179,7 @@ 'common-tests.logic.comp-main-edit-breaks-copy-slots-test 'common-tests.logic.comp-remove-swap-slots-test 'common-tests.logic.comp-reset-test + 'common-tests.logic.comp-sync-layout-items-test 'common-tests.logic.comp-sync-test 'common-tests.logic.comp-touched-test 'common-tests.logic.copying-and-duplicating-test @@ -217,6 +224,7 @@ 'common-tests.types.shape-decode-encode-test 'common-tests.types.shape-interactions-test 'common-tests.types.shape-layout-test + 'common-tests.types.shape-test 'common-tests.types.stroke-test 'common-tests.types.text-test 'common-tests.types.token-test diff --git a/common/test/common_tests/svg_test.cljc b/common/test/common_tests/svg_test.cljc index a8c8aa17e2..c528e37f98 100644 --- a/common/test/common_tests/svg_test.cljc +++ b/common/test/common_tests/svg_test.cljc @@ -8,7 +8,8 @@ (:require [app.common.data :as d] [app.common.svg :as svg] - [clojure.test :as t])) + [clojure.test :as t] + [cuerdas.core :as str])) (t/deftest clean-attrs-1 (let [attrs {:class "foobar"} @@ -41,3 +42,58 @@ :attrs {:id "shadow"} :content [{:tag :feDropShadow}]})] (t/is (= [:feDropShadow] (mapv :tag (get-in defs ["shadow" :content])))))) + +(t/deftest stored-attr-display-name-uses-spec-spelling + ;; The svg-attrs menu shows these labels: kebab storage matches the + ;; spec for most keys, but spec-camel keys show their source spelling. + (t/is (= "class" (svg/stored-attr-display-name :class-name))) + (t/is (= "viewBox" (svg/stored-attr-display-name :view-box))) + (t/is (= "stroke-width" (svg/stored-attr-display-name :stroke-width))) + (t/is (= "stdDeviation" (svg/stored-attr-display-name :std-deviation))) + (t/is (= "fill-rule" (svg/stored-attr-display-name :fill-rule))) + (t/is (= "data-foo" (svg/stored-attr-display-name :data-foo)) + "unknown keys fall back to their name")) + +(t/deftest stored-attr-display-names-round-trip + ;; Every whitelist source key maps back to its own spelling: guards + ;; against two source keys sharing one stored key (last-wins would + ;; silently mislabel one of them). + (doseq [k (concat svg/svg-attrs + svg/svg-presentation-attrs + svg/penpot-extra-attrs)] + (let [stored ((comp keyword str/kebab name) (svg/prop-key k))] + (t/is (= (name k) (svg/stored-attr-display-name stored)) + (str "display round-trip for " k))))) + +(t/deftest normalize-def-node-keywordizes-string-tags + ;; Stored files can hold def node tags as strings; nested nodes too. + (let [node (svg/normalize-def-node + {:tag "mask" + :attrs {:id "m1" :maskUnits "userSpaceOnUse"} + :content [{:tag "clipPath" + :attrs {} + :content [{:tag "rect" :attrs {:width "10"} :content []}]}]})] + (t/is (= :mask (:tag node))) + (t/is (= :clipPath (get-in node [:content 0 :tag])) "source spelling is kept") + (t/is (= :rect (get-in node [:content 0 :content 0 :tag]))) + (t/is (= "userSpaceOnUse" (get-in node [:attrs :mask-units]))))) + +(t/deftest kebabize-content-node-keywordizes-string-tags + (let [node (svg/kebabize-content-node + {:tag "text" + :attrs {:fontSize "12"} + :content ["Hi" {:tag "tspan" :attrs {} :content ["there"]}]})] + (t/is (= :text (:tag node))) + (t/is (= "12" (get-in node [:attrs :font-size]))) + (t/is (= "Hi" (get-in node [:content 0])) "text children are left as they are") + (t/is (= :tspan (get-in node [:content 1 :tag]))))) + +(t/deftest normalize-def-node-keeps-keyword-tags + (let [node {:tag :linearGradient :attrs {:id "g"} :content []}] + (t/is (= node (svg/normalize-def-node node))))) + +(t/deftest normalize-nodes-turn-nil-attrs-into-empty-map + (t/is (= {} (:attrs (svg/kebabize-content-node {:tag :style :attrs nil :content [".a{}"]})))) + (t/is (= {} (:attrs (svg/normalize-def-node {:tag :g :attrs nil :content []})))) + (t/is (not (contains? (svg/kebabize-content-node {:tag :g :content []}) :attrs)) + "absent attrs stay absent")) diff --git a/common/test/common_tests/types/organization_test.cljc b/common/test/common_tests/types/organization_test.cljc index 1d2c48be01..1555e23207 100644 --- a/common/test/common_tests/types/organization_test.cljc +++ b/common/test/common_tests/types/organization_test.cljc @@ -6,6 +6,7 @@ (ns common-tests.types.organization-test (:require + [app.common.flags :as flags] [app.common.types.organization :as cto] [clojure.test :as t])) @@ -73,12 +74,12 @@ (let [always-organization (assoc organization-perms :permissions {:create-teams "any" :delete-teams "onlyOwners" :move-teams "always"})] - ;; Organization owner should always be allowed + ;; Team owners can move the team anywhere, or take it out (t/is (true? (cto/allowed? :move-team {:organization-perms always-organization :profile-id :owner :team-perms {}}))) - ;; Regular member should be allowed when move-teams is "always" + ;; Being the organization owner makes no difference (t/is (true? (cto/allowed? :move-team {:organization-perms always-organization :profile-id :member @@ -88,7 +89,8 @@ (let [my-organizations (assoc organization-perms :permissions {:create-teams "any" :delete-teams "onlyOwners" :move-teams "myOrganizations"})] - ;; Organization owner must also stay within same-owner organizations + ;; Same owner means same subscription: the organization owner is + ;; bound by it too (t/is (false? (cto/allowed? :move-team {:organization-perms my-organizations :profile-id :owner @@ -99,7 +101,7 @@ :profile-id :owner :team-perms {} :target-organization-same-owner? true}))) - ;; Regular member should be allowed only if target has same owner + ;; A team owner who is not the organization owner, same rule (t/is (true? (cto/allowed? :move-team {:organization-perms my-organizations :profile-id :member @@ -111,11 +113,27 @@ :team-perms {} :target-organization-same-owner? false}))))) +(t/deftest move-team-myorganizations-denies-taking-the-team-out + (let [my-organizations (assoc organization-perms :permissions {:create-teams "any" + :delete-teams "onlyOwners" + :move-teams "myOrganizations"})] + ;; Taking a team out has no target organization, so no same owner + (t/is (false? (cto/allowed? :move-team + {:organization-perms my-organizations + :profile-id :owner + :team-perms {}}))) + (t/is (false? (cto/allowed? :move-team + {:organization-perms my-organizations + :profile-id :member + :team-perms {} + :target-organization-same-owner? nil}))))) + (t/deftest move-team-never-denies-all (let [never-organization (assoc organization-perms :permissions {:create-teams "any" :delete-teams "onlyOwners" :move-teams "never"})] - ;; Even organization owner should be denied + ;; Only the Admin Console moves teams: the organization owner is + ;; denied in Penpot too (t/is (false? (cto/allowed? :move-team {:organization-perms never-organization :profile-id :owner @@ -176,3 +194,160 @@ {:organization-perms invalid-organization :profile-id :member :team-perms {:is-owner true :is-admin true}}))))) + +;; --- Attribute restriction gate (:restrictions-met) --- + +(t/deftest create-team-owner-bypasses-restriction + ;; The organization owner keeps the create-teams bypass even when the + ;; attribute restriction is not met. + (let [restricted (assoc organization-perms :restrictions-met {:create-teams false})] + (t/is (true? (cto/allowed? :create-team + {:organization-perms restricted + :profile-id :owner + :team-perms {}}))))) + +(t/deftest create-team-restriction-narrows-any + (let [denied (assoc organization-perms :restrictions-met {:create-teams false}) + met (assoc organization-perms :restrictions-met {:create-teams true})] + ;; Non-owner with "any" but the restriction not met -> denied + (t/is (false? (cto/allowed? :create-team + {:organization-perms denied + :profile-id :member + :team-perms {}}))) + ;; Restriction met -> allowed + (t/is (true? (cto/allowed? :create-team + {:organization-perms met + :profile-id :member + :team-perms {}}))) + ;; Missing :restrictions-met key -> allowed (old Nitrate keeps behaviour) + (t/is (true? (cto/allowed? :create-team + {:organization-perms organization-perms + :profile-id :member + :team-perms {}}))))) + +(t/deftest create-team-onlyme-ignores-restriction + ;; "onlyMe" is not restrictable: the option itself denies a member even when + ;; a (bogus) restriction says met, and still allows the owner. + (let [only-me (assoc organization-perms + :permissions {:create-teams "onlyMe" :delete-teams "onlyOwners"} + :restrictions-met {:create-teams true})] + (t/is (false? (cto/allowed? :create-team + {:organization-perms only-me + :profile-id :member + :team-perms {}}))) + (t/is (true? (cto/allowed? :create-team + {:organization-perms only-me + :profile-id :owner + :team-perms {}}))))) + +(t/deftest move-team-restriction-narrows-always + (let [denied (assoc organization-perms + :permissions {:create-teams "any" :delete-teams "onlyOwners" :move-teams "always"} + :restrictions-met {:move-teams false}) + met (assoc organization-perms + :permissions {:create-teams "any" :delete-teams "onlyOwners" :move-teams "always"} + :restrictions-met {:move-teams true})] + (t/is (false? (cto/allowed? :move-team + {:organization-perms denied + :profile-id :member + :team-perms {}}))) + (t/is (true? (cto/allowed? :move-team + {:organization-perms met + :profile-id :member + :team-perms {}}))))) + +(t/deftest move-team-restriction-narrows-myorganizations + (let [denied (assoc organization-perms + :permissions {:create-teams "any" :delete-teams "onlyOwners" :move-teams "myOrganizations"} + :restrictions-met {:move-teams false}) + met (assoc organization-perms + :permissions {:create-teams "any" :delete-teams "onlyOwners" :move-teams "myOrganizations"} + :restrictions-met {:move-teams true})] + ;; Same owner but the restriction not met -> denied + (t/is (false? (cto/allowed? :move-team + {:organization-perms denied + :profile-id :member + :team-perms {} + :target-organization-same-owner? true}))) + ;; Same owner and restriction met -> allowed + (t/is (true? (cto/allowed? :move-team + {:organization-perms met + :profile-id :member + :team-perms {} + :target-organization-same-owner? true}))))) + +(t/deftest move-team-never-ignores-restriction + (let [never (assoc organization-perms + :permissions {:create-teams "any" :delete-teams "onlyOwners" :move-teams "never"} + :restrictions-met {:move-teams true})] + (t/is (false? (cto/allowed? :move-team + {:organization-perms never + :profile-id :member + :team-perms {}}))))) + +(t/deftest send-invitations-restriction-narrows + (let [denied (assoc organization-perms :restrictions-met {:send-invitations false}) + met (assoc organization-perms :restrictions-met {:send-invitations true})] + ;; Team admin, ownersAndAdmins, restriction not met -> denied + (t/is (false? (cto/allowed? :send-invitations + {:organization-perms denied + :profile-id :member + :team-perms {:is-owner false :is-admin true}}))) + ;; Restriction met -> allowed + (t/is (true? (cto/allowed? :send-invitations + {:organization-perms met + :profile-id :member + :team-perms {:is-owner false :is-admin true}}))))) + +(t/deftest send-invitations-owners-restriction-narrows + (let [denied (assoc organization-perms + :permissions {:create-teams "any" :delete-teams "onlyOwners" :send-invitations "owners"} + :restrictions-met {:send-invitations false})] + ;; Even a team owner is denied when the restriction is not met + (t/is (false? (cto/allowed? :send-invitations + {:organization-perms denied + :profile-id :member + :team-perms {:is-owner true :is-admin true}}))))) + +(t/deftest delete-team-unaffected-by-restriction + ;; deleteTeams is not restrictable: a stored restriction must not change it. + (let [restricted (assoc organization-perms :restrictions-met {:delete-teams false})] + (t/is (true? (cto/allowed? :delete-team + {:organization-perms restricted + :profile-id :member + :team-perms {:is-owner true :is-admin true}}))))) + +(t/deftest add-anybody-unaffected-by-restriction + (let [restricted (assoc organization-perms + :permissions {:create-teams "any" :delete-teams "onlyOwners" :new-team-members "anyone"} + :restrictions-met {:new-team-members false})] + (t/is (true? (cto/allowed? :add-anybody-to-team + {:organization-perms restricted + :profile-id :member + :team-perms {}}))))) + +(t/deftest can-send-invitations-respects-restriction + (let [denied {:owner-id :owner + :permissions {:send-invitations "ownersAndAdmins"} + :restrictions-met {:send-invitations false}} + met {:owner-id :owner + :permissions {:send-invitations "ownersAndAdmins"} + :restrictions-met {:send-invitations true}}] + (binding [flags/*current* #{:admin-console}] + (t/is (false? (cto/can-send-invitations? {:organization denied + :profile-id :member + :team-permissions {:is-owner false :is-admin true}}))) + (t/is (true? (cto/can-send-invitations? {:organization met + :profile-id :member + :team-permissions {:is-owner false :is-admin true}})))))) + +(t/deftest can-send-invitations-falls-back-without-flag + ;; Without the admin-console flag, team-level permissions decide and the + ;; restriction is never consulted. + (let [organization {:owner-id :owner + :permissions {:send-invitations "ownersAndAdmins"} + :restrictions-met {:send-invitations false}}] + (binding [flags/*current* #{}] + (t/is (true? (cto/can-send-invitations? {:organization organization + :profile-id :member + :team-permissions {:is-owner false :is-admin true}})))))) diff --git a/common/test/common_tests/types/shape_test.cljc b/common/test/common_tests/types/shape_test.cljc new file mode 100644 index 0000000000..2d98a7d405 --- /dev/null +++ b/common/test/common_tests/types/shape_test.cljc @@ -0,0 +1,109 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns common-tests.types.shape-test + (:require + [app.common.json :as json] + [app.common.schema :as sm] + [app.common.schema.generators :as sg] + [app.common.types.shape :as tsh :refer [schema:shape]] + [clojure.test :as t])) + +;; Shape schema hardening for https://github.com/penpot/penpot/issues/11947. +;; Stored svg attribute keys are kebab-case: `:svg-attrs` is closed over +;; the whitelist in stored spelling (derived from the same set birth +;; uses, so the two cannot drift apart), and `:svg-defs` nodes share one +;; node schema with svg-raw `:content`. + +(defn- sample-shape + "A generated shape: valid by construction, a base to assoc svg keys onto." + [] + (first (sg/sample (sg/generator schema:shape) {:size 20}))) + +(t/deftest svg-attrs-accepts-kebab-maps + (let [shape (assoc (sample-shape) + :svg-attrs {:fill-rule "evenodd" + :stroke-width "2" + :stroke-style "dotted" + :style {:fill-opacity "0.5"}})] + (t/is (tsh/check-shape-attrs shape) "kebab svg-attrs validate"))) + +(t/deftest svg-attrs-rejects-non-maps-and-unknown-keys + (let [shape (sample-shape) + throws? (fn [s] + (try (tsh/check-shape-attrs s) false + (catch #?(:clj Exception :cljs js/Error) _ true)))] + (t/is (throws? (assoc shape :svg-attrs "junk")) + "non-map svg-attrs fail validation") + (t/is (throws? (assoc shape :svg-attrs {:fillRule "evenodd"})) + "camel keys fail validation: storage is kebab-only") + (t/is (throws? (assoc shape :svg-attrs {:data-foo "x"})) + "unknown keys fail validation"))) + +(t/deftest svg-def-node-validates-tag-and-attrs + (let [shape (sample-shape) + node {:tag :linearGradient + :attrs {:id "g1" + :gradient-units "userSpaceOnUse"} + :content [{:tag :stop + :attrs {:offset "0" + :stop-color "#ffffff"} + :content []}]} + with-defs (fn [defs] (assoc shape :svg-defs defs)) + throws? (fn [s] + (try (tsh/check-shape-attrs s) false + (catch #?(:clj Exception :cljs js/Error) _ true)))] + (t/is (tsh/check-shape-attrs (with-defs {"g1" node})) + "string outer keys validate (birth spelling)") + (t/is (tsh/check-shape-attrs (with-defs {:g1 node})) + "keyword outer keys validate (post-decode spelling)") + (t/is (throws? (with-defs {"g1" (assoc node :tag "linearGradient")})) + "non-keyword tags fail validation") + (t/is (throws? (with-defs {"g1" (assoc node :attrs {:data-x "1"})})) + "unknown def attrs fail validation") + (t/is (throws? (with-defs {"g1" (assoc node :attrs "junk")})) + "non-map def attrs fail validation"))) + +(t/deftest svg-content-keeps-unknown-keys + ;; `:content` attrs are spelling-only: the sidebar reads keys outside + ;; the whitelist (e.g. `:stroke-style`), so the schema stays open. + ;; NOTE: the sample seed is pinned. Unpinned sampling flaked (~1% + ;; of runs): some seeds deal zero `:svg-raw` shapes in 50 samples, + ;; `first` yields nil, and the `assoc` below builds a typeless + ;; `{:content …}` map that fails the `:multi` dispatch on `:type`. + (let [shape (->> (sg/sample (sg/generator schema:shape) {:size 50 :seed 42}) + (filter #(= :svg-raw (:type %))) + (first) + (#(assoc % :content {:tag :g + :attrs {:stroke-style "dotted" + :stroke-width "2" + :data-foo "keep-me"} + :content []})))] + (t/is (some? shape) "an svg-raw shape was sampled") + (t/is (tsh/check-shape-attrs shape) + ":stroke-style in content attrs validates"))) + +(t/deftest generated-shapes-carry-no-svg-provenance + ;; The `:gen/fmap` mitigation: svg provenance is import-only data, + ;; random attrs would pollute every generative test. + (let [shapes (sg/sample (sg/generator schema:shape) {:size 20})] + (t/is (= 20 (count shapes))) + (doseq [shape shapes] + (t/is (not (contains? shape :svg-attrs)) "no generated svg-attrs") + (t/is (not (contains? shape :svg-defs)) "no generated svg-defs")))) + +(t/deftest svg-attrs-survive-json-encoding-untouched + ;; What binfile export relies on: kebab keys (and nested `:style`) + ;; round-trip byte-identical through the JSON layer. + (let [attrs {:fill-rule "evenodd" + :stroke-style "dotted" + :style {:fill-opacity "0.5"}} + shape (assoc (sample-shape) :svg-attrs attrs) + encode (sm/encoder schema:shape (sm/json-transformer)) + data (-> (encode shape) + (json/encode :key-fn json/write-camel-key) + (json/decode :key-fn json/read-kebab-key))] + (t/is (= attrs (:svg-attrs data))))) diff --git a/docker/devenv/Dockerfile b/docker/devenv/Dockerfile index 73294cb15d..2ad410058f 100644 --- a/docker/devenv/Dockerfile +++ b/docker/devenv/Dockerfile @@ -1,4 +1,4 @@ -FROM dhi.io/debian-base:trixie-debian13-dev@sha256:9b025768946856e44f9775005eb199f2e1820484ea27bb5c8bcbb187b80464dc AS base +FROM dhi.io/debian-base:trixie-debian13-dev@sha256:61025cea33d5910165ced8b33edd3f6b6bfe161e129174887936a1b9a1442b20 AS base ENV LANG='C.UTF-8' \ LC_ALL='C.UTF-8' \ @@ -67,7 +67,7 @@ RUN set -ex; \ FROM base AS setup-node ENV NODE_VERSION=v24.22.2 \ - PNPM_VERSION=12.16.1 \ + PNPM_VERSION=12.18.0 \ PATH=/opt/node/bin:$PATH RUN set -eux; \ @@ -78,14 +78,14 @@ RUN set -eux; \ BINARY_URL="https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-arm64.tar.gz"; \ NODE_SHA256='95c264150aceb7f3437a29fd6984924f5f8d1511ea3cbd6c508b83f6c555dae2'; \ PNPM_ARCH='arm64'; \ - PNPM_SHA256='f5847699e5d528f8f8e9bcfb385e58cb9fba7482af1b1aa7b70737be42277f81'; \ + PNPM_SHA256='700bd1aa4028465b80e62b7c5bec8455be60bbafaf53a3262a4ab8e2057712ac'; \ ;; \ amd64|x86_64) \ OPENSSL_ARCH='linux-x86_64'; \ BINARY_URL="https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.gz"; \ NODE_SHA256='361718c444e49af2ca5a110c83ba6a8901143f23ab285ebbef0341f391f482a9'; \ PNPM_ARCH='x64'; \ - PNPM_SHA256='565ff81174a14f6f7bd5d84c1b17e51031a80230d370a1fe24be5d16bd02a825'; \ + PNPM_SHA256='3726060e236c0db3e7124c0a5af123c840ba0cb76682149b63ea0baf290017c2'; \ ;; \ *) \ echo "Unsupported arch: ${ARCH}"; \ @@ -236,7 +236,9 @@ ENV CLJKONDO_VERSION=2026.10.20 \ UV_TOOL_DIR=/opt/uv/tools \ UV_TOOL_BIN_DIR=/opt/utils/bin \ UV_PYTHON_INSTALL_DIR=/opt/uv/python \ - SERENA_VERSION=1.7.0 + SERENA_VERSION=2.1.0 +# NOTE: When updating SERENA_VERSION, also update SERENA_UPDATE_VERSION in docker-compose.main.yml. +# The latter results in a dynamic update without rebuilding the image, so it must never be lower than SERENA_VERSION here. RUN set -ex; \ ARCH="$(dpkg --print-architecture)"; \ @@ -429,6 +431,8 @@ RUN set -ex; \ fontforge \ \ xvfb \ + xauth \ + libvulkan1 \ fonts-noto-color-emoji \ fonts-unifont \ libfontconfig1 \ diff --git a/docker/devenv/docker-compose.gpu.yml b/docker/devenv/docker-compose.gpu.yml new file mode 100644 index 0000000000..99c21037b0 --- /dev/null +++ b/docker/devenv/docker-compose.gpu.yml @@ -0,0 +1,14 @@ +services: + main: + environment: + - NVIDIA_DRIVER_CAPABILITIES=all + - PENPOT_GPU=1 + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] + volumes: + - /usr/share/vulkan/icd.d:/usr/share/vulkan/icd.d:ro diff --git a/docker/devenv/docker-compose.main.yml b/docker/devenv/docker-compose.main.yml index 219e348a56..c55330a80a 100644 --- a/docker/devenv/docker-compose.main.yml +++ b/docker/devenv/docker-compose.main.yml @@ -98,7 +98,7 @@ services: # Agentic devenv: set to a PyPI release version/tag to update Serena on startup, # leave empty to skip update and use the version baked into the image. - - SERENA_UPDATE_VERSION= + - SERENA_UPDATE_VERSION=2.1.0 - SHADOW_SERVER_URL=${SHADOW_SERVER_URL} networks: diff --git a/docker/devenv/files/nginx.conf b/docker/devenv/files/nginx.conf index 5a0a81f091..9a3cfd3222 100644 --- a/docker/devenv/files/nginx.conf +++ b/docker/devenv/files/nginx.conf @@ -141,6 +141,11 @@ http { proxy_pass http://127.0.0.1:6061; } + location = /.well-known/oauth-authorization-server/api/mcp/oauth { + proxy_pass http://127.0.0.1:6060; + proxy_http_version 1.1; + } + location /api { proxy_pass http://127.0.0.1:6060/api; proxy_http_version 1.1; diff --git a/docker/imagemagick/Dockerfile b/docker/imagemagick/Dockerfile index f875c09a95..28e1575a09 100644 --- a/docker/imagemagick/Dockerfile +++ b/docker/imagemagick/Dockerfile @@ -1,4 +1,4 @@ -FROM dhi.io/debian-base:trixie-debian13-dev@sha256:9b025768946856e44f9775005eb199f2e1820484ea27bb5c8bcbb187b80464dc AS build +FROM dhi.io/debian-base:trixie-debian13-dev@sha256:61025cea33d5910165ced8b33edd3f6b6bfe161e129174887936a1b9a1442b20 AS build LABEL maintainer="Penpot " ENV LANG='C.UTF-8' \ @@ -74,7 +74,7 @@ RUN set -ex; \ rm -rf /var/lib/apt/lists/* -FROM dhi.io/debian-base:trixie-debian13@sha256:de80b468d8c492508f24b30e529accfaa8c59fe4505ddd72e969b324d276ad21 AS image +FROM dhi.io/debian-base:trixie-debian13@sha256:834d20c209fe4aac50dc95877d68d421f97ea38a905586bfdaf938bebb3924c0 AS image LABEL maintainer="Penpot " ENV LANG='C.UTF-8' \ diff --git a/docker/images/Dockerfile.backend b/docker/images/Dockerfile.backend index 3edb969257..c9810802c3 100644 --- a/docker/images/Dockerfile.backend +++ b/docker/images/Dockerfile.backend @@ -1,4 +1,4 @@ -FROM dhi.io/debian-base:trixie-debian13-dev@sha256:af61a3296b0c92f16969f11ce8c2a93722e84c9e5c1bb42ed6afec796be71b04 AS build +FROM dhi.io/debian-base:trixie-debian13-dev@sha256:61025cea33d5910165ced8b33edd3f6b6bfe161e129174887936a1b9a1442b20 AS build LABEL maintainer="Penpot " ENV LANG='C.UTF-8' \ @@ -53,7 +53,7 @@ RUN set -eux; \ --output /opt/jre; -FROM dhi.io/debian-base:trixie-debian13-dev@sha256:af61a3296b0c92f16969f11ce8c2a93722e84c9e5c1bb42ed6afec796be71b04 AS image +FROM dhi.io/debian-base:trixie-debian13-dev@sha256:61025cea33d5910165ced8b33edd3f6b6bfe161e129174887936a1b9a1442b20 AS image LABEL maintainer="Penpot " ENV LANG='C.UTF-8' \ diff --git a/docker/images/Dockerfile.exporter b/docker/images/Dockerfile.exporter index bb6fa216da..8172f3516b 100644 --- a/docker/images/Dockerfile.exporter +++ b/docker/images/Dockerfile.exporter @@ -1,4 +1,4 @@ -FROM dhi.io/node:24.22.2-debian13-dev@sha256:df819cfcb0de8a9e461bb8437d96d6fcfac341e565129e50597338756a88db5b +FROM dhi.io/node:24.22.2-debian13-dev@sha256:24214ddb02c90cb64829112570250e1723148bdae9ba8b181f7c1cd675cc3da6 LABEL maintainer="Penpot " ENV LANG=en_US.UTF-8 \ @@ -95,17 +95,17 @@ WORKDIR /opt/penpot/exporter # pnpm ships as a system binary (same tarball + SHA pin as # docker/devenv/Dockerfile); the generated ./setup expects it on PATH. # Corepack is gone from Node 25+, so nothing here may use it. -ARG PNPM_VERSION=12.16.1 +ARG PNPM_VERSION=12.18.0 RUN set -eux; \ ARCH="$(dpkg --print-architecture)"; \ case "${ARCH}" in \ aarch64|arm64) \ PNPM_ARCH='arm64'; \ - PNPM_SHA256='f5847699e5d528f8f8e9bcfb385e58cb9fba7482af1b1aa7b70737be42277f81'; \ + PNPM_SHA256='700bd1aa4028465b80e62b7c5bec8455be60bbafaf53a3262a4ab8e2057712ac'; \ ;; \ amd64|x86_64) \ PNPM_ARCH='x64'; \ - PNPM_SHA256='565ff81174a14f6f7bd5d84c1b17e51031a80230d370a1fe24be5d16bd02a825'; \ + PNPM_SHA256='3726060e236c0db3e7124c0a5af123c840ba0cb76682149b63ea0baf290017c2'; \ ;; \ *) \ echo "Unsupported arch: ${ARCH}"; \ diff --git a/docker/images/Dockerfile.frontend b/docker/images/Dockerfile.frontend index aba52d5bcc..56db3e3d7d 100644 --- a/docker/images/Dockerfile.frontend +++ b/docker/images/Dockerfile.frontend @@ -1,4 +1,4 @@ -FROM dhi.io/nginx:1.31.8-debian13-dev@sha256:f69a0ea1762c51080fe96ce5f12103fb2977231308ae1784acd915bf63a0a1a2 +FROM dhi.io/nginx:1.31.8-debian13-dev@sha256:f88f28c085f327cee96325f38222c9a3c75eb6cdb8993b4e2cf8630889f34364 LABEL maintainer="Penpot " ENV DEBIAN_FRONTEND=noninteractive diff --git a/docker/images/Dockerfile.mcp b/docker/images/Dockerfile.mcp index 4054a613b0..b4a0637cbb 100644 --- a/docker/images/Dockerfile.mcp +++ b/docker/images/Dockerfile.mcp @@ -1,4 +1,4 @@ -FROM dhi.io/node:24.22.2-debian13-dev@sha256:df819cfcb0de8a9e461bb8437d96d6fcfac341e565129e50597338756a88db5b AS build +FROM dhi.io/node:24.22.2-debian13-dev@sha256:24214ddb02c90cb64829112570250e1723148bdae9ba8b181f7c1cd675cc3da6 AS build LABEL maintainer="Penpot " ENV DEBIAN_FRONTEND=noninteractive @@ -11,7 +11,7 @@ WORKDIR /opt/penpot/mcp # pnpm ships as a system binary (same tarball + SHA pin as # docker/devenv/Dockerfile); ./setup expects it on PATH. # Corepack is gone from Node 25+, so nothing here may use it. -ARG PNPM_VERSION=12.16.1 +ARG PNPM_VERSION=12.18.0 RUN set -eux; \ apt-get -qq update; \ apt-get -qqy --no-install-recommends install curl ca-certificates gzip; \ @@ -20,11 +20,11 @@ RUN set -eux; \ case "${ARCH}" in \ aarch64|arm64) \ PNPM_ARCH='arm64'; \ - PNPM_SHA256='f5847699e5d528f8f8e9bcfb385e58cb9fba7482af1b1aa7b70737be42277f81'; \ + PNPM_SHA256='700bd1aa4028465b80e62b7c5bec8455be60bbafaf53a3262a4ab8e2057712ac'; \ ;; \ amd64|x86_64) \ PNPM_ARCH='x64'; \ - PNPM_SHA256='565ff81174a14f6f7bd5d84c1b17e51031a80230d370a1fe24be5d16bd02a825'; \ + PNPM_SHA256='3726060e236c0db3e7124c0a5af123c840ba0cb76682149b63ea0baf290017c2'; \ ;; \ *) \ echo "Unsupported arch: ${ARCH}"; \ diff --git a/docker/images/Dockerfile.media-processor b/docker/images/Dockerfile.media-processor index 218fd84ecd..6a7343df2b 100644 --- a/docker/images/Dockerfile.media-processor +++ b/docker/images/Dockerfile.media-processor @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM dhi.io/node:24.22.2-debian13-dev@sha256:df819cfcb0de8a9e461bb8437d96d6fcfac341e565129e50597338756a88db5b +FROM dhi.io/node:24.22.2-debian13-dev@sha256:24214ddb02c90cb64829112570250e1723148bdae9ba8b181f7c1cd675cc3da6 LABEL maintainer="Penpot " ENV LANG=en_US.UTF-8 \ @@ -58,7 +58,7 @@ WORKDIR /opt/penpot/media-processor # pnpm ships as a system binary (same tarball + SHA pin as # docker/devenv/Dockerfile); ./setup expects it on PATH. # Corepack is gone from Node 25+, so nothing here may use it. -ARG PNPM_VERSION=12.16.1 +ARG PNPM_VERSION=12.18.0 RUN set -eux; \ apt-get -qq update; \ apt-get -qqy --no-install-recommends install gzip; \ @@ -67,11 +67,11 @@ RUN set -eux; \ case "${ARCH}" in \ aarch64|arm64) \ PNPM_ARCH='arm64'; \ - PNPM_SHA256='f5847699e5d528f8f8e9bcfb385e58cb9fba7482af1b1aa7b70737be42277f81'; \ + PNPM_SHA256='700bd1aa4028465b80e62b7c5bec8455be60bbafaf53a3262a4ab8e2057712ac'; \ ;; \ amd64|x86_64) \ PNPM_ARCH='x64'; \ - PNPM_SHA256='565ff81174a14f6f7bd5d84c1b17e51031a80230d370a1fe24be5d16bd02a825'; \ + PNPM_SHA256='3726060e236c0db3e7124c0a5af123c840ba0cb76682149b63ea0baf290017c2'; \ ;; \ *) \ echo "Unsupported arch: ${ARCH}"; \ diff --git a/docker/images/Dockerfile.storybook b/docker/images/Dockerfile.storybook index bf67fabf88..de670de369 100644 --- a/docker/images/Dockerfile.storybook +++ b/docker/images/Dockerfile.storybook @@ -1,4 +1,4 @@ -FROM dhi.io/nginx:1.31.8-alpine3.24@sha256:7a4e9021737f414ad0f851efb939600690d9d092f51712ce6512bfd0a767ceae +FROM dhi.io/nginx:1.31.8-alpine3.24@sha256:34c5f1f63c57c0fb1b52505aecbb213520e15918dc822eb16371edff5183cbd1 LABEL maintainer="Penpot " ARG BUNDLE_PATH="./bundle-storybook/" diff --git a/docker/images/docker-compose.yaml b/docker/images/docker-compose.yaml index 932fda6279..cb0fe7d676 100644 --- a/docker/images/docker-compose.yaml +++ b/docker/images/docker-compose.yaml @@ -26,6 +26,9 @@ x-flags: &penpot-flags PENPOT_FLAGS: disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-mcp +## Public URI where Penpot is reached. To serve Penpot from a subpath, include +## it with a trailing slash, e.g. https://example.com/penpot/. A reverse proxy in +## front must forward the subpath unchanged, without rewriting it. x-uri: &penpot-public-uri PENPOT_PUBLIC_URI: http://localhost:9001 @@ -110,6 +113,8 @@ services: # ## internet with HTTPS using traefik. # - "traefik.http.routers.penpot-https.rule=Host(``)" + # ## When Penpot is served from a subpath, match it too: + # - "traefik.http.routers.penpot-https.rule=Host(``) && PathPrefix(`/`)" # - "traefik.http.routers.penpot-https.entrypoints=websecure" # - "traefik.http.routers.penpot-https.tls.certresolver=letsencrypt" # - "traefik.http.routers.penpot-https.tls=true" @@ -186,6 +191,10 @@ services: PENPOT_SMTP_TLS: "false" PENPOT_SMTP_SSL: "false" + ## Inbox for the feedback that MCP agents send. Set it together with + ## PENPOT_MCP_BACKEND_URI on the penpot-mcp service. + # PENPOT_MCP_FEEDBACK_EMAIL: mcp-feedback@example.com + penpot-admin-console: image: "penpotapp/admin-console:${PENPOT_VERSION:-2.18}" restart: always @@ -210,6 +219,11 @@ services: networks: - penpot + # environment: + ## Offers the submit_feedback tool, which emails agent feedback through the + ## backend. Set it together with PENPOT_MCP_FEEDBACK_EMAIL on the backend. + # PENPOT_MCP_BACKEND_URI: http://penpot-backend:6060/ + penpot-exporter: image: "penpotapp/exporter:${PENPOT_VERSION:-2.18}" restart: always diff --git a/docker/images/files/nginx-admin-console-locations.conf.template b/docker/images/files/nginx-admin-console-locations.conf.template index fead3783c9..a2a90913b0 100644 --- a/docker/images/files/nginx-admin-console-locations.conf.template +++ b/docker/images/files/nginx-admin-console-locations.conf.template @@ -1,7 +1,11 @@ +# The Admin Console is served at the root of its own server. Keep proxy_pass +# without a URI: building it from $request_uri escapes the path twice. location /admin-console { + rewrite "^/admin-console(?:/(.*))?$" /$1 break; + proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $http_cf_connecting_ip; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_pass $PENPOT_ADMIN_CONSOLE_URI$request_uri; + proxy_pass $PENPOT_ADMIN_CONSOLE_URI; } diff --git a/docker/images/files/nginx-entrypoint.sh b/docker/images/files/nginx-entrypoint.sh index e76e581f1c..f809591827 100644 --- a/docker/images/files/nginx-entrypoint.sh +++ b/docker/images/files/nginx-entrypoint.sh @@ -51,6 +51,23 @@ update_oidc_name /var/www/app/js/config.js ## Nginx Config ######################################### +# Path of PENPOT_PUBLIC_URI (e.g. /penpot/ for https://example.com/penpot/). +# Requests below it are rewritten to the root, so the proxy in front of this +# server forwards them unchanged. +PENPOT_PUBLIC_PATH="$(sed -E 's|^[A-Za-z][A-Za-z0-9+.-]*://[^/]*||; s|[?#].*$||' <<< "${PENPOT_PUBLIC_URI:-}")" +PENPOT_PUBLIC_PATH="/${PENPOT_PUBLIC_PATH#/}" +export PENPOT_PUBLIC_PATH="${PENPOT_PUBLIC_PATH%/}/" + +if [ "$PENPOT_PUBLIC_PATH" != "/" ]; then + # shellcheck disable=SC2016 # literal regex metacharacters + _public_path_regex="$(sed -E 's/[][\.*^$(){}+?|]/\\&/g' <<< "$PENPOT_PUBLIC_PATH")" + export PENPOT_PUBLIC_PATH_DIRECTIVES="location = ${PENPOT_PUBLIC_PATH%/} { absolute_redirect off; return 301 ${PENPOT_PUBLIC_PATH}\$is_args\$args; } + rewrite ^${_public_path_regex}(.*)\$ /\$1 last;" + unset _public_path_regex +else + export PENPOT_PUBLIC_PATH_DIRECTIVES="" +fi + export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060} export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061} export PENPOT_HTTP_SERVER_MAX_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_BODY_SIZE:-367001600} # Default to 350MiB @@ -58,7 +75,7 @@ export PENPOT_IPV6_LISTEN_DIRECTIVE=${PENPOT_IPV6_LISTEN_DIRECTIVE:-"listen [::] if is_truthy "${PENPOT_DISABLE_IPV6_LISTEN:-}"; then export PENPOT_IPV6_LISTEN_DIRECTIVE="" fi -envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_HTTP_SERVER_MAX_BODY_SIZE,\$PENPOT_IPV6_LISTEN_DIRECTIVE" \ +envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_HTTP_SERVER_MAX_BODY_SIZE,\$PENPOT_IPV6_LISTEN_DIRECTIVE,\$PENPOT_PUBLIC_PATH,\$PENPOT_PUBLIC_PATH_DIRECTIVES" \ < /tmp/nginx.conf.template > /etc/nginx/nginx.conf if [[ " $PENPOT_FLAGS " != *" disable-admin-console "* ]]; then diff --git a/docker/images/files/nginx.conf.template b/docker/images/files/nginx.conf.template index 0451e7a24a..54d62e18eb 100644 --- a/docker/images/files/nginx.conf.template +++ b/docker/images/files/nginx.conf.template @@ -102,6 +102,8 @@ http { root /var/www/app/; + ${PENPOT_PUBLIC_PATH_DIRECTIVES} + location @handle_redirect { set $redirect_uri "$upstream_http_location"; set $redirect_host "$upstream_http_x_host"; @@ -153,6 +155,11 @@ http { proxy_pass $PENPOT_EXPORTER_URI; } + location = /.well-known/oauth-authorization-server/api/mcp/oauth { + proxy_set_header Host $proxy_host; + proxy_pass $PENPOT_BACKEND_URI$request_uri; + } + location /api { proxy_set_header Host $proxy_host; proxy_pass $PENPOT_BACKEND_URI/api; @@ -167,7 +174,7 @@ http { location /readyz { access_log off; proxy_set_header Host $proxy_host; - proxy_pass $PENPOT_BACKEND_URI$request_uri; + proxy_pass $PENPOT_BACKEND_URI$uri$is_args$args; } location /ws/notifications { @@ -198,7 +205,7 @@ http { } location ~ ^/[^/]+/(.*)$ { - return 301 " /404"; + return 301 " ${PENPOT_PUBLIC_PATH}404"; } if ($penpot_link_preview_agent) { diff --git a/docs/mcp/index.md b/docs/mcp/index.md index 3f11995d2b..6b55a3f816 100644 --- a/docs/mcp/index.md +++ b/docs/mcp/index.md @@ -64,7 +64,7 @@ Watch more applications in the **[Penpot MCP video playlist](https://www.youtube There are three key pieces: * **MCP server**: a service that exposes tools to your AI client. It receives requests from the client and forwards them to Penpot. -* **MCP plugin in Penpot**: a plugin that runs inside Penpot and connects your open file to the MCP server. It is what allows the server to access the currently focused page. +* **MCP plugin in Penpot**: a plugin that runs inside Penpot and connects your open file to the MCP server. It allows the server to access that file and its pages. * **MCP client**: the tool where you write prompts (Cursor, Claude Code, Copilot-style tools, etc.). It connects to the MCP server using a server URL and an MCP key (or your active Penpot session in the current local setup). ![How the AI client, Penpot MCP server, plugin, and design file connect](/img/mcp/mcp-flow.webp) @@ -74,8 +74,24 @@ There are three key pieces: Some important concepts for users: * **Integrations page**: MCP is configured under **Your account → Integrations → MCP Server**. Here you enable or disable MCP, get the server URL and manage the MCP key. * **MCP key**: a personal, non-recoverable token that authenticates your AI client with the MCP server. Only one key can exist per user at a time. This is used by the remote MCP setup. -* **Currently focused page**: MCP always operates on the page you have in focus in Penpot. If you change the focused page (even in another browser window), the MCP context follows that page. -* **Active MCP tab**: MCP can only be active in one browser tab at a time. If you have Penpot open in several tabs, you choose explicitly which one owns MCP before running agents. +* **MCP session**: a connection from a Penpot tab to the MCP server, identified by a short session ID derived from the tab's user session. Several tabs can connect independently, including tabs showing the same file. +* **Current page**: the page open in the selected session. Switching browser focus to another tab does not change which session the agent uses. + +### Working with multiple files + +Connect the tabs you want to use: + +* **Remote MCP**: choose **MCP → Connect** in the dashboard header or a file's toolbar. Use **Disconnect** in the same place to disconnect that tab. +* **Local MCP**: run the plugin in each file and connect it to your local server. Each plugin connection has its own session ID. +* Connecting or disconnecting one tab does not disconnect the others. +* A connected tab stays connected while navigating between the dashboard and files. Its session ID stays the same; requests operate on the currently open file. + +Choose a session for your agent: + +* With exactly one connected session, the server selects it automatically when the agent does not specify a session ID. +* With several connected sessions, the server lists them and asks the agent to have you select one. The agent then passes that session ID with its requests. +* You can also give the agent an ID directly. For remote MCP, open the toolbar's **MCP** menu and choose **Copy session ID**. For local MCP, use **Copy** beside the session ID in the plugin window. +* For example: "Use Penpot session `o37vgcqsvt` and list the pages in that file." ### Tools and capabilities @@ -88,6 +104,9 @@ Current tools in **local MCP**: * `penpot_api_info` * `export_shape` * `import_image` +* `submit_feedback`, only when the server has `PENPOT_MCP_BACKEND_URI` set (see the [configuration guide](/technical-guide/configuration/#mcp)) + +`submit_feedback` emails plain-text feedback about Penpot MCP to the team that runs your Penpot instance. The agent sends it only when you ask or approve, and the email includes your name and email address. It needs your MCP key: the remote setup already sends it, and in local MCP you add it to the server URL as `userToken`. Because **remote MCP** does not expose local file-system access: @@ -98,7 +117,7 @@ Because **remote MCP** does not expose local file-system access: ### Agents can edit designs -**Be mindful:** when MCP is connected, your AI client can run **write operations** that change the currently focused Penpot page (create, rename, move, delete, restyle, etc.). To stay safe: +**Be mindful:** when MCP is connected, your AI client can run **write operations** that change the file in the selected session (create, rename, move, delete, restyle, etc.). To stay safe: * Start with **read-only** actions (inspect, list, export) to verify your setup. * Ask the agent to **describe the intended changes** before applying them. @@ -142,7 +161,7 @@ If you just want to try Penpot AI workflows quickly through the MCP, follow this See the section **Connect your MCP client** for more details on how to connect. 5. #### Open a Penpot file and connect MCP - In Penpot, open a design file and use **File → MCP Server → Connect** to connect the plugin to your current file. + In Penpot, open a design file and choose **MCP → Connect** in the toolbar. Enabling MCP does not connect a file automatically. ![Managing MCP Server from Penpot Integrations](/img/mcp/mcp-manage.webp) @@ -156,9 +175,12 @@ When this happens, MCP fails fast instead of waiting for a long task timeout: * In Chrome and Chromium-based browsers, the plugin can report when the tab is being frozen. * In Firefox, Safari, and other browsers that do not expose the same freeze event, MCP uses plugin heartbeats. If the browser stops running the plugin JavaScript, the heartbeat becomes stale and the MCP server reports that the Penpot tab appears to be suspended. -* If the browser unloads the tab completely, the plugin disconnects and MCP reports that no Penpot plugin instance is connected. +* If the browser unloads the tab completely, its plugin disconnects and that session is no longer available. Other connected sessions remain available. -To recover, open or focus the Penpot tab again, wait until MCP reconnects, and retry the prompt. +To recover: + +* If the tab was only suspended, focus it, wait until MCP reconnects, and retry the prompt. +* If the tab was unloaded or reloaded, connect again from the dashboard or a file. Copy the new session ID if your agent was using the previous one. To reduce the chances of the browser putting Penpot to sleep during long MCP sessions: @@ -239,7 +261,7 @@ Note: For clients that do not support HTTP servers directly (like Claude Desktop ### Final check -In Penpot, open a file and connect the plugin from **File → MCP Server → Connect**, then run a read-only prompt first. +In Penpot, open a file and choose **MCP → Connect** in the toolbar, then run a read-only prompt first. *** @@ -253,7 +275,7 @@ Remote MCP is the easiest way to start using AI agents with Penpot. It's hosted 1. Open **Your account → Integrations**. 2. In the **MCP Server** section, read the short description to confirm that feature is available for your account. -3. Use the **Status** toggle to enable MCP Server. Penpot remembers this state per user across sessions. +3. Use the **Status** toggle to enable MCP Server. Penpot remembers this state per user across sessions. You still need to connect each tab explicitly. 4. If this is your first time, Penpot will ask you to **generate an MCP key**. The key is shown only once, store it safely. * Treat the MCP key like a password/token: do not share it in screenshots, logs, or code samples. 5. Once enabled, you will see: @@ -309,11 +331,11 @@ Once everything is configured, day-to-day use of Penpot MCP follows a simple pat 1. **Enable MCP** * Go to **Your account → Integrations → MCP Server** and set **Status** to **Enabled**. 2. **Connect plugin**: - * Open a design file and use **File → MCP Server → Connect**. + * Open a design file and choose **MCP → Connect** in the toolbar. Repeat in each tab you want the agent to access. 3. **Run prompts**: * Open your MCP client and start with read-only prompts first (`list`, `inspect`, `analyze`), then continue with write actions. -MCP always acts on the **currently focused page** in the active Penpot tab. +Requests target the selected MCP session. See [Working with multiple files](#working-with-multiple-files) to choose a session. #### Manage @@ -343,7 +365,7 @@ Security recommendations to highlight in the Help Center: * Treat your MCP key like a password or access token, do not share it in screenshots or code samples. * Regenerate the key if you suspect it may have leaked. -* Remember that disabling MCP Server or disconnecting the plugin stops agents from modifying your files, even if a client is still configured. +* Disabling MCP Server disconnects your integrated sessions. Disconnecting one tab stops access through that connection; other connected sessions remain available. *** @@ -375,7 +397,7 @@ Leave this terminal running while you use MCP. 5. Run the plugin and click **Connect to MCP server**. -6. Make sure the plugin shows **Connected** and keep the plugin window open while working with AI agents. +6. Make sure the plugin shows **Connected** and keep the plugin window open while working with AI agents. The plugin displays the session ID and a **Copy** button. > Some Chromium-based browsers may block the connection from `https://design.penpot.app` to `http://localhost`. If that happens, explicitly allow local network access or use a browser like Firefox. @@ -420,7 +442,7 @@ Once everything is configured, day-to-day use of Penpot MCP follows a simple pat Open your MCP client and start with read-only prompts first (`list`, `inspect`, `analyze`), then continue with write actions. -MCP always acts on the **currently focused page** in the active Penpot tab. +Requests target the selected MCP session. See [Working with multiple files](#working-with-multiple-files) to choose a session. #### Manage diff --git a/docs/package.json b/docs/package.json index 4d1cae270b..4fc709e695 100644 --- a/docs/package.json +++ b/docs/package.json @@ -39,5 +39,5 @@ "markdown-it-anchor": "^10.0.0", "markdown-it-plantuml": "^1.4.1" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/docs/plugins/getting-started.md b/docs/plugins/getting-started.md index ea993640a9..e74cbce541 100644 --- a/docs/plugins/getting-started.md +++ b/docs/plugins/getting-started.md @@ -160,6 +160,7 @@ Set "version": 2 in your - **Code**: your plugin's file location. It needs to be compiled to JavaScript and reachable. - **Icon**: your plugin's icon, which will be also displayed in the plugin manager modal. It'll be a tag so you can use whichever image format works better for you. **It's recommended to use a 56x56 pixel icon for the best appearance on all devices**. - **Permissions**: your plugin's permissions, which allow access to different parts of the Penpot API. +- **Scope** (optional): "workspace" (the default) or "global". A workspace plugin closes when its file closes. A global plugin keeps running while the user moves between the dashboard and files, and gets the penpotMgmt object to find, create and open projects and files. Global scope adds the allow:global permission, which the user must accept. #### Types of permissions @@ -185,6 +186,15 @@ Typical use cases: downloading the full project file for backup or sharing. - allow:localstorage: Allows the access to the local storage proxy to store information. This info is only available for the plugin installation but be aware that a user can see this information in the browser. Typical use cases: storing authentication tokens for a plugin login +- allow:global: Allows the plugin to keep running while the user moves between the dashboard and files, and to find and open their projects and files. Added automatically when the manifest sets "scope": "global". +Typical use cases: assistants or integrations that work across several files. + +- manage:projects: Allows a global plugin to create, rename, pin and duplicate projects. + +- manage:teams: Allows a global plugin to list, create and rename the user's teams, and move projects between them. + +- manage:delete: Allows a global plugin to delete files, projects and teams. + _Note: Write permissions automatically includes its corresponding read permission (e.g., content:write includes content:read) because reading is required to perform write or modification actions._ ### What are plugin.ts and plugin.js files? diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index e647b1e78b..fda4bd4ad5 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' diff --git a/docs/technical-guide/configuration.md b/docs/technical-guide/configuration.md index e99e8d5cd5..4759855e14 100644 --- a/docs/technical-guide/configuration.md +++ b/docs/technical-guide/configuration.md @@ -372,6 +372,44 @@ PENPOT_PUBLIC_URI: https://penpot.mycompany.com If you're using the official docker-compose.yml you only need to configure the PENPOT_PUBLIC_URI envvar in the top of the file. +### Serve Penpot from a subpath + +Penpot can be served from a subpath such as `https://mycompany.com/penpot/`. +Set `PENPOT_PUBLIC_URI` to the complete public URL, including the subpath and its +trailing slash: + +```bash +PENPOT_PUBLIC_URI: https://mycompany.com/penpot/ +``` + +Your reverse proxy or Ingress must also: + +- Route requests under `/penpot/` to the Penpot frontend without rewriting the + path. The frontend expects to receive the subpath. +- Preserve WebSocket upgrade headers for notifications and other services that + use WebSockets. + +#### Migrate an existing installation to a subpath + +Moving an installation from `/` to a subpath does not require a database or +asset migration. Users, teams, projects, files, and uploaded assets remain in +the existing storage. + +Before changing the public URL: + +1. Back up the database, assets, and current deployment configuration. +2. Configure the proxy or Ingress route for the subpath. +3. Update `PENPOT_PUBLIC_URI` for every Penpot component that uses it. +4. Keep the existing `PENPOT_SECRET_KEY`, database, and asset storage + configuration. +5. Restart or redeploy Penpot and verify login, static assets, API requests, + WebSockets, file editing, and image uploads through the new URL. + +Existing bookmarks and integrations that use the previous root URL must be +updated. You may also configure a redirect from the previous URL. If the scheme +or hostname changes in addition to the path, review your authentication provider +callback URLs and cookie settings. Active users may need to sign in again. +

If you plan to serve Penpot under different domain than `localhost` without HTTPS, you need to disable the `secure` flag on cookies, with the `disable-secure-session-cookies` flag. @@ -790,6 +828,8 @@ PENPOT_FLAGS: [...] enable-tiered-file-data-storage PENPOT_FILE_DATA_BACKEND: db ``` +Once enabled, the offload job moves one file per run inside a single transaction: it locks the file row (skipping the run while the file is being edited) and migrates every live data row to the objects storage. You can follow it with `penpot_file_data_offload_rows_total` and `penpot_file_data_offload_bytes_total` (label `type="main|snapshot|fragment"`) and `penpot_file_data_offload_timing_ms` (label `result="ok|skip|error"`). + ### Autosave By default, Penpot stores manually saved versions indefinitely; these can be found in the History tab and can be renamed, restored, deleted, etc. Additionally, the default behavior of on-premise instances is to not keep automatic version history. This automatic behavior can be modified and adapted to each on-premise installation with the corresponding configuration. @@ -919,6 +959,30 @@ The defaults match the service name used in the official `docker-compose.yaml`. them only if your MCP service has a different name or listens on other ports. Both variables are ignored when the `enable-mcp` flag is not set. +#### MCP feedback + +Agents can email feedback about Penpot MCP to an inbox you choose, through the +`submit_feedback` tool. Set the backend URI on the MCP container to offer the tool, +and the inbox on the backend to accept the emails: + +```bash +# MCP +PENPOT_MCP_BACKEND_URI: http://penpot-backend:6060/ + +# Backend +PENPOT_MCP_FEEDBACK_EMAIL: mcp-feedback@example.com +``` + +- `PENPOT_MCP_BACKEND_URI`: The URI the MCP server uses to reach the backend. When + unset, the MCP server does not offer the tool. +- `PENPOT_MCP_FEEDBACK_EMAIL`: The address that receives the feedback. When unset, + the backend rejects submissions. + +The backend accepts feedback when the `enable-mcp` or `enable-access-tokens` flag is +set, and sends it through the [email configuration](#email-configuration). Each +email carries the name and email address of the user who sent it, and uses that +address as reply-to. + ### Internal resolver The frontend container resolves the backend, exporter and MCP service names with the diff --git a/docs/technical-guide/developer/agentic-devenv.md b/docs/technical-guide/developer/agentic-devenv.md index 932f555d6f..c754574207 100644 --- a/docs/technical-guide/developer/agentic-devenv.md +++ b/docs/technical-guide/developer/agentic-devenv.md @@ -209,15 +209,18 @@ below): The files are committed templates + an `envsubst` pass; see [`.devenv/README.md`](../../../.devenv/README.md) for the full layout. -`./manage.sh start-coding-agent [--ws N] [...passthrough]` +`./manage.sh start-coding-agent [--ws N] [--bin EXE] [...passthrough]` launches the chosen client against one workspace, `cd`'ing into the right -directory and refusing to launch if the instance is not running: +directory and refusing to launch if the instance is not running. `--bin` +runs another executable in place of the client's default one (for example a +wrapper script), with the same MCP wiring: ```bash ./manage.sh start-coding-agent claude # ws0 ./manage.sh start-coding-agent opencode --ws 1 # ws1 ./manage.sh start-coding-agent vscode # opens VS Code on ws0 ./manage.sh start-coding-agent codex --ws 2 # ws2 +./manage.sh start-coding-agent claude --bin claude-alt ``` A given AI client session drives **exactly one workspace**, so running N diff --git a/docs/technical-guide/developer/devenv.md b/docs/technical-guide/developer/devenv.md index 89144fd399..697d119b1c 100644 --- a/docs/technical-guide/developer/devenv.md +++ b/docs/technical-guide/developer/devenv.md @@ -12,7 +12,7 @@ in order to correctly set up the development environment. You can [look here][1] for complete instructions. -[1]: /technical-guide/getting-started/#install-with-docker +[1]: /technical-guide/getting-started/docker Optionally, to improve performance, you can also increase the maximum number of @@ -66,8 +66,9 @@ The devenv runs as separate compose projects: * `penpotdev-wsN` project per runtime instance. - `ws0` (a.k.a. `main`) is the current state of your repo; - `ws1` and up are clones that you maintain explicitly under `${PENPOT_WORKSPACES_DIR}/wsN/` - (default `~/.penpot/penpot_workspaces/`). You can explicitly sync them - with the `--sync` flag (automatic on first start). + (default `${XDG_DATA_HOME:-~/.local/share}/penpot-devenv/workspaces/`, or + `~/.penpot/penpot_workspaces/` when that directory already exists). You + can explicitly sync them with the `--sync` flag (automatic on first start). Each call to `run-devenv` brings up one instance. Workspaces are independent and can be started and stopped in any order: @@ -119,8 +120,9 @@ Container-internal ports stay fixed. Target a specific instance with accepts a **non-negative integer only** — `--ws main` or `--ws ws1` is rejected, keeping the flag shape uniform across commands. `run-devenv` is ws0-only and takes no workspace flag. `run-devenv` also accepts -`--serena-context CTX` (used together with `--agentic`) and `--git-user-name NAME` / `--git-user-email -EMAIL` (see below). +`--serena-context CTX` (used together with `--agentic`), `--gpu` to enable +hardware rendering in Chromium (e.g. for benchmarks), and `--git-user-name NAME` +/ `--git-user-email EMAIL` (see below). Configuration lives in one tracked file, `docker/devenv/defaults.env` (the ws0 baseline); `ws1+` values (offset ports, `wsN` container/volume names) are @@ -166,6 +168,49 @@ container (same UID mapping as the source tree). Opencode's own state (sessions, `auth.json`) lives in `~/.local/share/opencode`, which stays in the container's data volume regardless of this flag. +### NVIDIA GPU access + +On Linux, `run-devenv --gpu` grants the selected container access to NVIDIA +GPUs. The host needs an NVIDIA driver, NVIDIA Container Toolkit configured +with a Docker runtime named `nvidia`, and a readable Vulkan driver manifest +at `/usr/share/vulkan/icd.d/nvidia_icd.json`. The option checks the runtime +and manifest before provisioning. + +Build an image from this checkout to include `xauth` and the Vulkan loader +(`libvulkan1`), then use the same tag when starting the container: + +```bash +./manage.sh build-devenv --tag gpu-local +DEVENV_TAG=gpu-local ./manage.sh run-devenv --gpu +``` + +`--gpu` works with `--ws N`, `--agentic`, `--attach`, and +`--opencode-config-dir DIR`. For example: + +```bash +DEVENV_TAG=gpu-local ./manage.sh run-devenv --gpu --ws 1 --agentic +``` + +The GPU overlay requests all NVIDIA devices and driver capabilities and +mounts the host Vulkan manifest directory read-only. Processes inside the +container inherit `PENPOT_GPU=1` and `NVIDIA_DRIVER_CAPABILITIES=all`. +`PENPOT_GPU` signals GPU access to applications. The explicit `--gpu` flag +selects the overlay. + +GPU mode applies at container creation and lasts for that container's +lifetime. To change it, stop the selected instance and run it again with +or without `--gpu`: + +```bash +./manage.sh stop-devenv --ws 1 +DEVENV_TAG=gpu-local ./manage.sh run-devenv --ws 1 +``` + +Run browser commands inside devenv. The Chromium ANGLE Vulkan backend +needs an X display in this setup, so run those commands under +`xvfb-run -a`. Each caller starts its own Xvfb display. Applications choose +their rendering backend. + ### Shared state and workers All instances share one Penpot database and one RustFS bucket; users, teams, @@ -385,7 +430,7 @@ Warning: by default Cursor won't support HTTPS with a self-signed certificate. I An example of your cursor configuration can be: -```javascript +```json { "mcpServers": { "penpot-devenv": { diff --git a/docs/technical-guide/getting-started/docker.md b/docs/technical-guide/getting-started/docker.md index 3d204f6a60..22dd5d9fdc 100644 --- a/docs/technical-guide/getting-started/docker.md +++ b/docs/technical-guide/getting-started/docker.md @@ -323,6 +323,50 @@ server { For full documentation, go to the [official website][2] +#### Serve Penpot from a subpath with NGINX + +First, set the complete public URL in `docker-compose.yaml`: + +```yaml +PENPOT_PUBLIC_URI: https://mycompany.com/penpot/ +``` + +Then route the subpath to the Penpot frontend, forwarding the path unchanged: + +```nginx +server { + listen 443 ssl; + server_name mycompany.com; + + client_max_body_size 367001600; + + # Configure TLS and logs following your organization's practices. + ssl_certificate /path/to/fullchain; + ssl_certificate_key /path/to/privkey; + + location = /penpot { + return 301 /penpot/; + } + + location /penpot/ { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Scheme $scheme; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_redirect off; + proxy_pass http://localhost:9001; + } +} +``` + +This single location covers every Penpot endpoint, WebSockets included. Keep +the trailing slash in `PENPOT_PUBLIC_URI`, and do not add a path to `proxy_pass` +(such as `http://localhost:9001/`): NGINX would then replace the `/penpot/` +prefix instead of forwarding it. + ### Example with CADDY SERVER ```bash diff --git a/docs/technical-guide/getting-started/kubernetes.md b/docs/technical-guide/getting-started/kubernetes.md index 1582d4015b..fa0f5f8f45 100644 --- a/docs/technical-guide/getting-started/kubernetes.md +++ b/docs/technical-guide/getting-started/kubernetes.md @@ -96,6 +96,22 @@ file itself, which you can use as a basis for creating your own settings. You can also consult the list of parameters on the ArtifactHub page of the project. +### Serve Penpot from a subpath + +The chart serves Penpot from `/` by default and can also expose it from a +subpath. The required values depend on whether the cluster uses an Ingress +controller or Gateway API. + +See +Serving Penpot from a subpath +in the Helm chart documentation for the current configuration examples. + +When moving an existing release to a subpath, keep the existing database, +assets, and secret key settings. No data migration is required. Follow the +migration considerations in the [Penpot URI](/technical-guide/configuration/#penpot-uri) +section and verify the +installation through its new public URL after running `helm upgrade`. + ### Using OpenShift? If you are deploying Penpot on OpenShift, we recommend following the specific guidelines provided in our Penpot-helm documentation: diff --git a/exporter/package.json b/exporter/package.json index d50b5b7a40..d5932a8aaf 100644 --- a/exporter/package.json +++ b/exporter/package.json @@ -4,7 +4,7 @@ "license": "MPL-2.0", "author": "Kaleidos INC Sucursal en España SL", "private": true, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "repository": { "type": "git", "url": "https://github.com/penpot/penpot" diff --git a/exporter/pnpm-lock.yaml b/exporter/pnpm-lock.yaml index ad05f01e75..88059a630d 100644 --- a/exporter/pnpm-lock.yaml +++ b/exporter/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' diff --git a/exporter/src/app/wasm/serialize.cljs b/exporter/src/app/wasm/serialize.cljs index 6f316187c7..93071eaa31 100644 --- a/exporter/src/app/wasm/serialize.cljs +++ b/exporter/src/app/wasm/serialize.cljs @@ -23,12 +23,12 @@ (defn set-shape! "Serializes a single shape into the WASM design state. The host-independent properties (base props, children, blur, shadows, svg-attrs, mask, bool-type, - path geometry, grow-type) go through the shared `serialize-shape!` — the same + path geometry, grow-type) go through the shared `serialize-shape` — the same code the workspace's `set-object` uses, so the two can't drift. Only the host-specific parts are handled here: fills/strokes (image bytes are provisioned separately) and text content (fonts provisioned separately)." [shape] - (let [prepared (serialize-shape/serialize-shape! shape) + (let [prepared (serialize-shape/serialize-shape shape) type (:type prepared)] (props/write-shape-fills! (:fills prepared)) (when-not (= type :group) diff --git a/frontend/deps.edn b/frontend/deps.edn index c5184e43ae..ab7197c661 100644 --- a/frontend/deps.edn +++ b/frontend/deps.edn @@ -30,6 +30,11 @@ :git/sha "d726717" :git/url "https://github.com/funcool/rumext.git"} + penpot/tokenscript-interpreter + {:git/tag "v0.2.0" + :git/sha "20db9b1" + :git/url "https://github.com/penpot/tokenscript-interpreter.git"} + instaparse/instaparse {:mvn/version "1.5.0"} garden/garden {:git/url "https://github.com/noprompt/garden" :git/sha "05590ecb5f6fa670856f3d1ab400aa4961047480"} diff --git a/frontend/package.json b/frontend/package.json index 9b45e5348f..d5b917acd0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,7 +4,7 @@ "license": "MPL-2.0", "author": "Kaleidos INC Sucursal en España SL", "private": true, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "browserslist": [ "defaults" ], @@ -56,7 +56,6 @@ "@penpot/plugins-runtime": "link:../plugins/libs/plugins-runtime", "@penpot/svgo": "penpot/svgo#3.3.0", "@penpot/text-editor": "link:text-editor", - "@penpot/tokenscript": "link:packages/tokenscript", "@penpot/ua-parser": "penpot/ua-parser#1.0.0", "@playwright/test": "1.63.0", "@storybook/addon-docs": "10.6.1", diff --git a/frontend/packages/draft-js/package.json b/frontend/packages/draft-js/package.json index 6531f4ca78..19fc00e59f 100644 --- a/frontend/packages/draft-js/package.json +++ b/frontend/packages/draft-js/package.json @@ -4,7 +4,7 @@ "description": "Penpot Draft-JS Wrapper", "main": "index.js", "type": "module", - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "author": "Andrey Antukh", "license": "MPL-2.0", "dependencies": { diff --git a/frontend/packages/mousetrap/package.json b/frontend/packages/mousetrap/package.json index 3c22c33ff5..5b22f4faac 100644 --- a/frontend/packages/mousetrap/package.json +++ b/frontend/packages/mousetrap/package.json @@ -7,7 +7,7 @@ "scripts": { "test": "node --test \"test/*.test.js\"" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "author": "Craig Campbell", "license": "Apache-2.0 WITH LLVM-exception" } diff --git a/frontend/packages/tokenscript/.gitignore b/frontend/packages/tokenscript/.gitignore deleted file mode 100644 index 0db2a5c1d2..0000000000 --- a/frontend/packages/tokenscript/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -*.sublime-workspace -/.yarn \ No newline at end of file diff --git a/frontend/packages/tokenscript/index.js b/frontend/packages/tokenscript/index.js deleted file mode 100644 index 4d7deb9d72..0000000000 --- a/frontend/packages/tokenscript/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./schemas.js"; -export * from "@tokens-studio/tokenscript-interpreter"; diff --git a/frontend/packages/tokenscript/package.json b/frontend/packages/tokenscript/package.json deleted file mode 100644 index a7d149ce62..0000000000 --- a/frontend/packages/tokenscript/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "@penpot/tokenscript", - "version": "1.0.0", - "description": "", - "main": "index.js", - "type": "module", - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", - "author": "Andrey Antukh", - "license": "MPL-2.0", - "dependencies": { - "@tokens-studio/tokenscript-interpreter": "^0.26.0" - } -} diff --git a/frontend/packages/tokenscript/schemas.js b/frontend/packages/tokenscript/schemas.js deleted file mode 100644 index 75ead35ce7..0000000000 --- a/frontend/packages/tokenscript/schemas.js +++ /dev/null @@ -1,1631 +0,0 @@ -// Auto-generated by @tokens-studio/tokenscript-schemas -// Version: @tokens-studio/tokenscript-schemas@v0.4.0 -// GitHub: https://github.com/tokens-studio/tokenscript-schemas -// Command: npx @tokens-studio/tokenscript-schemas bundle preset:css preset:cssColors --output ./schemas.js -// Generated: 2026-02-11T08:46:40.467Z - -import { Config } from "@tokens-studio/tokenscript-interpreter"; - -export const SCHEMAS = [ - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hex-color/0/", - schema: { - "name": "Hex", - "type": "color", - "description": "A color in hex format, e.g. #ff0000", - "schema": { - "type": "object", - "properties": { - "value": { - "type": "string" - } - } - }, - "initializers": [ - { - "title": "Hex Color Initializer", - "keyword": "hex", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "variable c: Color.Hex;\nc.value = {input};\nreturn c;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/", - "target": "$self", - "description": "Converts sRGB (0-1) to Hex format", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// sRGB to Hex Conversion\n// Converts sRGB (0-1) to hexadecimal string format\n//\n// Examples:\n// sRGB(1, 0, 0) → #ff0000\n// sRGB(0, 1, 0.5) → #00ff80\n\nvariable hex: String = \"#\";\nvariable value: Number = 0;\n\n// Red channel\nvalue = round({input}.r * 255);\nif (value < 0) [ value = 0; ];\nif (value > 255) [ value = 255; ];\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\n// Green channel\nvalue = round({input}.g * 255);\nif (value < 0) [ value = 0; ];\nif (value > 255) [ value = 255; ];\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\n// Blue channel\nvalue = round({input}.b * 255);\nif (value < 0) [ value = 0; ];\nif (value > 255) [ value = 255; ];\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\nreturn hex;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-color/0/", - "target": "$self", - "description": "Converts Display P3 to Hex format (clamps to sRGB gamut)", - "lossless": false, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Display P3 to Hex Conversion\n// Converts P3 (0-1) to hexadecimal string format\n// Note: P3 colors may be out of sRGB gamut, values are clamped to 0-1\n//\n// Examples:\n// P3(1, 0, 0) → #ff0000\n// P3(0, 1, 0.5) → #00ff80\n\nvariable hex: String = \"#\";\nvariable value: Number = 0;\n\n// Red channel (clamp P3 to sRGB range)\nvalue = {input}.r;\nif (value < 0) [ value = 0; ];\nif (value > 1) [ value = 1; ];\nvalue = round(value * 255);\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\n// Green channel\nvalue = {input}.g;\nif (value < 0) [ value = 0; ];\nif (value > 1) [ value = 1; ];\nvalue = round(value * 255);\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\n// Blue channel\nvalue = {input}.b;\nif (value < 0) [ value = 0; ];\nif (value > 1) [ value = 1; ];\nvalue = round(value * 255);\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\nreturn hex;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsl-color/0/", - "target": "$self", - "description": "Converts HSL to Hex format", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HSL to Hex Conversion\n// Converts HSL to hexadecimal string format\n// Reference: Standard HSL to RGB algorithm\n//\n// Input: Color.HSL with h (0-360), s (0-1), l (0-1)\n// Output: Hex string #rrggbb\n\n// Get input HSL values\nvariable h: Number = {input}.h;\nvariable s: Number = {input}.s;\nvariable l: Number = {input}.l;\n\n// Normalize hue to 0-1 range\nvariable hue: Number = h / 360;\n\n// RGB values (default to achromatic)\nvariable r: Number = l;\nvariable g: Number = l;\nvariable b: Number = l;\n\n// Only calculate if there's saturation\nif (s > 0) [\n variable q: Number = 0;\n if (l < 0.5) [\n q = l * (1 + s);\n ] else [\n q = l + s - l * s;\n ];\n\n variable p: Number = 2 * l - q;\n\n // Red (hue + 1/3)\n variable tr: Number = hue + 0.333333333333333;\n if (tr < 0) [ tr = tr + 1; ];\n if (tr > 1) [ tr = tr - 1; ];\n\n if (tr < 0.166666666666667) [\n r = p + (q - p) * 6 * tr;\n ] else [\n if (tr < 0.5) [\n r = q;\n ] else [\n if (tr < 0.666666666666667) [\n r = p + (q - p) * (0.666666666666667 - tr) * 6;\n ] else [\n r = p;\n ];\n ];\n ];\n\n // Green (hue)\n variable tg: Number = hue;\n if (tg < 0) [ tg = tg + 1; ];\n if (tg > 1) [ tg = tg - 1; ];\n\n if (tg < 0.166666666666667) [\n g = p + (q - p) * 6 * tg;\n ] else [\n if (tg < 0.5) [\n g = q;\n ] else [\n if (tg < 0.666666666666667) [\n g = p + (q - p) * (0.666666666666667 - tg) * 6;\n ] else [\n g = p;\n ];\n ];\n ];\n\n // Blue (hue - 1/3)\n variable tb: Number = hue - 0.333333333333333;\n if (tb < 0) [ tb = tb + 1; ];\n if (tb > 1) [ tb = tb - 1; ];\n\n if (tb < 0.166666666666667) [\n b = p + (q - p) * 6 * tb;\n ] else [\n if (tb < 0.5) [\n b = q;\n ] else [\n if (tb < 0.666666666666667) [\n b = p + (q - p) * (0.666666666666667 - tb) * 6;\n ] else [\n b = p;\n ];\n ];\n ];\n];\n\n// Convert RGB to hex\nvariable hex: String = \"#\";\nvariable value: Number = 0;\n\n// Red\nvalue = round(r * 255);\nif (value < 0) [ value = 0; ];\nif (value > 255) [ value = 255; ];\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\n// Green\nvalue = round(g * 255);\nif (value < 0) [ value = 0; ];\nif (value > 255) [ value = 255; ];\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\n// Blue\nvalue = round(b * 255);\nif (value < 0) [ value = 0; ];\nif (value > 255) [ value = 255; ];\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\nreturn hex;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/", - "target": "$self", - "description": "Converts OKLCH to Hex format (clamps to sRGB gamut)", - "lossless": false, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLCH to Hex Conversion\n// Converts OKLCH perceptual color to hexadecimal string format\n// Path: OKLCH → OKLab → XYZ-D65 → Linear sRGB → sRGB → Hex\n//\n// Input: Color.OKLCH with l (0-1), c, h (0-360)\n// Output: Hex string #rrggbb\n\n// Get input OKLCH values\nvariable ok_l: Number = {input}.l;\nvariable ok_c: Number = {input}.c;\nvariable ok_h: Number = {input}.h;\n\n// === Step 1: OKLCH to OKLab (polar to cartesian) ===\nvariable pi: Number = pi();\nvariable deg_to_rad: Number = pi / 180;\nvariable h_rad: Number = ok_h * deg_to_rad;\n\nvariable lab_a: Number = ok_c * cos(h_rad);\nvariable lab_b: Number = ok_c * sin(h_rad);\n\n// === Step 2: OKLab to XYZ-D65 ===\n// Inverse Lab-to-LMS matrix\nvariable lms_l: Number = 1.0 * ok_l + 0.3963377773761749 * lab_a + 0.2158037573099136 * lab_b;\nvariable lms_m: Number = 1.0 * ok_l + -0.1055613458156586 * lab_a + -0.0638541728258133 * lab_b;\nvariable lms_s: Number = 1.0 * ok_l + -0.0894841775298119 * lab_a + -1.2914855480194092 * lab_b;\n\n// Cube the values (inverse of cube root)\nvariable lms_l_cubed: Number = lms_l * lms_l * lms_l;\nvariable lms_m_cubed: Number = lms_m * lms_m * lms_m;\nvariable lms_s_cubed: Number = lms_s * lms_s * lms_s;\n\n// Inverse LMS-to-XYZ matrix\nvariable xyz_x: Number = 1.2268798758459243 * lms_l_cubed + -0.5578149944602171 * lms_m_cubed + 0.2813910456659647 * lms_s_cubed;\nvariable xyz_y: Number = -0.0405757452148008 * lms_l_cubed + 1.1122868032803170 * lms_m_cubed + -0.0717110580655164 * lms_s_cubed;\nvariable xyz_z: Number = -0.0763729366746601 * lms_l_cubed + -0.4214933324022432 * lms_m_cubed + 1.5869240198367816 * lms_s_cubed;\n\n// === Step 3: XYZ-D65 to Linear sRGB ===\nvariable linear_r: Number = 3.2409699419045226 * xyz_x + -1.537383177570094 * xyz_y + -0.4986107602930034 * xyz_z;\nvariable linear_g: Number = -0.9692436362808796 * xyz_x + 1.8759675015077202 * xyz_y + 0.04155505740717559 * xyz_z;\nvariable linear_b: Number = 0.05563007969699366 * xyz_x + -0.20397695888897652 * xyz_y + 1.0569715142428786 * xyz_z;\n\n// === Step 4: Linear sRGB to sRGB (gamma correction) ===\nvariable threshold: Number = 0.0031308;\nvariable linear_scale: Number = 12.92;\nvariable gamma_offset: Number = 0.055;\nvariable gamma_scale: Number = 1.055;\nvariable gamma_exp: Number = 0.416666666666667;\n\nvariable srgb_r: Number = 0;\nif (linear_r <= threshold) [\n srgb_r = linear_r * linear_scale;\n] else [\n if (linear_r > 0) [\n srgb_r = gamma_scale * pow(linear_r, gamma_exp) - gamma_offset;\n ] else [\n srgb_r = 0;\n ];\n];\n\nvariable srgb_g: Number = 0;\nif (linear_g <= threshold) [\n srgb_g = linear_g * linear_scale;\n] else [\n if (linear_g > 0) [\n srgb_g = gamma_scale * pow(linear_g, gamma_exp) - gamma_offset;\n ] else [\n srgb_g = 0;\n ];\n];\n\nvariable srgb_b: Number = 0;\nif (linear_b <= threshold) [\n srgb_b = linear_b * linear_scale;\n] else [\n if (linear_b > 0) [\n srgb_b = gamma_scale * pow(linear_b, gamma_exp) - gamma_offset;\n ] else [\n srgb_b = 0;\n ];\n];\n\n// === Step 5: sRGB to Hex ===\nvariable hex: String = \"#\";\nvariable value: Number = 0;\n\n// Red (clamp to 0-1)\nvalue = srgb_r;\nif (value < 0) [ value = 0; ];\nif (value > 1) [ value = 1; ];\nvalue = round(value * 255);\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\n// Green\nvalue = srgb_g;\nif (value < 0) [ value = 0; ];\nif (value > 1) [ value = 1; ];\nvalue = round(value * 255);\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\n// Blue\nvalue = srgb_b;\nif (value < 0) [ value = 0; ];\nif (value > 1) [ value = 1; ];\nvalue = round(value * 255);\nif (value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n] else [\n hex = hex.concat(value.to_string(16));\n];\n\nreturn hex;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/", - schema: { - "name": "SRGB", - "type": "color", - "description": "sRGB color space with normalized 0-1 range. The standard color space for web and displays.", - "schema": { - "type": "object", - "properties": { - "r": { - "type": "number", - "description": "Red channel (0-1)" - }, - "g": { - "type": "number", - "description": "Green channel (0-1)" - }, - "b": { - "type": "number", - "description": "Blue channel (0-1)" - } - }, - "required": [ - "r", - "g", - "b" - ], - "order": [ - "r", - "g", - "b" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "sRGB Color Initializer", - "keyword": "srgb", - "description": "Creates an sRGB color from normalized 0-1 values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// sRGB Color Initializer\n// Creates an sRGB color from normalized 0-1 values\n// Input: List of [r, g, b] or [r, g, b, alpha] values in 0-1 range\n\nvariable color_values: List = {input};\nvariable output: Color.SRGB;\n\noutput.r = color_values.get(0);\noutput.g = color_values.get(1);\noutput.b = color_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (color_values.length() > 3) [\n output.alpha = color_values.get(3);\n];\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/rgb-color/0/", - "target": "$self", - "description": "Converts RGB (0-255) to sRGB (0-1) by normalizing", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// RGB to sRGB Conversion\n// Converts RGB (0-255) to sRGB (0-1) by normalizing\n// Input: Color.Rgb with r, g, b in 0-255 range\n// Output: Color.SRGB with r, g, b in 0-1 range\n// Lossless: Yes (simple division)\n\nvariable r_normalized: Number = {input}.r / 255;\nvariable g_normalized: Number = {input}.g / 255;\nvariable b_normalized: Number = {input}.b / 255;\n\nvariable output: Color.SRGB;\noutput.r = r_normalized;\noutput.g = g_normalized;\noutput.b = b_normalized;\n\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsl-color/0/", - "target": "$self", - "description": "Converts HSL to sRGB", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HSL to sRGB Conversion\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/hsl.js\n//\n// Algorithm:\n// 1. If saturation is 0, it's achromatic: R=G=B=L\n// 2. Otherwise use the HSL to RGB formula:\n// - Calculate intermediate values based on L\n// - Use hue to determine RGB components\n//\n// Input: Color.HSL with h (0-360), s (0-1), l (0-1)\n// Output: Color.SRGB with r, g, b in 0-1 range\n\n// Get input HSL values\nvariable h: Number = {input}.h;\nvariable s: Number = {input}.s;\nvariable l: Number = {input}.l;\n\n// Normalize hue to 0-1 range\nvariable hue: Number = h / 360;\n\n// Output values\nvariable r: Number = l;\nvariable g: Number = l;\nvariable b: Number = l;\n\n// Only calculate if there's saturation (not achromatic)\nif (s > 0) [\n // Calculate intermediate value\n variable q: Number = 0;\n if (l < 0.5) [\n q = l * (1 + s);\n ] else [\n q = l + s - l * s;\n ];\n \n variable p: Number = 2 * l - q;\n \n // Helper function logic inlined for R (hue + 1/3)\n variable tr: Number = hue + 0.333333333333333;\n if (tr < 0) [ tr = tr + 1; ];\n if (tr > 1) [ tr = tr - 1; ];\n \n if (tr < 0.166666666666667) [\n r = p + (q - p) * 6 * tr;\n ] else [\n if (tr < 0.5) [\n r = q;\n ] else [\n if (tr < 0.666666666666667) [\n r = p + (q - p) * (0.666666666666667 - tr) * 6;\n ] else [\n r = p;\n ];\n ];\n ];\n \n // Helper function logic inlined for G (hue)\n variable tg: Number = hue;\n if (tg < 0) [ tg = tg + 1; ];\n if (tg > 1) [ tg = tg - 1; ];\n \n if (tg < 0.166666666666667) [\n g = p + (q - p) * 6 * tg;\n ] else [\n if (tg < 0.5) [\n g = q;\n ] else [\n if (tg < 0.666666666666667) [\n g = p + (q - p) * (0.666666666666667 - tg) * 6;\n ] else [\n g = p;\n ];\n ];\n ];\n \n // Helper function logic inlined for B (hue - 1/3)\n variable tb: Number = hue - 0.333333333333333;\n if (tb < 0) [ tb = tb + 1; ];\n if (tb > 1) [ tb = tb - 1; ];\n \n if (tb < 0.166666666666667) [\n b = p + (q - p) * 6 * tb;\n ] else [\n if (tb < 0.5) [\n b = q;\n ] else [\n if (tb < 0.666666666666667) [\n b = p + (q - p) * (0.666666666666667 - tb) * 6;\n ] else [\n b = p;\n ];\n ];\n ];\n];\n\n// Create output\nvariable output: Color.SRGB;\noutput.r = r;\noutput.g = g;\noutput.b = b;\n\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/", - "target": "$self", - "description": "Converts Linear sRGB to sRGB by applying gamma correction", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Linear sRGB to sRGB Conversion\n// Applies gamma correction (transfer function)\n// Reference: IEC 61966-2-1:1999 (sRGB specification)\n//\n// Algorithm:\n// if linear ≤ 0.0031308: srgb = linear * 12.92\n// else: srgb = 1.055 * linear^(1/2.4) - 0.055\n//\n// Input: Color.LinearSRGB with r, g, b in linear 0-1 range\n// Output: Color.SRGB with r, g, b in gamma-corrected 0-1 range\n\n// Gamma correction constants (IEC 61966-2-1)\nvariable threshold: Number = 0.0031308;\nvariable linear_scale: Number = 12.92;\nvariable gamma_offset: Number = 0.055;\nvariable gamma_scale: Number = 1.055;\nvariable gamma_exponent: Number = 0.416666666666667;\n\n// Get input linear values\nvariable linear_r: Number = {input}.r;\nvariable linear_g: Number = {input}.g;\nvariable linear_b: Number = {input}.b;\n\n// Convert red channel\nvariable srgb_r: Number = 0;\nif (linear_r <= threshold) [\n srgb_r = linear_r * linear_scale;\n] else [\n srgb_r = gamma_scale * pow(linear_r, gamma_exponent) - gamma_offset;\n];\n\n// Convert green channel\nvariable srgb_g: Number = 0;\nif (linear_g <= threshold) [\n srgb_g = linear_g * linear_scale;\n] else [\n srgb_g = gamma_scale * pow(linear_g, gamma_exponent) - gamma_offset;\n];\n\n// Convert blue channel\nvariable srgb_b: Number = 0;\nif (linear_b <= threshold) [\n srgb_b = linear_b * linear_scale;\n] else [\n srgb_b = gamma_scale * pow(linear_b, gamma_exponent) - gamma_offset;\n];\n\n// Create output\nvariable output: Color.SRGB;\noutput.r = srgb_r;\noutput.g = srgb_g;\noutput.b = srgb_b;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/rgb-color/0/", - schema: { - "name": "Rgb", - "type": "color", - "description": "RGB color", - "schema": { - "type": "object", - "properties": { - "r": { - "type": "number" - }, - "g": { - "type": "number" - }, - "b": { - "type": "number" - } - }, - "required": [ - "r", - "g", - "b" - ], - "order": [ - "r", - "g", - "b" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "function", - "keyword": "rgb", - "description": "Creates an RGB color", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// RGB Color Initializer\n// Creates an RGB color from r, g, b values (0-255 range)\n//\n// Usage: rgb(255, 128, 64) → Color.Rgb { r: 255, g: 128, b: 64 }\n// rgb(255, 128, 64, 0.5) → Color.Rgb { r: 255, g: 128, b: 64, alpha: 0.5 }\n//\n// Input: List of 3 or 4 numbers [r, g, b] or [r, g, b, alpha]\n// Output: Color.Rgb\n\nvariable color_parts: List = {input}; \n\nvariable output: Color.Rgb;\noutput.r = color_parts.get(0);\noutput.g = color_parts.get(1);\noutput.b = color_parts.get(2);\n\n// Set alpha if provided as 4th parameter\nif (color_parts.length() > 3) [\n output.alpha = color_parts.get(3);\n];\n\nreturn output;" - } - }, - { - "title": "function", - "keyword": "rgba", - "description": "Creates an RGB color with alpha", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// RGBA Color Initializer\n// Creates an RGB color with alpha from r, g, b, a values\n//\n// Usage: rgba(255, 128, 64, 0.5) → Color.Rgb { r: 255, g: 128, b: 64, alpha: 0.5 }\n//\n// Input: List of 4 numbers [r, g, b, alpha]\n// Output: Color.Rgb with alpha\n\nvariable color_parts: List = {input}; \n\nvariable output: Color.Rgb;\noutput.r = color_parts.get(0);\noutput.g = color_parts.get(1);\noutput.b = color_parts.get(2);\noutput.alpha = color_parts.get(3);\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hex-color/0/", - "target": "$self", - "description": "Converts HEX to RGB", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Hex to RGB Conversion\n// Converts hexadecimal color string to RGB (0-255 range)\n//\n// Supports both shorthand (#RGB) and standard (#RRGGBB) formats:\n// #f00 → RGB(255, 0, 0)\n// #ff0000 → RGB(255, 0, 0)\n//\n// Algorithm:\n// 1. Remove '#' prefix and split into characters\n// 2. For shorthand: duplicate each digit (f → ff)\n// 3. Parse hex pairs to decimal values\n//\n// Input: Color.Hex (e.g., #ff5733)\n// Output: Color.Rgb with r, g, b in 0-255 range\n\n// Split hex string on '#' and get the color value\nvariable color_parts: List = {input}.to_string().split('#'); \nvariable color: List = color_parts.get(1).split(); \nvariable length: Number = color.length(); \n\n// Initialize RGB values\nvariable rgb: List = 0, 0, 0; \n\n// Handle shorthand (#RGB) vs standard (#RRGGBB) format\nif(length == 3) [ \n // Shorthand: duplicate each digit\n rgb.update(0, parse_int(color.get(0).concat(color.get(0)), 16)); \n rgb.update(1, parse_int(color.get(1).concat(color.get(1)), 16)); \n rgb.update(2, parse_int(color.get(2).concat(color.get(2)), 16)); \n] else [ \n // Standard: pair adjacent digits\n rgb.update(0, parse_int(color.get(0).concat(color.get(1)), 16)); \n rgb.update(1, parse_int(color.get(2).concat(color.get(3)), 16)); \n rgb.update(2, parse_int(color.get(4).concat(color.get(5)), 16)); \n]; \n\n// Create output color\nvariable output: Color.Rgb; \noutput.r = rgb.get(0); \noutput.g = rgb.get(1); \noutput.b = rgb.get(2); \n\nreturn output;" - } - }, - { - "source": "$self", - "target": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hex-color/0/", - "description": "Converts RGB to HEX", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// RGB to Hex Conversion\n// Converts RGB (0-255) color to hexadecimal string format\n//\n// Examples:\n// RGB(255, 0, 0) → #ff0000\n// RGB(0, 255, 128) → #00ff80\n//\n// Algorithm:\n// 1. Round each channel to nearest integer\n// 2. Convert to base-16 string\n// 3. Pad single digits with leading zero\n// 4. Concatenate with '#' prefix\n//\n// Input: Color.Rgb with r, g, b in 0-255 range\n// Output: String (hex format #rrggbb)\n\nvariable rgba: List = {input}.r, {input}.g, {input}.b;\nvariable hex: String = \"#\";\nvariable i: Number = 0;\nvariable value: Number = 0;\n\n// Convert each RGB channel to hex\nwhile( i < min(rgba.length(), 3)) [\n value = round(rgba.get(i));\n if(value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n ] else [\n hex = hex.concat(value.to_string(16));\n ];\n i = i + 1;\n];\n\nif (rgba.length() == 4) [\n value = rgba.get(3) * 255; // Convert alpha to 0-255 range\n if(value < 16) [\n hex = hex.concat(\"0\").concat(value.to_string(16));\n ] else [\n hex = hex.concat(value.to_string(16));\n ];\n];\n\nreturn hex;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsl-color/0/", - schema: { - "name": "HSL", - "type": "color", - "description": "HSL color space - Hue, Saturation, Lightness. Popular for color pickers and CSS.", - "schema": { - "type": "object", - "properties": { - "h": { - "type": "number", - "description": "Hue angle (0-360 degrees)" - }, - "s": { - "type": "number", - "description": "Saturation (0-1)" - }, - "l": { - "type": "number", - "description": "Lightness (0-1)" - } - }, - "required": [ - "h", - "s", - "l" - ], - "order": [ - "h", - "s", - "l" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "HSL Color Initializer", - "keyword": "hsl", - "description": "Creates an HSL color from H, S, L values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HSL Color Initializer\n// Creates an HSL color from H, S, L values\n// Input: List of [h, s, l] or [h, s, l, alpha] values\n\nvariable hsl_values: List = {input};\nvariable output: Color.HSL;\n\noutput.h = hsl_values.get(0);\noutput.s = hsl_values.get(1);\noutput.l = hsl_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (hsl_values.length() > 3) [\n output.alpha = hsl_values.get(3);\n];\n\nreturn output;" - } - }, - { - "title": "HSLA Color Initializer", - "keyword": "hsla", - "description": "Creates an HSL color with alpha from H, S, L, A values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HSLA Color Initializer\n// Creates an HSL color with alpha from H, S, L, A values\n//\n// Usage: hsla(180, 0.5, 0.5, 0.8) → Color.HSL { h: 180, s: 0.5, l: 0.5, alpha: 0.8 }\n//\n// Input: List of 4 numbers [h, s, l, alpha]\n// Output: Color.HSL with alpha\n\nvariable hsl_values: List = {input};\nvariable output: Color.HSL;\n\noutput.h = hsl_values.get(0);\noutput.s = hsl_values.get(1);\noutput.l = hsl_values.get(2);\noutput.alpha = hsl_values.get(3);\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/", - "target": "$self", - "description": "Converts sRGB to HSL", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// sRGB to HSL Conversion\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/hsl.js\n//\n// Algorithm:\n// 1. Find max and min of R, G, B\n// 2. L = (max + min) / 2\n// 3. If max == min, S = 0 (achromatic)\n// 4. Else S = (max - min) / (1 - |2L - 1|)\n// 5. H depends on which channel is max\n//\n// Input: Color.SRGB with r, g, b in 0-1 range\n// Output: Color.HSL with h (0-360), s (0-1), l (0-1)\n\n// Get input sRGB values\nvariable r: Number = {input}.r;\nvariable g: Number = {input}.g;\nvariable b: Number = {input}.b;\n\n// Find max and min\nvariable max_val: Number = r;\nif (g > max_val) [\n max_val = g;\n];\nif (b > max_val) [\n max_val = b;\n];\n\nvariable min_val: Number = r;\nif (g < min_val) [\n min_val = g;\n];\nif (b < min_val) [\n min_val = b;\n];\n\n// Calculate lightness\nvariable l: Number = (max_val + min_val) / 2;\n\n// Calculate saturation and hue\nvariable s: Number = 0;\nvariable h: Number = 0;\nvariable delta: Number = max_val - min_val;\n\nif (delta > 0) [\n // Not achromatic\n \n // Saturation formula\n variable abs_2l_minus_1: Number = 2 * l - 1;\n if (abs_2l_minus_1 < 0) [\n abs_2l_minus_1 = 0 - abs_2l_minus_1;\n ];\n s = delta / (1 - abs_2l_minus_1);\n \n // Hue calculation depends on which channel is max\n if (max_val == r) [\n h = ((g - b) / delta);\n if (g < b) [\n h = h + 6;\n ];\n ] else [\n if (max_val == g) [\n h = (b - r) / delta + 2;\n ] else [\n h = (r - g) / delta + 4;\n ];\n ];\n \n // Convert to degrees\n h = h * 60;\n];\n\n// Normalize hue to 0-360\nif (h < 0) [\n h = h + 360;\n];\n\n// Create output\nvariable output: Color.HSL;\noutput.h = h;\noutput.s = s;\noutput.l = l;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/", - schema: { - "name": "LinearSRGB", - "type": "color", - "description": "Linear sRGB color space (gamma-decoded). Used for matrix transformations to XYZ and other linear operations.", - "schema": { - "type": "object", - "properties": { - "r": { - "type": "number", - "description": "Linear red channel (0-1)" - }, - "g": { - "type": "number", - "description": "Linear green channel (0-1)" - }, - "b": { - "type": "number", - "description": "Linear blue channel (0-1)" - } - }, - "required": [ - "r", - "g", - "b" - ], - "order": [ - "r", - "g", - "b" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "Linear sRGB Color Initializer", - "keyword": "linearsrgb", - "description": "Creates a linear sRGB color from linear 0-1 values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Linear sRGB Color Initializer\n// Creates a linear sRGB color from linear 0-1 values\n// Input: List of [r, g, b] linear values\n\nvariable color_values: List = {input};\nvariable output: Color.LinearSRGB;\n\noutput.r = color_values.get(0);\noutput.g = color_values.get(1);\noutput.b = color_values.get(2);\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/", - "target": "$self", - "description": "Converts sRGB to Linear sRGB by removing gamma correction (IEC 61966-2-1)", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// sRGB to Linear sRGB Conversion\n// Removes gamma correction (inverse transfer function)\n// Reference: IEC 61966-2-1:1999 (sRGB specification)\n//\n// Algorithm:\n// if srgb ≤ 0.04045: linear = srgb / 12.92\n// else: linear = ((srgb + 0.055) / 1.055) ^ 2.4\n//\n// Input: Color.SRGB with r, g, b in 0-1 range\n// Output: Color.LinearSRGB with r, g, b in linear 0-1 range\n\n// Gamma correction constants (IEC 61966-2-1)\nvariable threshold: Number = 0.04045;\nvariable linear_scale: Number = 12.92;\nvariable gamma_offset: Number = 0.055;\nvariable gamma_scale: Number = 1.055;\nvariable gamma_exponent: Number = 2.4;\n\n// Get input sRGB values\nvariable srgb_r: Number = {input}.r;\nvariable srgb_g: Number = {input}.g;\nvariable srgb_b: Number = {input}.b;\n\n// Convert red channel\nvariable linear_r: Number = 0;\nif (srgb_r <= threshold) [\n linear_r = srgb_r / linear_scale;\n] else [\n linear_r = pow((srgb_r + gamma_offset) / gamma_scale, gamma_exponent);\n];\n\n// Convert green channel\nvariable linear_g: Number = 0;\nif (srgb_g <= threshold) [\n linear_g = srgb_g / linear_scale;\n] else [\n linear_g = pow((srgb_g + gamma_offset) / gamma_scale, gamma_exponent);\n];\n\n// Convert blue channel\nvariable linear_b: Number = 0;\nif (srgb_b <= threshold) [\n linear_b = srgb_b / linear_scale;\n] else [\n linear_b = pow((srgb_b + gamma_offset) / gamma_scale, gamma_exponent);\n];\n\n// Create output\nvariable output: Color.LinearSRGB;\noutput.r = linear_r;\noutput.g = linear_g;\noutput.b = linear_b;\n\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/", - "target": "$self", - "description": "Converts XYZ-D65 to Linear sRGB using inverse transformation matrix", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// XYZ-D65 to Linear sRGB Conversion\n// Reference: ColorJS exact matrix values (extracted via unit vector conversion)\n//\n// Matrix (XYZ D65 to Linear sRGB):\n// [ 3.2409699419045226, -1.537383177570094, -0.4986107602930034 ]\n// [-0.9692436362808796, 1.8759675015077202, 0.04155505740717559]\n// [ 0.05563007969699366, -0.20397695888897652, 1.0569715142428786]\n//\n// Input: Color.XYZD65 with x, y, z tristimulus values\n// Output: Color.LinearSRGB with r, g, b in linear 0-1 range\n\n// Get input XYZ values\nvariable x: Number = {input}.x;\nvariable y: Number = {input}.y;\nvariable z: Number = {input}.z;\n\n// Apply inverse matrix transformation (ColorJS exact values)\n// Row 1: R\nvariable linear_r: Number = 3.2409699419045226 * x + -1.537383177570094 * y + -0.4986107602930034 * z;\n\n// Row 2: G\nvariable linear_g: Number = -0.9692436362808796 * x + 1.8759675015077202 * y + 0.04155505740717559 * z;\n\n// Row 3: B\nvariable linear_b: Number = 0.05563007969699366 * x + -0.20397695888897652 * y + 1.0569715142428786 * z;\n\n// Create output\nvariable output: Color.LinearSRGB;\noutput.r = linear_r;\noutput.g = linear_g;\noutput.b = linear_b;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/", - schema: { - "name": "XYZD65", - "type": "color", - "description": "CIE XYZ color space with D65 white point. The primary connection hub for color space conversions.", - "schema": { - "type": "object", - "properties": { - "x": { - "type": "number", - "description": "X tristimulus value" - }, - "y": { - "type": "number", - "description": "Y tristimulus value (luminance)" - }, - "z": { - "type": "number", - "description": "Z tristimulus value" - } - }, - "required": [ - "x", - "y", - "z" - ], - "order": [ - "x", - "y", - "z" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "XYZ-D65 Color Initializer", - "keyword": "xyzd65", - "description": "Creates an XYZ-D65 color from tristimulus values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// XYZ-D65 Color Initializer\n// Creates an XYZ-D65 color from tristimulus values\n// Input: List of [x, y, z] tristimulus values\n\nvariable xyz_values: List = {input};\nvariable output: Color.XYZD65;\n\noutput.x = xyz_values.get(0);\noutput.y = xyz_values.get(1);\noutput.z = xyz_values.get(2);\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/", - "target": "$self", - "description": "Converts Linear sRGB to XYZ-D65 using the sRGB transformation matrix", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Linear sRGB to XYZ-D65 Conversion\n// Uses the sRGB primaries transformation matrix\n// Reference: IEC 61966-2-1:1999 (sRGB specification)\n//\n// Matrix (Linear RGB → XYZ-D65):\n// [X] [0.4123908 0.3575843 0.1804808] [R]\n// [Y] = [0.2126390 0.7151687 0.0721923] [G]\n// [Z] [0.0193308 0.1191948 0.9505322] [B]\n//\n// Input: Color.LinearSRGB with r, g, b in linear 0-1 range\n// Output: Color.XYZD65 with x, y, z tristimulus values\n\n// Get input linear sRGB values\nvariable r: Number = {input}.r;\nvariable g: Number = {input}.g;\nvariable b: Number = {input}.b;\n\n// Matrix multiplication: M × [R, G, B]ᵀ\n// Row 1: X = 0.4123908 * R + 0.3575843 * G + 0.1804808 * B\nvariable x: Number = r * 0.41239079926595934 + g * 0.357584339383878 + b * 0.1804807884018343;\n\n// Row 2: Y = 0.2126390 * R + 0.7151687 * G + 0.0721923 * B\nvariable y: Number = r * 0.21263900587151027 + g * 0.715168678767756 + b * 0.07219231536073371;\n\n// Row 3: Z = 0.0193308 * R + 0.1191948 * G + 0.9505322 * B\nvariable z: Number = r * 0.01933081871559182 + g * 0.11919477979462598 + b * 0.9505321522496607;\n\n// Create output\nvariable output: Color.XYZD65;\noutput.x = x;\noutput.y = y;\noutput.z = z;\n\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-linear-color/0/", - "target": "$self", - "description": "Converts Linear P3 to XYZ-D65 using the P3 transformation matrix", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Linear P3 to XYZ-D65 Conversion\n// Converts linear Display-P3 RGB to CIE XYZ (D65)\n// Reference: CSS Color Level 4 specification\n//\n// Uses the P3 to XYZ-D65 transformation matrix\n// Matrix values from ColorJS / CSS Color Level 4\n//\n// Input: Color.LinearP3 with linear r, g, b values\n// Output: Color.XYZD65 with x, y, z tristimulus values\n\n// Get linear P3 values\nvariable r: Number = {input}.r;\nvariable g: Number = {input}.g;\nvariable b: Number = {input}.b;\n\n// Linear P3 to XYZ-D65 matrix\n// Row 1\nvariable m00: Number = 0.4865709486482162;\nvariable m01: Number = 0.26566769316909306;\nvariable m02: Number = 0.1982172852343625;\n\n// Row 2\nvariable m10: Number = 0.2289745640697488;\nvariable m11: Number = 0.6917385218365064;\nvariable m12: Number = 0.079286914093745;\n\n// Row 3\nvariable m20: Number = 0.0;\nvariable m21: Number = 0.04511338185890264;\nvariable m22: Number = 1.043944368900976;\n\n// Matrix multiplication: [x, y, z] = M × [r, g, b]\nvariable x: Number = m00 * r + m01 * g + m02 * b;\nvariable y: Number = m10 * r + m11 * g + m12 * b;\nvariable z: Number = m20 * r + m21 * g + m22 * b;\n\n// Create output\nvariable output: Color.XYZD65;\noutput.x = x;\noutput.y = y;\noutput.z = z;\n\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/", - "target": "$self", - "description": "Converts OKLab to XYZ-D65 using inverse LMS transformation", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLab to XYZ-D65 Conversion\n// Reference: ColorJS oklab.js (inverse transformation)\n//\n// Algorithm:\n// 1. Apply inverse Lab-to-LMS matrix to get LMS cone responses\n// 2. Cube the LMS values (inverse of cube root)\n// 3. Apply inverse LMS-to-XYZ matrix\n//\n// Input: Color.OKLab with l, a, b perceptual coordinates\n// Output: Color.XYZD65 with x, y, z tristimulus values\n\n// Get input OKLab values\nvariable ok_l: Number = {input}.l;\nvariable ok_a: Number = {input}.a;\nvariable ok_b: Number = {input}.b;\n\n// Inverse LMStoLab_M matrix (Lab to LMS')\n// These are the inverse of the matrix used in from-xyz-d65.tokenscript\nvariable lms_l: Number = 1.0 * ok_l + 0.3963377773761749 * ok_a + 0.2158037573099136 * ok_b;\nvariable lms_m: Number = 1.0 * ok_l + -0.1055613458156586 * ok_a + -0.0638541728258133 * ok_b;\nvariable lms_s: Number = 1.0 * ok_l + -0.0894841775298119 * ok_a + -1.2914855480194092 * ok_b;\n\n// Cube the values (inverse of cube root)\nvariable lms_l_cubed: Number = lms_l * lms_l * lms_l;\nvariable lms_m_cubed: Number = lms_m * lms_m * lms_m;\nvariable lms_s_cubed: Number = lms_s * lms_s * lms_s;\n\n// Inverse XYZtoLMS_M matrix (LMS to XYZ)\n// From ColorJS oklab.js\nvariable x: Number = 1.2268798758459243 * lms_l_cubed + -0.5578149944602171 * lms_m_cubed + 0.2813910456659647 * lms_s_cubed;\nvariable y: Number = -0.0405757452148008 * lms_l_cubed + 1.1122868032803170 * lms_m_cubed + -0.0717110580655164 * lms_s_cubed;\nvariable z: Number = -0.0763729366746601 * lms_l_cubed + -0.4214933324022432 * lms_m_cubed + 1.5869240198367816 * lms_s_cubed;\n\n// Create output\nvariable output: Color.XYZD65;\noutput.x = x;\noutput.y = y;\noutput.z = z;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-linear-color/0/", - schema: { - "name": "LinearP3", - "type": "color", - "description": "Linear Display-P3 color space (gamma-decoded). Used for matrix transformations to XYZ.", - "schema": { - "type": "object", - "properties": { - "r": { - "type": "number", - "description": "Linear red channel (0-1, can exceed for HDR)" - }, - "g": { - "type": "number", - "description": "Linear green channel (0-1, can exceed for HDR)" - }, - "b": { - "type": "number", - "description": "Linear blue channel (0-1, can exceed for HDR)" - } - }, - "required": [ - "r", - "g", - "b" - ], - "order": [ - "r", - "g", - "b" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "Linear P3 Color Initializer", - "keyword": "linearp3", - "description": "Creates a linear Display-P3 color from linear 0-1 values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Linear P3 Color Initializer\n// Creates a linear Display-P3 color from linear 0-1 values\n// Input: List of [r, g, b] linear values\n\nvariable color_values: List = {input};\nvariable output: Color.LinearP3;\n\noutput.r = color_values.get(0);\noutput.g = color_values.get(1);\noutput.b = color_values.get(2);\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/", - "target": "$self", - "description": "Converts XYZ-D65 to Linear P3 using the P3 transformation matrix", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// XYZ-D65 to Linear P3 Conversion\n// Converts CIE XYZ (D65) to linear Display-P3 RGB\n// Reference: CSS Color Level 4 specification\n//\n// Uses the inverse of the P3 to XYZ-D65 matrix\n// Matrix values from ColorJS / CSS Color Level 4\n//\n// Input: Color.XYZD65 with x, y, z tristimulus values\n// Output: Color.LinearP3 with linear r, g, b values (may be outside 0-1 for out-of-gamut)\n\n// Get XYZ values\nvariable x: Number = {input}.x;\nvariable y: Number = {input}.y;\nvariable z: Number = {input}.z;\n\n// XYZ to Linear P3 matrix (inverse of P3 to XYZ)\n// Row 1\nvariable m00: Number = 2.4934969119414254;\nvariable m01: Number = -0.9313836179191239;\nvariable m02: Number = -0.40271078445071684;\n\n// Row 2\nvariable m10: Number = -0.8294889695615747;\nvariable m11: Number = 1.7626640603183463;\nvariable m12: Number = 0.023624685841943577;\n\n// Row 3\nvariable m20: Number = 0.03584583024378447;\nvariable m21: Number = -0.07617238926804182;\nvariable m22: Number = 0.9568845240076872;\n\n// Matrix multiplication: [r, g, b] = M × [x, y, z]\nvariable linear_r: Number = m00 * x + m01 * y + m02 * z;\nvariable linear_g: Number = m10 * x + m11 * y + m12 * z;\nvariable linear_b: Number = m20 * x + m21 * y + m22 * z;\n\n// Create output (note: values may be outside 0-1 for out-of-gamut colors)\nvariable output: Color.LinearP3;\noutput.r = linear_r;\noutput.g = linear_g;\noutput.b = linear_b;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/", - schema: { - "name": "OKLab", - "type": "color", - "description": "OKLab perceptually uniform color space by Björn Ottosson. L is lightness (0-1), a is green-red axis, b is blue-yellow axis.", - "schema": { - "type": "object", - "properties": { - "l": { - "type": "number", - "description": "Lightness (0-1)" - }, - "a": { - "type": "number", - "description": "Green-red axis (typically -0.4 to 0.4)" - }, - "b": { - "type": "number", - "description": "Blue-yellow axis (typically -0.4 to 0.4)" - } - }, - "required": [ - "l", - "a", - "b" - ], - "order": [ - "l", - "a", - "b" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "OKLab Color Initializer", - "keyword": "oklab", - "description": "Creates an OKLab color from L, a, b values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLab Color Initializer\n// Creates an OKLab color from L, a, b values\n// Input: List of [l, a, b] or [l, a, b, alpha] values\n\nvariable lab_values: List = {input};\nvariable output: Color.OKLab;\n\noutput.l = lab_values.get(0);\noutput.a = lab_values.get(1);\noutput.b = lab_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (lab_values.length() > 3) [\n output.alpha = lab_values.get(3);\n];\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/", - "target": "$self", - "description": "Converts XYZ-D65 to OKLab using Björn Ottosson's algorithm", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// XYZ-D65 to OKLab Conversion\n// Uses ColorJS-compatible matrices (recalculated for consistent D65 white)\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/oklab.js\n//\n// Algorithm:\n// 1. XYZ-D65 → LMS (M1 matrix)\n// 2. LMS → LMS' (cube root transformation)\n// 3. LMS' → OKLab (M2 matrix)\n//\n// Input: Color.XYZD65 with x, y, z tristimulus\n// Output: Color.OKLab with l, a, b coordinates\n\n// Get input XYZ-D65 values\nvariable x: Number = {input}.x;\nvariable y: Number = {input}.y;\nvariable z: Number = {input}.z;\n\n// Step 1: XYZ-D65 → LMS cone response\n// ColorJS XYZtoLMS_M matrix (recalculated for consistent reference white)\nvariable lms_l: Number = x * 0.8190224379967030 + y * 0.3619062600528904 + z * -0.1288737815209879;\nvariable lms_m: Number = x * 0.0329836539323885 + y * 0.9292868615863434 + z * 0.0361446663506424;\nvariable lms_s: Number = x * 0.0481771893596242 + y * 0.2642395317527308 + z * 0.6335478284694309;\n\n// Step 2: Apply cube root (γ = 1/3)\n// Note: Using pow with 1/3 exponent (cbrt equivalent for positive values)\nvariable cube_root_exp: Number = 0.3333333333333333;\nvariable lms_l_prime: Number = pow(lms_l, cube_root_exp);\nvariable lms_m_prime: Number = pow(lms_m, cube_root_exp);\nvariable lms_s_prime: Number = pow(lms_s, cube_root_exp);\n\n// Step 3: LMS' → OKLab\n// ColorJS LMStoLab_M matrix\nvariable oklab_l: Number = lms_l_prime * 0.2104542683093140 + lms_m_prime * 0.7936177747023054 + lms_s_prime * -0.0040720430116193;\nvariable oklab_a: Number = lms_l_prime * 1.9779985324311684 + lms_m_prime * -2.4285922420485799 + lms_s_prime * 0.4505937096174110;\nvariable oklab_b: Number = lms_l_prime * 0.0259040424655478 + lms_m_prime * 0.7827717124575296 + lms_s_prime * -0.8086757549230774;\n\n// Create output\nvariable output: Color.OKLab;\noutput.l = oklab_l;\noutput.a = oklab_a;\noutput.b = oklab_b;\n\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/", - "target": "$self", - "description": "Converts OKLCH to OKLab (polar to cartesian)", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLCH to OKLab Conversion\n// Converts polar (C, H) back to cartesian (a, b) coordinates\n// Reference: Björn Ottosson's OKLab specification\n//\n// Algorithm:\n// L stays the same (lightness)\n// a = C * cos(H * π/180)\n// b = C * sin(H * π/180)\n//\n// Input: Color.OKLCH with l, c, h coordinates\n// Output: Color.OKLab with l, a, b coordinates\n\n// Get input OKLCH values\nvariable l: Number = {input}.l;\nvariable c: Number = {input}.c;\nvariable h: Number = {input}.h;\n\n// Constants\nvariable pi: Number = pi();\nvariable deg_to_rad: Number = pi / 180;\n\n// Convert hue to radians\nvariable h_rad: Number = h * deg_to_rad;\n\n// Convert polar to cartesian\nvariable a: Number = c * cos(h_rad);\nvariable b: Number = c * sin(h_rad);\n\n// Create output\nvariable output: Color.OKLab;\noutput.l = l;\noutput.a = a;\noutput.b = b;\n\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/okhsl-color/0/", - "target": "$self", - "description": "Converts OKHSL to OKLab", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKHSL to OKLab Conversion\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/okhsl.js\n//\n// Converts OKHSL back to OKLab using:\n// 1. Inverse toe function to recover OKLab lightness\n// 2. Cusp finding to determine max chroma at this hue\n// 3. Saturation denormalization to get actual chroma\n//\n// Input: Color.OKHSL with h (0-360), s (0-1), l (0-1)\n// Output: Color.OKLab with l (0-1), a, b coordinates\n\nvariable h: Number = input.h;\nvariable s: Number = input.s;\nvariable l: Number = input.l;\n\n// Native constants\nvariable pi_val: Number = pi();\n\n// Toe function constants\nvariable toe_k1: Number = 0.206;\nvariable toe_k2: Number = 0.03;\nvariable toe_k3: Number = 1.17009708737864;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 1: Apply inverse toe function to get OKLab lightness\n// toe_inv(x) = (x^2 + k1*x) / (k3 * (x + k2))\n// ═══════════════════════════════════════════════════════════════════════════\nvariable lab_l: Number = l;\nif (l > 0.0001 && l < 0.9999) [\n lab_l = (l * l + toe_k1 * l) / (toe_k3 * (l + toe_k2));\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 2: Handle achromatic case\n// ═══════════════════════════════════════════════════════════════════════════\nvariable lab_a: Number = 0;\nvariable lab_b: Number = 0;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 3: Compute hue direction\n// ═══════════════════════════════════════════════════════════════════════════\nvariable h_rad: Number = h * pi_val / 180;\nvariable a_: Number = cos(h_rad);\nvariable b_: Number = sin(h_rad);\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Pre-compute LMS coefficients (used in steps 4 and 5)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable kl: Number = 0.3963377774 * a_ + 0.2158037573 * b_;\nvariable km: Number = -0.1055613458 * a_ - 0.0638541728 * b_;\nvariable ks: Number = -0.0894841775 * a_ - 1.2914855480 * b_;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 4: Find maximum saturation at this hue (same as forward conversion)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable s_max: Number = 0;\n\n// Variables for LMS intermediate calculations (reused in step 5)\nvariable l_lms: Number = 0;\nvariable m_lms: Number = 0;\nvariable s_lms: Number = 0;\n\nif (s >= 0.0001) [\n variable k0: Number = 0;\n variable k1_coef: Number = 0;\n variable k2_coef: Number = 0;\n variable k3_coef: Number = 0;\n variable k4_coef: Number = 0;\n variable wl: Number = 0;\n variable wm: Number = 0;\n variable ws: Number = 0;\n \n variable test_r: Number = -1.88170328 * a_ - 0.80936493 * b_;\n variable test_g: Number = 1.81444104 * a_ - 1.19445276 * b_;\n \n if (test_r > 1) [\n k0 = 1.19086277;\n k1_coef = 1.76576728;\n k2_coef = 0.59662641;\n k3_coef = 0.75515197;\n k4_coef = 0.56771245;\n wl = 4.0767416621;\n wm = -3.3077115913;\n ws = 0.2309699292;\n ] else [\n if (test_g > 1) [\n k0 = 0.73956515;\n k1_coef = -0.45954404;\n k2_coef = 0.08285427;\n k3_coef = 0.12541073;\n k4_coef = -0.14503204;\n wl = -1.2684380046;\n wm = 2.6097574011;\n ws = -0.3413193965;\n ] else [\n k0 = 1.35733652;\n k1_coef = -0.00915799;\n k2_coef = -1.15130210;\n k3_coef = -0.50559606;\n k4_coef = 0.00692167;\n wl = -0.0041960863;\n wm = -0.7034186147;\n ws = 1.7076147010;\n ];\n ];\n \n s_max = k0 + k1_coef * a_ + k2_coef * b_ + k3_coef * a_ * a_ + k4_coef * a_ * b_;\n \n // Halley's method refinement\n variable l_temp: Number = 1 + s_max * kl;\n variable m_temp: Number = 1 + s_max * km;\n variable s_temp: Number = 1 + s_max * ks;\n \n variable l_cubed: Number = l_temp * l_temp * l_temp;\n variable m_cubed: Number = m_temp * m_temp * m_temp;\n variable s_cubed: Number = s_temp * s_temp * s_temp;\n \n variable l_ds: Number = 3 * kl * l_temp * l_temp;\n variable m_ds: Number = 3 * km * m_temp * m_temp;\n variable s_ds: Number = 3 * ks * s_temp * s_temp;\n \n variable l_ds2: Number = 6 * kl * kl * l_temp;\n variable m_ds2: Number = 6 * km * km * m_temp;\n variable s_ds2: Number = 6 * ks * ks * s_temp;\n \n variable f: Number = wl * l_cubed + wm * m_cubed + ws * s_cubed;\n variable f1: Number = wl * l_ds + wm * m_ds + ws * s_ds;\n variable f2: Number = wl * l_ds2 + wm * m_ds2 + ws * s_ds2;\n \n variable denom: Number = f1 * f1 - 0.5 * f * f2;\n if (abs(denom) > 0.000001) [\n s_max = s_max - f * f1 / denom;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 5: Find cusp\n// ═══════════════════════════════════════════════════════════════════════════\nvariable l_cusp: Number = 1;\nvariable c_cusp: Number = 0;\n\nif (s >= 0.0001 && s_max > 0) [\n // Recompute LMS values with refined s_max (kl, km, ks already computed)\n variable l_cusp_temp: Number = 1 + s_max * kl;\n variable m_cusp_temp: Number = 1 + s_max * km;\n variable s_cusp_temp: Number = 1 + s_max * ks;\n \n l_lms = l_cusp_temp * l_cusp_temp * l_cusp_temp;\n m_lms = m_cusp_temp * m_cusp_temp * m_cusp_temp;\n s_lms = s_cusp_temp * s_cusp_temp * s_cusp_temp;\n \n variable r_lin: Number = 4.0767416621 * l_lms - 3.3077115913 * m_lms + 0.2309699292 * s_lms;\n variable g_lin: Number = -1.2684380046 * l_lms + 2.6097574011 * m_lms - 0.3413193965 * s_lms;\n variable b_lin: Number = -0.0041960863 * l_lms - 0.7034186147 * m_lms + 1.7076147010 * s_lms;\n \n variable max_rgb: Number = r_lin;\n if (g_lin > max_rgb) [ max_rgb = g_lin; ];\n if (b_lin > max_rgb) [ max_rgb = b_lin; ];\n \n if (max_rgb > 0) [\n l_cusp = pow(1 / max_rgb, 0.3333333333333333);\n c_cusp = l_cusp * s_max;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 6: Compute chroma from saturation\n// ═══════════════════════════════════════════════════════════════════════════\nvariable c: Number = 0;\n\nif (s >= 0.0001 && l_cusp > 0.0001 && c_cusp > 0.0001) [\n variable s_t_cusp: Number = c_cusp / l_cusp;\n \n // Denormalize saturation to get chroma\n c = s * lab_l * s_t_cusp;\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 7: Convert polar to cartesian\n// ═══════════════════════════════════════════════════════════════════════════\nif (s >= 0.0001) [\n lab_a = c * a_;\n lab_b = c * b_;\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Output\n// ═══════════════════════════════════════════════════════════════════════════\nvariable output: Color.OKLab;\noutput.l = lab_l;\noutput.a = lab_a;\noutput.b = lab_b;\noutput" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/okhsv-color/0/", - "target": "$self", - "description": "Converts OKHSV to OKLab", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKHSV to OKLab Conversion\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/okhsv.js\n//\n// Converts OKHSV back to OKLab using:\n// 1. Cusp finding to determine gamut boundary at this hue\n// 2. V and S denormalization to get L and C\n//\n// Input: Color.OKHSV with h (0-360), s (0-1), v (0-1)\n// Output: Color.OKLab with l (0-1), a, b coordinates\n\nvariable h: Number = input.h;\nvariable s: Number = input.s;\nvariable v: Number = input.v;\n\n// Native constants\nvariable pi_val: Number = pi();\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 1: Handle edge cases\n// ═══════════════════════════════════════════════════════════════════════════\nvariable lab_l: Number = v;\nvariable lab_a: Number = 0;\nvariable lab_b: Number = 0;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 2: Compute hue direction\n// ═══════════════════════════════════════════════════════════════════════════\nvariable h_rad: Number = h * pi_val / 180;\nvariable a_: Number = cos(h_rad);\nvariable b_: Number = sin(h_rad);\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Pre-compute LMS coefficients (used in steps 3 and 4)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable kl: Number = 0.3963377774 * a_ + 0.2158037573 * b_;\nvariable km: Number = -0.1055613458 * a_ - 0.0638541728 * b_;\nvariable ks: Number = -0.0894841775 * a_ - 1.2914855480 * b_;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 3: Find maximum saturation at this hue\n// ═══════════════════════════════════════════════════════════════════════════\nvariable s_max: Number = 0;\n\n// Variables for LMS intermediate calculations (reused in step 4)\nvariable l_lms: Number = 0;\nvariable m_lms: Number = 0;\nvariable s_lms: Number = 0;\n\nif (s >= 0.0001 && v >= 0.0001) [\n variable k0: Number = 0;\n variable k1_coef: Number = 0;\n variable k2_coef: Number = 0;\n variable k3_coef: Number = 0;\n variable k4_coef: Number = 0;\n variable wl: Number = 0;\n variable wm: Number = 0;\n variable ws: Number = 0;\n \n variable test_r: Number = -1.88170328 * a_ - 0.80936493 * b_;\n variable test_g: Number = 1.81444104 * a_ - 1.19445276 * b_;\n \n if (test_r > 1) [\n k0 = 1.19086277;\n k1_coef = 1.76576728;\n k2_coef = 0.59662641;\n k3_coef = 0.75515197;\n k4_coef = 0.56771245;\n wl = 4.0767416621;\n wm = -3.3077115913;\n ws = 0.2309699292;\n ] else [\n if (test_g > 1) [\n k0 = 0.73956515;\n k1_coef = -0.45954404;\n k2_coef = 0.08285427;\n k3_coef = 0.12541073;\n k4_coef = -0.14503204;\n wl = -1.2684380046;\n wm = 2.6097574011;\n ws = -0.3413193965;\n ] else [\n k0 = 1.35733652;\n k1_coef = -0.00915799;\n k2_coef = -1.15130210;\n k3_coef = -0.50559606;\n k4_coef = 0.00692167;\n wl = -0.0041960863;\n wm = -0.7034186147;\n ws = 1.7076147010;\n ];\n ];\n \n s_max = k0 + k1_coef * a_ + k2_coef * b_ + k3_coef * a_ * a_ + k4_coef * a_ * b_;\n \n // Halley's method refinement\n variable l_temp: Number = 1 + s_max * kl;\n variable m_temp: Number = 1 + s_max * km;\n variable s_temp: Number = 1 + s_max * ks;\n \n variable l_cubed: Number = l_temp * l_temp * l_temp;\n variable m_cubed: Number = m_temp * m_temp * m_temp;\n variable s_cubed: Number = s_temp * s_temp * s_temp;\n \n variable l_ds: Number = 3 * kl * l_temp * l_temp;\n variable m_ds: Number = 3 * km * m_temp * m_temp;\n variable s_ds: Number = 3 * ks * s_temp * s_temp;\n \n variable l_ds2: Number = 6 * kl * kl * l_temp;\n variable m_ds2: Number = 6 * km * km * m_temp;\n variable s_ds2: Number = 6 * ks * ks * s_temp;\n \n variable f: Number = wl * l_cubed + wm * m_cubed + ws * s_cubed;\n variable f1: Number = wl * l_ds + wm * m_ds + ws * s_ds;\n variable f2: Number = wl * l_ds2 + wm * m_ds2 + ws * s_ds2;\n \n variable denom: Number = f1 * f1 - 0.5 * f * f2;\n if (abs(denom) > 0.000001) [\n s_max = s_max - f * f1 / denom;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 4: Find cusp\n// ═══════════════════════════════════════════════════════════════════════════\nvariable l_cusp: Number = 1;\nvariable c_cusp: Number = 0;\n\nif (s >= 0.0001 && v >= 0.0001 && s_max > 0) [\n // Recompute LMS values with refined s_max (kl, km, ks already computed)\n variable l_cusp_temp: Number = 1 + s_max * kl;\n variable m_cusp_temp: Number = 1 + s_max * km;\n variable s_cusp_temp: Number = 1 + s_max * ks;\n \n l_lms = l_cusp_temp * l_cusp_temp * l_cusp_temp;\n m_lms = m_cusp_temp * m_cusp_temp * m_cusp_temp;\n s_lms = s_cusp_temp * s_cusp_temp * s_cusp_temp;\n \n variable r_lin: Number = 4.0767416621 * l_lms - 3.3077115913 * m_lms + 0.2309699292 * s_lms;\n variable g_lin: Number = -1.2684380046 * l_lms + 2.6097574011 * m_lms - 0.3413193965 * s_lms;\n variable b_lin: Number = -0.0041960863 * l_lms - 0.7034186147 * m_lms + 1.7076147010 * s_lms;\n \n variable max_rgb: Number = r_lin;\n if (g_lin > max_rgb) [ max_rgb = g_lin; ];\n if (b_lin > max_rgb) [ max_rgb = b_lin; ];\n \n if (max_rgb > 0) [\n l_cusp = pow(1 / max_rgb, 0.3333333333333333);\n c_cusp = l_cusp * s_max;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 5: Compute L and C from V and S\n// ═══════════════════════════════════════════════════════════════════════════\nvariable c: Number = 0;\n\nif (s >= 0.0001 && v >= 0.0001 && l_cusp > 0.0001 && c_cusp > 0.0001) [\n variable s_t_cusp: Number = c_cusp / l_cusp;\n variable t_t_cusp: Number = c_cusp / (1 - l_cusp + 0.0001);\n \n // Compute max chroma at this V\n variable c_max_at_v: Number = 0;\n \n if (v <= l_cusp) [\n c_max_at_v = v * s_t_cusp;\n ] else [\n c_max_at_v = (1 - v) * t_t_cusp;\n ];\n \n // Compute actual chroma\n c = s * c_max_at_v;\n \n // Compute L from V and C\n // Reverse of: V = L + C / s_t_cusp\n lab_l = v - c / (s_t_cusp + 0.0001);\n if (lab_l < 0) [ lab_l = 0; ];\n if (lab_l > 1) [ lab_l = 1; ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 6: Convert polar to cartesian\n// ═══════════════════════════════════════════════════════════════════════════\nif (s >= 0.0001 && v >= 0.0001) [\n lab_a = c * a_;\n lab_b = c * b_;\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Output\n// ═══════════════════════════════════════════════════════════════════════════\nvariable output: Color.OKLab;\noutput.l = lab_l;\noutput.a = lab_a;\noutput.b = lab_b;\noutput" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/", - schema: { - "name": "OKLCH", - "type": "color", - "description": "OKLCH color space - the polar form of OKLab. L is lightness (0-1), C is chroma, H is hue angle (0-360).", - "schema": { - "type": "object", - "properties": { - "l": { - "type": "number", - "description": "Lightness (0-1)" - }, - "c": { - "type": "number", - "description": "Chroma (0 to ~0.4 for sRGB gamut)" - }, - "h": { - "type": "number", - "description": "Hue angle (0-360 degrees)" - } - }, - "required": [ - "l", - "c", - "h" - ], - "order": [ - "l", - "c", - "h" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "OKLCH Color Initializer", - "keyword": "oklch", - "description": "Creates an OKLCH color from L, C, H values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLCH Color Initializer\n// Creates an OKLCH color from L, C, H values\n// Input: List of [l, c, h] or [l, c, h, alpha] values\n\nvariable lch_values: List = {input};\nvariable output: Color.OKLCH;\n\noutput.l = lch_values.get(0);\noutput.c = lch_values.get(1);\noutput.h = lch_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (lch_values.length() > 3) [\n output.alpha = lch_values.get(3);\n];\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/", - "target": "$self", - "description": "Converts OKLab to OKLCH using Cartesian to polar transformation", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLab to OKLCH Conversion\n// Converts Cartesian (a, b) to polar (C, H) coordinates\n// Reference: Björn Ottosson's OKLab specification\n//\n// Algorithm:\n// L stays the same (lightness)\n// C = sqrt(a² + b²) (chroma - distance from neutral axis)\n// H = atan2(b, a) * 180/π (hue angle in degrees)\n//\n// Input: Color.OKLab with l, a, b coordinates\n// Output: Color.OKLCH with l, c, h coordinates\n\n// Get input OKLab values\nvariable l: Number = {input}.l;\nvariable a: Number = {input}.a;\nvariable b: Number = {input}.b;\n\n// Constants\nvariable pi: Number = pi();\nvariable rad_to_deg: Number = 180 / pi;\n\n// Calculate chroma (distance from neutral axis)\nvariable c: Number = sqrt(a * a + b * b);\n\n// Calculate hue angle using atan2\n// atan2(y, x) returns angle in radians from -π to π\nvariable h_rad: Number = atan2(b, a);\nvariable h: Number = h_rad * rad_to_deg;\n\n// Normalize hue to 0-360 range\nif (h < 0) [\n h = h + 360;\n];\n\n// Create output\nvariable output: Color.OKLCH;\noutput.l = l;\noutput.c = c;\noutput.h = h;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/okhsl-color/0/", - schema: { - "name": "OKHSL", - "type": "color", - "description": "OKHSL color space by Björn Ottosson - a perceptually uniform HSL based on OKLab. H is hue (0-360), S is saturation (0-1, normalized to sRGB gamut), L is lightness (0-1). Reference: https://bottosson.github.io/posts/colorpicker/", - "schema": { - "type": "object", - "properties": { - "h": { - "type": "number", - "description": "Hue angle (0-360 degrees), same as OKLCH hue" - }, - "s": { - "type": "number", - "description": "Saturation (0-1), normalized relative to sRGB gamut boundary" - }, - "l": { - "type": "number", - "description": "Lightness (0-1), perceptually uniform with toe function applied" - } - }, - "required": [ - "h", - "s", - "l" - ], - "order": [ - "h", - "s", - "l" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "OKHSL Color Initializer", - "keyword": "okhsl", - "description": "Creates an OKHSL color from H, S, L values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKHSL Color Initializer\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n//\n// Creates an OKHSL color from hue, saturation, and lightness values.\n// OKHSL is a perceptually uniform version of HSL built on OKLab.\n//\n// Parameters:\n// - h: Hue angle (0-360 degrees), same as OKLCH hue\n// - s: Saturation (0-1), normalized to sRGB gamut boundary\n// - l: Lightness (0-1), perceptually uniform with toe function\n// - alpha: Optional alpha channel (0-1)\n//\n// Input: Object with h, s, l, and optional alpha properties\n// Output: Color.OKHSL\n\nvariable h: Number = input.h;\nvariable s: Number = input.s;\nvariable l: Number = input.l;\n\nvariable output: Color.OKHSL;\noutput.h = h;\noutput.s = s;\noutput.l = l;\n\n// Set alpha if provided\nif (input.alpha != null) [\n output.alpha = input.alpha;\n];\n\noutput" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/", - "target": "$self", - "description": "Converts OKLab to OKHSL using Ottosson's algorithm with Halley's method refinement for gamut boundary", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLab to OKHSL Conversion\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/okhsl.js\n// Reference: https://bottosson.github.io/posts/oklab/ (original OKLab paper)\n//\n// OKHSL provides a perceptually uniform HSL with proper saturation scaling.\n// The algorithm uses three key chroma values (C_0, C_mid, C_max) and\n// performs piecewise interpolation to map chroma to saturation.\n//\n// This implementation includes the FULL findGamutIntersection algorithm\n// with Halley's method refinement for accurate gamut boundary detection.\n//\n// Input: Color.OKLab with l (0-1), a, b coordinates\n// Output: Color.OKHSL with h (0-360), s (0-1), l (0-1)\n\nvariable lab_l: Number = input.l;\nvariable lab_a: Number = input.a;\nvariable lab_b: Number = input.b;\n\n// Native constants\nvariable pi_val: Number = pi();\nvariable float_max: Number = 999999999;\n\n// Toe function constants\n// K3 = (1 + K1) / (1 + K2)\nvariable toe_k1: Number = 0.206;\nvariable toe_k2: Number = 0.03;\nvariable toe_k3: Number = 1.17009708737864;\n\n// LMS to OKLab matrix coefficients (LabtoLMS_M columns 1,2)\nvariable lab_lms_kl_a: Number = 0.3963377774;\nvariable lab_lms_kl_b: Number = 0.2158037573;\nvariable lab_lms_km_a: Number = -0.1055613458;\nvariable lab_lms_km_b: Number = -0.0638541728;\nvariable lab_lms_ks_a: Number = -0.0894841775;\nvariable lab_lms_ks_b: Number = -1.2914855480;\n\n// LMS to sRGB-linear matrix\nvariable lms_r0: Number = 4.0767416360759583;\nvariable lms_r1: Number = -3.3077115392580629;\nvariable lms_r2: Number = 0.2309699031821043;\nvariable lms_g0: Number = -1.2684379732850315;\nvariable lms_g1: Number = 2.6097573492876882;\nvariable lms_g2: Number = -0.3413193760026570;\nvariable lms_b0: Number = -0.0041960761386756;\nvariable lms_b1: Number = -0.7034186179359362;\nvariable lms_b2: Number = 1.7076146940746117;\n\n// RGB limit coefficients for determining which channel clips first\nvariable red_limit_a: Number = -1.8817031;\nvariable red_limit_b: Number = -0.80936501;\nvariable green_limit_a: Number = 1.8144408;\nvariable green_limit_b: Number = -1.19445267;\n\n// Red Kn coefficients\nvariable red_k0: Number = 1.19086277;\nvariable red_k1: Number = 1.76576728;\nvariable red_k2: Number = 0.59662641;\nvariable red_k3: Number = 0.75515197;\nvariable red_k4: Number = 0.56771245;\n\n// Green Kn coefficients\nvariable green_k0: Number = 0.73956515;\nvariable green_k1: Number = -0.45954404;\nvariable green_k2: Number = 0.08285427;\nvariable green_k3: Number = 0.12541073;\nvariable green_k4: Number = -0.14503204;\n\n// Blue Kn coefficients\nvariable blue_k0: Number = 1.35733652;\nvariable blue_k1: Number = -0.00915799;\nvariable blue_k2: Number = -1.1513021;\nvariable blue_k3: Number = -0.50559606;\nvariable blue_k4: Number = 0.00692167;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 1: Convert to polar coordinates and apply toe function\n// ═══════════════════════════════════════════════════════════════════════════\nvariable c: Number = sqrt(lab_a * lab_a + lab_b * lab_b);\n\n// Apply toe function: toe(x) = 0.5 * (k3*x - k1 + sqrt((k3*x - k1)^2 + 4*k2*k3*x))\nvariable l: Number = lab_l;\nif (lab_l > 0.0001 && lab_l < 0.9999) [\n variable term: Number = toe_k3 * lab_l - toe_k1;\n l = 0.5 * (term + sqrt(term * term + 4 * toe_k2 * toe_k3 * lab_l));\n];\n\n// Compute hue using atan2(-b, -a) + 0.5 (ColorJS convention)\nvariable h: Number = 0;\nif (c > 0.00001) [\n variable h_normalized: Number = 0.5 + atan2(-lab_b, -lab_a) / (2 * pi_val);\n h = h_normalized * 360;\n if (h < 0) [ h = h + 360; ];\n if (h >= 360) [ h = h - 360; ];\n];\n\n// Initialize saturation\nvariable s: Number = 0;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 2: Check if chromatic (non-achromatic, non-edge)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable is_chromatic: Number = 1;\nif (c < 0.00001) [ is_chromatic = 0; ];\nif (l < 0.0001) [ is_chromatic = 0; ];\nif (l > 0.9999) [ is_chromatic = 0; ];\n\nif (is_chromatic > 0.5) [\n // ═══════════════════════════════════════════════════════════════════════\n // Step 3: Normalize hue direction (a_, b_)\n // ═══════════════════════════════════════════════════════════════════════\n variable a_: Number = lab_a / c;\n variable b_: Number = lab_b / c;\n \n // Pre-compute LMS coefficients\n variable kl: Number = lab_lms_kl_a * a_ + lab_lms_kl_b * b_;\n variable km: Number = lab_lms_km_a * a_ + lab_lms_km_b * b_;\n variable ks: Number = lab_lms_ks_a * a_ + lab_lms_ks_b * b_;\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 4: computeMaxSaturation - Find S_max for this hue\n // ═══════════════════════════════════════════════════════════════════════\n variable test_red: Number = red_limit_a * a_ + red_limit_b * b_;\n variable test_green: Number = green_limit_a * a_ + green_limit_b * b_;\n \n variable k0: Number = blue_k0;\n variable k1_coef: Number = blue_k1;\n variable k2_coef: Number = blue_k2;\n variable k3_coef: Number = blue_k3;\n variable k4_coef: Number = blue_k4;\n variable wl: Number = lms_b0;\n variable wm: Number = lms_b1;\n variable ws: Number = lms_b2;\n \n if (test_red > 1) [\n k0 = red_k0; k1_coef = red_k1; k2_coef = red_k2; k3_coef = red_k3; k4_coef = red_k4;\n wl = lms_r0; wm = lms_r1; ws = lms_r2;\n ] else [\n if (test_green > 1) [\n k0 = green_k0; k1_coef = green_k1; k2_coef = green_k2; k3_coef = green_k3; k4_coef = green_k4;\n wl = lms_g0; wm = lms_g1; ws = lms_g2;\n ];\n ];\n \n // Polynomial approximation\n variable s_max: Number = k0 + k1_coef * a_ + k2_coef * b_ + k3_coef * a_ * a_ + k4_coef * a_ * b_;\n \n // Halley's method refinement for s_max\n variable l_temp: Number = 1 + s_max * kl;\n variable m_temp: Number = 1 + s_max * km;\n variable s_temp: Number = 1 + s_max * ks;\n \n variable l_cubed: Number = l_temp * l_temp * l_temp;\n variable m_cubed: Number = m_temp * m_temp * m_temp;\n variable s_cubed: Number = s_temp * s_temp * s_temp;\n \n variable l_ds: Number = 3 * kl * l_temp * l_temp;\n variable m_ds: Number = 3 * km * m_temp * m_temp;\n variable s_ds: Number = 3 * ks * s_temp * s_temp;\n \n variable l_ds2: Number = 6 * kl * kl * l_temp;\n variable m_ds2: Number = 6 * km * km * m_temp;\n variable s_ds2: Number = 6 * ks * ks * s_temp;\n \n variable f: Number = wl * l_cubed + wm * m_cubed + ws * s_cubed;\n variable f1: Number = wl * l_ds + wm * m_ds + ws * s_ds;\n variable f2: Number = wl * l_ds2 + wm * m_ds2 + ws * s_ds2;\n \n variable halley_denom: Number = f1 * f1 - 0.5 * f * f2;\n if (abs(halley_denom) > 0.000001) [\n s_max = s_max - f * f1 / halley_denom;\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 5: findCusp - Get L_cusp and C_cusp\n // ═══════════════════════════════════════════════════════════════════════\n variable l_cusp: Number = 1;\n variable c_cusp: Number = 0;\n \n if (s_max > 0) [\n variable cusp_l_: Number = 1 + s_max * kl;\n variable cusp_m_: Number = 1 + s_max * km;\n variable cusp_s_: Number = 1 + s_max * ks;\n \n variable cusp_l: Number = cusp_l_ * cusp_l_ * cusp_l_;\n variable cusp_m: Number = cusp_m_ * cusp_m_ * cusp_m_;\n variable cusp_s: Number = cusp_s_ * cusp_s_ * cusp_s_;\n \n variable cusp_r: Number = lms_r0 * cusp_l + lms_r1 * cusp_m + lms_r2 * cusp_s;\n variable cusp_g: Number = lms_g0 * cusp_l + lms_g1 * cusp_m + lms_g2 * cusp_s;\n variable cusp_b: Number = lms_b0 * cusp_l + lms_b1 * cusp_m + lms_b2 * cusp_s;\n \n variable max_rgb: Number = cusp_r;\n if (cusp_g > max_rgb) [ max_rgb = cusp_g; ];\n if (cusp_b > max_rgb) [ max_rgb = cusp_b; ];\n if (max_rgb < 0.0001) [ max_rgb = 0.0001; ];\n \n l_cusp = pow(1 / max_rgb, 0.3333333333333333);\n c_cusp = l_cusp * s_max;\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 6: findGamutIntersection - Find C_max at current L\n // This is the FULL algorithm with Halley's method for upper half\n //\n // Finds intersection of line: L = L0*(1-t) + t*L1, C = t*C1\n // where L0=L1=lab_l and C1=1 (seeking max chroma at this L)\n // ═══════════════════════════════════════════════════════════════════════\n variable c_max: Number = 0;\n \n if (l_cusp > 0.0001 && c_cusp > 0.0001) [\n // Parameters for gamut intersection: l1=lab_l, c1=1, l0=lab_l\n variable l1: Number = lab_l;\n variable c1: Number = 1;\n variable l0: Number = lab_l;\n \n // Check which half: (l1 - l0) * c_cusp - (l_cusp - l0) * c1\n // Since l1 = l0 = lab_l, this simplifies to: -(l_cusp - lab_l) * 1 = lab_l - l_cusp\n variable half_test: Number = lab_l - l_cusp;\n variable t_intersect: Number = 0;\n \n if (half_test <= 0) [\n // Lower half (below cusp) - simple triangle intersection\n // t = (c_cusp * l0) / (c1 * l_cusp + c_cusp * (l0 - l1))\n // Since l0 = l1, denominator = c1 * l_cusp = l_cusp\n variable lower_denom: Number = l_cusp;\n if (abs(lower_denom) > 0.00001) [\n t_intersect = (c_cusp * lab_l) / lower_denom;\n ];\n ] else [\n // Upper half (above cusp) - triangle + Halley's method\n // First: triangle intersection\n // t = (c_cusp * (l0 - 1)) / (c1 * (l_cusp - 1) + c_cusp * (l0 - l1))\n // Since l0 = l1 = lab_l: t = (c_cusp * (lab_l - 1)) / (l_cusp - 1)\n variable upper_denom: Number = l_cusp - 1;\n if (abs(upper_denom) > 0.00001) [\n t_intersect = c_cusp * (lab_l - 1) / upper_denom;\n ];\n \n // Halley's method refinement for upper half\n // dl = l1 - l0 = 0, dc = c1 = 1\n variable dl: Number = 0;\n variable dc: Number = 1;\n \n variable ldt_: Number = dl + dc * kl;\n variable mdt_: Number = dl + dc * km;\n variable sdt_: Number = dl + dc * ks;\n \n // Compute L and C at current t\n variable L_at_t: Number = l0 * (1 - t_intersect) + t_intersect * l1;\n variable C_at_t: Number = t_intersect * c1;\n \n // LMS values at (L, C)\n variable l_at: Number = L_at_t + C_at_t * kl;\n variable m_at: Number = L_at_t + C_at_t * km;\n variable s_at: Number = L_at_t + C_at_t * ks;\n \n variable l_lms: Number = l_at * l_at * l_at;\n variable m_lms: Number = m_at * m_at * m_at;\n variable s_lms: Number = s_at * s_at * s_at;\n \n // First derivatives\n variable ldt: Number = 3 * ldt_ * l_at * l_at;\n variable mdt: Number = 3 * mdt_ * m_at * m_at;\n variable sdt: Number = 3 * sdt_ * s_at * s_at;\n \n // Second derivatives\n variable ldt2: Number = 6 * ldt_ * ldt_ * l_at;\n variable mdt2: Number = 6 * mdt_ * mdt_ * m_at;\n variable sdt2: Number = 6 * sdt_ * sdt_ * s_at;\n \n // Red channel Halley step\n variable r_val: Number = lms_r0 * l_lms + lms_r1 * m_lms + lms_r2 * s_lms - 1;\n variable r1: Number = lms_r0 * ldt + lms_r1 * mdt + lms_r2 * sdt;\n variable r2: Number = lms_r0 * ldt2 + lms_r1 * mdt2 + lms_r2 * sdt2;\n variable r_denom: Number = r1 * r1 - 0.5 * r_val * r2;\n variable ur: Number = 0;\n variable tr: Number = float_max;\n if (abs(r_denom) > 0.000001) [\n ur = r1 / r_denom;\n if (ur >= 0) [ tr = -r_val * ur; ];\n ];\n \n // Green channel Halley step\n variable g_val: Number = lms_g0 * l_lms + lms_g1 * m_lms + lms_g2 * s_lms - 1;\n variable g1: Number = lms_g0 * ldt + lms_g1 * mdt + lms_g2 * sdt;\n variable g2: Number = lms_g0 * ldt2 + lms_g1 * mdt2 + lms_g2 * sdt2;\n variable g_denom: Number = g1 * g1 - 0.5 * g_val * g2;\n variable ug: Number = 0;\n variable tg: Number = float_max;\n if (abs(g_denom) > 0.000001) [\n ug = g1 / g_denom;\n if (ug >= 0) [ tg = -g_val * ug; ];\n ];\n \n // Blue channel Halley step\n variable b_val: Number = lms_b0 * l_lms + lms_b1 * m_lms + lms_b2 * s_lms - 1;\n variable b1: Number = lms_b0 * ldt + lms_b1 * mdt + lms_b2 * sdt;\n variable b2: Number = lms_b0 * ldt2 + lms_b1 * mdt2 + lms_b2 * sdt2;\n variable b_denom: Number = b1 * b1 - 0.5 * b_val * b2;\n variable ub: Number = 0;\n variable tb: Number = float_max;\n if (abs(b_denom) > 0.000001) [\n ub = b1 / b_denom;\n if (ub >= 0) [ tb = -b_val * ub; ];\n ];\n \n // Take minimum of the three corrections\n variable t_correction: Number = tr;\n if (tg < t_correction) [ t_correction = tg; ];\n if (tb < t_correction) [ t_correction = tb; ];\n if (t_correction < float_max) [\n t_intersect = t_intersect + t_correction;\n ];\n ];\n \n // C_max = t * c1 = t (since c1 = 1)\n c_max = t_intersect;\n if (c_max < 0) [ c_max = 0; ];\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 7: getStMid - Polynomial approximation for mid-saturation ST\n // ═══════════════════════════════════════════════════════════════════════\n variable st_mid_s: Number = 0.11516993 + 1 / (\n 7.44778970 + 4.15901240 * b_ +\n a_ * (-2.19557347 + 1.75198401 * b_ +\n a_ * (-2.13704948 - 10.02301043 * b_ +\n a_ * (-4.24894561 + 5.38770819 * b_ + 4.69891013 * a_))));\n \n variable st_mid_t: Number = 0.11239642 + 1 / (\n 1.61320320 - 0.68124379 * b_ +\n a_ * (0.40370612 + 0.90148123 * b_ +\n a_ * (-0.27087943 + 0.61223990 * b_ +\n a_ * (0.00299215 - 0.45399568 * b_ - 0.14661872 * a_))));\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 8: getCs - Compute C_0, C_mid, C_max with scale factor k\n // ═══════════════════════════════════════════════════════════════════════\n variable st_max_s: Number = c_cusp / (l_cusp + 0.0001);\n variable st_max_t: Number = c_cusp / (1 - l_cusp + 0.0001);\n \n // Scale factor k = c_max / min(L * st_max_s, (1-L) * st_max_t)\n variable min_st: Number = lab_l * st_max_s;\n variable min_st_t: Number = (1 - lab_l) * st_max_t;\n if (min_st_t < min_st) [ min_st = min_st_t; ];\n \n variable k_factor: Number = 1;\n if (min_st > 0.0001 && c_max > 0.0001) [\n k_factor = c_max / min_st;\n ];\n \n // C_mid = 0.9 * k * sqrt(sqrt(1 / (1/ca^4 + 1/cb^4)))\n variable ca: Number = lab_l * st_mid_s;\n variable cb: Number = (1 - lab_l) * st_mid_t;\n variable ca4: Number = ca * ca * ca * ca;\n variable cb4: Number = cb * cb * cb * cb;\n variable c_mid: Number = 0;\n if (ca4 > 0.0000001 && cb4 > 0.0000001) [\n c_mid = 0.9 * k_factor * sqrt(sqrt(1 / (1 / ca4 + 1 / cb4)));\n ];\n \n // C_0 using average ST values (0.4, 0.8)\n variable ca0: Number = lab_l * 0.4;\n variable cb0: Number = (1 - lab_l) * 0.8;\n variable ca0_sq: Number = ca0 * ca0;\n variable cb0_sq: Number = cb0 * cb0;\n variable c_0: Number = 0;\n if (ca0_sq > 0.0000001 && cb0_sq > 0.0000001) [\n c_0 = sqrt(1 / (1 / ca0_sq + 1 / cb0_sq));\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Step 9: Compute saturation using piecewise interpolation\n // ═══════════════════════════════════════════════════════════════════════\n variable mid: Number = 0.8;\n \n if (c < c_mid && c_mid > 0.0001 && c_0 > 0.0001) [\n // Below mid-point: s = t * 0.8 where t = c / (k1 + k2*c)\n variable k1_low: Number = mid * c_0;\n variable k2_low: Number = 1 - k1_low / c_mid;\n variable t_low: Number = c / (k1_low + k2_low * c + 0.0001);\n s = t_low * mid;\n ] else [\n if (c_mid > 0.0001 && c_0 > 0.0001 && c_max > c_mid) [\n // Above mid-point: s = 0.8 + 0.2 * t where t = (c - c_mid) / (k1 + k2*(c - c_mid))\n variable mid_inv: Number = 1.25;\n variable k0_high: Number = c_mid;\n variable k1_high: Number = 0.2 * c_mid * c_mid * mid_inv * mid_inv / c_0;\n variable k2_high: Number = 1 - k1_high / (c_max - c_mid + 0.0001);\n variable c_diff: Number = c - k0_high;\n variable t_high: Number = c_diff / (k1_high + k2_high * c_diff + 0.0001);\n s = mid + 0.2 * t_high;\n ];\n ];\n \n // Clamp saturation\n if (s > 1) [ s = 1; ];\n if (s < 0) [ s = 0; ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Output\n// ═══════════════════════════════════════════════════════════════════════════\nvariable output: Color.OKHSL;\noutput.h = h;\noutput.s = s;\noutput.l = l;\noutput" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/okhsv-color/0/", - schema: { - "name": "OKHSV", - "type": "color", - "description": "OKHSV color space by Björn Ottosson - a perceptually uniform HSV based on OKLab. H is hue (0-360), S is saturation (0-1), V is value (0-1). Reference: https://bottosson.github.io/posts/colorpicker/", - "schema": { - "type": "object", - "properties": { - "h": { - "type": "number", - "description": "Hue angle (0-360 degrees), same as OKLCH hue" - }, - "s": { - "type": "number", - "description": "Saturation (0-1), ratio of chroma to maximum at this value" - }, - "v": { - "type": "number", - "description": "Value/brightness (0-1), with V=1 being the brightest for that saturation" - } - }, - "required": [ - "h", - "s", - "v" - ], - "order": [ - "h", - "s", - "v" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "OKHSV Color Initializer", - "keyword": "okhsv", - "description": "Creates an OKHSV color from H, S, V values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKHSV Color Initializer\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n//\n// Creates an OKHSV color from hue, saturation, and value.\n// OKHSV is a perceptually uniform version of HSV built on OKLab.\n//\n// Parameters:\n// - h: Hue angle (0-360 degrees), same as OKLCH hue\n// - s: Saturation (0-1), ratio of chroma to maximum at this value\n// - v: Value/brightness (0-1), with V=1 being brightest for that saturation\n// - alpha: Optional alpha channel (0-1)\n//\n// Input: Object with h, s, v, and optional alpha properties\n// Output: Color.OKHSV\n\nvariable h: Number = input.h;\nvariable s: Number = input.s;\nvariable v: Number = input.v;\n\nvariable output: Color.OKHSV;\noutput.h = h;\noutput.s = s;\noutput.v = v;\n\n// Set alpha if provided\nif (input.alpha != null) [\n output.alpha = input.alpha;\n];\n\noutput" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/", - "target": "$self", - "description": "Converts OKLab to OKHSV using Ottosson's algorithm with Halley's method refinement for gamut boundary", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLab to OKHSV Conversion\n// Reference: Björn Ottosson - \"A perceptual color picker: OKHSL and OKHSV\"\n// URL: https://bottosson.github.io/posts/colorpicker/\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/okhsv.js\n// Reference: https://bottosson.github.io/posts/oklab/ (original OKLab paper)\n//\n// OKHSV maps colors to a perceptually uniform HSV cylinder.\n// The algorithm finds the exact gamut boundary using compute_max_saturation\n// and positions colors relative to the cusp (maximum chroma point).\n//\n// Key difference from OKHSL:\n// - V=1 means maximum brightness (white at S=0, cusp color at S=1)\n// - More intuitive for picking saturated colors\n//\n// Input: Color.OKLab with l (0-1), a, b coordinates\n// Output: Color.OKHSV with h (0-360), s (0-1), v (0-1)\n\nvariable lab_l: Number = input.l;\nvariable lab_a: Number = input.a;\nvariable lab_b: Number = input.b;\n\n// Native constants\nvariable pi_val: Number = pi();\n\n// Toe function constants (same as OKHSL)\nvariable toe_k1: Number = 0.206;\nvariable toe_k2: Number = 0.03;\nvariable toe_k3: Number = 1.17009708737864;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 1: Convert to polar coordinates (L, C, H)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable c: Number = sqrt(lab_a * lab_a + lab_b * lab_b);\nvariable h: Number = 0;\n\nif (c > 0.00001) [\n // Note: ColorJS uses atan2(-b, -a) and adds 0.5, we use atan2(b, a)\n h = atan2(lab_b, lab_a) * 180 / pi_val;\n if (h < 0) [\n h = h + 360;\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 2: Initialize defaults and apply toe function to L for V\n// toe(x) = 0.5 * (k3*x - k1 + sqrt((k3*x - k1)^2 + 4*k2*k3*x))\n// ═══════════════════════════════════════════════════════════════════════════\nvariable s: Number = 0;\nvariable v: Number = lab_l;\n\n// Apply toe function to get initial V\nif (lab_l > 0.0001 && lab_l < 0.9999) [\n variable term: Number = toe_k3 * lab_l - toe_k1;\n v = 0.5 * (term + sqrt(term * term + 4 * toe_k2 * toe_k3 * lab_l));\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 3: Compute normalized hue direction\n// ═══════════════════════════════════════════════════════════════════════════\nvariable a_: Number = 0;\nvariable b_: Number = 0;\nif (c > 0.00001) [\n a_ = lab_a / c;\n b_ = lab_b / c;\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Pre-compute LMS coefficients (used in multiple steps)\n// ═══════════════════════════════════════════════════════════════════════════\nvariable kl: Number = 0.3963377774 * a_ + 0.2158037573 * b_;\nvariable km: Number = -0.1055613458 * a_ - 0.0638541728 * b_;\nvariable ks: Number = -0.0894841775 * a_ - 1.2914855480 * b_;\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 4: Find maximum saturation and cusp\n// Only process if we have chroma (non-achromatic) and L is valid\n// ═══════════════════════════════════════════════════════════════════════════\nvariable s_max: Number = 0;\nvariable l_cusp: Number = 1;\nvariable c_cusp: Number = 0;\n\n// Variables for LMS intermediate calculations\nvariable l_lms: Number = 0;\nvariable m_lms: Number = 0;\nvariable s_lms: Number = 0;\n\nif (c > 0.00001 && lab_l > 0.00001 && lab_l < 0.99999) [\n // Determine which RGB component clips first\n variable k0: Number = 0;\n variable k1_coef: Number = 0;\n variable k2_coef: Number = 0;\n variable k3_coef: Number = 0;\n variable k4_coef: Number = 0;\n variable wl: Number = 0;\n variable wm: Number = 0;\n variable ws: Number = 0;\n \n variable test_r: Number = -1.88170328 * a_ - 0.80936493 * b_;\n variable test_g: Number = 1.81444104 * a_ - 1.19445276 * b_;\n \n if (test_r > 1) [\n k0 = 1.19086277;\n k1_coef = 1.76576728;\n k2_coef = 0.59662641;\n k3_coef = 0.75515197;\n k4_coef = 0.56771245;\n wl = 4.0767416621;\n wm = -3.3077115913;\n ws = 0.2309699292;\n ] else [\n if (test_g > 1) [\n k0 = 0.73956515;\n k1_coef = -0.45954404;\n k2_coef = 0.08285427;\n k3_coef = 0.12541073;\n k4_coef = -0.14503204;\n wl = -1.2684380046;\n wm = 2.6097574011;\n ws = -0.3413193965;\n ] else [\n k0 = 1.35733652;\n k1_coef = -0.00915799;\n k2_coef = -1.15130210;\n k3_coef = -0.50559606;\n k4_coef = 0.00692167;\n wl = -0.0041960863;\n wm = -0.7034186147;\n ws = 1.7076147010;\n ];\n ];\n \n // Polynomial approximation\n s_max = k0 + k1_coef * a_ + k2_coef * b_ + k3_coef * a_ * a_ + k4_coef * a_ * b_;\n \n // Halley's method refinement\n variable l_temp: Number = 1 + s_max * kl;\n variable m_temp: Number = 1 + s_max * km;\n variable s_temp: Number = 1 + s_max * ks;\n \n variable l_cubed: Number = l_temp * l_temp * l_temp;\n variable m_cubed: Number = m_temp * m_temp * m_temp;\n variable s_cubed: Number = s_temp * s_temp * s_temp;\n \n variable l_ds: Number = 3 * kl * l_temp * l_temp;\n variable m_ds: Number = 3 * km * m_temp * m_temp;\n variable s_ds: Number = 3 * ks * s_temp * s_temp;\n \n variable l_ds2: Number = 6 * kl * kl * l_temp;\n variable m_ds2: Number = 6 * km * km * m_temp;\n variable s_ds2: Number = 6 * ks * ks * s_temp;\n \n variable f: Number = wl * l_cubed + wm * m_cubed + ws * s_cubed;\n variable f1: Number = wl * l_ds + wm * m_ds + ws * s_ds;\n variable f2: Number = wl * l_ds2 + wm * m_ds2 + ws * s_ds2;\n \n variable denom: Number = f1 * f1 - 0.5 * f * f2;\n if (abs(denom) > 0.000001) [\n s_max = s_max - f * f1 / denom;\n ];\n \n // ═══════════════════════════════════════════════════════════════════════\n // Find cusp (L_cusp, C_cusp)\n // ═══════════════════════════════════════════════════════════════════════\n if (s_max > 0) [\n variable l_cusp_temp: Number = 1 + s_max * kl;\n variable m_cusp_temp: Number = 1 + s_max * km;\n variable s_cusp_temp: Number = 1 + s_max * ks;\n \n l_lms = l_cusp_temp * l_cusp_temp * l_cusp_temp;\n m_lms = m_cusp_temp * m_cusp_temp * m_cusp_temp;\n s_lms = s_cusp_temp * s_cusp_temp * s_cusp_temp;\n \n variable r_lin: Number = 4.0767416621 * l_lms - 3.3077115913 * m_lms + 0.2309699292 * s_lms;\n variable g_lin: Number = -1.2684380046 * l_lms + 2.6097574011 * m_lms - 0.3413193965 * s_lms;\n variable b_lin: Number = -0.0041960863 * l_lms - 0.7034186147 * m_lms + 1.7076147010 * s_lms;\n \n variable max_rgb: Number = r_lin;\n if (g_lin > max_rgb) [ max_rgb = g_lin; ];\n if (b_lin > max_rgb) [ max_rgb = b_lin; ];\n \n if (max_rgb > 0) [\n l_cusp = pow(1 / max_rgb, 0.3333333333333333);\n c_cusp = l_cusp * s_max;\n ];\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Step 5: Compute OKHSV S and V using ColorJS algorithm\n//\n// The algorithm uses the ST coordinate system where:\n// - S_t = C/L (slope from origin)\n// - T_t = C/(1-L) (slope from white)\n//\n// Key formulas from ColorJS:\n// - t = tMax / (c + l * tMax)\n// - lv = t * l, cv = t * c\n// - Apply RGB scaling and toe compensation\n// - v = l / lv, s = ((s0 + tMax) * cv) / (tMax * s0 + tMax * k * cv)\n// ═══════════════════════════════════════════════════════════════════════════\nif (c > 0.00001 && lab_l > 0.00001 && lab_l < 0.99999 && l_cusp > 0.0001 && c_cusp > 0.0001) [\n // Compute ST values at cusp\n variable s_t_cusp: Number = c_cusp / l_cusp;\n variable t_t_cusp: Number = c_cusp / (1 - l_cusp + 0.0001);\n \n // Fixed s0 parameter\n variable s_0: Number = 0.5;\n variable k_param: Number = 1 - s_0 / s_t_cusp;\n \n // Compute t and derived values (following ColorJS exactly)\n variable t: Number = t_t_cusp / (c + lab_l * t_t_cusp + 0.0001);\n variable lv: Number = t * lab_l;\n variable cv: Number = t * c;\n \n // Apply inverse toe to lv for compensation\n variable lvt: Number = lv;\n if (lv > 0.0001 && lv < 0.9999) [\n lvt = (lv * lv + toe_k1 * lv) / (toe_k3 * (lv + toe_k2));\n ];\n \n variable cvt: Number = cv;\n if (lv > 0.0001) [\n cvt = cv * lvt / lv;\n ];\n \n // RGB scale computation\n variable scale_l_: Number = 1 + s_max * kl;\n variable scale_m_: Number = 1 + s_max * km;\n variable scale_s_: Number = 1 + s_max * ks;\n \n variable lms_l: Number = lvt + a_ * cvt * kl;\n variable lms_m: Number = lvt + a_ * cvt * km;\n variable lms_s: Number = lvt + a_ * cvt * ks;\n \n // Convert to linear RGB using LMS\n variable lms_l_cubed: Number = lms_l * lms_l * lms_l;\n variable lms_m_cubed: Number = lms_m * lms_m * lms_m;\n variable lms_s_cubed: Number = lms_s * lms_s * lms_s;\n \n variable rs: Number = 4.0767416621 * lms_l_cubed - 3.3077115913 * lms_m_cubed + 0.2309699292 * lms_s_cubed;\n variable gs: Number = -1.2684380046 * lms_l_cubed + 2.6097574011 * lms_m_cubed - 0.3413193965 * lms_s_cubed;\n variable bs: Number = -0.0041960863 * lms_l_cubed - 0.7034186147 * lms_m_cubed + 1.7076147010 * lms_s_cubed;\n \n variable max_s: Number = rs;\n if (gs > max_s) [ max_s = gs; ];\n if (bs > max_s) [ max_s = bs; ];\n if (max_s < 0.0001) [ max_s = 0.0001; ];\n \n variable scale_l: Number = pow(1 / max_s, 0.3333333333333333);\n \n // Scale L and C\n variable l_scaled: Number = lab_l / scale_l;\n variable c_scaled: Number = c / scale_l;\n \n // Apply toe to scaled L for compensation\n variable l_toed: Number = l_scaled;\n if (l_scaled > 0.0001 && l_scaled < 0.9999) [\n variable term2: Number = toe_k3 * l_scaled - toe_k1;\n l_toed = 0.5 * (term2 + sqrt(term2 * term2 + 4 * toe_k2 * toe_k3 * l_scaled));\n ];\n \n c_scaled = c_scaled * l_toed / (l_scaled + 0.0001);\n \n // Compute final v and s\n if (lv > 0.0001) [\n v = l_toed / lv;\n if (v > 1) [ v = 1; ];\n if (v < 0) [ v = 0; ];\n ];\n \n variable denom_s: Number = t_t_cusp * s_0 + t_t_cusp * k_param * cv;\n if (abs(denom_s) > 0.0001) [\n s = ((s_0 + t_t_cusp) * cv) / denom_s;\n if (s > 1) [ s = 1; ];\n if (s < 0) [ s = 0; ];\n ];\n];\n\n// ═══════════════════════════════════════════════════════════════════════════\n// Output\n// ═══════════════════════════════════════════════════════════════════════════\nvariable output: Color.OKHSV;\noutput.h = h;\noutput.s = s;\noutput.v = v;\noutput" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-color/0/", - schema: { - "name": "P3", - "type": "color", - "description": "Display-P3 color space with sRGB transfer function. Wider gamut than sRGB, common on modern Apple displays.", - "schema": { - "type": "object", - "properties": { - "r": { - "type": "number", - "description": "Red channel (0-1, can exceed for out-of-gamut)" - }, - "g": { - "type": "number", - "description": "Green channel (0-1, can exceed for out-of-gamut)" - }, - "b": { - "type": "number", - "description": "Blue channel (0-1, can exceed for out-of-gamut)" - } - }, - "required": [ - "r", - "g", - "b" - ], - "order": [ - "r", - "g", - "b" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "Display-P3 Color Initializer", - "keyword": "p3", - "description": "Creates a Display-P3 color from 0-1 values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Display-P3 Color Initializer\n// Creates a Display-P3 color from 0-1 values\n// Input: List of [r, g, b] or [r, g, b, alpha] values\n\nvariable color_values: List = {input};\nvariable output: Color.P3;\n\noutput.r = color_values.get(0);\noutput.g = color_values.get(1);\noutput.b = color_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (color_values.length() > 3) [\n output.alpha = color_values.get(3);\n];\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-linear-color/0/", - "target": "$self", - "description": "Converts Linear P3 to P3 by applying sRGB transfer function", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Linear P3 to P3 Conversion\n// Applies sRGB transfer function (gamma encoding)\n// P3 uses the same transfer function as sRGB\n// Reference: CSS Color Level 4\n//\n// Algorithm (same as sRGB):\n// if linear ≤ 0.0031308: encoded = 12.92 × linear\n// else: encoded = 1.055 × linear^(1/2.4) - 0.055\n//\n// Input: Color.LinearP3 with linear r, g, b values\n// Output: Color.P3 with gamma-encoded r, g, b values\n\n// Transfer function constants (same as sRGB)\nvariable threshold: Number = 0.0031308;\nvariable linear_scale: Number = 12.92;\nvariable gamma_scale: Number = 1.055;\nvariable gamma_offset: Number = 0.055;\nvariable gamma_exponent: Number = 0.4166666666666667;\n\n// Get linear values\nvariable linear_r: Number = {input}.r;\nvariable linear_g: Number = {input}.g;\nvariable linear_b: Number = {input}.b;\n\n// Convert red channel\nvariable encoded_r: Number = 0;\nif (linear_r <= threshold) [\n encoded_r = linear_scale * linear_r;\n] else [\n encoded_r = gamma_scale * pow(linear_r, gamma_exponent) - gamma_offset;\n];\n\n// Convert green channel\nvariable encoded_g: Number = 0;\nif (linear_g <= threshold) [\n encoded_g = linear_scale * linear_g;\n] else [\n encoded_g = gamma_scale * pow(linear_g, gamma_exponent) - gamma_offset;\n];\n\n// Convert blue channel\nvariable encoded_b: Number = 0;\nif (linear_b <= threshold) [\n encoded_b = linear_scale * linear_b;\n] else [\n encoded_b = gamma_scale * pow(linear_b, gamma_exponent) - gamma_offset;\n];\n\n// Create output\nvariable output: Color.P3;\noutput.r = encoded_r;\noutput.g = encoded_g;\noutput.b = encoded_b;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/css-color/0/", - schema: { - "name": "CSS", - "type": "color", - "description": "CSS color string representation. Outputs the appropriate CSS syntax for any color space.", - "schema": { - "type": "object", - "properties": { - "value": { - "type": "string", - "description": "CSS color string (e.g., 'rgb(255 128 64)', 'oklch(0.7 0.15 180)')" - } - }, - "required": [ - "value" - ] - }, - "initializers": [ - { - "title": "CSS Color Initializer", - "keyword": "css", - "description": "Creates a CSS color from a string value", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// CSS Color Initializer\n// Accepts a CSS color string and returns it\n// This is primarily used for type registration; \n// the main usage is converting TO css from other color types\n//\n// Input: String (CSS color value)\n// Output: String (same value)\n\nvariable input: List = {input};\nvariable value: String = input.get(0);\n\nreturn value;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/rgb-color/0/", - "target": "$self", - "description": "Converts RGB (0-255) to CSS rgb() syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// RGB to CSS Conversion\n// Converts RGB (0-255) to CSS rgb() syntax\n// CSS Color Level 4 modern syntax: rgb(r g b) or rgb(r g b / alpha)\n//\n// Input: Color.Rgb with r, g, b in 0-255 range, optional alpha (0-1)\n// Output: Color.CSS with value like \"rgb(255 128 64)\" or \"rgb(255 128 64 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable r: Number = round({input}.r);\nvariable g: Number = round({input}.g);\nvariable b: Number = round({input}.b);\n\nvariable css_value: String = \"rgb(\".concat(r.to_string()).concat(\" \").concat(g.to_string()).concat(\" \").concat(b.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/", - "target": "$self", - "description": "Converts sRGB (0-1) to CSS color(srgb) syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// sRGB to CSS Conversion\n// Converts sRGB (0-1) to CSS color(srgb) syntax\n// CSS Color Level 4: color(srgb r g b) or color(srgb r g b / alpha)\n//\n// Input: Color.SRGB with r, g, b in 0-1 range, optional alpha (0-1)\n// Output: Color.CSS with value like \"color(srgb 1 0.5 0.25)\" or \"color(srgb 1 0.5 0.25 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable r: Number = round({input}.r * precision) / precision;\nvariable g: Number = round({input}.g * precision) / precision;\nvariable b: Number = round({input}.b * precision) / precision;\n\nvariable css_value: String = \"color(srgb \".concat(r.to_string()).concat(\" \").concat(g.to_string()).concat(\" \").concat(b.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsl-color/0/", - "target": "$self", - "description": "Converts HSL to CSS hsl() syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HSL to CSS Conversion\n// Converts HSL to CSS hsl() syntax\n// CSS Color Level 4: hsl(h s l) or hsl(h s l / alpha) where s and l are percentages\n//\n// Input: Color.HSL with h (0-360), s (0-1), l (0-1), optional alpha (0-1)\n// Output: Color.CSS with value like \"hsl(120 50% 75%)\" or \"hsl(120 50% 75% / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100;\nvariable h: Number = round({input}.h * precision) / precision;\nvariable s: Number = round({input}.s * 100 * precision) / precision;\nvariable l: Number = round({input}.l * 100 * precision) / precision;\n\nvariable css_value: String = \"hsl(\".concat(h.to_string()).concat(\" \").concat(s.to_string()).concat(\"% \").concat(l.to_string()).concat(\"%\");\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hwb-color/0/", - "target": "$self", - "description": "Converts HWB to CSS hwb() syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HWB to CSS Conversion\n// Converts HWB to CSS hwb() syntax\n// CSS Color Level 4: hwb(h w b) or hwb(h w b / alpha) where w and b are percentages\n//\n// Input: Color.HWB with h (0-360), w (0-1), b (0-1), optional alpha (0-1)\n// Output: Color.CSS with value like \"hwb(120 10% 20%)\" or \"hwb(120 10% 20% / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100;\nvariable h: Number = round({input}.h * precision) / precision;\nvariable w: Number = round({input}.w * 100 * precision) / precision;\nvariable b_val: Number = round({input}.b * 100 * precision) / precision;\n\nvariable css_value: String = \"hwb(\".concat(h.to_string()).concat(\" \").concat(w.to_string()).concat(\"% \").concat(b_val.to_string()).concat(\"%\");\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lab-color/0/", - "target": "$self", - "description": "Converts CIE Lab to CSS lab() syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// CIE Lab to CSS Conversion\n// Converts CIE Lab to CSS lab() syntax\n// CSS Color Level 4: lab(L a b) or lab(L a b / alpha) where L is a percentage\n//\n// Input: Color.Lab with l (0-100), a, b, optional alpha (0-1)\n// Output: Color.CSS with value like \"lab(75% 20 -30)\" or \"lab(75% 20 -30 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 10000;\nvariable l: Number = round({input}.l * precision) / precision;\nvariable a: Number = round({input}.a * precision) / precision;\nvariable b_val: Number = round({input}.b * precision) / precision;\n\nvariable css_value: String = \"lab(\".concat(l.to_string()).concat(\"% \").concat(a.to_string()).concat(\" \").concat(b_val.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lch-color/0/", - "target": "$self", - "description": "Converts CIE LCH to CSS lch() syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// CIE LCH to CSS Conversion\n// Converts CIE LCH to CSS lch() syntax\n// CSS Color Level 4: lch(L C H) or lch(L C H / alpha) where L is a percentage\n//\n// Input: Color.LCH with l (0-100), c, h (0-360), optional alpha (0-1)\n// Output: Color.CSS with value like \"lch(75% 50 180)\" or \"lch(75% 50 180 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 10000;\nvariable l: Number = round({input}.l * precision) / precision;\nvariable c: Number = round({input}.c * precision) / precision;\nvariable h: Number = round({input}.h * precision) / precision;\n\nvariable css_value: String = \"lch(\".concat(l.to_string()).concat(\"% \").concat(c.to_string()).concat(\" \").concat(h.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/", - "target": "$self", - "description": "Converts OKLab to CSS oklab() syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLab to CSS Conversion\n// Converts OKLab to CSS oklab() syntax\n// CSS Color Level 4: oklab(L a b) or oklab(L a b / alpha) where L is 0-1 decimal\n//\n// Input: Color.OKLab with l (0-1), a, b, optional alpha (0-1)\n// Output: Color.CSS with value like \"oklab(0.7 0.1 -0.05)\" or \"oklab(0.7 0.1 -0.05 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable l: Number = round({input}.l * precision) / precision;\nvariable a: Number = round({input}.a * precision) / precision;\nvariable b_val: Number = round({input}.b * precision) / precision;\n\nvariable css_value: String = \"oklab(\".concat(l.to_string()).concat(\" \").concat(a.to_string()).concat(\" \").concat(b_val.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/", - "target": "$self", - "description": "Converts OKLCH to CSS oklch() syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// OKLCH to CSS Conversion\n// Converts OKLCH to CSS oklch() syntax\n// CSS Color Level 4: oklch(L C H) or oklch(L C H / alpha) where L is 0-1 decimal\n//\n// Input: Color.OKLCH with l (0-1), c, h (0-360), optional alpha (0-1)\n// Output: Color.CSS with value like \"oklch(0.7 0.15 180)\" or \"oklch(0.7 0.15 180 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable l: Number = round({input}.l * precision) / precision;\nvariable c: Number = round({input}.c * precision) / precision;\nvariable h: Number = round({input}.h * precision) / precision;\n\nvariable css_value: String = \"oklch(\".concat(l.to_string()).concat(\" \").concat(c.to_string()).concat(\" \").concat(h.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-linear-color/0/", - "target": "$self", - "description": "Converts Linear sRGB to CSS color(srgb-linear) syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Linear sRGB to CSS Conversion\n// Converts Linear sRGB to CSS color(srgb-linear) syntax\n// CSS Color Level 4: color(srgb-linear r g b)\n//\n// Input: Color.LinearSRGB with r, g, b in 0-1 range\n// Output: Color.CSS with value like \"color(srgb-linear 1 0.25 0.0625)\"\n\nvariable precision: Number = 100000;\nvariable r: Number = round({input}.r * precision) / precision;\nvariable g: Number = round({input}.g * precision) / precision;\nvariable b: Number = round({input}.b * precision) / precision;\n\nvariable output: Color.CSS;\noutput.value = \"color(srgb-linear \".concat(r.to_string()).concat(\" \").concat(g.to_string()).concat(\" \").concat(b.to_string()).concat(\")\");\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/", - "target": "$self", - "description": "Converts XYZ-D65 to CSS color(xyz-d65) syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// XYZ-D65 to CSS Conversion\n// Converts XYZ-D65 to CSS color(xyz-d65) syntax\n// CSS Color Level 4: color(xyz-d65 x y z) or color(xyz-d65 x y z / alpha)\n//\n// Input: Color.XYZD65 with x, y, z tristimulus values, optional alpha (0-1)\n// Output: Color.CSS with value like \"color(xyz-d65 0.4124 0.2126 0.0193)\" or \"color(xyz-d65 0.4124 0.2126 0.0193 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable x: Number = round({input}.x * precision) / precision;\nvariable y: Number = round({input}.y * precision) / precision;\nvariable z: Number = round({input}.z * precision) / precision;\n\nvariable css_value: String = \"color(xyz-d65 \".concat(x.to_string()).concat(\" \").concat(y.to_string()).concat(\" \").concat(z.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d50-color/0/", - "target": "$self", - "description": "Converts XYZ-D50 to CSS color(xyz-d50) syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// XYZ-D50 to CSS Conversion\n// Converts XYZ-D50 to CSS color(xyz-d50) syntax\n// CSS Color Level 4: color(xyz-d50 x y z) or color(xyz-d50 x y z / alpha)\n//\n// Input: Color.XYZD50 with x, y, z tristimulus values, optional alpha (0-1)\n// Output: Color.CSS with value like \"color(xyz-d50 0.4360 0.2225 0.0139)\" or \"color(xyz-d50 0.4360 0.2225 0.0139 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable x: Number = round({input}.x * precision) / precision;\nvariable y: Number = round({input}.y * precision) / precision;\nvariable z: Number = round({input}.z * precision) / precision;\n\nvariable css_value: String = \"color(xyz-d50 \".concat(x.to_string()).concat(\" \").concat(y.to_string()).concat(\" \").concat(z.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - }, - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/p3-color/0/", - "target": "$self", - "description": "Converts Display-P3 to CSS color(display-p3) syntax", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Display-P3 to CSS Conversion\n// Converts Display-P3 to CSS color(display-p3) syntax\n// CSS Color Level 4: color(display-p3 r g b) or color(display-p3 r g b / alpha)\n//\n// Input: Color.P3 with r, g, b in 0-1 range, optional alpha (0-1)\n// Output: Color.CSS with value like \"color(display-p3 1 0.5 0.25)\" or \"color(display-p3 1 0.5 0.25 / 0.5)\"\n// Note: Alpha is omitted if null or 1.0 (fully opaque)\n\nvariable precision: Number = 100000;\nvariable r: Number = round({input}.r * precision) / precision;\nvariable g: Number = round({input}.g * precision) / precision;\nvariable b: Number = round({input}.b * precision) / precision;\n\nvariable css_value: String = \"color(display-p3 \".concat(r.to_string()).concat(\" \").concat(g.to_string()).concat(\" \").concat(b.to_string());\n\n// Add alpha if present, not null, and not 1.0 (per CSS convention)\nvariable alpha: Number = {input}.alpha;\nif (alpha != null) [\n if (alpha != 1) [\n css_value = css_value.concat(\" / \").concat(alpha.to_string());\n ];\n];\n\ncss_value = css_value.concat(\")\");\n\nvariable output: Color.CSS;\noutput.value = css_value;\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hwb-color/0/", - schema: { - "name": "HWB", - "type": "color", - "description": "HWB color space - Hue, Whiteness, Blackness. CSS Color Level 4.", - "schema": { - "type": "object", - "properties": { - "h": { - "type": "number", - "description": "Hue angle (0-360 degrees)" - }, - "w": { - "type": "number", - "description": "Whiteness (0-1)" - }, - "b": { - "type": "number", - "description": "Blackness (0-1)" - } - }, - "required": [ - "h", - "w", - "b" - ], - "order": [ - "h", - "w", - "b" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "HWB Color Initializer", - "keyword": "hwb", - "description": "Creates an HWB color from H, W, B values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HWB Color Initializer\n// Creates an HWB color from H, W, B values\n// Input: List of [h, w, b] or [h, w, b, alpha] values\n\nvariable hwb_values: List = {input};\nvariable output: Color.HWB;\n\noutput.h = hwb_values.get(0);\noutput.w = hwb_values.get(1);\noutput.b = hwb_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (hwb_values.length() > 3) [\n output.alpha = hwb_values.get(3);\n];\n\nreturn output;" - } - }, - { - "title": "HWBA Color Initializer", - "keyword": "hwba", - "description": "Creates an HWB color with alpha from H, W, B, A values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HWBA Color Initializer\n// Creates an HWB color with alpha from H, W, B, A values\n//\n// Usage: hwba(180, 0.2, 0.3, 0.9) → Color.HWB { h: 180, w: 0.2, b: 0.3, alpha: 0.9 }\n//\n// Input: List of 4 numbers [h, w, b, alpha]\n// Output: Color.HWB with alpha\n\nvariable hwb_values: List = {input};\nvariable output: Color.HWB;\n\noutput.h = hwb_values.get(0);\noutput.w = hwb_values.get(1);\noutput.b = hwb_values.get(2);\noutput.alpha = hwb_values.get(3);\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsv-color/0/", - "target": "$self", - "description": "Converts HSV to HWB", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HSV to HWB Conversion\n// Reference: CSS Color Level 4\n//\n// Algorithm:\n// H stays the same\n// W = (1 - S) * V (Whiteness)\n// B = 1 - V (Blackness)\n//\n// Input: Color.HSV with h, s, v values\n// Output: Color.HWB with h, w, b values\n\n// Get input HSV values\nvariable h: Number = {input}.h;\nvariable s: Number = {input}.s;\nvariable v: Number = {input}.v;\n\n// Calculate whiteness and blackness\nvariable w: Number = (1 - s) * v;\nvariable b: Number = 1 - v;\n\n// Create output\nvariable output: Color.HWB;\noutput.h = h;\noutput.w = w;\noutput.b = b;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/hsv-color/0/", - schema: { - "name": "HSV", - "type": "color", - "description": "HSV color space - Hue, Saturation, Value. Also known as HSB (Hue, Saturation, Brightness).", - "schema": { - "type": "object", - "properties": { - "h": { - "type": "number", - "description": "Hue angle (0-360 degrees)" - }, - "s": { - "type": "number", - "description": "Saturation (0-1)" - }, - "v": { - "type": "number", - "description": "Value/Brightness (0-1)" - } - }, - "required": [ - "h", - "s", - "v" - ], - "order": [ - "h", - "s", - "v" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "HSV Color Initializer", - "keyword": "hsv", - "description": "Creates an HSV color from H, S, V values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// HSV Color Initializer\n// Creates an HSV color from H, S, V values\n// Input: List of [h, s, v] or [h, s, v, alpha] values\n\nvariable hsv_values: List = {input};\nvariable output: Color.HSV;\n\noutput.h = hsv_values.get(0);\noutput.s = hsv_values.get(1);\noutput.v = hsv_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (hsv_values.length() > 3) [\n output.alpha = hsv_values.get(3);\n];\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/", - "target": "$self", - "description": "Converts sRGB to HSV", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// sRGB to HSV Conversion\n// Reference: Standard RGB to HSV algorithm\n//\n// Algorithm:\n// V = max(R, G, B)\n// S = (V - min(R, G, B)) / V (if V > 0)\n// H depends on which channel is max\n//\n// Input: Color.SRGB with r, g, b in 0-1 range\n// Output: Color.HSV with h (0-360), s (0-1), v (0-1)\n\n// Get input sRGB values\nvariable r: Number = {input}.r;\nvariable g: Number = {input}.g;\nvariable b: Number = {input}.b;\n\n// Find max and min\nvariable max_val: Number = r;\nif (g > max_val) [\n max_val = g;\n];\nif (b > max_val) [\n max_val = b;\n];\n\nvariable min_val: Number = r;\nif (g < min_val) [\n min_val = g;\n];\nif (b < min_val) [\n min_val = b;\n];\n\n// Value is the max channel\nvariable v: Number = max_val;\n\n// Calculate saturation and hue\nvariable s: Number = 0;\nvariable h: Number = 0;\nvariable delta: Number = max_val - min_val;\n\nif (max_val > 0) [\n s = delta / max_val;\n];\n\nif (delta > 0) [\n // Hue calculation depends on which channel is max\n if (max_val == r) [\n h = ((g - b) / delta);\n if (g < b) [\n h = h + 6;\n ];\n ] else [\n if (max_val == g) [\n h = (b - r) / delta + 2;\n ] else [\n h = (r - g) / delta + 4;\n ];\n ];\n \n // Convert to degrees\n h = h * 60;\n];\n\n// Normalize hue to 0-360\nif (h < 0) [\n h = h + 360;\n];\n\n// Create output\nvariable output: Color.HSV;\noutput.h = h;\noutput.s = s;\noutput.v = v;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lab-color/0/", - schema: { - "name": "Lab", - "type": "color", - "description": "CIE Lab (L*a*b*) perceptually uniform color space. L is lightness (0-100), a is green-red axis, b is blue-yellow axis.", - "schema": { - "type": "object", - "properties": { - "l": { - "type": "number", - "description": "Lightness (0-100)" - }, - "a": { - "type": "number", - "description": "Green-red axis (typically -125 to 125)" - }, - "b": { - "type": "number", - "description": "Blue-yellow axis (typically -125 to 125)" - } - }, - "required": [ - "l", - "a", - "b" - ], - "order": [ - "l", - "a", - "b" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "Lab Color Initializer", - "keyword": "lab", - "description": "Creates a CIE Lab color from L, a, b values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// CIE Lab Color Initializer\n// Creates a Lab color from L, a, b values\n// Input: List of [l, a, b] or [l, a, b, alpha] values\n\nvariable lab_values: List = {input};\nvariable output: Color.Lab;\n\noutput.l = lab_values.get(0);\noutput.a = lab_values.get(1);\noutput.b = lab_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (lab_values.length() > 3) [\n output.alpha = lab_values.get(3);\n];\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d50-color/0/", - "target": "$self", - "description": "Converts XYZ-D50 to CIE Lab using the CIE standard formula", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// XYZ-D50 to CIE Lab Conversion\n// Reference: CIE 15.3:2004 section 8.2.1.1\n// Reference: https://github.com/color-js/color.js/blob/main/src/spaces/lab.js\n//\n// Algorithm:\n// 1. Scale XYZ by D50 white point reference\n// 2. Apply f function: f(x) = x > ε ? cbrt(x) : (κ*x + 16)/116\n// 3. L = 116 * f[Y] - 16\n// 4. a = 500 * (f[X] - f[Y])\n// 5. b = 200 * (f[Y] - f[Z])\n//\n// Input: Color.XYZD50 with x, y, z tristimulus\n// Output: Color.Lab with l, a, b coordinates\n\n// Get input XYZ-D50 values\nvariable x: Number = {input}.x;\nvariable y: Number = {input}.y;\nvariable z: Number = {input}.z;\n\n// CIE constants (exact rational fractions)\n// ε = 216/24389 = (6/29)^3\nvariable epsilon: Number = 0.008856451679035631;\n// κ = 24389/27 = (29/3)^3 \nvariable kappa: Number = 903.2962962962963;\n\n// D50 white point reference (ColorJS exact values)\n// D50: [0.3457/0.3585, 1.0, (1-0.3457-0.3585)/0.3585]\nvariable white_x: Number = 0.9642956764295677;\nvariable white_y: Number = 1.0;\nvariable white_z: Number = 0.8251046025104602;\n\n// Scale XYZ by white point\nvariable xr: Number = x / white_x;\nvariable yr: Number = y / white_y;\nvariable zr: Number = z / white_z;\n\n// Apply f function with cube root\n// f(t) = t > ε ? t^(1/3) : (κt + 16) / 116\nvariable cube_root_exp: Number = 0.3333333333333333;\n\nvariable fx: Number = 0;\nvariable fy: Number = 0;\nvariable fz: Number = 0;\n\nif (xr > epsilon) [\n fx = pow(xr, cube_root_exp);\n] else [\n fx = (kappa * xr + 16) / 116;\n];\n\nif (yr > epsilon) [\n fy = pow(yr, cube_root_exp);\n] else [\n fy = (kappa * yr + 16) / 116;\n];\n\nif (zr > epsilon) [\n fz = pow(zr, cube_root_exp);\n] else [\n fz = (kappa * zr + 16) / 116;\n];\n\n// Calculate Lab values\nvariable lab_l: Number = 116 * fy - 16;\nvariable lab_a: Number = 500 * (fx - fy);\nvariable lab_b: Number = 200 * (fy - fz);\n\n// Create output\nvariable output: Color.Lab;\noutput.l = lab_l;\noutput.a = lab_a;\noutput.b = lab_b;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d50-color/0/", - schema: { - "name": "XYZD50", - "type": "color", - "description": "CIE XYZ color space with D50 white point. Used for Lab and LCH color spaces.", - "schema": { - "type": "object", - "properties": { - "x": { - "type": "number", - "description": "X tristimulus value" - }, - "y": { - "type": "number", - "description": "Y tristimulus value (luminance)" - }, - "z": { - "type": "number", - "description": "Z tristimulus value" - } - }, - "required": [ - "x", - "y", - "z" - ], - "order": [ - "x", - "y", - "z" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "XYZ-D50 Color Initializer", - "keyword": "xyzd50", - "description": "Creates an XYZ-D50 color from tristimulus values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// XYZ-D50 Color Initializer\n// Creates an XYZ-D50 color from tristimulus values\n// Input: List of [x, y, z] tristimulus values\n\nvariable xyz_values: List = {input};\nvariable output: Color.XYZD50;\n\noutput.x = xyz_values.get(0);\noutput.y = xyz_values.get(1);\noutput.z = xyz_values.get(2);\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/xyz-d65-color/0/", - "target": "$self", - "description": "Converts XYZ-D65 to XYZ-D50 using Bradford chromatic adaptation", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// XYZ-D65 to XYZ-D50 Conversion\n// Chromatic adaptation using Bradford transform\n// Reference: https://github.com/color-js/color.js/blob/main/src/adapt.js\n//\n// Bradford CAT matrix for D65 → D50:\n// [1.0479298 0.0229469 -0.0501923]\n// [0.0296278 0.9904344 -0.0170738]\n// [-0.0092430 0.0150552 0.7518743]\n//\n// Input: Color.XYZD65 with x, y, z tristimulus (D65 white)\n// Output: Color.XYZD50 with x, y, z tristimulus (D50 white)\n\n// Get input XYZ-D65 values\nvariable x65: Number = {input}.x;\nvariable y65: Number = {input}.y;\nvariable z65: Number = {input}.z;\n\n// Bradford chromatic adaptation matrix (ColorJS exact values)\n// Row 1\nvariable x50: Number = x65 * 1.0479297925449969 + y65 * 0.022946870601609652 + z65 * -0.05019226628920524;\n// Row 2\nvariable y50: Number = x65 * 0.02962780877005599 + y65 * 0.9904344267538799 + z65 * -0.017073799063418826;\n// Row 3\nvariable z50: Number = x65 * -0.009243040646204504 + y65 * 0.015055191490298152 + z65 * 0.7518742814281371;\n\n// Create output\nvariable output: Color.XYZD50;\noutput.x = x50;\noutput.y = y50;\noutput.z = z50;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lch-color/0/", - schema: { - "name": "LCH", - "type": "color", - "description": "CIE LCH color space - the polar form of CIE Lab. L is lightness (0-100), C is chroma, H is hue angle (0-360).", - "schema": { - "type": "object", - "properties": { - "l": { - "type": "number", - "description": "Lightness (0-100)" - }, - "c": { - "type": "number", - "description": "Chroma" - }, - "h": { - "type": "number", - "description": "Hue angle (0-360 degrees)" - } - }, - "required": [ - "l", - "c", - "h" - ], - "order": [ - "l", - "c", - "h" - ], - "additionalProperties": false - }, - "initializers": [ - { - "title": "LCH Color Initializer", - "keyword": "lch", - "description": "Creates a CIE LCH color from L, C, H values", - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// CIE LCH Color Initializer\n// Creates a LCH color from L, C, H values\n// Input: List of [l, c, h] or [l, c, h, alpha] values\n\nvariable lch_values: List = {input};\nvariable output: Color.LCH;\n\noutput.l = lch_values.get(0);\noutput.c = lch_values.get(1);\noutput.h = lch_values.get(2);\n\n// Set alpha if provided as 4th parameter\nif (lch_values.length() > 3) [\n output.alpha = lch_values.get(3);\n];\n\nreturn output;" - } - } - ], - "conversions": [ - { - "source": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/lab-color/0/", - "target": "$self", - "description": "Converts CIE Lab to LCH using Cartesian to polar transformation", - "lossless": true, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// CIE Lab to LCH Conversion\n// Converts Cartesian (a, b) to polar (C, H) coordinates\n// Same algorithm as OKLab → OKLCH, but for CIE Lab\n//\n// Algorithm:\n// L stays the same (lightness 0-100)\n// C = sqrt(a² + b²) (chroma)\n// H = atan2(b, a) * 180/π (hue angle in degrees)\n//\n// Input: Color.Lab with l, a, b coordinates\n// Output: Color.LCH with l, c, h coordinates\n\n// Get input Lab values\nvariable l: Number = {input}.l;\nvariable a: Number = {input}.a;\nvariable b: Number = {input}.b;\n\n// Constants\nvariable pi: Number = pi();\nvariable rad_to_deg: Number = 180 / pi;\n\n// Calculate chroma (distance from neutral axis)\nvariable c: Number = sqrt(a * a + b * b);\n\n// Calculate hue angle using atan2\nvariable h_rad: Number = atan2(b, a);\nvariable h: Number = h_rad * rad_to_deg;\n\n// Normalize hue to 0-360 range\nif (h < 0) [\n h = h + 360;\n];\n\n// Create output\nvariable output: Color.LCH;\noutput.l = l;\noutput.c = c;\noutput.h = h;\n\nreturn output;" - } - } - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/function/lighten/0/", - schema: { - "name": "Lighten", - "type": "function", - "description": "Makes a color lighter by increasing its lightness in OKLab space. Amount is 0-1 where 0.25 = 25% lighter.", - "keyword": "lighten", - "input": { - "type": "object", - "properties": { - "color": { - "type": "color", - "description": "The color to lighten" - }, - "amount": { - "type": "number", - "description": "Amount to lighten (0-1). Default is 0.25" - } - } - }, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// lighten: Increase lightness proportionally towards white\n// Amount: 0-1 where 0.25 means 25% closer to white\n//\n// Algorithm: L' = L + (1 - L) * amount\n// This ensures we approach white (L=1) proportionally.\n//\n// Input: Any color space (converted to OKLab internally)\n// Output: OKLCH (working space)\n// To get sRGB: lighten(color, 0.25).to.srgb()\n\nvariable input: List = {input};\nvariable color: Color.OKLab = input.get(0).to.oklab();\n\n// Default amount is 0.25 (25%)\nvariable amount: Number = 0.25;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Calculate new lightness (move toward 1)\nvariable current_l: Number = color.l;\nvariable new_l: Number = current_l + (1 - current_l) * amount;\n\n// Clamp to valid range\nif (new_l > 1) [ new_l = 1; ];\n\n// Create output in OKLab, return as OKLCH (working space)\nvariable result: Color.OKLab;\nresult.l = new_l;\nresult.a = color.a;\nresult.b = color.b;\n\nreturn result.to.oklch();" - }, - "requirements": [ - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/", - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/" - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/function/darken/0/", - schema: { - "name": "Darken", - "type": "function", - "description": "Makes a color darker by decreasing its lightness in OKLab space. Amount is 0-1 where 0.25 = 25% darker.", - "keyword": "darken", - "input": { - "type": "object", - "properties": { - "color": { - "type": "color", - "description": "The color to darken" - }, - "amount": { - "type": "number", - "description": "Amount to darken (0-1). Default is 0.25" - } - } - }, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// darken: Decrease lightness proportionally towards black\n// Amount: 0-1 where 0.25 means 25% closer to black\n//\n// Algorithm: L' = L * (1 - amount)\n// This ensures we approach black (L=0) proportionally.\n//\n// Input: Any color space (converted to OKLab internally)\n// Output: OKLCH (working space)\n// To get sRGB: darken(color, 0.25).to.srgb()\n\nvariable input: List = {input};\nvariable color: Color.OKLab = input.get(0).to.oklab();\n\n// Default amount is 0.25 (25%)\nvariable amount: Number = 0.25;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Calculate new lightness (move toward 0)\nvariable current_l: Number = color.l;\nvariable new_l: Number = current_l * (1 - amount);\n\n// Clamp to valid range\nif (new_l < 0) [ new_l = 0; ];\n\n// Create output in OKLab, return as OKLCH (working space)\nvariable result: Color.OKLab;\nresult.l = new_l;\nresult.a = color.a;\nresult.b = color.b;\n\nreturn result.to.oklch();" - }, - "requirements": [ - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklab-color/0/", - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/" - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/function/saturate/0/", - schema: { - "name": "Saturate", - "type": "function", - "description": "Increases color saturation by boosting chroma in OKLCH space. Amount is 0-1 where 0.25 = 25% more saturated.", - "keyword": "saturate", - "input": { - "type": "object", - "properties": { - "color": { - "type": "color", - "description": "The color to saturate" - }, - "amount": { - "type": "number", - "description": "Amount to saturate (0-1). Default is 0.25" - } - } - }, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Saturate a color by increasing its OKLCH chroma\n// Amount: 0-1 where 0.25 means 25% increase\n//\n// Algorithm: C' = C * (1 + amount)\n// Chroma has no strict upper bound but ~0.4 is practical max for sRGB\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Default amount is 0.25 (25%)\nvariable amount: Number = 0.25;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Calculate new chroma\nvariable current_c: Number = color.c;\nvariable new_c: Number = current_c * (1 + amount);\n\n// Soft clamp at 0.4 (approximate sRGB gamut max)\nif (new_c > 0.4) [ new_c = 0.4; ];\n\n// Create output in OKLCH, return as sRGB\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h;\n\nreturn result;" - }, - "requirements": [ - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/", - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/" - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/function/desaturate/0/", - schema: { - "name": "Desaturate", - "type": "function", - "description": "Decreases color saturation by reducing chroma in OKLCH space. Amount is 0-1 where 0.25 = 25% less saturated.", - "keyword": "desaturate", - "input": { - "type": "object", - "properties": { - "color": { - "type": "color", - "description": "The color to desaturate" - }, - "amount": { - "type": "number", - "description": "Amount to desaturate (0-1). Default is 0.25" - } - } - }, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Desaturate a color by decreasing its OKLCH chroma\n// Amount: 0-1 where 0.25 means 25% decrease, 1.0 = full grayscale\n//\n// Algorithm: C' = C * (1 - amount)\n\nvariable input: List = {input};\nvariable color: Color.OKLCH = input.get(0).to.oklch();\n\n// Default amount is 0.25 (25%)\nvariable amount: Number = 0.25;\nif (input.length() > 1) [\n amount = input.get(1);\n];\n\n// Calculate new chroma\nvariable current_c: Number = color.c;\nvariable new_c: Number = current_c * (1 - amount);\n\n// Clamp to valid range\nif (new_c < 0) [ new_c = 0; ];\n\n// Create output in OKLCH, return as sRGB\nvariable result: Color.OKLCH;\nresult.l = color.l;\nresult.c = new_c;\nresult.h = color.h;\n\nreturn result;" - }, - "requirements": [ - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/", - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/" - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/function/mix/0/", - schema: { - "name": "Mix", - "type": "function", - "description": "Blends two colors together in OKLCH space. Amount 0 = first color, 1 = second color, 0.5 = equal blend.", - "keyword": "mix", - "input": { - "type": "object", - "properties": { - "color1": { - "type": "color", - "description": "First color" - }, - "color2": { - "type": "color", - "description": "Second color" - }, - "amount": { - "type": "number", - "description": "Blend amount (0-1). Default is 0.5" - } - } - }, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// mix: Blend two colors in perceptual space\n// Uses shortest hue path for natural color blending.\n//\n// Algorithm: Linear interpolation on L, C, and shortest-path H\n//\n// Input: Any color space (converted to OKLCH internally)\n// Output: OKLCH (working space)\n// To get sRGB: mix(color1, color2, 0.5).to.srgb()\n\nvariable input: List = {input};\nvariable color1: Color.OKLCH = input.get(0).to.oklch();\nvariable color2: Color.OKLCH = input.get(1).to.oklch();\n\n// Default amount is 0.5 (50/50 blend)\nvariable amount: Number = 0.5;\nif (input.length() > 2) [\n amount = input.get(2);\n];\n\n// Interpolate lightness and chroma linearly\nvariable new_l: Number = color1.l + (color2.l - color1.l) * amount;\nvariable new_c: Number = color1.c + (color2.c - color1.c) * amount;\n\n// Interpolate hue using shortest path\nvariable h1: Number = color1.h;\nvariable h2: Number = color2.h;\nvariable h_diff: Number = h2 - h1;\n\n// Normalize to shortest path (-180 to 180)\nif (h_diff > 180) [ h_diff = h_diff - 360; ];\nif (h_diff < -180) [ h_diff = h_diff + 360; ];\n\nvariable new_h: Number = h1 + h_diff * amount;\nif (new_h < 0) [ new_h = new_h + 360; ];\nif (new_h >= 360) [ new_h = new_h - 360; ];\n\n// Create mixed color\nvariable result: Color.OKLCH;\nresult.l = new_l;\nresult.c = new_c;\nresult.h = new_h;\n\nreturn result;" - }, - "requirements": [ - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/oklch-color/0/", - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/srgb-color/0/" - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/function/invert/0/", - schema: { - "name": "Invert Color", - "type": "function", - "description": "Inverts a color by inverting each RGB channel (R' = 255 - R, G' = 255 - G, B' = 255 - B).", - "keyword": "invert", - "input": { - "type": "object", - "properties": { - "color": { - "type": "color", - "description": "The color to invert." - } - } - }, - "script": { - "type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/", - "script": "// Invert Color Function\n// Returns the inverse/negative of a color by subtracting each RGB channel from 255\n//\n// Algorithm:\n// R' = 255 - R\n// G' = 255 - G \n// B' = 255 - B\n//\n// Use cases:\n// - Creating negative images\n// - High contrast accessibility variants\n// - Artistic color effects\n//\n// Input: Any color (converted to RGB internally)\n// Output: Color.Rgb with inverted channels\n\nvariable input: List = {input};\n\n// Convert input to RGB (0-255 range)\nvariable rgb_color: Color.Rgb = input.get(0).to.rgb();\n\n// Invert each channel by subtracting from 255\nvariable inverted_r: Number = 255 - rgb_color.r;\nvariable inverted_g: Number = 255 - rgb_color.g;\nvariable inverted_b: Number = 255 - rgb_color.b;\n\n// Create and return the inverted color\nvariable inverted_color: Color.Rgb = rgb(inverted_r, inverted_g, inverted_b);\nreturn inverted_color;" - }, - "requirements": [ - "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/rgb-color/0/" - ] - } - }, - { - uri: "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/constants/css-hex-colors/0/", - schema: { - "name": "CSS Hex Colors", - "type": "constants", - "description": "CSS named colors mapped to their hex values (CSS Color Level 4)", - "inline": true, - "values": { - "aliceblue": "#F0F8FF", - "antiquewhite": "#FAEBD7", - "aqua": "#00FFFF", - "aquamarine": "#7FFFD4", - "azure": "#F0FFFF", - "beige": "#F5F5DC", - "bisque": "#FFE4C4", - "black": "#000000", - "blanchedalmond": "#FFEBCD", - "blue": "#0000FF", - "blueviolet": "#8A2BE2", - "brown": "#A52A2A", - "burlywood": "#DEB887", - "cadetblue": "#5F9EA0", - "chartreuse": "#7FFF00", - "chocolate": "#D2691E", - "coral": "#FF7F50", - "cornflowerblue": "#6495ED", - "cornsilk": "#FFF8DC", - "crimson": "#DC143C", - "cyan": "#00FFFF", - "darkblue": "#00008B", - "darkcyan": "#008B8B", - "darkgoldenrod": "#B8860B", - "darkgray": "#A9A9A9", - "darkgreen": "#006400", - "darkgrey": "#A9A9A9", - "darkkhaki": "#BDB76B", - "darkmagenta": "#8B008B", - "darkolivegreen": "#556B2F", - "darkorange": "#FF8C00", - "darkorchid": "#9932CC", - "darkred": "#8B0000", - "darksalmon": "#E9967A", - "darkseagreen": "#8FBC8F", - "darkslateblue": "#483D8B", - "darkslategray": "#2F4F4F", - "darkslategrey": "#2F4F4F", - "darkturquoise": "#00CED1", - "darkviolet": "#9400D3", - "deeppink": "#FF1493", - "deepskyblue": "#00BFFF", - "dimgray": "#696969", - "dimgrey": "#696969", - "dodgerblue": "#1E90FF", - "firebrick": "#B22222", - "floralwhite": "#FFFAF0", - "forestgreen": "#228B22", - "fuchsia": "#FF00FF", - "gainsboro": "#DCDCDC", - "ghostwhite": "#F8F8FF", - "gold": "#FFD700", - "goldenrod": "#DAA520", - "gray": "#808080", - "green": "#008000", - "greenyellow": "#ADFF2F", - "grey": "#808080", - "honeydew": "#F0FFF0", - "hotpink": "#FF69B4", - "indianred": "#CD5C5C", - "indigo": "#4B0082", - "ivory": "#FFFFF0", - "khaki": "#F0E68C", - "lavender": "#E6E6FA", - "lavenderblush": "#FFF0F5", - "lawngreen": "#7CFC00", - "lemonchiffon": "#FFFACD", - "lightblue": "#ADD8E6", - "lightcoral": "#F08080", - "lightcyan": "#E0FFFF", - "lightgoldenrodyellow": "#FAFAD2", - "lightgray": "#D3D3D3", - "lightgreen": "#90EE90", - "lightgrey": "#D3D3D3", - "lightpink": "#FFB6C1", - "lightsalmon": "#FFA07A", - "lightseagreen": "#20B2AA", - "lightskyblue": "#87CEFA", - "lightslategray": "#778899", - "lightslategrey": "#778899", - "lightsteelblue": "#B0C4DE", - "lightyellow": "#FFFFE0", - "lime": "#00FF00", - "limegreen": "#32CD32", - "linen": "#FAF0E6", - "magenta": "#FF00FF", - "maroon": "#800000", - "mediumaquamarine": "#66CDAA", - "mediumblue": "#0000CD", - "mediumorchid": "#BA55D3", - "mediumpurple": "#9370DB", - "mediumseagreen": "#3CB371", - "mediumslateblue": "#7B68EE", - "mediumspringgreen": "#00FA9A", - "mediumturquoise": "#48D1CC", - "mediumvioletred": "#C71585", - "midnightblue": "#191970", - "mintcream": "#F5FFFA", - "mistyrose": "#FFE4E1", - "moccasin": "#FFE4B5", - "navajowhite": "#FFDEAD", - "navy": "#000080", - "oldlace": "#FDF5E6", - "olive": "#808000", - "olivedrab": "#6B8E23", - "orange": "#FFA500", - "orangered": "#FF4500", - "orchid": "#DA70D6", - "palegoldenrod": "#EEE8AA", - "palegreen": "#98FB98", - "paleturquoise": "#AFEEEE", - "palevioletred": "#DB7093", - "papayawhip": "#FFEFD5", - "peachpuff": "#FFDAB9", - "peru": "#CD853F", - "pink": "#FFC0CB", - "plum": "#DDA0DD", - "powderblue": "#B0E0E6", - "purple": "#800080", - "rebeccapurple": "#663399", - "red": "#FF0000", - "rosybrown": "#BC8F8F", - "royalblue": "#4169E1", - "saddlebrown": "#8B4513", - "salmon": "#FA8072", - "sandybrown": "#F4A460", - "seagreen": "#2E8B57", - "seashell": "#FFF5EE", - "sienna": "#A0522D", - "silver": "#C0C0C0", - "skyblue": "#87CEEB", - "slateblue": "#6A5ACD", - "slategray": "#708090", - "slategrey": "#708090", - "snow": "#FFFAFA", - "springgreen": "#00FF7F", - "steelblue": "#4682B4", - "tan": "#D2B48C", - "teal": "#008080", - "thistle": "#D8BFD8", - "tomato": "#FF6347", - "turquoise": "#40E0D0", - "violet": "#EE82EE", - "wheat": "#F5DEB3", - "white": "#FFFFFF", - "whitesmoke": "#F5F5F5", - "yellow": "#FFFF00", - "yellowgreen": "#9ACD32" - } - } - }, -]; - -export function makeConfig() { - return new Config().registerSchemas(SCHEMAS); -} diff --git a/frontend/packages/ui/package.json b/frontend/packages/ui/package.json index aea882f601..b65dfe3405 100644 --- a/frontend/packages/ui/package.json +++ b/frontend/packages/ui/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "types": "./dist/index.d.ts", "type": "module", - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "exports": { ".": { "import": "./dist/index.js" diff --git a/frontend/playwright.config.js b/frontend/playwright.config.js index 1e74d525eb..087e6ba56f 100644 --- a/frontend/playwright.config.js +++ b/frontend/playwright.config.js @@ -65,7 +65,9 @@ export default defineConfig({ }, { name: "ds", - use: { ...devices["Desktop Chrome"] }, + // Accounts without a theme follow the system one; keep the visual + // specs on the dark theme so their screenshots stay comparable. + use: { ...devices["Desktop Chrome"], colorScheme: "dark" }, testDir: "./playwright/ui/visual-specs", expect: { toHaveScreenshot: { maxDiffPixelRatio: 0.005 }, diff --git a/frontend/playwright/data/dashboard/get-builtin-templates.json b/frontend/playwright/data/dashboard/get-builtin-templates.json new file mode 100644 index 0000000000..ab08f3b31d --- /dev/null +++ b/frontend/playwright/data/dashboard/get-builtin-templates.json @@ -0,0 +1,34 @@ +[ + { + "~:id": "tokens-starter-kit", + "~:name": "Design tokens starter kit" + }, + { + "~:id": "penpot-design-system", + "~:name": "Penpot Design System | Pencil" + }, + { + "~:id": "wireframing-kit", + "~:name": "Wireframe library" + }, + { + "~:id": "prototype-examples", + "~:name": "Prototype template" + }, + { + "~:id": "plants-app", + "~:name": "UI mockup example" + }, + { + "~:id": "tutorial-for-beginners", + "~:name": "Tutorial for beginners" + }, + { + "~:id": "lucide-icons", + "~:name": "Lucide Icons" + }, + { + "~:id": "font-awesome", + "~:name": "Font Awesome" + } +] diff --git a/frontend/playwright/data/dashboard/get-projects-deleted.json b/frontend/playwright/data/dashboard/get-projects-deleted.json new file mode 100644 index 0000000000..bb1fe484ee --- /dev/null +++ b/frontend/playwright/data/dashboard/get-projects-deleted.json @@ -0,0 +1,32 @@ +[ + { + "~:id": "~ue5a24d1b-ef1e-812f-8004-52bab84be6f7", + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1715266551088", + "~:modified-at": "~m1715266551088", + "~:is-default": false, + "~:name": "New Project 1", + "~:is-pinned": false, + "~:count": 1 + }, + { + "~:id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1713533116382", + "~:modified-at": "~m1713873823633", + "~:is-default": true, + "~:name": "Drafts", + "~:count": 1 + }, + { + "~:id": "~uc7ce0794-0992-8105-8004-38e630f7920c", + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1715266551088", + "~:modified-at": "~m1715266551088", + "~:is-default": false, + "~:name": "Old Project", + "~:is-pinned": false, + "~:count": 1, + "~:deleted-at": "~m1732111500000" + } +] diff --git a/frontend/playwright/data/dashboard/get-team-deleted-files-pending.json b/frontend/playwright/data/dashboard/get-team-deleted-files-pending.json new file mode 100644 index 0000000000..d85f16b500 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-team-deleted-files-pending.json @@ -0,0 +1,47 @@ +[ + { + "~:id": "~uc7ce0794-0992-8105-8004-38e630f41234", + "~:revn": 1, + "~:vern": 1, + "~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:created-at": "~m1705307400000", + "~:modified-at": "~m1732111500000", + "~:deleted-at": "~m1732111500000", + "~:name": "Deleted Design File 1", + "~:is-shared": false, + "~:will-be-deleted-at": "~m4102444800000", + "~:thumbnail-id": null, + "~:row-num": 1, + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d" + }, + { + "~:id": "~uc7ce0794-0992-8105-8004-38e630f41235", + "~:revn": 2, + "~:vern": 2, + "~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:created-at": "~m1704875700000", + "~:modified-at": "~m1732025400000", + "~:deleted-at": "~m1732025400000", + "~:name": "Deleted Design File 2", + "~:is-shared": true, + "~:will-be-deleted-at": "~m4102444800000", + "~:thumbnail-id": null, + "~:row-num": 2, + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d" + }, + { + "~:id": "~uc7ce0794-0992-8105-8004-38e630f41236", + "~:revn": 3, + "~:vern": 3, + "~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920c", + "~:created-at": "~m1706792400000", + "~:modified-at": "~m1731939600000", + "~:deleted-at": "~m1731939600000", + "~:name": "Old Project Design", + "~:is-shared": false, + "~:will-be-deleted-at": "~m4102444800000", + "~:thumbnail-id": null, + "~:row-num": 3, + "~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d" + } +] diff --git a/frontend/playwright/data/dashboard/get-team-members-owner.json b/frontend/playwright/data/dashboard/get-team-members-owner.json new file mode 100644 index 0000000000..10771dcd4d --- /dev/null +++ b/frontend/playwright/data/dashboard/get-team-members-owner.json @@ -0,0 +1,30 @@ +[ + { + "~:is-admin": false, + "~:email": "bar@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Han Solo", + "~:fullname": "Han Solo", + "~:is-owner": false, + "~:modified-at": "~m1713533116365", + "~:can-edit": true, + "~:is-active": true, + "~:id": "~u1e162163-87b7-805b-8005-5fd05514b6d3", + "~:profile-id": "~u1e162163-87b7-805b-8005-5fd05514b6d3", + "~:created-at": "~m1733324626956" + }, + { + "~:is-admin": true, + "~:email": "foo@example.com", + "~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:name": "Princesa Leia", + "~:fullname": "Princesa Leia", + "~:is-owner": true, + "~:modified-at": "~m1713533116365", + "~:can-edit": true, + "~:is-active": true, + "~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", + "~:profile-id": "~uf56647eb-19a7-8115-8003-b6bc939ecd1b", + "~:created-at": "~m1713533116365" + } +] diff --git a/frontend/playwright/data/dashboard/get-teams-second-owner.json b/frontend/playwright/data/dashboard/get-teams-second-owner.json new file mode 100644 index 0000000000..cb8eb7f114 --- /dev/null +++ b/frontend/playwright/data/dashboard/get-teams-second-owner.json @@ -0,0 +1,48 @@ +[ + { + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true + }, + "~:name": "Default", + "~:modified-at": "~m1713533116375", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1713533116375", + "~:is-default": true + }, + { + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true + }, + "~:name": "Second team", + "~:modified-at": "~m1701164272671", + "~:id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3", + "~:created-at": "~m1701164272671", + "~:is-default": false + } +] diff --git a/frontend/playwright/data/logged-in-user/get-profile-with-plugins.json b/frontend/playwright/data/logged-in-user/get-profile-with-plugins.json new file mode 100644 index 0000000000..38dbba2b31 --- /dev/null +++ b/frontend/playwright/data/logged-in-user/get-profile-with-plugins.json @@ -0,0 +1,44 @@ +{ + "~:email": "foo@example.com", + "~:is-demo": false, + "~:auth-backend": "penpot", + "~:fullname": "Princesa Leia", + "~:modified-at": "~m1713533116365", + "~:is-active": true, + "~:default-project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", + "~:is-muted": false, + "~:default-team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1713533116365", + "~:is-blocked": false, + "~:props": { + "~:nudge": { + "~:big": 10, + "~:small": 1 + }, + "~:v2-info-shown": true, + "~:viewed-tutorial?": false, + "~:viewed-walkthrough?": false, + "~:plugins": { + "~:ids": [ + "0a5b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d" + ], + "~:data": { + "0a5b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d": { + "~:plugin-id": "0a5b1c2d-3e4f-4a5b-8c6d-7e8f9a0b1c2d", + "~:version": 2, + "~:name": "Lorem Ipsum generator", + "~:description": "Fill text layers with placeholder text.", + "~:host": "https://plugin.example.com/", + "~:code": "plugin.js", + "~:permissions": { + "~#set": [ + "content:read", + "content:write" + ] + } + } + } + } + } +} diff --git a/frontend/playwright/data/viewer/create-share-link.json b/frontend/playwright/data/viewer/create-share-link.json new file mode 100644 index 0000000000..2d92a75545 --- /dev/null +++ b/frontend/playwright/data/viewer/create-share-link.json @@ -0,0 +1,8 @@ +{ + "~:id": "~u0c7a1f2e-1b2c-8180-8007-0000000000a1", + "~:file-id": "~udd5cc0bb-91ff-81b9-8004-77df9cd3edb1", + "~:who-comment": "team", + "~:who-inspect": "team", + "~:pages": { "~#set": ["~udd5cc0bb-91ff-81b9-8004-77df9cd3edb2"] }, + "~:owner-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b" +} diff --git a/frontend/playwright/data/workspace/get-file-9066-swap-groups.json b/frontend/playwright/data/workspace/get-file-9066-swap-groups.json new file mode 100644 index 0000000000..d20df8426c --- /dev/null +++ b/frontend/playwright/data/workspace/get-file-9066-swap-groups.json @@ -0,0 +1,1935 @@ +{ + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~: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": 10, + "~:modified-at": "~m1729604566305", + "~:id": "~ue179d9df-de35-80bf-8005-283bbd5516b0", + "~:is-shared": false, + "~:version": 55, + "~:project-id": "~u3ffbd505-2f26-800f-8004-f34da98bdad8", + "~:created-at": "~m1729594560852", + "~:data": { + "~:pages": [ + "~ue179d9df-de35-80bf-8005-283bbd5516b1" + ], + "~:pages-index": { + "~ue179d9df-de35-80bf-8005-283bbd5516b1": { + "~:options": {}, + "~:objects": { + "~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 + } + }, + { + "~#point": { + "~:x": 0.01, + "~:y": 0.0 + } + }, + { + "~#point": { + "~:x": 0.01, + "~:y": 0.01 + } + }, + { + "~#point": { + "~:x": 0.0, + "~:y": 0.01 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.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, + "~:selrect": { + "~#rect": { + "~:x": 0, + "~:y": 0, + "~:width": 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, + "~:height": 0.01, + "~:flip-y": null, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bca" + ] + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcd": { + "~#shape": { + "~:y": 589.9999999999999, + "~: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, + "~:hide-in-viewer": true, + "~:name": "E", + "~:width": 291.0000000000002, + "~:type": "~:frame", + "~:touched": { + "~#set": [] + }, + "~:points": [ + { + "~#point": { + "~:x": 1408.3799999999999, + "~:y": 589.9999999999999 + } + }, + { + "~#point": { + "~:x": 1699.38, + "~:y": 589.9999999999999 + } + }, + { + "~#point": { + "~:x": 1699.38, + "~:y": 784.0000000000003 + } + }, + { + "~#point": { + "~:x": 1408.3799999999999, + "~:y": 784.0000000000003 + } + } + ], + "~:component-root": true, + "~:show-content": true, + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:top", + "~:constraints-h": "~:left", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcd", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:component-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe39bb51", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:strokes": [], + "~:x": 1408.3799999999999, + "~:main-instance": true, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 1408.3799999999999, + "~:y": 589.9999999999999, + "~:width": 291.0000000000002, + "~:height": 194.00000000000045, + "~:x1": 1408.3799999999999, + "~:y1": 589.9999999999999, + "~:x2": 1699.38, + "~:y2": 784.0000000000003 + } + }, + "~:fills": [], + "~:flip-x": null, + "~:height": 194.00000000000045, + "~:component-file": "~ue179d9df-de35-80bf-8005-283bbd5516b0", + "~:flip-y": null, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd3", + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd4" + ] + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcc": { + "~#shape": { + "~:y": 793.0000000000007, + "~: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, + "~:hide-in-viewer": true, + "~:name": "F", + "~:width": 176, + "~:type": "~:frame", + "~:points": [ + { + "~#point": { + "~:x": 1465.88, + "~:y": 793.0000000000007 + } + }, + { + "~#point": { + "~:x": 1641.88, + "~:y": 793.0000000000007 + } + }, + { + "~#point": { + "~:x": 1641.88, + "~:y": 969.0000000000009 + } + }, + { + "~#point": { + "~:x": 1465.88, + "~:y": 969.0000000000009 + } + } + ], + "~:component-root": true, + "~:show-content": true, + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:top", + "~:constraints-h": "~:left", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcc", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:component-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe38dba8", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:strokes": [], + "~:x": 1465.88, + "~:main-instance": true, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 1465.88, + "~:y": 793.0000000000007, + "~:width": 176, + "~:height": 176.00000000000023, + "~:x1": 1465.88, + "~:y1": 793.0000000000007, + "~:x2": 1641.88, + "~:y2": 969.0000000000009 + } + }, + "~:fills": [], + "~:flip-x": null, + "~:height": 176.00000000000023, + "~:component-file": "~ue179d9df-de35-80bf-8005-283bbd5516b0", + "~:flip-y": null, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd2" + ] + } + }, + "~u9b0db134-3c1b-800f-8005-285b833b92a1": { + "~#shape": { + "~:y": 599.0000000000002, + "~: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, + "~:hide-in-viewer": true, + "~:name": "A", + "~:width": 176, + "~:type": "~:frame", + "~:touched": { + "~#set": [ + "~:name-group" + ] + }, + "~:points": [ + { + "~#point": { + "~:x": 881.1199999999999, + "~:y": 599.0000000000002 + } + }, + { + "~#point": { + "~:x": 1057.12, + "~:y": 599.0000000000002 + } + }, + { + "~#point": { + "~:x": 1057.12, + "~:y": 775.0000000000005 + } + }, + { + "~#point": { + "~:x": 881.1199999999999, + "~:y": 775.0000000000005 + } + } + ], + "~:component-root": true, + "~:shape-ref": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcc", + "~:show-content": true, + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:top", + "~:constraints-h": "~:left", + "~:id": "~u9b0db134-3c1b-800f-8005-285b833b92a1", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:component-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe38dba8", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:strokes": [], + "~:x": 881.1199999999999, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 881.1199999999999, + "~:y": 599.0000000000002, + "~:width": 176, + "~:height": 176.00000000000023, + "~:x1": 881.1199999999999, + "~:y1": 599.0000000000002, + "~:x2": 1057.12, + "~:y2": 775.0000000000005 + } + }, + "~:fills": [], + "~:flip-x": null, + "~:height": 176.00000000000023, + "~:component-file": "~ue179d9df-de35-80bf-8005-283bbd5516b0", + "~:flip-y": null, + "~:shapes": [ + "~u9b0db134-3c1b-800f-8005-285b833b92a2" + ] + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcf": { + "~#shape": { + "~:y": 599, + "~: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, + "~:hide-in-viewer": true, + "~:name": "C", + "~:width": 176, + "~:type": "~:frame", + "~:points": [ + { + "~#point": { + "~:x": 1173.5, + "~:y": 599 + } + }, + { + "~#point": { + "~:x": 1349.5, + "~:y": 599 + } + }, + { + "~#point": { + "~:x": 1349.5, + "~:y": 775.0000000000005 + } + }, + { + "~#point": { + "~:x": 1173.5, + "~:y": 775.0000000000005 + } + } + ], + "~:component-root": true, + "~:show-content": true, + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:top", + "~:constraints-h": "~:left", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcf", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:component-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe3a9014", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:strokes": [], + "~:x": 1173.5, + "~:main-instance": true, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 1173.5, + "~:y": 599, + "~:width": 176, + "~:height": 176.00000000000045, + "~:x1": 1173.5, + "~:y1": 599, + "~:x2": 1349.5, + "~:y2": 775.0000000000005 + } + }, + "~:fills": [], + "~:flip-x": null, + "~:height": 176.00000000000045, + "~:component-file": "~ue179d9df-de35-80bf-8005-283bbd5516b0", + "~:flip-y": null, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd6" + ] + } + }, + "~u9b0db134-3c1b-800f-8005-285b833b92a2": { + "~#shape": { + "~:y": 599.0000000000002, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Ellipse", + "~:width": 176, + "~:type": "~:circle", + "~:points": [ + { + "~#point": { + "~:x": 881.1199999999999, + "~:y": 599.0000000000002 + } + }, + { + "~#point": { + "~:x": 1057.12, + "~:y": 599.0000000000002 + } + }, + { + "~#point": { + "~:x": 1057.12, + "~:y": 775.0000000000005 + } + }, + { + "~#point": { + "~:x": 881.1199999999999, + "~:y": 775.0000000000005 + } + } + ], + "~:shape-ref": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd2", + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:scale", + "~:constraints-h": "~:scale", + "~:id": "~u9b0db134-3c1b-800f-8005-285b833b92a2", + "~:parent-id": "~u9b0db134-3c1b-800f-8005-285b833b92a1", + "~:frame-id": "~u9b0db134-3c1b-800f-8005-285b833b92a1", + "~:strokes": [ + { + "~:stroke-alignment": "~:inner", + "~:stroke-style": "~:solid", + "~:stroke-color": "#e1c1dc", + "~:stroke-opacity": 1, + "~:stroke-width": 1 + } + ], + "~:x": 881.1199999999999, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 881.1199999999999, + "~:y": 599.0000000000002, + "~:width": 176, + "~:height": 176.00000000000023, + "~:x1": 881.1199999999999, + "~:y1": 599.0000000000002, + "~:x2": 1057.12, + "~:y2": 775.0000000000005 + } + }, + "~:fills": [ + { + "~:fill-color": "#f4f6ff", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:height": 176.00000000000023, + "~:flip-y": null + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bce": { + "~#shape": { + "~:y": 793.0000000000007, + "~: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, + "~:hide-in-viewer": true, + "~:name": "D", + "~:width": 176, + "~:type": "~:frame", + "~:touched": { + "~#set": [ + "~:name-group" + ] + }, + "~:points": [ + { + "~#point": { + "~:x": 1173.5, + "~:y": 793.0000000000007 + } + }, + { + "~#point": { + "~:x": 1349.5, + "~:y": 793.0000000000007 + } + }, + { + "~#point": { + "~:x": 1349.5, + "~:y": 969.0000000000007 + } + }, + { + "~#point": { + "~:x": 1173.5, + "~:y": 969.0000000000007 + } + } + ], + "~:component-root": true, + "~:shape-ref": "~u6ad3e6b9-c5a0-80cf-8005-2830309d281e", + "~:show-content": true, + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:top", + "~:constraints-h": "~:left", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bce", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:component-id": "~u6ad3e6b9-c5a0-80cf-8005-283030a2d6be", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:strokes": [], + "~:x": 1173.5, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 1173.5, + "~:y": 793.0000000000007, + "~:width": 176, + "~:height": 176, + "~:x1": 1173.5, + "~:y1": 793.0000000000007, + "~:x2": 1349.5, + "~:y2": 969.0000000000007 + } + }, + "~:fills": [], + "~:flip-x": null, + "~:height": 176, + "~:component-file": "~ue179d9df-de35-80bf-8005-28302f57b1b5", + "~:flip-y": null, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd5" + ] + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb": { + "~#shape": { + "~:y": 589.9999999999999, + "~:layout-grid-columns": [ + { + "~:type": "~:percent", + "~:value": 33 + }, + { + "~:type": "~:percent", + "~:value": 33 + }, + { + "~:type": "~:percent", + "~:value": 33 + } + ], + "~:hide-fill-on-export": false, + "~:layout-gap-type": "~:multiple", + "~:layout-padding": { + "~:p1": 0, + "~:p2": 0, + "~:p3": 0, + "~:p4": 0 + }, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:layout": "~:grid", + "~:hide-in-viewer": true, + "~:name": "Group", + "~:layout-align-items": "~:center", + "~:width": 886, + "~:layout-grid-cells": { + "~ud874937d-e901-8094-8005-280b10c40285": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud874937d-e901-8094-8005-280b10c40285", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 1, + "~:row-span": 1, + "~:shapes": [ + "~u9b0db134-3c1b-800f-8005-285b833b92a1" + ] + }, + "~ud874937d-e901-8094-8005-280b1e40abf0": { + "~:justify-self": "~:auto", + "~:column": 3, + "~:id": "~ud874937d-e901-8094-8005-280b1e40abf0", + "~:position": "~:manual", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 2, + "~:row-span": 1, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcc" + ] + }, + "~ud874937d-e901-8094-8005-280b1e40abef": { + "~:justify-self": "~:auto", + "~:column": 3, + "~:id": "~ud874937d-e901-8094-8005-280b1e40abef", + "~:position": "~:manual", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 1, + "~:row-span": 1, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcd" + ] + }, + "~ud874937d-e901-8094-8005-280b1dbe21db": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud874937d-e901-8094-8005-280b1dbe21db", + "~:position": "~:manual", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 1, + "~:row-span": 1, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcf" + ] + }, + "~ud874937d-e901-8094-8005-280b10c44c06": { + "~:justify-self": "~:center", + "~:column": 1, + "~:id": "~ud874937d-e901-8094-8005-280b10c44c06", + "~:position": "~:manual", + "~:column-span": 1, + "~:align-self": "~:center", + "~:row": 2, + "~:row-span": 1, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd0" + ] + }, + "~ud874937d-e901-8094-8005-280b1dbe21dc": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud874937d-e901-8094-8005-280b1dbe21dc", + "~:position": "~:manual", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 2, + "~:row-span": 1, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bce" + ] + } + }, + "~:layout-padding-type": "~:simple", + "~:type": "~:frame", + "~:points": [ + { + "~#point": { + "~:x": 818.5, + "~:y": 590 + } + }, + { + "~#point": { + "~:x": 1704.5, + "~:y": 590 + } + }, + { + "~#point": { + "~:x": 1704.5, + "~:y": 978.0000000000011 + } + }, + { + "~#point": { + "~:x": 818.5, + "~:y": 978.0000000000011 + } + } + ], + "~:show-content": true, + "~:layout-item-h-sizing": "~:fix", + "~:proportion-lock": false, + "~:layout-gap": { + "~:row-gap": 0, + "~:column-gap": 0 + }, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:layout-item-v-sizing": "~:auto", + "~:layout-justify-content": "~:center", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:layout-justify-items": "~:center", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bca", + "~:layout-align-content": "~:center", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bca", + "~:strokes": [], + "~:x": 818.5, + "~:proportion": 1, + "~:layout-grid-rows": [ + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + } + ], + "~:selrect": { + "~#rect": { + "~:x": 818.5, + "~:y": 589.9999999999999, + "~:width": 886, + "~:height": 388.000000000001, + "~:x1": 818.5, + "~:y1": 589.9999999999999, + "~:x2": 1704.5, + "~:y2": 978.0000000000009 + } + }, + "~:fills": [], + "~:layout-grid-dir": "~:column", + "~:flip-x": null, + "~:height": 388.000000000001, + "~:flip-y": null, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcc", + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcd", + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bce", + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcf", + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd0", + "~u9b0db134-3c1b-800f-8005-285b833b92a1" + ] + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bca": { + "~#shape": { + "~:y": 553, + "~:layout-grid-columns": [ + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + } + ], + "~: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, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Board", + "~:width": 975, + "~:layout-grid-cells": { + "~ud48fcd31-2d57-8057-8005-2711ee36df37": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ee36df37", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 7, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ed8cabd4": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ed8cabd4", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 4, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ed8cabd3": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ed8cabd3", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 4, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ef8afebd": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ef8afebd", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 10, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711e762253b": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711e762253b", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 1, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711e762253a": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711e762253a", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 1, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711eea7b59a": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711eea7b59a", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 9, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711eea7b599": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711eea7b599", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 9, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ee36df38": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ee36df38", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 7, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ef8b1898": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ef8b1898", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 10, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711e7627244": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711e7627244", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 2, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711e7627243": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711e7627243", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 2, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711edc8124d": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711edc8124d", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 5, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ed0fc9ed": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ed0fc9ed", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 3, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711edc8124c": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711edc8124c", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 5, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ed0fc9ec": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ed0fc9ec", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 3, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ee71bbca": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ee71bbca", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 8, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ee02246a": { + "~:justify-self": "~:auto", + "~:column": 2, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ee02246a", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 6, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ee71bbc9": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ee71bbc9", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 8, + "~:row-span": 1, + "~:shapes": [] + }, + "~ud48fcd31-2d57-8057-8005-2711ee022469": { + "~:justify-self": "~:auto", + "~:column": 1, + "~:id": "~ud48fcd31-2d57-8057-8005-2711ee022469", + "~:position": "~:auto", + "~:column-span": 1, + "~:align-self": "~:auto", + "~:row": 6, + "~:row-span": 1, + "~:shapes": [] + } + }, + "~:type": "~:frame", + "~:points": [ + { + "~#point": { + "~:x": 774, + "~:y": 553 + } + }, + { + "~#point": { + "~:x": 1749, + "~:y": 553 + } + }, + { + "~#point": { + "~:x": 1749, + "~:y": 1015 + } + }, + { + "~#point": { + "~:x": 774, + "~:y": 1015 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bca", + "~:layout-justify-items": "~:start", + "~:parent-id": "~u00000000-0000-0000-0000-000000000000", + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 774, + "~:proportion": 1, + "~:layout-grid-rows": [ + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + }, + { + "~:type": "~:flex", + "~:value": 1 + } + ], + "~:selrect": { + "~#rect": { + "~:x": 774, + "~:y": 553, + "~:width": 975, + "~:height": 462, + "~:x1": 774, + "~:y1": 553, + "~:x2": 1749, + "~:y2": 1015 + } + }, + "~:fills": [ + { + "~:fill-color": "#FFFFFF", + "~:fill-opacity": 1 + } + ], + "~:layout-grid-dir": "~:row", + "~:flip-x": null, + "~:height": 462, + "~:flip-y": null, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb" + ] + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd5": { + "~#shape": { + "~:y": 793.0000000000007, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Ellipse", + "~:width": 176, + "~:type": "~:circle", + "~:points": [ + { + "~#point": { + "~:x": 1173.5, + "~:y": 793.0000000000007 + } + }, + { + "~#point": { + "~:x": 1349.5, + "~:y": 793.0000000000007 + } + }, + { + "~#point": { + "~:x": 1349.5, + "~:y": 969.0000000000007 + } + }, + { + "~#point": { + "~:x": 1173.5, + "~:y": 969.0000000000007 + } + } + ], + "~:shape-ref": "~u6ad3e6b9-c5a0-80cf-8005-2830309d2825", + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:scale", + "~:constraints-h": "~:scale", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd5", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bce", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bce", + "~:strokes": [], + "~:x": 1173.5, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 1173.5, + "~:y": 793.0000000000007, + "~:width": 176, + "~:height": 176, + "~:x1": 1173.5, + "~:y1": 793.0000000000007, + "~:x2": 1349.5, + "~:y2": 969.0000000000007 + } + }, + "~:fills": [ + { + "~:fill-color": "#c1c9e1", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:height": 176, + "~:flip-y": null + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd4": { + "~#shape": { + "~:y": 598.9999999999999, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Ellipse", + "~:width": 176.00000000000023, + "~:type": "~:circle", + "~:points": [ + { + "~#point": { + "~:x": 1465.8799999999999, + "~:y": 598.9999999999999 + } + }, + { + "~#point": { + "~:x": 1641.88, + "~:y": 598.9999999999999 + } + }, + { + "~#point": { + "~:x": 1641.88, + "~:y": 775.0000000000003 + } + }, + { + "~#point": { + "~:x": 1465.8799999999999, + "~:y": 775.0000000000003 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:scale", + "~:constraints-h": "~:scale", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd4", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcd", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcd", + "~:strokes": [], + "~:x": 1465.8799999999999, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 1465.8799999999999, + "~:y": 598.9999999999999, + "~:width": 176.00000000000023, + "~:height": 176.00000000000045, + "~:x1": 1465.8799999999999, + "~:y1": 598.9999999999999, + "~:x2": 1641.88, + "~:y2": 775.0000000000003 + } + }, + "~:fills": [ + { + "~:fill-color": "#f4f6ff", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:height": 176.00000000000045, + "~:flip-y": null + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd7": { + "~#shape": { + "~:y": 793.0000000000006, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Ellipse", + "~:width": 176, + "~:type": "~:circle", + "~:points": [ + { + "~#point": { + "~:x": 881.1200000000001, + "~:y": 793.0000000000006 + } + }, + { + "~#point": { + "~:x": 1057.1200000000001, + "~:y": 793.0000000000006 + } + }, + { + "~#point": { + "~:x": 1057.1200000000001, + "~:y": 969.0000000000006 + } + }, + { + "~#point": { + "~:x": 881.1200000000001, + "~:y": 969.0000000000006 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:scale", + "~:constraints-h": "~:scale", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd7", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd0", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd0", + "~:strokes": [], + "~:x": 881.1200000000001, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 881.1200000000001, + "~:y": 793.0000000000006, + "~:width": 176, + "~:height": 176, + "~:x1": 881.1200000000001, + "~:y1": 793.0000000000006, + "~:x2": 1057.1200000000001, + "~:y2": 969.0000000000006 + } + }, + "~:fills": [ + { + "~:fill-color": "#c1c9e1", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:height": 176, + "~:flip-y": null + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd6": { + "~#shape": { + "~:y": 599, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Ellipse", + "~:width": 176, + "~:type": "~:circle", + "~:points": [ + { + "~#point": { + "~:x": 1173.5, + "~:y": 599 + } + }, + { + "~#point": { + "~:x": 1349.5, + "~:y": 599 + } + }, + { + "~#point": { + "~:x": 1349.5, + "~:y": 775.0000000000005 + } + }, + { + "~#point": { + "~:x": 1173.5, + "~:y": 775.0000000000005 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:scale", + "~:constraints-h": "~:scale", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd6", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcf", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcf", + "~:strokes": [], + "~:x": 1173.5, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 1173.5, + "~:y": 599, + "~:width": 176, + "~:height": 176.00000000000045, + "~:x1": 1173.5, + "~:y1": 599, + "~:x2": 1349.5, + "~:y2": 775.0000000000005 + } + }, + "~:fills": [ + { + "~:fill-color": "#c9e1c1", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:height": 176.00000000000045, + "~:flip-y": null + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd0": { + "~#shape": { + "~:y": 793.0000000000006, + "~: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, + "~:hide-in-viewer": true, + "~:name": "B", + "~:width": 176, + "~:type": "~:frame", + "~:points": [ + { + "~#point": { + "~:x": 881.1200000000001, + "~:y": 793.0000000000006 + } + }, + { + "~#point": { + "~:x": 1057.1200000000001, + "~:y": 793.0000000000006 + } + }, + { + "~#point": { + "~:x": 1057.1200000000001, + "~:y": 969.0000000000006 + } + }, + { + "~#point": { + "~:x": 881.1200000000001, + "~:y": 969.0000000000006 + } + } + ], + "~:component-root": true, + "~:show-content": true, + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:top", + "~:constraints-h": "~:left", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd0", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:component-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe3b1793", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcb", + "~:strokes": [], + "~:x": 881.1200000000001, + "~:main-instance": true, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 881.1200000000001, + "~:y": 793.0000000000006, + "~:width": 176, + "~:height": 176, + "~:x1": 881.1200000000001, + "~:y1": 793.0000000000006, + "~:x2": 1057.1200000000001, + "~:y2": 969.0000000000006 + } + }, + "~:fills": [], + "~:flip-x": null, + "~:height": 176, + "~:component-file": "~ue179d9df-de35-80bf-8005-283bbd5516b0", + "~:flip-y": null, + "~:shapes": [ + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd7" + ] + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd3": { + "~#shape": { + "~:y": 589.9999999999999, + "~:r1": 0, + "~:r2": 0, + "~:r3": 0, + "~:r4": 0, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Rectangle", + "~:width": 291.0000000000002, + "~:type": "~:rect", + "~:points": [ + { + "~#point": { + "~:x": 1408.3799999999999, + "~:y": 589.9999999999999 + } + }, + { + "~#point": { + "~:x": 1699.38, + "~:y": 589.9999999999999 + } + }, + { + "~#point": { + "~:x": 1699.38, + "~:y": 784.0000000000003 + } + }, + { + "~#point": { + "~:x": 1408.3799999999999, + "~:y": 784.0000000000003 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:scale", + "~:constraints-h": "~:scale", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd3", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcd", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcd", + "~:strokes": [], + "~:x": 1408.3799999999999, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 1408.3799999999999, + "~:y": 589.9999999999999, + "~:width": 291.0000000000002, + "~:height": 194.00000000000045, + "~:x1": 1408.3799999999999, + "~:y1": 589.9999999999999, + "~:x2": 1699.38, + "~:y2": 784.0000000000003 + } + }, + "~:fills": [ + { + "~:fill-color": "#e1c1dc", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:ry": 0, + "~:height": 194.00000000000045, + "~:flip-y": null + } + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd2": { + "~#shape": { + "~:y": 793.0000000000007, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Ellipse", + "~:width": 176, + "~:type": "~:circle", + "~:points": [ + { + "~#point": { + "~:x": 1465.88, + "~:y": 793.0000000000007 + } + }, + { + "~#point": { + "~:x": 1641.88, + "~:y": 793.0000000000007 + } + }, + { + "~#point": { + "~:x": 1641.88, + "~:y": 969.0000000000009 + } + }, + { + "~#point": { + "~:x": 1465.88, + "~:y": 969.0000000000009 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:constraints-v": "~:scale", + "~:constraints-h": "~:scale", + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd2", + "~:parent-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcc", + "~:frame-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcc", + "~:strokes": [ + { + "~:stroke-alignment": "~:inner", + "~:stroke-style": "~:solid", + "~:stroke-color": "#e1c1dc", + "~:stroke-opacity": 1, + "~:stroke-width": 1, + "~:color": "#e1c1dc", + "~:opacity": 1 + } + ], + "~:x": 1465.88, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 1465.88, + "~:y": 793.0000000000007, + "~:width": 176, + "~:height": 176.00000000000023, + "~:x1": 1465.88, + "~:y1": 793.0000000000007, + "~:x2": 1641.88, + "~:y2": 969.0000000000009 + } + }, + "~:fills": [ + { + "~:fill-color": "#f4f6ff", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:height": 176.00000000000023, + "~:flip-y": null + } + } + }, + "~:id": "~ue179d9df-de35-80bf-8005-283bbd5516b1", + "~:name": "Page 1" + } + }, + "~:id": "~ue179d9df-de35-80bf-8005-283bbd5516b0", + "~:options": { + "~:components-v2": true + }, + "~:components": { + "~u6ad3e6b9-c5a0-80cf-8005-283bbe38dba8": { + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe38dba8", + "~:name": "F", + "~:path": "", + "~:modified-at": "~m1729604566311", + "~:main-instance-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcc", + "~:main-instance-page": "~ue179d9df-de35-80bf-8005-283bbd5516b1" + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe39bb51": { + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe39bb51", + "~:name": "E", + "~:path": "Letters", + "~:modified-at": "~m1729604566311", + "~:main-instance-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcd", + "~:main-instance-page": "~ue179d9df-de35-80bf-8005-283bbd5516b1" + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe3a9014": { + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe3a9014", + "~:name": "C", + "~:path": "Others", + "~:modified-at": "~m1729604566311", + "~:main-instance-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bcf", + "~:main-instance-page": "~ue179d9df-de35-80bf-8005-283bbd5516b1" + }, + "~u6ad3e6b9-c5a0-80cf-8005-283bbe3b1793": { + "~:id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe3b1793", + "~:name": "B", + "~:path": "", + "~:modified-at": "~m1729604566311", + "~:main-instance-id": "~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd0", + "~:main-instance-page": "~ue179d9df-de35-80bf-8005-283bbd5516b1" + } + } + } +} diff --git a/frontend/playwright/data/workspace/get-file-many-colors.json b/frontend/playwright/data/workspace/get-file-many-colors.json new file mode 100644 index 0000000000..bd2c2816c2 --- /dev/null +++ b/frontend/playwright/data/workspace/get-file-many-colors.json @@ -0,0 +1,423 @@ +{ + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~: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": "Many colors", + "~:revn": 2, + "~:modified-at": "~m1717512948250", + "~:id": "~uc1249a66-fce0-8175-8004-7433fe4be8bc", + "~:is-shared": true, + "~:version": 48, + "~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:created-at": "~m1717512934704", + "~:data": { + "~:pages": [ + "~uc1249a66-fce0-8175-8004-7433fe4be8bd" + ], + "~:pages-index": { + "~uc1249a66-fce0-8175-8004-7433fe4be8bd": { + "~:options": {}, + "~:objects": { + "~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, + "~:y": 0 + } + }, + { + "~#point": { + "~:x": 0.01, + "~:y": 0 + } + }, + { + "~#point": { + "~:x": 0.01, + "~:y": 0.01 + } + }, + { + "~#point": { + "~:x": 0, + "~:y": 0.01 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.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, + "~:selrect": { + "~#rect": { + "~:x": 0, + "~:y": 0, + "~:width": 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, + "~:height": 0.01, + "~:flip-y": null, + "~:shapes": [ + "~uc70224ec-c410-807b-8004-743400e00be8" + ] + } + }, + "~uc70224ec-c410-807b-8004-743400e00be8": { + "~#shape": { + "~:y": 255, + "~:r1": 0, + "~:r2": 0, + "~:r3": 0, + "~:r4": 0, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Rectangle", + "~:width": 279.0000000000001, + "~:type": "~:rect", + "~:points": [ + { + "~#point": { + "~:x": 523, + "~:y": 255 + } + }, + { + "~#point": { + "~:x": 802.0000000000001, + "~:y": 255 + } + }, + { + "~#point": { + "~:x": 802.0000000000001, + "~:y": 534 + } + }, + { + "~#point": { + "~:x": 523, + "~:y": 534 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:id": "~uc70224ec-c410-807b-8004-743400e00be8", + "~:parent-id": "~u00000000-0000-0000-0000-000000000000", + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 523, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 523, + "~:y": 255, + "~:width": 279.0000000000001, + "~:height": 279, + "~:x1": 523, + "~:y1": 255, + "~:x2": 802.0000000000001, + "~:y2": 534 + } + }, + "~:fills": [ + { + "~:fill-color": "#B1B2B5", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:ry": 0, + "~:height": 279, + "~:flip-y": null + } + } + }, + "~:id": "~uc1249a66-fce0-8175-8004-7433fe4be8bd", + "~:name": "Page 1" + } + }, + "~:id": "~uc1249a66-fce0-8175-8004-7433fe4be8bc", + "~:options": { + "~:components-v2": true + }, + "~:recent-colors": [ + { + "~:color": "#187cd5", + "~:opacity": 1 + } + ], + "~:colors": { + "~uc70224ec-c410-807b-8004-74340616d000": { + "~:path": "", + "~:color": "#d84b4b", + "~:name": "color-01", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d000" + }, + "~uc70224ec-c410-807b-8004-74340616d001": { + "~:path": "", + "~:color": "#d86f4b", + "~:name": "color-02", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d001" + }, + "~uc70224ec-c410-807b-8004-74340616d002": { + "~:path": "", + "~:color": "#d8924b", + "~:name": "color-03", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d002" + }, + "~uc70224ec-c410-807b-8004-74340616d003": { + "~:path": "", + "~:color": "#d8b54b", + "~:name": "color-04", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d003" + }, + "~uc70224ec-c410-807b-8004-74340616d004": { + "~:path": "", + "~:color": "#d8d84b", + "~:name": "color-05", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d004" + }, + "~uc70224ec-c410-807b-8004-74340616d005": { + "~:path": "", + "~:color": "#b5d84b", + "~:name": "color-06", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d005" + }, + "~uc70224ec-c410-807b-8004-74340616d006": { + "~:path": "", + "~:color": "#92d84b", + "~:name": "color-07", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d006" + }, + "~uc70224ec-c410-807b-8004-74340616d007": { + "~:path": "", + "~:color": "#6fd84b", + "~:name": "color-08", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d007" + }, + "~uc70224ec-c410-807b-8004-74340616d008": { + "~:path": "", + "~:color": "#4bd84b", + "~:name": "color-09", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d008" + }, + "~uc70224ec-c410-807b-8004-74340616d009": { + "~:path": "", + "~:color": "#4bd86f", + "~:name": "color-10", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d009" + }, + "~uc70224ec-c410-807b-8004-74340616d00a": { + "~:path": "", + "~:color": "#4bd892", + "~:name": "color-11", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d00a" + }, + "~uc70224ec-c410-807b-8004-74340616d00b": { + "~:path": "", + "~:color": "#4bd8b5", + "~:name": "color-12", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d00b" + }, + "~uc70224ec-c410-807b-8004-74340616d00c": { + "~:path": "", + "~:color": "#4bd8d8", + "~:name": "color-13", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d00c" + }, + "~uc70224ec-c410-807b-8004-74340616d00d": { + "~:path": "", + "~:color": "#4bb5d8", + "~:name": "color-14", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d00d" + }, + "~uc70224ec-c410-807b-8004-74340616d00e": { + "~:path": "", + "~:color": "#4b92d8", + "~:name": "color-15", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d00e" + }, + "~uc70224ec-c410-807b-8004-74340616d00f": { + "~:path": "", + "~:color": "#4b6fd8", + "~:name": "color-16", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d00f" + }, + "~uc70224ec-c410-807b-8004-74340616d010": { + "~:path": "", + "~:color": "#4b4bd8", + "~:name": "color-17", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d010" + }, + "~uc70224ec-c410-807b-8004-74340616d011": { + "~:path": "", + "~:color": "#6f4bd8", + "~:name": "color-18", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d011" + }, + "~uc70224ec-c410-807b-8004-74340616d012": { + "~:path": "", + "~:color": "#924bd8", + "~:name": "color-19", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d012" + }, + "~uc70224ec-c410-807b-8004-74340616d013": { + "~:path": "", + "~:color": "#b54bd8", + "~:name": "color-20", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d013" + }, + "~uc70224ec-c410-807b-8004-74340616d014": { + "~:path": "", + "~:color": "#d84bd8", + "~:name": "color-21", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d014" + }, + "~uc70224ec-c410-807b-8004-74340616d015": { + "~:path": "", + "~:color": "#d84bb5", + "~:name": "color-22", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d015" + }, + "~uc70224ec-c410-807b-8004-74340616d016": { + "~:path": "", + "~:color": "#d84b92", + "~:name": "color-23", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d016" + }, + "~uc70224ec-c410-807b-8004-74340616d017": { + "~:path": "", + "~:color": "#d84b6f", + "~:name": "color-24", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616d017" + } + } + } +} \ No newline at end of file diff --git a/frontend/playwright/data/workspace/get-file-many-typographies.json b/frontend/playwright/data/workspace/get-file-many-typographies.json new file mode 100644 index 0000000000..1ec32392eb --- /dev/null +++ b/frontend/playwright/data/workspace/get-file-many-typographies.json @@ -0,0 +1,465 @@ +{ + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~: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": "Many typographies", + "~:revn": 2, + "~:modified-at": "~m1717512948250", + "~:id": "~uc1249a66-fce0-8175-8004-7433fe4be8bc", + "~:is-shared": true, + "~:version": 48, + "~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b", + "~:created-at": "~m1717512934704", + "~:data": { + "~:pages": [ + "~uc1249a66-fce0-8175-8004-7433fe4be8bd" + ], + "~:pages-index": { + "~uc1249a66-fce0-8175-8004-7433fe4be8bd": { + "~:options": {}, + "~:objects": { + "~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, + "~:y": 0 + } + }, + { + "~#point": { + "~:x": 0.01, + "~:y": 0 + } + }, + { + "~#point": { + "~:x": 0.01, + "~:y": 0.01 + } + }, + { + "~#point": { + "~:x": 0, + "~:y": 0.01 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.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, + "~:selrect": { + "~#rect": { + "~:x": 0, + "~:y": 0, + "~:width": 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, + "~:height": 0.01, + "~:flip-y": null, + "~:shapes": [ + "~uc70224ec-c410-807b-8004-743400e00be8" + ] + } + }, + "~uc70224ec-c410-807b-8004-743400e00be8": { + "~#shape": { + "~:y": 255, + "~:r1": 0, + "~:r2": 0, + "~:r3": 0, + "~:r4": 0, + "~:transform": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:rotation": 0, + "~:grow-type": "~:fixed", + "~:hide-in-viewer": false, + "~:name": "Rectangle", + "~:width": 279.0000000000001, + "~:type": "~:rect", + "~:points": [ + { + "~#point": { + "~:x": 523, + "~:y": 255 + } + }, + { + "~#point": { + "~:x": 802.0000000000001, + "~:y": 255 + } + }, + { + "~#point": { + "~:x": 802.0000000000001, + "~:y": 534 + } + }, + { + "~#point": { + "~:x": 523, + "~:y": 534 + } + } + ], + "~:proportion-lock": false, + "~:transform-inverse": { + "~#matrix": { + "~:a": 1.0, + "~:b": 0.0, + "~:c": 0.0, + "~:d": 1.0, + "~:e": 0.0, + "~:f": 0.0 + } + }, + "~:id": "~uc70224ec-c410-807b-8004-743400e00be8", + "~:parent-id": "~u00000000-0000-0000-0000-000000000000", + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 523, + "~:proportion": 1, + "~:selrect": { + "~#rect": { + "~:x": 523, + "~:y": 255, + "~:width": 279.0000000000001, + "~:height": 279, + "~:x1": 523, + "~:y1": 255, + "~:x2": 802.0000000000001, + "~:y2": 534 + } + }, + "~:fills": [ + { + "~:fill-color": "#B1B2B5", + "~:fill-opacity": 1 + } + ], + "~:flip-x": null, + "~:ry": 0, + "~:height": 279, + "~:flip-y": null + } + } + }, + "~:id": "~uc1249a66-fce0-8175-8004-7433fe4be8bd", + "~:name": "Page 1" + } + }, + "~:id": "~uc1249a66-fce0-8175-8004-7433fe4be8bc", + "~:options": { + "~:components-v2": true + }, + "~:recent-colors": [ + { + "~:color": "#187cd5", + "~:opacity": 1 + } + ], + "~:colors": { + "~uc70224ec-c410-807b-8004-74340616cffb": { + "~:path": "", + "~:color": "#187cd5", + "~:name": "test-color-187cd5", + "~:modified-at": "~m1717512945259", + "~:opacity": 1, + "~:id": "~uc70224ec-c410-807b-8004-74340616cffb" + } + }, + "~:typographies": { + "~ue117f7f6-433c-807e-8004-862ab6ae3000": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "12", + "~:font-weight": "400", + "~:name": "Type 01 Regular 12", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "regular", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3000", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae3001": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "12", + "~:font-weight": "700", + "~:name": "Type 02 Bold 12", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "700", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3001", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae3002": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "14", + "~:font-weight": "400", + "~:name": "Type 03 Regular 14", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "regular", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3002", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae3003": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "14", + "~:font-weight": "700", + "~:name": "Type 04 Bold 14", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "700", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3003", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae3004": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "16", + "~:font-weight": "400", + "~:name": "Type 05 Regular 16", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "regular", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3004", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae3005": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "16", + "~:font-weight": "700", + "~:name": "Type 06 Bold 16", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "700", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3005", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae3006": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "18", + "~:font-weight": "400", + "~:name": "Type 07 Regular 18", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "regular", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3006", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae3007": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "18", + "~:font-weight": "700", + "~:name": "Type 08 Bold 18", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "700", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3007", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae3008": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "20", + "~:font-weight": "400", + "~:name": "Type 09 Regular 20", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "regular", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3008", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae3009": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "20", + "~:font-weight": "700", + "~:name": "Type 10 Bold 20", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "700", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae3009", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae300a": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "22", + "~:font-weight": "400", + "~:name": "Type 11 Regular 22", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "regular", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae300a", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae300b": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "22", + "~:font-weight": "700", + "~:name": "Type 12 Bold 22", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "700", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae300b", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae300c": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "24", + "~:font-weight": "400", + "~:name": "Type 13 Regular 24", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "regular", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae300c", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae300d": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "24", + "~:font-weight": "700", + "~:name": "Type 14 Bold 24", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "700", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae300d", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae300e": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "26", + "~:font-weight": "400", + "~:name": "Type 15 Regular 26", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "regular", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae300e", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + }, + "~ue117f7f6-433c-807e-8004-862ab6ae300f": { + "~:line-height": "1.2", + "~:font-style": "normal", + "~:text-transform": "none", + "~:font-id": "sourcesanspro", + "~:font-size": "26", + "~:font-weight": "700", + "~:name": "Type 16 Bold 26", + "~:modified-at": "~m1718718464655", + "~:font-variant-id": "700", + "~:id": "~ue117f7f6-433c-807e-8004-862ab6ae300f", + "~:letter-spacing": "0", + "~:font-family": "sourcesanspro" + } + } + } +} \ No newline at end of file diff --git a/frontend/playwright/data/workspace/get-library-file-references-empty.json b/frontend/playwright/data/workspace/get-library-file-references-empty.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/frontend/playwright/data/workspace/get-library-file-references-empty.json @@ -0,0 +1 @@ +[] diff --git a/frontend/playwright/data/workspace/get-team-plugins.json b/frontend/playwright/data/workspace/get-team-plugins.json new file mode 100644 index 0000000000..83d7cf47ce --- /dev/null +++ b/frontend/playwright/data/workspace/get-team-plugins.json @@ -0,0 +1,24 @@ +{ + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type", + "plugins/runtime" + ] + }, + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true + }, + "~:name": "Default", + "~:modified-at": "~m1713533116375", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f40f6d", + "~:created-at": "~m1713533116375", + "~:is-default": true +} diff --git a/frontend/playwright/data/workspace/get-teams-shared.json b/frontend/playwright/data/workspace/get-teams-shared.json new file mode 100644 index 0000000000..2c6119c75f --- /dev/null +++ b/frontend/playwright/data/workspace/get-teams-shared.json @@ -0,0 +1,25 @@ +[ + { + "~:features": { + "~#set": [ + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true + }, + "~:name": "Default", + "~:modified-at": "~m1713533116375", + "~:id": "~uc7ce0794-0992-8105-8004-38e630f7920a", + "~:created-at": "~m1713533116375", + "~:is-default": false + } +] diff --git a/frontend/playwright/ui/pages/DashboardPage.js b/frontend/playwright/ui/pages/DashboardPage.js index 8532455c88..8d0f7df082 100644 --- a/frontend/playwright/ui/pages/DashboardPage.js +++ b/frontend/playwright/ui/pages/DashboardPage.js @@ -82,7 +82,10 @@ export class DashboardPage extends BaseWebSocketPage { this.fontsLink = this.sidebar.getByText("Fonts"); this.librariesLink = this.sidebar.getByText("Libraries"); - this.searchButton = page.getByRole("button", { name: "dashboard-search" }); + this.searchButton = page.getByRole("button", { + name: "Search", + exact: true, + }); this.searchInput = page.getByPlaceholder("Search…"); this.teamDropdown = this.sidebar.getByRole("button", { diff --git a/frontend/playwright/ui/specs/colorpicker.spec.js b/frontend/playwright/ui/specs/colorpicker.spec.js index ee9dbcd770..6625cb4b8a 100644 --- a/frontend/playwright/ui/specs/colorpicker.spec.js +++ b/frontend/playwright/ui/specs/colorpicker.spec.js @@ -144,15 +144,14 @@ test("Create a RADIAL gradient", async ({ page }) => { const gradOption = workspacePage.page.getByText("Gradient"); await gradOption.click(); - const gradTypeOptions = workspacePage.colorpicker - .locator("div") - .filter({ hasText: "Linear" }) - .nth(3); - await gradTypeOptions.click(); + const gradTypeSelect = workspacePage.colorpicker + .getByRole("combobox") + .filter({ hasText: "Linear" }); + await gradTypeSelect.click(); - const gradRadialOption = workspacePage.page - .locator("li") - .filter({ hasText: "Radial" }); + const gradRadialOption = workspacePage.page.getByRole("option", { + name: "Radial", + }); await gradRadialOption.click(); const addStopBtn = workspacePage.page.getByRole("button", { diff --git a/frontend/playwright/ui/specs/dashboard.spec.js b/frontend/playwright/ui/specs/dashboard.spec.js index 044ed5226a..d20b90cfd4 100644 --- a/frontend/playwright/ui/specs/dashboard.spec.js +++ b/frontend/playwright/ui/specs/dashboard.spec.js @@ -131,9 +131,9 @@ test("Bug 9443, Admin can not demote owner", async ({ page }) => { await dashboardPage.goToSecondTeamMembersSection(); await expect(page.getByRole("heading", { name: "Members" })).toBeVisible(); - await expect(page.getByRole("combobox", { name: "Admin" })).toBeVisible(); + await expect(page.getByRole("button", { name: "Admin" })).toBeVisible(); await expect(page.getByText("Owner")).toBeVisible(); - await expect(page.getByRole("combobox", { name: "Owner" })).toHaveCount(0); + await expect(page.getByRole("button", { name: "Owner" })).toHaveCount(0); }); test("Bug 9927, Don't show the banner to invite team members if the user has dismissed it", async ({ diff --git a/frontend/playwright/ui/specs/email-verification.spec.js b/frontend/playwright/ui/specs/email-verification.spec.js index 65c1a9cb99..f15be88e20 100644 --- a/frontend/playwright/ui/specs/email-verification.spec.js +++ b/frontend/playwright/ui/specs/email-verification.spec.js @@ -1,32 +1,33 @@ import { test, expect } from "@playwright/test"; import { RegisterPage } from "../pages/RegisterPage"; -// Regression test for the bug where a freshly verified account (whose -// profile never had a theme persisted) ended up with an empty string as -// its theme instead of falling back to the dark default: the workspace -// switched to light mode and Settings > UI Theme showed a blank field. +// A freshly verified account has no saved theme preference. It must follow +// the system theme and show that preference in settings, not a blank field. test.beforeEach(async ({ page }) => { await RegisterPage.initWithLoggedOutUser(page); }); test.describe("Email verification", () => { - test("Newly verified account defaults to the dark theme", async ({ - page, - }) => { - const registerPage = new RegisterPage(page); - await registerPage.setupEmailVerificationSuccess(); + for (const colorScheme of ["light", "dark"]) { + test(`Newly verified account follows the ${colorScheme} system theme`, async ({ + page, + }) => { + await page.emulateMedia({ colorScheme }); + const registerPage = new RegisterPage(page); + await registerPage.setupEmailVerificationSuccess(); - await registerPage.goToVerifyToken(); - await page.waitForURL(/screen=dashboard/); + await registerPage.goToVerifyToken(); + await page.waitForURL(/screen=dashboard/); - // `default` is the body class applied for dark theme, `light` for - // light theme (see app.util.theme/set-color-scheme). - await expect(page.locator("body")).toHaveClass(/default/); - await expect(page.locator("body")).not.toHaveClass(/light/); - }); + // The dark theme uses the legacy body class "default". + await expect(page.locator("body")).toHaveClass( + colorScheme === "dark" ? "default" : "light", + ); + }); + } - test("Settings > UI Theme shows Penpot Dark (default) selected, not blank", async ({ + test("Settings > UI Theme shows System theme selected, not blank", async ({ page, }) => { const registerPage = new RegisterPage(page); @@ -40,6 +41,6 @@ test.describe("Email verification", () => { // The language select is the first combobox on the page, the theme // select is the second one. const themeSelect = page.getByRole("combobox").nth(1); - await expect(themeSelect).toHaveText("Penpot Dark (default)"); + await expect(themeSelect).toHaveText("System theme"); }); }); diff --git a/frontend/playwright/ui/specs/escape-closes-panels.spec.js b/frontend/playwright/ui/specs/escape-closes-panels.spec.js new file mode 100644 index 0000000000..23f86539f8 --- /dev/null +++ b/frontend/playwright/ui/specs/escape-closes-panels.spec.js @@ -0,0 +1,138 @@ +import { test, expect } from "@playwright/test"; +import { WorkspacePage } from "../pages/WorkspacePage"; + +const VIEWPORT_POINTS = [ + [200, 200], + [400, 220], + [320, 420], +]; + +const openSearch = async (page) => { + await page.getByRole("button", { name: "Search" }).click(); + const findInput = page.getByRole("textbox", { name: /^Find/ }); + await expect(findInput).toBeVisible(); + return findInput; +}; + +const openShortcuts = async (page) => { + await page.keyboard.press("?"); + const searchInput = page.getByPlaceholder("Search shortcuts"); + await expect(searchInput).toBeVisible(); + return searchInput; +}; + +const saveVersionButton = (page) => + page.getByRole("button", { name: "Save version" }); + +const openHistoryAssertion = (page) => + expect(saveVersionButton(page)).toBeVisible(); + +test.beforeEach(async ({ page }) => { + await WorkspacePage.init(page); + const workspacePage = new WorkspacePage(page); + await workspacePage.setupEmptyFile(); + await workspacePage.mockRPC( + "get-file-snapshots?file-id=*", + "workspace/versions-snapshot-1.json", + ); + await workspacePage.goToWorkspace(); +}); + +test("Escape closes the layers search panel", async ({ page }) => { + const findInput = await openSearch(page); + + await page.keyboard.press("Escape"); + + await expect(findInput).toHaveCount(0); +}); + +test("Escape closes the search filters menu before the search panel", async ({ + page, +}) => { + const findInput = await openSearch(page); + await page.getByRole("button", { name: "Filter" }).click(); + const menuItem = page.getByText("Masks", { exact: true }); + await expect(menuItem).toBeVisible(); + + await page.keyboard.press("Escape"); + await expect(menuItem).toHaveCount(0); + await expect(findInput).toBeVisible(); + + await page.keyboard.press("Escape"); + await expect(findInput).toHaveCount(0); +}); + +test("Escape keeps the search panel open after clicking the viewport", async ({ + page, +}) => { + const workspacePage = new WorkspacePage(page); + const findInput = await openSearch(page); + + await workspacePage.clickAt(...VIEWPORT_POINTS[0]); + await page.keyboard.press("Escape"); + await expect(findInput).toBeVisible(); + + // The click moves focus out of the panel, so a real key press would + // never reach its handler. Dispatch Escape on the input instead, to + // exercise the case the hook exists for: the event reaches the panel + // although the user's last action was on the canvas. + await findInput.dispatchEvent("keydown", { + key: "Escape", + code: "Escape", + keyCode: 27, + bubbles: true, + }); + + await expect(findInput).toBeVisible(); +}); + +test("Escape closes the shortcuts panel right after it opens", async ({ + page, +}) => { + const searchInput = await openShortcuts(page); + + await page.keyboard.press("Escape"); + + await expect(searchInput).toHaveCount(0); +}); + +test("Escape closes History opened with its shortcut", async ({ page }) => { + await page.keyboard.press("ControlOrMeta+Alt+h"); + await openHistoryAssertion(page); + + await page.keyboard.press("Escape"); + + await expect(saveVersionButton(page)).toHaveCount(0); +}); + +test("Escape closes History opened from the main menu", async ({ page }) => { + await page.getByRole("button", { name: "Main menu" }).click(); + await page.getByText("file").last().click(); + await page.locator("#file-menu-show-version-history").click(); + await openHistoryAssertion(page); + + await page.keyboard.press("Escape"); + + await expect(saveVersionButton(page)).toHaveCount(0); +}); + +test("Escape finishes a path drawn with the shortcuts panel open", async ({ + page, +}) => { + const workspacePage = new WorkspacePage(page); + const searchInput = await openShortcuts(page); + await page.getByRole("button", { name: "BASICS" }).click(); + + await page.keyboard.press("p"); + for (const [x, y] of VIEWPORT_POINTS) { + await workspacePage.clickAt(x, y); + } + await page.keyboard.press("Escape"); + + // The shortcuts panel replaces the layers tree, so the new path is + // observed through the design tab of the selected shape. + await expect( + page.getByRole("region", { name: "Stroke section" }), + ).toBeVisible(); + await expect(searchInput).toBeVisible(); +}); diff --git a/frontend/playwright/ui/specs/export-frames.spec.js b/frontend/playwright/ui/specs/export-frames.spec.js index fee390204d..aa34953a83 100644 --- a/frontend/playwright/ui/specs/export-frames.spec.js +++ b/frontend/playwright/ui/specs/export-frames.spec.js @@ -142,7 +142,10 @@ test.describe("Export frames to PDF", () => { await expect(page.getByText("1 of 2 elements selected")).toBeVisible(); // The export button should still be enabled - const exportButton = page.locator("input[type='button'][value='Export']"); + const exportButton = page.getByRole("button", { + name: "Export", + exact: true, + }); await expect(exportButton).toBeEnabled(); }); diff --git a/frontend/playwright/ui/specs/tokens/detach-reference.spec.js b/frontend/playwright/ui/specs/tokens/detach-reference.spec.js new file mode 100644 index 0000000000..b56f14f655 --- /dev/null +++ b/frontend/playwright/ui/specs/tokens/detach-reference.spec.js @@ -0,0 +1,194 @@ +import { test, expect } from "@playwright/test"; +import { WasmWorkspacePage } from "../../pages/WasmWorkspacePage"; +import { BaseWebSocketPage } from "../../pages/BaseWebSocketPage"; +import { + setupEmptyTokensFileRender, + setupTypographyTokensFileRender, + unfoldTokenType, +} from "./helpers"; + +test.beforeEach(async ({ page }) => { + await WasmWorkspacePage.init(page); + await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-tokens.json"); +}); + +const openEditModal = async (page, tokensTabPanel, name) => { + await tokensTabPanel + .getByRole("button", { name, exact: true }) + .click({ button: "right" }); + await page.getByText("Edit token").click(); +}; + +// Hovers the reference input and clicks the detach button, which is only +// visible while the reference row is hovered or focused. +const detachReference = async (modal) => { + const referenceField = modal.getByRole("textbox", { name: "Reference" }); + const detachButton = modal.getByRole("button", { name: "Detach reference" }); + + await expect(referenceField).toBeVisible(); + await expect(detachButton).toBeHidden(); + + await referenceField.hover(); + await expect(detachButton).toBeVisible(); + await detachButton.click(); +}; + +test.describe("Tokens - detach reference", () => { + test("User detaches a typography reference and edits the copied values", async ({ + page, + }) => { + const { tokensUpdateCreateModal, tokensSidebar } = + await setupTypographyTokensFileRender(page); + const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); + const saveButton = tokensUpdateCreateModal.getByRole("button", { + name: "Save", + }); + + // Create a typography token that references "Full" from the fixture. + await tokensTabPanel + .getByRole("button", { name: "Add Token: Typography" }) + .click(); + await expect(tokensUpdateCreateModal).toBeVisible(); + await tokensUpdateCreateModal.getByLabel("Name").fill("detached"); + await tokensUpdateCreateModal.getByTestId("reference-opt").click(); + await tokensUpdateCreateModal + .getByRole("textbox", { name: "Reference" }) + .fill("{Full}"); + await expect(saveButton).toBeEnabled(); + await saveButton.click(); + await expect(tokensUpdateCreateModal).not.toBeVisible(); + + await unfoldTokenType(tokensSidebar, "typography"); + await openEditModal(page, tokensTabPanel, "detached"); + await expect(tokensUpdateCreateModal).toBeVisible(); + + await detachReference(tokensUpdateCreateModal); + + // The values of "Full" are now in the composite tab. + const fontFamilyField = tokensUpdateCreateModal + .getByRole("textbox", { name: "Font family" }) + .first(); + const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Font Size", + }); + + await expect(fontFamilyField).toHaveValue("42dot Sans"); + await expect(fontSizeField).toHaveValue("100"); + await expect( + tokensUpdateCreateModal.getByRole("textbox", { name: "Font Weight" }), + ).toHaveValue("300"); + await expect( + tokensUpdateCreateModal.getByRole("textbox", { name: "Line Height" }), + ).toHaveValue("2"); + await expect( + tokensUpdateCreateModal.getByRole("textbox", { name: "Letter Spacing" }), + ).toHaveValue("2"); + await expect( + tokensUpdateCreateModal.getByRole("textbox", { name: "Text Case" }), + ).toHaveValue("uppercase"); + await expect( + tokensUpdateCreateModal.getByRole("textbox", { name: "Text Decoration" }), + ).toHaveValue("underline"); + + await fontSizeField.fill("24"); + await expect(saveButton).toBeEnabled(); + await saveButton.click(); + await expect(tokensUpdateCreateModal).not.toBeVisible(); + + // The detached token keeps its own values. + await openEditModal(page, tokensTabPanel, "detached"); + await expect(fontSizeField).toHaveValue("24"); + await tokensUpdateCreateModal + .getByRole("button", { name: "Cancel" }) + .click(); + + // The referenced token is not modified. + await openEditModal(page, tokensTabPanel, "Full"); + await expect(fontSizeField).toHaveValue("100"); + }); + + test("User detaches a shadow reference and edits the copied values", async ({ + page, + }) => { + const { tokensUpdateCreateModal } = await setupEmptyTokensFileRender(page, { + flags: ["enable-token-shadow"], + }); + const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); + const addShadowButton = tokensTabPanel.getByRole("button", { + name: "Add Token: Shadow", + }); + const nameField = tokensUpdateCreateModal.getByLabel("Name"); + const colorField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Color", + }); + const offsetXField = tokensUpdateCreateModal.getByRole("textbox", { + name: "X", + }); + const offsetYField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Y", + }); + const blurField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Blur", + }); + const spreadField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Spread", + }); + const saveButton = tokensUpdateCreateModal.getByRole("button", { + name: "Save", + }); + + // Base shadow token. + await addShadowButton.click(); + await expect(tokensUpdateCreateModal).toBeVisible(); + await nameField.fill("base-shadow"); + await colorField.fill("red"); + await offsetXField.fill("2"); + await offsetYField.fill("3"); + await blurField.fill("5"); + await spreadField.fill("1"); + await expect(saveButton).toBeEnabled(); + await saveButton.click(); + await expect(tokensUpdateCreateModal).not.toBeVisible(); + + // Shadow token that references the base one. + await addShadowButton.click(); + await expect(tokensUpdateCreateModal).toBeVisible(); + await nameField.fill("alias-shadow"); + await tokensUpdateCreateModal.getByTestId("reference-opt").click(); + await tokensUpdateCreateModal + .getByRole("textbox", { name: "Reference" }) + .fill("{base-shadow}"); + await expect(saveButton).toBeEnabled(); + await saveButton.click(); + await expect(tokensUpdateCreateModal).not.toBeVisible(); + + await unfoldTokenType(tokensTabPanel, "shadow"); + await openEditModal(page, tokensTabPanel, "alias-shadow"); + await expect(tokensUpdateCreateModal).toBeVisible(); + + await detachReference(tokensUpdateCreateModal); + + // The values of "base-shadow" are now in the shadow tab. + await expect(colorField).toHaveValue("red"); + await expect(offsetXField).toHaveValue("2"); + await expect(offsetYField).toHaveValue("3"); + await expect(blurField).toHaveValue("5"); + await expect(spreadField).toHaveValue("1"); + + await offsetXField.fill("10"); + await expect(saveButton).toBeEnabled(); + await saveButton.click(); + await expect(tokensUpdateCreateModal).not.toBeVisible(); + + // The detached token keeps its own values. + await openEditModal(page, tokensTabPanel, "alias-shadow"); + await expect(offsetXField).toHaveValue("10"); + await tokensUpdateCreateModal + .getByRole("button", { name: "Cancel" }) + .click(); + + // The referenced token is not modified. + await openEditModal(page, tokensTabPanel, "base-shadow"); + await expect(offsetXField).toHaveValue("2"); + }); +}); diff --git a/frontend/playwright/ui/specs/workspace.spec.js b/frontend/playwright/ui/specs/workspace.spec.js index 051530b877..b59266f5eb 100644 --- a/frontend/playwright/ui/specs/workspace.spec.js +++ b/frontend/playwright/ui/specs/workspace.spec.js @@ -107,6 +107,8 @@ test("Selection size badge appears on selection and hides on deselect", async ({ test("Selection size badge uses component color for component selection", async ({ page, }) => { + // The expected component color below is the dark palette value. + await page.emulateMedia({ colorScheme: "dark" }); const workspacePage = new WasmWorkspacePage(page); await workspacePage.setupEmptyFile(); await workspacePage.mockGetFile("components/get-file-13267.json"); diff --git a/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js index 3b783793f6..e53180e4dc 100644 --- a/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js +++ b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js @@ -317,3 +317,750 @@ test("User goes to the team settings section", async ({ page }) => { await expect(page.getByText("TEAM INFO")).toBeVisible(); await expect(dashboardPage.page).toHaveScreenshot(); }); + +test.describe("Delete account modal", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Delete account modal buttons", async ({ page }) => { + await page.goto("/?screen=settings-profile"); + await page.getByText("Want to remove your account?").click(); + + const modal = page.locator("[class*='modal-container']"); + await expect(page.getByTestId("delete-account-btn")).toBeVisible(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + + await modal.locator("[class*='modal-header'] button").hover(); + await expect(modal).toHaveScreenshot(shot); + + await page.getByTestId("delete-account-btn").hover(); + await expect(modal).toHaveScreenshot(shot); + }); +}); + +test.describe("Fonts page controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Installed font variant, menu and edition", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.goToDashboard(); + await dashboardPage.fontsLink.click(); + await expect(dashboardPage.mainHeading).toHaveText("Fonts"); + + const installed = page.locator("[class*='dashboard-installed-fonts']"); + const row = installed.locator("[class*='font-item']").first(); + await expect(row).toBeVisible(); + + // The variant delete icon only shows on hover. + await row.locator("[class$='__variant'], [class*='__variant ']").hover(); + await expect(installed).toHaveScreenshot(shot); + + // The menu button: a span before the DS migration, a DS icon button + // named "Options" after it. + const options = row.locator("[class*='options']"); + await options + .locator("span[class*='icon']") + .or(options.getByRole("button", { name: "Options" })) + .first() + .click(); + await expect(page.getByText("Download")).toBeVisible(); + await expect(page).toHaveScreenshot(shot); + + await page.getByText("Edit", { exact: true }).click(); + await expect(row.locator("input")).toBeVisible(); + await page.mouse.move(10, 700); + await expect(installed).toHaveScreenshot(shot); + }); + + test("Fonts ready to upload", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.goToDashboard(); + await dashboardPage.fontsLink.click(); + await expect(dashboardPage.mainHeading).toHaveText("Fonts"); + + await page + .locator("#font-upload") + .setInputFiles("playwright/data/render-wasm/assets/ebgaramond.ttf"); + await expect(page.getByTestId("upload-all")).toBeVisible(); + await page.mouse.move(10, 700); + await expect( + page.locator("[class*='dashboard-fonts-upload']"), + ).toHaveScreenshot(shot); + }); + + test("Fonts loading placeholder", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + // Never answer, so the page keeps showing the loader. + await page.route(/get-font-variants/, () => {}); + await dashboardPage.goToDashboard(); + await dashboardPage.fontsLink.click(); + await expect(dashboardPage.mainHeading).toHaveText("Fonts"); + + const placeholder = page.locator("[class*='fonts-placeholder']"); + await expect(placeholder).toBeVisible(); + await page.mouse.move(10, 700); + await expect(placeholder).toHaveScreenshot(shot); + }); +}); + +test.describe("Subscription controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + // An unlimited trial that the logged-in user owns, as in + // specs/subscriptions-dashboard.spec.js. + const setupSubscription = async ( + page, + members, + secondTeam = true, + usage = "subscription/get-subscription-usage.json", + ) => { + await DashboardPage.mockConfigFlags(page, [ + "enable-subscriptions", + "disable-onboarding", + ]); + await DashboardPage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + await DashboardPage.mockRPC(page, "get-subscription-usage", usage); + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-owner.json", + ); + if (secondTeam) { + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + } + await DashboardPage.mockRPC(page, "get-team-members?team-id=*", members); + await DashboardPage.mockRPC( + page, + "get-team-stats?team-id=*", + "dashboard/get-team-stats.json", + ); + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + return dashboardPage; + }; + + test("Sidebar plan card", async ({ page }) => { + const dashboardPage = await setupSubscription( + page, + "subscription/get-team-members-subscription-owner.json", + false, + "subscription/get-subscription-usage-one-editor.json", + ); + await dashboardPage.goToDashboard(); + + const name = page.getByTestId("subscription-name"); + await expect(name).toHaveText("Unlimited plan (trial)"); + const card = page.locator("[class*='cta-power-up']").first(); + await page.mouse.move(640, 300); + await expect(card).toHaveScreenshot(shot); + + await name.click(); + await page.mouse.move(640, 300); + await expect(card).toHaveScreenshot(shot); + }); + + test("Team settings plan", async ({ page }) => { + const dashboardPage = await setupSubscription( + page, + "subscription/get-team-members-subscription-owner.json", + ); + await dashboardPage.goToSecondTeamSettingsSection(); + + await expect( + page.getByRole("button", { name: "Manage your subscription" }), + ).toBeVisible(); + await expect( + page.locator("[class$='subscription__team']"), + ).toHaveScreenshot(shot); + }); + + test("Members seats warning", async ({ page }) => { + const dashboardPage = await setupSubscription( + page, + "subscription/get-team-members-subscription-eight-member.json", + ); + await dashboardPage.goToSecondTeamMembersSection(); + + await expect(page.getByTestId("cta")).toHaveCount(2); + await page.mouse.move(640, 700); + await expect(page).toHaveScreenshot(shot); + }); +}); + +test.describe("Dashboard list controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Trash project options", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + // Deleted files due in 2100, so the trash keeps them. + await dashboardPage.mockRPC( + "get-team-deleted-files?team-id=*", + "dashboard/get-team-deleted-files-pending.json", + ); + // A deleted project, so its row shows the options button. + await dashboardPage.mockRPC( + "get-projects?team-id=*", + "dashboard/get-projects-deleted.json", + ); + await dashboardPage.goToDeleted(); + + const options = page.getByTestId("project-options").first(); + await expect(options).toBeVisible(); + const row = page.locator("[class*='dashboard-project-row']").first(); + await page.mouse.move(640, 700); + await expect(row.locator("header")).toHaveScreenshot(shot); + await options.hover(); + await expect(row.locator("header")).toHaveScreenshot(shot); + }); + + test("Empty project create button", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupNewProject(); + await dashboardPage.goToDashboard(); + await expect(dashboardPage.projectName).toBeVisible(); + + const create = page.locator("[class*='create-new']").first(); + await expect(create).toBeVisible(); + await page.mouse.move(640, 700); + await expect(create).toHaveScreenshot(shot); + await create.hover(); + await expect(create).toHaveScreenshot(shot); + }); + + test("Project rename", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.goToDashboard(); + + await page.getByTestId("project-options").first().click(); + await page.getByText("Rename", { exact: true }).click(); + const edit = page.locator("[class*='edit-wrapper']").first(); + await expect(edit).toBeVisible(); + await expect(edit).toHaveScreenshot(shot); + }); + + test("Comments placeholder", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.mockRPC( + "get-unread-comment-threads?team-id=*", + "logged-in-user/get-unread-comment-threads-empty.json", + ); + await dashboardPage.goToDashboard(); + + await page.getByTestId("open-comments").click(); + const placeholder = page.locator("[class*='thread-groups-placeholder']"); + await expect(placeholder).toBeVisible(); + await expect(placeholder).toHaveScreenshot(shot); + // The whole dropdown, with the header title. + await expect( + page.locator("[class*='comment-threads-section']"), + ).toHaveScreenshot(shot); + }); +}); + +test.describe("Team modals controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Create team modal", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.goToDashboard(); + await dashboardPage.teamDropdown.click(); + await page.getByText("Create new team").click(); + + const modal = page.locator("[class*='modal-container']"); + await expect(modal).toBeVisible(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + + await modal.locator("[class*='modal-header'] button").hover(); + await expect(modal).toHaveScreenshot(shot); + }); + + test("Leave and reassign modal", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + // The logged-in user owns the second team, which has another member. + await dashboardPage.mockRPC( + "get-teams", + "dashboard/get-teams-second-owner.json", + ); + await dashboardPage.mockRPC( + "get-team-members?team-id=*", + "dashboard/get-team-members-owner.json", + ); + await dashboardPage.goToSecondTeamMembersSection(); + await expect(page.getByText("role")).toBeVisible(); + + const rows = page.locator("[class*='table-rows']"); + await rows + .locator("[class*='menu-btn']") + .or(rows.getByRole("button", { name: "Options" })) + .first() + .click(); + await rows.getByText("Leave team").click(); + + const modal = page.locator("[class*='modal-container']"); + await expect(modal).toBeVisible(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + }); +}); + +test.describe("Settings subscription controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Plans page and management dialog", async ({ page }) => { + await DashboardPage.mockConfigFlags( + page, + ["enable-subscriptions", "disable-onboarding"], + { penpotIsSaas: true }, + ); + await DashboardPage.mockRPC( + page, + "get-subscription-usage", + "subscription/get-subscription-usage.json", + ); + await page.goto("/?screen=settings-subscription"); + await expect( + page.getByRole("heading", { name: "Subscription", level: 2 }), + ).toBeVisible(); + + const trial = page + .getByRole("button", { name: "Try it free for 14 days" }) + .first(); + await expect(trial).toBeVisible(); + await page.mouse.move(10, 700); + await expect(page).toHaveScreenshot(shot); + + await trial.click(); + const dialog = page.locator("[class*='modal-dialog']"); + await expect(dialog).toBeVisible(); + await page.mouse.move(10, 700); + await expect(dialog).toHaveScreenshot(shot); + + await dialog.locator("button").first().hover(); + await expect(dialog).toHaveScreenshot(shot); + }); +}); + +test.describe("Dashboard sidebar controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Search box, pinned placeholder and profile menu", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.goToDashboard(); + + const search = page.locator("[class*='sidebar-search']"); + await expect(search).toBeVisible(); + await expect(search).toHaveScreenshot(shot); + + await expect(page.getByTestId("pinned-projects")).toHaveScreenshot(shot); + + // A search term swaps the search icon for the clear button. + await page.locator("#search-input").fill("test"); + await expect(page).toHaveURL(/search/); + const clear = search.locator("button"); + await clear.hover(); + await expect(search).toHaveScreenshot(shot); + + await page.getByTestId("profile-btn").click(); + await expect(page.getByTestId("logout-profile-opt")).toBeVisible(); + await expect( + page.getByTestId("logout-profile-opt").locator("xpath=.."), + ).toHaveScreenshot(shot); + }); +}); + +test.describe("Team pages controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + // The row menu button: a raw button with the menu-btn class before the DS + // migration, a DS icon button named "Options" after it. + const openRowMenu = (page) => { + const rows = page.locator("[class*='table-rows']"); + return rows + .locator("[class*='menu-btn']") + .or(rows.getByRole("button", { name: "Options" })) + .first() + .click(); + }; + + test("Members role and actions menus", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + // Han Solo owns the team; the logged-in user is an admin. + await dashboardPage.mockRPC( + "get-team-members?team-id=*", + "dashboard/get-team-members-admin.json", + ); + await dashboardPage.goToSecondTeamMembersSection(); + await expect(page.getByText("role")).toBeVisible(); + + await page.locator("[class*='has-priv']").first().click(); + await expect(page.locator("[class*='roles-dropdown']")).toBeVisible(); + await expect(page).toHaveScreenshot(shot); + + await page.keyboard.press("Escape"); + await page.mouse.click(10, 700); + await openRowMenu(page); + await expect(page.locator("[class*='actions-dropdown']")).toBeVisible(); + await expect(page).toHaveScreenshot(shot); + }); + + test("Invitations role menu and resend modal", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamInvitations(); + await dashboardPage.goToSecondTeamInvitationsSection(); + await expect(page.getByText("test1@mail.com")).toBeVisible(); + + await page.locator("[class*='has-priv']").first().click(); + await expect(page.locator("[class*='roles-dropdown']")).toBeVisible(); + await expect(page).toHaveScreenshot(shot); + + await page.mouse.click(10, 700); + await page.getByText("test1@mail.com").click(); + await page.getByRole("button", { name: "Resend invitation" }).click(); + await expect(page.getByText("Resend invitations")).toBeVisible(); + await page.mouse.move(10, 700); + await expect(page).toHaveScreenshot(shot); + }); + + test("Webhooks actions menu and modal", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamWebhooks(); + await dashboardPage.goToSecondTeamWebhooksSection(); + await expect(page.getByText("https://www.google.com")).toBeVisible(); + + await openRowMenu(page); + await expect( + page.locator("[class*='webhook-actions-dropdown']"), + ).toBeVisible(); + await expect(page).toHaveScreenshot(shot); + + await page.mouse.click(10, 700); + await page.getByText("Create webhook").click(); + await expect(page.getByText("Payload URL")).toBeVisible(); + await page.mouse.move(10, 700); + await expect(page).toHaveScreenshot(shot); + }); + + test("Team settings image overlay", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.setupTeamSettings(); + await dashboardPage.goToSecondTeamSettingsSection(); + await expect(page.getByText("TEAM INFO")).toBeVisible(); + + await page.locator("[class*='team-icon']").hover(); + await expect(page.locator("[class*='team-icon']")).toHaveScreenshot(shot); + }); +}); + +test.describe("Dashboard headers", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + const header = (page) => page.getByTestId("dashboard-header"); + + test("Page headers", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToDashboard(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToDrafts(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToLibraries(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToFonts(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToDeleted(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToSearch(); + await expect(dashboardPage.mainHeading).toBeVisible(); + await expect(header(page)).toHaveScreenshot(shot); + }); + + test("Team header menu", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.goToSecondTeamMembersSection(); + await expect(dashboardPage.mainHeading).toHaveText("Members"); + + await page.mouse.move(10, 700); + await expect(header(page)).toHaveScreenshot(shot); + await header(page).getByText("Invitations").hover(); + await expect(header(page)).toHaveScreenshot(shot); + }); + + test("Libraries page and search placeholder", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.goToLibraries(); + await expect(page).toHaveScreenshot(shot); + + await dashboardPage.goToSearch(); + const placeholder = page.locator("[class*='grid-empty-placeholder']"); + await expect(placeholder).toBeVisible(); + await expect(placeholder).toHaveScreenshot(shot); + }); + + test("Account header", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.goToDashboard(); + await dashboardPage.goToAccount(); + await expect(page.getByTestId("account-title")).toBeVisible(); + await expect(header(page)).toHaveScreenshot(shot); + }); +}); + +test.describe("Settings controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Sidebar and change email modal", async ({ page }) => { + await DashboardPage.mockConfigFlags(page, ["enable-user-feedback"]); + await page.goto("/?screen=settings-profile"); + const sidebar = page.locator( + "[class*='settings_sidebar__dashboard-sidebar']", + ); + await expect(page.getByText("Contact us")).toBeVisible(); + await page.mouse.move(640, 700); + await expect(sidebar).toHaveScreenshot(shot); + + await page.getByText("Dashboard", { exact: true }).hover(); + await expect(sidebar).toHaveScreenshot(shot); + + await page.getByText("Change email", { exact: true }).click(); + const modal = page.locator("[class*='modal-container']"); + await expect(modal).toBeVisible(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + await modal.locator("[class*='modal-header'] button").hover(); + await expect(modal).toHaveScreenshot(shot); + }); +}); + +test.describe("Onboarding questions controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Previous and next buttons", async ({ page }) => { + await DashboardPage.mockConfigFlags(page, ["enable-onboarding"]); + const dashboardPage = new DashboardPage(page); + await dashboardPage.goToDashboard(); + await expect( + page.getByRole("heading", { name: "Help us get to know you" }), + ).toBeVisible(); + + // Step 1, as in OnboardingPage.fillOnboardingInputsStep1. + await page.getByText("Personal", { exact: true }).click(); + await page.getByText("Select option").click(); + await page.getByText("Product Management").click(); + await page.getByRole("button", { name: "Next" }).click(); + + const previous = page.getByRole("button", { name: "Previous" }); + await expect(previous).toBeVisible(); + const actions = previous.locator("xpath=.."); + await page.mouse.move(10, 700); + await expect(actions).toHaveScreenshot(shot); + + await previous.hover(); + await expect(actions).toHaveScreenshot(shot); + }); +}); + +test.describe("Profile page controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Profile page avatar, form and links", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.goToDashboard(); + await dashboardPage.goToAccount(); + + const container = page.locator( + "[class*='settings_profile__form-container']", + ); + await expect(page.getByText("Change email")).toBeVisible(); + await page.mouse.move(10, 700); + await expect(container).toHaveScreenshot(shot); + + // The "Update" overlay shows when the pointer is on the avatar. + await page.locator("[class*='image-change-field']").hover(); + await expect(container).toHaveScreenshot(shot); + + // Save turns enabled once the name changes. + await page.getByLabel("Your name").fill("Renamed user"); + await page.mouse.move(10, 700); + await expect(container).toHaveScreenshot(shot); + + await page.getByText("Change email").hover(); + await expect(container).toHaveScreenshot(shot); + + await page.getByText("Want to remove your account?").hover(); + await expect(container).toHaveScreenshot(shot); + + // The "Update" overlay is a button: it shows on keyboard focus too. + await page.mouse.move(10, 700); + await page.getByRole("button", { name: "Update" }).focus(); + await page.keyboard.press("Shift+Tab"); + await page.keyboard.press("Tab"); + await expect(container).toHaveScreenshot(shot); + }); +}); + +test.describe("Templates section controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Template cards, scroll buttons and collapse", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.mockRPC( + "get-builtin-templates", + "dashboard/get-builtin-templates.json", + ); + await dashboardPage.goToDashboard(); + + const section = page.locator("[class*='dashboard-templates-section']"); + const card = section.locator("[class*='template-card']").first(); + await expect(card).toBeVisible(); + await expect(section.locator("img").first()).toHaveJSProperty( + "complete", + true, + ); + await page.mouse.move(640, 100); + await expect(section).toHaveScreenshot(shot); + + // Hovering a card shows its add icon in the accent color. + await card.hover(); + await expect(card).toHaveScreenshot(shot); + + // Focused from the keyboard, a card looks like on hover. + await page.mouse.move(640, 100); + await page.keyboard.press("Shift"); + await card.focus(); + await expect(card).toHaveScreenshot(shot); + await card.blur(); + + const moveButtons = section.locator("[class*='move-button']"); + await moveButtons.last().hover(); + await expect(moveButtons.last()).toHaveScreenshot(shot); + + await moveButtons.last().click(); + await expect(moveButtons).toHaveCount(2); + await page.mouse.move(640, 100); + await expect(moveButtons.first()).toHaveScreenshot(shot); + + await section.locator("[class*='title-btn']").click(); + await expect(section).toHaveClass(/collapsed/); + await page.waitForTimeout(500); + await expect(section.locator("[class*='title-btn']")).toHaveScreenshot( + shot, + ); + }); +}); + +test.describe("Context notification", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Info and warning levels", async ({ page }) => { + await page.goto("/?screen=settings-profile"); + + // Info: the change email modal. + await page.getByText("Change email", { exact: true }).click(); + const info = page.locator("[class*='modal-container'] aside"); + await expect(info).toBeVisible(); + await expect(info).toHaveScreenshot(shot); + await page.keyboard.press("Escape"); + await expect(info).toBeHidden(); + + // Warning: the delete account modal. + await page.getByText("Want to remove your account?").click(); + const warning = page.locator("[class*='modal-container'] aside"); + await expect(warning).toBeVisible(); + await expect(warning).toHaveScreenshot(shot); + }); + + test("Warning with html content", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.goToDashboard(); + await dashboardPage.fontsLink.click(); + await page + .locator("#font-upload") + .setInputFiles("playwright/data/render-wasm/assets/ebgaramond.ttf"); + const warning = page + .locator("[class*='dashboard-fonts-hero'] aside") + .last(); + await expect(warning).toBeVisible(); + await expect(warning).toHaveScreenshot(shot); + }); +}); + +test.describe("Download report", () => { + test("Error page and feedback page links", async ({ page }) => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + await DashboardPage.mockConfigFlags(page, [ + "disable-onboarding", + "enable-user-feedback", + ]); + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await page.route("**/api/main/methods/get-projects?*", (route) => + route.fulfill({ + status: 500, + contentType: "application/transit+json", + body: '["^ ","~:type","~:server-error","~:code","~:unexpected"]', + }), + ); + await page.goto( + `/?screen=dashboard-recent&team-id=${DashboardPage.anyTeamId}`, + ); + + const errorLink = page.getByText("Download report.txt", { exact: true }); + await expect(errorLink).toBeVisible(); + await page.mouse.move(10, 10); + await expect(errorLink).toHaveScreenshot(shot); + await errorLink.hover(); + await expect(errorLink).toHaveScreenshot(shot); + + const errorDownload = page.waitForEvent("download"); + await errorLink.focus(); + await page.keyboard.press("Enter"); + expect((await errorDownload).suggestedFilename()).toBe("report.txt"); + + await page.getByRole("button", { name: "Contact support" }).click(); + const feedbackLink = page.getByText("Download report.txt", { + exact: true, + }); + await expect(page.getByText("Send")).toBeVisible(); + await page.mouse.move(10, 700); + await expect(feedbackLink).toHaveScreenshot(shot); + await feedbackLink.hover(); + await expect(feedbackLink).toHaveScreenshot(shot); + + const feedbackDownload = page.waitForEvent("download"); + await feedbackLink.focus(); + await page.keyboard.press("Enter"); + expect((await feedbackDownload).suggestedFilename()).toBe("report.txt"); + // The button is in the feedback form: Enter must not send it. + await expect(page.getByText("Send", { exact: true })).toBeVisible(); + }); +}); diff --git a/frontend/playwright/ui/visual-specs/visual-login.spec.js b/frontend/playwright/ui/visual-specs/visual-login.spec.js index 6b53a2d5d9..7c9650d656 100644 --- a/frontend/playwright/ui/visual-specs/visual-login.spec.js +++ b/frontend/playwright/ui/visual-specs/visual-login.spec.js @@ -37,3 +37,74 @@ test.describe("Login form", () => { await expect(login.page).toHaveScreenshot(); }); }); + +test.describe("Login dialog section links", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + // The links switch the dialog section: the tests use the keyboard to + // check they work as buttons. + test("Login and register modal", async ({ page }) => { + // The viewer and the MCP authorize page share this modal; the MCP page + // opens it on a 401, with no file to load. + await LoginPage.mockConfigFlags(page, ["enable-registration"]); + await page.route("**/api/mcp/oauth/request?*", (route) => + route.fulfill({ + status: 401, + contentType: "application/json", + body: "{}", + }), + ); + await page.goto("/?screen=mcp-authorize&request=test"); + + const modal = page.getByRole("dialog"); + const forgot = modal.getByText("Forgot password?", { exact: true }); + await expect(forgot).toBeVisible(); + await page.mouse.move(10, 10); + await expect(forgot).toHaveScreenshot(shot); + await forgot.hover(); + await expect(forgot).toHaveScreenshot(shot); + + const create = modal.getByText("Create an account", { exact: true }); + await create.hover(); + await expect(create).toHaveScreenshot(shot); + + await create.focus(); + await page.keyboard.press("Enter"); + const loginHere = modal.getByText("Login here.", { exact: true }); + await expect(loginHere).toBeVisible(); + await page.mouse.move(10, 10); + await expect(loginHere).toHaveScreenshot(shot); + await loginHere.hover(); + await expect(loginHere).toHaveScreenshot(shot); + + await loginHere.focus(); + await page.keyboard.press("Enter"); + await page.mouse.move(10, 10); + await expect(modal).toHaveScreenshot(shot); + }); + + test("Not found page dialog", async ({ page }) => { + await LoginPage.mockConfigFlags(page, ["enable-registration"]); + // An invalid team id is a not found error; logged out users get the + // login dialog on top of it. + await page.goto("/?screen=dashboard-recent&team-id=not-a-uuid"); + + const create = page.getByText("Create an account", { exact: true }); + await expect(create).toBeVisible(); + await page.mouse.move(10, 10); + await expect(create).toHaveScreenshot(shot); + await create.hover(); + await expect(create).toHaveScreenshot(shot); + + await create.focus(); + await page.keyboard.press("Enter"); + const loginHere = page.getByText("Login here.", { exact: true }); + await expect(loginHere).toBeVisible(); + // The new section reuses the focused button: blur it for the rest shot. + await loginHere.blur(); + await page.mouse.move(10, 10); + await expect(loginHere).toHaveScreenshot(shot); + await loginHere.hover(); + await expect(loginHere).toHaveScreenshot(shot); + }); +}); diff --git a/frontend/playwright/ui/visual-specs/visual-viewer.spec.js b/frontend/playwright/ui/visual-specs/visual-viewer.spec.js index 8361c263fb..450bba4a2e 100644 --- a/frontend/playwright/ui/visual-specs/visual-viewer.spec.js +++ b/frontend/playwright/ui/visual-specs/visual-viewer.spec.js @@ -151,3 +151,341 @@ test("User opens Share modal", async ({ page }) => { ).toBeVisible(); await expect(viewerPage.page).toHaveScreenshot(); }); + +test.describe("Thumbnails panel", () => { + test("Shows the thumbnails panel collapsed and expanded", async ({ + page, + }) => { + const viewerPage = new ViewerPage(page); + await viewerPage.setupLoggedInUser(); + await viewerPage.setupFileWithMultipleBoards(); + await viewerPage.goToViewer({ + fileId: "dd5cc0bb-91ff-81b9-8004-77df9cd3edb0", + pageId: "dd5cc0bb-91ff-81b9-8004-77df9cd3edb3", + }); + + await page.locator("#current-frame").click(); + const panel = page.locator("section").filter({ hasText: /boards/i }); + await expect(panel).toBeVisible(); + await page.mouse.move(640, 600); + // Collapsed: the summary with its expand and close buttons. + await expect(panel).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + // The scroll handlers only show on hover. + await panel.locator("button").nth(3).hover(); + await expect(panel).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await panel.locator("button").first().click(); + await page.mouse.move(640, 700); + // Expanded: the expand arrow points up. + await expect(panel).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Comments filter", () => { + test("Shows the comments filter closed and open", async ({ page }) => { + const viewerPage = new ViewerPage(page); + await viewerPage.setupLoggedInUser(); + await viewerPage.setupFileWithComments(); + await viewerPage.goToViewer({ + fileId: singleBoardFileId, + pageId: singleBoardPageId, + }); + await viewerPage.showComments(); + + const trigger = page.getByTestId("viewer-comments-dropdown"); + await expect(trigger).toBeVisible(); + await page.mouse.move(640, 600); + // Closed: the "Comments" trigger with its arrow. + await expect(trigger).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await trigger.click(); + await expect(page.getByText("Show comments list")).toBeVisible(); + await page.mouse.move(640, 600); + // Open: the filter menu with the selected option ticked. + const menu = page.getByText("Show comments list").locator("../.."); + await expect(menu).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Board navigation", () => { + test("Shows the previous, next and first board buttons", async ({ page }) => { + const viewerPage = new ViewerPage(page); + await viewerPage.setupLoggedInUser(); + await viewerPage.setupFileWithMultipleBoards(); + await viewerPage.goToViewer({ + fileId: "dd5cc0bb-91ff-81b9-8004-77df9cd3edb0", + pageId: "dd5cc0bb-91ff-81b9-8004-77df9cd3edb3", + }); + + const next = page.getByRole("button", { name: "Next" }); + await expect(next).toBeVisible(); + await page.mouse.move(640, 300); + // Wait for the slide-in transition of the controls. + await page.waitForTimeout(800); + // First board: the next and first board buttons. + await expect(page).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await next.hover(); + await expect(next).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await next.click(); + const previous = page.getByRole("button", { name: "Previous" }); + await previous.hover(); + await expect(previous).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + const first = page.locator("[class*='viewer-bottom'] button"); + await first.hover(); + await expect(first).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Viewer header", () => { + test("Shows the header buttons and its menus", async ({ page }) => { + const viewerPage = new ViewerPage(page); + await viewerPage.setupLoggedInUser(); + await viewerPage.setupFileWithComments(); + await viewerPage.goToViewer({ + fileId: singleBoardFileId, + pageId: singleBoardPageId, + }); + + const header = page.locator("header").first(); + await expect(header).toBeVisible(); + await page.mouse.move(640, 600); + // Interactions section. + await expect(header).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + // Zoom menu. + await page.getByTitle("Zoom").click(); + await expect(page.getByText("Fit - Scale down to fit")).toBeVisible(); + await page.mouse.move(640, 600); + await expect(page).toHaveScreenshot({ + clip: { x: 760, y: 0, width: 520, height: 260 }, + maxDiffPixelRatio: 0, + threshold: 0, + }); + await page.keyboard.press("Escape"); + await page.mouse.click(640, 600); + + // Sitemap menu. + await page + .locator("#current-frame") + .locator("..") + .locator("div") + .first() + .click(); + await page.mouse.move(640, 600); + await expect(page).toHaveScreenshot({ + clip: { x: 0, y: 0, width: 520, height: 200 }, + maxDiffPixelRatio: 0, + threshold: 0, + }); + await page.mouse.click(640, 600); + + // Comments section, with its button selected. + await viewerPage.showComments(); + await page.mouse.move(640, 600); + await expect(header).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Inspect code tab", () => { + test("Shows the code tab buttons, expanded and collapsed", async ({ + page, + }) => { + const viewerPage = new ViewerPage(page); + await viewerPage.setupLoggedInUser(); + await viewerPage.setupFileWithComments(); + await viewerPage.goToViewer({ + fileId: singleBoardFileId, + pageId: singleBoardPageId, + }); + await viewerPage.showCode(); + await page.getByRole("combobox").filter({ hasText: "Styles" }).click(); + await page.getByRole("option", { name: "Code" }).click(); + + const copyAll = page.getByRole("button", { name: "Copy all code" }); + await expect(copyAll).toBeVisible(); + // The CSS block header, with its toggle, expand and copy buttons. + const cssToggle = page.locator('button[data-type="css"]'); + const cssHeader = cssToggle.locator(".."); + await page.mouse.move(0, 0); + await expect(copyAll).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + await expect(cssHeader).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await cssToggle.click(); + await page.mouse.move(0, 0); + // Collapsed: the arrow turns. + await expect(cssHeader).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Interactions menu", () => { + test("Shows the interactions menu closed and open", async ({ page }) => { + const viewerPage = new ViewerPage(page); + await viewerPage.setupLoggedInUser(); + await viewerPage.setupFileWithMultipleBoards(); + await viewerPage.goToViewer({ + fileId: "dd5cc0bb-91ff-81b9-8004-77df9cd3edb0", + pageId: "dd5cc0bb-91ff-81b9-8004-77df9cd3edb3", + }); + + const trigger = page + .getByText("Interactions", { exact: true }) + .locator(".."); + await expect(trigger).toBeVisible(); + await page.mouse.move(640, 600); + // Closed: the "Interactions" trigger with its arrow. + await expect(trigger).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await trigger.click(); + const menu = page.getByText("Show interactions", { exact: true }); + await expect(menu).toBeVisible(); + await page.mouse.move(640, 600); + // Open: the menu with the selected mode ticked. + await expect(menu.locator("../..")).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Copy buttons", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + const openInspectTab = async (page, name) => { + const viewerPage = new ViewerPage(page); + await viewerPage.setupLoggedInUser(); + await viewerPage.setupFileWithComments(); + await viewerPage.goToViewer({ + fileId: singleBoardFileId, + pageId: singleBoardPageId, + }); + await viewerPage.showCode(); + await page.getByRole("combobox").filter({ hasText: "Styles" }).click(); + await page.getByRole("option", { name }).click(); + }; + + test("Computed attribute rows", async ({ page }) => { + await openInspectTab(page, "Computed"); + const row = page.locator("[class*='copy-wrapper']").first(); + await expect(row).toBeVisible(); + await page.mouse.move(10, 700); + await expect(row).toHaveScreenshot(shot); + + await row.hover(); + await expect(row).toHaveScreenshot(shot); + + await row.click(); + await expect(row).toHaveAttribute("data-active", "true"); + await expect(row).toHaveScreenshot(shot); + }); + + test("Code block copy button", async ({ page }) => { + await openInspectTab(page, "Code"); + const copy = page.locator("[class*='css-copy-btn']"); + await expect(copy).toBeVisible(); + await page.mouse.move(10, 700); + await expect(copy).toHaveScreenshot(shot); + + await copy.hover(); + await expect(copy).toHaveScreenshot(shot); + + await copy.click(); + await expect(copy).toHaveAttribute("data-active", "true"); + await expect(copy).toHaveScreenshot(shot); + }); +}); + +test.describe("Share link modal controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Get, copy and destroy link", async ({ page }) => { + const viewerPage = new ViewerPage(page); + await viewerPage.setupLoggedInUser(); + await viewerPage.setupFileWithSingleBoard(); + await viewerPage.mockRPC( + "create-share-link", + "viewer/create-share-link.json", + ); + await viewerPage.goToViewer({ + fileId: singleBoardFileId, + pageId: singleBoardPageId, + }); + + await page.getByRole("button", { name: "Share" }).click(); + const getLink = page.getByRole("button", { name: "Get link" }); + await expect(getLink).toBeVisible(); + const modal = page.locator("[class*='share-link-dialog']"); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + + await getLink.click(); + const destroy = page.getByRole("button", { name: "Destroy link" }); + await expect(destroy).toBeVisible(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + + // A raw button with a title before, a DS icon button named "Copy link" after. + await modal + .getByTitle("Copy link") + .or(modal.getByRole("button", { name: "Copy link" })) + .hover(); + await expect(modal).toHaveScreenshot(shot); + + await page.getByText("Manage permissions").click(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + + await destroy.click(); + await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + }); +}); diff --git a/frontend/playwright/ui/visual-specs/workspace.spec.js b/frontend/playwright/ui/visual-specs/workspace.spec.js index a134ecf245..bc3f1544ed 100644 --- a/frontend/playwright/ui/visual-specs/workspace.spec.js +++ b/frontend/playwright/ui/visual-specs/workspace.spec.js @@ -28,6 +28,50 @@ const setupFileWithAssets = async (workspace) => { return { fileId, pageId }; }; +test.describe("Color palette", () => { + test("Shows the scroll arrows and the palette menu", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC( + /get\-file\?/, + "workspace/get-file-many-colors.json", + ); + await workspace.goToWorkspace({ + fileId: "c1249a66-fce0-8175-8004-7433fe4be8bc", + pageId: "c1249a66-fce0-8175-8004-7433fe4be8bd", + }); + + await workspace.clickColorPalette(); + await workspace.palette.locator('[class*="palette-actions"]').click(); + await page.getByText("File library").click(); + await expect( + workspace.palette.getByRole("button", { name: "color-01" }), + ).toBeVisible(); + await page.mouse.move(640, 300); + // The arrows are a small part of the palette: compare every pixel. + await expect(workspace.palette).toHaveScreenshot("color-palette.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await workspace.palette.locator('[class*="right-arrow"]').click(); + await page.mouse.move(640, 300); + await expect(workspace.palette).toHaveScreenshot( + "color-palette-scrolled.png", + { maxDiffPixelRatio: 0, threshold: 0 }, + ); + + await workspace.palette.locator('[class*="palette-actions"]').click(); + await expect(page.getByText("Recent colors")).toBeVisible(); + await page.mouse.move(640, 300); + await expect(page).toHaveScreenshot("color-palette-menu.png", { + clip: { x: 300, y: 400, width: 700, height: 320 }, + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + test.describe("Viewport", () => { test("Shows the workspace correctly for a blank file", async ({ page }) => { const workspace = new WorkspacePage(page); @@ -53,7 +97,128 @@ test.describe("Viewport", () => { }); }); +test.describe("Grid layout", () => { + test("Shows the grid layout options and the grid editor", async ({ + page, + }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9042.json"); + await workspace.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-fragment-9042.json", + ); + await workspace.goToWorkspace({ + fileId: "af2494d0-39ba-8184-8005-230696f6df5c", + pageId: "af2494d0-39ba-8184-8005-230696f6df5d", + }); + + await workspace.clickLeafLayer("Board"); + const section = page.getByTestId("inspect-layout"); + await expect( + section.getByRole("button", { name: "Edit grid" }), + ).toBeVisible(); + // The buttons are a small part of the section: compare every pixel. + await expect(section).toHaveScreenshot("grid-layout-options.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await section.getByRole("button", { name: "Edit grid" }).click(); + await expect(workspace.rightSidebar.getByText("GRID LAYOUT")).toBeVisible(); + // Expand the column tracks so their rows show too. + await workspace.rightSidebar.getByText(/^Columns? \d/).click(); + await page.mouse.move(0, 0); + await expect(workspace.rightSidebar).toHaveScreenshot( + "grid-layout-editor.png", + { maxDiffPixelRatio: 0, threshold: 0 }, + ); + }); +}); + +test.describe("Grid editor", () => { + test("Shows the track options button on the canvas", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9042.json"); + await workspace.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-fragment-9042.json", + ); + await workspace.goToWorkspace({ + fileId: "af2494d0-39ba-8184-8005-230696f6df5c", + pageId: "af2494d0-39ba-8184-8005-230696f6df5d", + }); + + await workspace.clickLeafLayer("Board"); + // Tracks need 110px or more on screen to show their options button. + await page.keyboard.press("Control+="); + await page.keyboard.press("Control+="); + await page + .getByTestId("inspect-layout") + .getByRole("button", { name: "Edit grid" }) + .click(); + + const shotTrack = async () => { + const track = page.getByTestId("grid-track-editor-wrapper").first(); + await track.hover(); + await expect( + page.getByTestId("grid-track-options-btn").first(), + ).toBeVisible(); + // Wait until the zoom animation leaves the track in place. + let box = await track.boundingBox(); + await expect + .poll(async () => { + const prev = box; + box = await track.boundingBox(); + return prev.x === box.x && prev.width === box.width; + }) + .toBe(true); + await expect(page).toHaveScreenshot({ + clip: { + x: Math.floor(box.x) - 8, + y: Math.floor(box.y) - 8, + width: Math.ceil(box.width) + 16, + height: Math.ceil(box.height) + 16, + }, + maxDiffPixelRatio: 0, + threshold: 0, + }); + }; + + // The track editors draw at a fixed screen size at any zoom. + await shotTrack(); + await page.keyboard.press("Control+="); + await shotTrack(); + }); +}); + test.describe("Design tab", () => { + test("Shows the boolean options when selecting two shapes", async ({ + page, + }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + + await workspace.rectShapeButton.click(); + await workspace.clickWithDragViewportAt(128, 128, 200, 100); + await workspace.clickLeafLayer("Rectangle"); + await page.keyboard.press("Control+D"); + await expect(workspace.layers.getByText("Rectangle")).toHaveCount(2); + await page.keyboard.press("Control+A"); + + await expect( + workspace.rightSidebar.getByRole("button", { name: "Flatten" }), + ).toBeVisible(); + // The boolean options are a small part of the sidebar: compare every + // pixel. + await expect(workspace.rightSidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); + test("Shows the design tab when selecting a shape", async ({ page }) => { const workspace = new WorkspacePage(page); await workspace.setupEmptyFile(); @@ -84,6 +249,224 @@ test.describe("Design tab", () => { await expect(workspace.page).toHaveScreenshot(); }); + + test("Shows the wrap and padding modes of a flex layout", async ({ + page, + }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-7760.json"); + await workspace.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-fragment-7760.json", + ); + await workspace.mockRPC( + "update-file?id=*", + "workspace/update-file-create-rect.json", + ); + await workspace.goToWorkspace({ + fileId: "cd90e028-326a-80b4-8004-7cdec16ffad5", + pageId: "cd90e028-326a-80b4-8004-7cdec16ffad6", + }); + + await workspace.clickLeafLayer("Flex Board"); + + const section = workspace.rightSidebar.getByRole("region", { + name: "Layout container section", + }); + const wrap = section.getByRole("button", { name: /^(No w|W)rap$/ }); + const padding = section.getByRole("button", { + name: "Show 4 sided padding options", + }); + await expect(wrap).toBeVisible(); + // The buttons are a small part of the section: compare every pixel. + await expect(section).toHaveScreenshot("flex-layout-rest.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await wrap.click(); + await padding.click(); + await padding.blur(); + await page.mouse.move(0, 0); + await expect( + section.getByRole("textbox", { name: /top padding/i }), + ).toBeVisible(); + await expect(section).toHaveScreenshot("flex-layout-pressed.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); + + test("Shows the align options when selecting two shapes", async ({ + page, + }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + + await workspace.rectShapeButton.click(); + await workspace.clickWithDragViewportAt(128, 128, 200, 100); + await workspace.clickLeafLayer("Rectangle"); + await page.keyboard.press("Control+D"); + await expect(workspace.layers.getByText("Rectangle")).toHaveCount(2); + await page.keyboard.press("Control+A"); + + await expect( + workspace.rightSidebar.getByRole("button", { name: /^Align left/ }), + ).toBeVisible(); + // The align options are a small part of the sidebar: compare every pixel. + await expect(workspace.rightSidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); + + test("Shows the margin modes of a flex layout element", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-7760.json"); + await workspace.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-fragment-7760.json", + ); + await workspace.mockRPC( + "update-file?id=*", + "workspace/update-file-create-rect.json", + ); + await workspace.goToWorkspace({ + fileId: "cd90e028-326a-80b4-8004-7cdec16ffad5", + pageId: "cd90e028-326a-80b4-8004-7cdec16ffad6", + }); + + await workspace.clickToggableLayer("Flex Board"); + await workspace.clickLeafLayer("Rectangle"); + + const section = workspace.rightSidebar.getByRole("region", { + name: "Layout item section", + }); + const marginMode = section.getByRole("button", { + name: "Margin - multiple", + }); + await expect(marginMode).toBeVisible(); + // The margin button is a small part of the section: compare every pixel. + await expect(section).toHaveScreenshot("margin-simple.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await marginMode.click(); + await marginMode.blur(); + await page.mouse.move(0, 0); + await expect( + section.getByRole("textbox", { name: "Top margin" }), + ).toBeVisible(); + await expect(section).toHaveScreenshot("margin-multiple.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Component options", () => { + test("Shows the component pill of a copy", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9066.json"); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.clickToggableLayer("Board"); + await workspace.clickToggableLayer("Group"); + await workspace.layers.getByText("A", { exact: true }).click(); + + await expect( + workspace.rightSidebar.getByTestId("component-pill-button"), + ).toBeVisible(); + // The component pill is a small part of the sidebar: compare every + // pixel. + await expect(workspace.rightSidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); + + test("Shows the groups of the swap panel", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC( + /get\-file\?/, + "workspace/get-file-9066-swap-groups.json", + ); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.clickToggableLayer("Board"); + await workspace.clickToggableLayer("Group"); + await workspace.layers.getByText("A", { exact: true }).click(); + await workspace.rightSidebar.getByRole("button", { name: /^A / }).click(); + + await expect(workspace.rightSidebar.getByText("Letters")).toBeVisible(); + await workspace.rightSidebar.getByText("Letters").hover(); + await expect(workspace.rightSidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); + + test("Shows the annotation actions of a main component", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9066.json"); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.clickToggableLayer("Board"); + await workspace.clickToggableLayer("Group"); + await workspace.layers.getByText("B", { exact: true }).click(); + await workspace.rightSidebar + .getByRole("button", { name: "Options" }) + .click(); + await workspace.rightSidebar.getByText("Create annotation").click(); + + // Creating: save (disabled while the text is empty) and discard. + const annotation = workspace.rightSidebar.locator("#annotation-textarea"); + await expect(annotation).toBeFocused(); + await expect(workspace.rightSidebar).toHaveScreenshot( + "annotation-creating.png", + { maxDiffPixelRatio: 0, threshold: 0 }, + ); + + // Saved: edit and delete show when hovering the title. + await annotation.fill("An annotation"); + await workspace.rightSidebar + .getByRole("button", { name: "Create", exact: true }) + .click(); + const title = workspace.rightSidebar.getByText("Annotation", { + exact: true, + }); + await title.hover(); + await expect(workspace.rightSidebar).toHaveScreenshot( + "annotation-saved.png", + { maxDiffPixelRatio: 0, threshold: 0 }, + ); + + // Collapsed by clicking its title. + await title.click(); + await expect( + workspace.rightSidebar.getByText("An annotation"), + ).toBeHidden(); + await expect(workspace.rightSidebar).toHaveScreenshot( + "annotation-collapsed.png", + { maxDiffPixelRatio: 0, threshold: 0 }, + ); + }); }); test.describe("Assets tab", () => { @@ -109,10 +492,15 @@ test.describe("Assets tab", () => { await workspace.openLibrariesModal(); await expect(workspace.page).toHaveScreenshot(); + await workspace.librariesModal + .getByRole("tab", { name: "Libraries" }) + .click(); + await expect(workspace.page).toHaveScreenshot(); + + // Now the get-file call should return a library + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-library.json"); await workspace.clickLibrary("Testing library 1"); - await expect( - workspace.librariesModal.getByText("File library"), - ).toBeVisible(); + await expect(workspace.librariesModal.getByText("Added")).toBeVisible(); await expect(workspace.page).toHaveScreenshot(); }); @@ -137,6 +525,402 @@ test.describe("Assets tab", () => { }); }); +test.describe("Debug shape panel", () => { + test("Shows the debug info of the selected shape", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9066.json"); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.clickLeafLayer("Board"); + await page.getByRole("button", { name: "Debugging tool" }).click(); + await page.getByText("shape-panel", { exact: true }).click(); + await expect( + workspace.rightSidebar.getByText("Board", { exact: true }), + ).toBeVisible(); + await page.mouse.move(640, 600); + // Only the title row with the dump buttons: the attributes list below + // holds ids that change on every run. + const title = workspace.rightSidebar + .getByText("Board", { exact: true }) + .locator(".."); + await expect(title).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Layers panel", () => { + test("Shows the focus mode title and the layer filters", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9066.json"); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.clickLeafLayer("Board"); + await page.keyboard.press("f"); + await expect(workspace.sidebar.getByText("Focus mode")).toBeVisible(); + await page.mouse.move(640, 600); + await expect(workspace.sidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await page.keyboard.press("f"); + await expect(workspace.sidebar.getByText("Focus mode")).toBeHidden(); + await workspace.sidebar.getByRole("button", { name: "Search" }).click(); + await workspace.sidebar.getByRole("button", { name: "Filter" }).click(); + await workspace.sidebar.getByText("Boards", { exact: true }).click(); + await page.mouse.move(640, 600); + // The "Boards" filter pill. The clip leaves out the pages divider and + // the search bar, whose transitions may still be running. + const filtersClip = { x: 0, y: 340, width: 318, height: 380 }; + await expect(page).toHaveScreenshot({ + clip: filtersClip, + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await workspace.sidebar.getByRole("button", { name: "Filter" }).click(); + await page.mouse.move(640, 600); + // The filter menu open, with its option ticked. + await expect(page).toHaveScreenshot({ + clip: filtersClip, + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Layers", () => { + test("Shows the buttons of the layer rows", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9066.json"); + await workspace.mockRPC( + "update-file?id=*", + "workspace/update-file-empty.json", + ); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.clickToggableLayer("Board"); + await workspace.clickToggableLayer("Group"); + const layer = workspace.layers.getByText("A", { exact: true }); + await layer.click(); + await layer.hover(); + await workspace.layers.getByRole("button", { name: "Hide" }).click(); + await workspace.layers.getByRole("button", { name: "Lock" }).click(); + await expect( + workspace.layers.getByRole("button", { name: "Show" }), + ).toBeVisible(); + await expect( + workspace.layers.getByRole("button", { name: "Unlock" }), + ).toBeVisible(); + + // Selected, hidden and blocked layer, with the pointer on its row. + await expect(workspace.layers).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + // The pointer on a collapsed group, away from the selected layer. + await workspace.layers.getByText("Board", { exact: true }).hover(); + await expect(workspace.layers).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); + + test("Shows the rename input of a layer", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9066.json"); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.layers.getByText("Board", { exact: true }).dblclick(); + const input = workspace.layers.getByRole("textbox"); + await expect(input).toBeVisible(); + await page.mouse.move(0, 0); + await expect(workspace.layers).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Guides", () => { + test("Shows the advanced options of a square guide", async ({ page }) => { + // A taller window keeps the whole Design tab visible: a scrolled + // sidebar can land on a fractional offset and change the antialiasing. + await page.setViewportSize({ width: 1280, height: 900 }); + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + + await workspace.boardButton.click(); + await workspace.clickWithDragViewportAt(128, 128, 200, 200); + await workspace.rightSidebar + .getByRole("button", { name: "Add guide" }) + .click(); + + // The advanced options toggle has no accessible name yet. + await workspace.rightSidebar + .locator("button[class*='frame_grid__show-options']") + .click(); + await expect(workspace.rightSidebar.getByText("Color")).toBeVisible(); + // The guide options are a small part of the sidebar: compare every + // pixel. The color rows antialias their rounded corners one color step + // differently between runs, so allow that much and nothing more. + await expect(workspace.rightSidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0.02, + }); + }); +}); + +test.describe("Path actions", () => { + test("Shows the path editing bar and its menus", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + + // A path with a curve node, so the handler menu shows. + await page.keyboard.press("p"); + await workspace.clickAt(200, 200); + await workspace.clickAndMove(400, 200, 450, 300); + await workspace.clickAt(300, 400); + await page.keyboard.press("Enter"); + + const bar = page.locator("[class*='sub-actions']").first(); + await expect(bar).toBeVisible(); + await page.mouse.move(640, 600); + await expect(bar).toHaveScreenshot("path-actions-draw.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + // Select every node so all the node actions show. + await bar.getByRole("button", { name: /Move nodes/ }).click(); + await workspace.clickWithDragViewportAt(150, 150, 350, 300); + await page.mouse.move(640, 600); + await expect(bar).toHaveScreenshot("path-actions.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + const box = await bar.boundingBox(); + const clip = { + x: box.x - 40, + y: box.y, + width: box.width + 80, + height: box.height + 46, + }; + + await bar.getByRole("button", { name: /Merge nodes/ }).hover(); + // Let the tooltip show. + await page.waitForTimeout(1000); + await expect(page).toHaveScreenshot("path-actions-hover.png", { + clip, + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await bar.getByRole("button", { name: /Handler/ }).click(); + await expect( + bar.getByRole("button", { name: "Independent" }), + ).toBeVisible(); + // Let the menu fade in. + await page.waitForTimeout(500); + await expect(page).toHaveScreenshot("path-actions-handlers.png", { + clip, + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Viewport top bar", () => { + test("Shows the view only bar of the inspect tab", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9066.json"); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.clickLeafLayer("Board"); + await workspace.rightSidebar.getByRole("tab", { name: "Inspect" }).click(); + const bar = page.getByText("Inspecting code").locator("../../.."); + await expect(bar).toBeVisible(); + await page.mouse.move(640, 600); + await expect(bar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Left header", () => { + test("Shows the shared badge and the save status", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-library.json"); + await workspace.mockRPC( + "update-file?id=*", + "workspace/update-file-create-rect.json", + ); + await workspace.goToWorkspace({ + fileId: "c1249a66-fce0-8175-8004-7433fe4be8bc", + pageId: "c1249a66-fce0-8175-8004-7433fe4be8bd", + }); + + const header = page.locator("header", { hasText: "Testing library 1" }); + await expect(header).toBeVisible(); + // The badge and the status dot are tiny: compare every pixel. + await expect(header).toHaveScreenshot("left-header-shared.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await workspace.rectShapeButton.click(); + await workspace.clickWithDragViewportAt(128, 128, 200, 100); + await expect(header.getByTitle("Saved")).toBeVisible(); + await page.mouse.move(640, 600); + await expect(header).toHaveScreenshot("left-header-saved.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); + + test("Shows the Penpot logo link", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + + const logo = page.locator("header a[class*='main-icon']"); + await expect(logo).toBeVisible(); + await page.mouse.move(640, 600); + await expect(logo).toHaveScreenshot("left-header-logo.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await logo.hover(); + await expect(logo).toHaveScreenshot("left-header-logo-hover.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Right header", () => { + test("Shows the header actions and the zoom menu", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + + const header = page.locator('[class*="workspace-header-right"]'); + await expect(header).toBeVisible(); + await page.mouse.move(640, 300); + // The buttons are a small part of the header: compare every pixel. + await expect(header).toHaveScreenshot("right-header.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await header.getByRole("button", { name: /Comments/ }).click(); + await page.mouse.move(640, 300); + await expect(header).toHaveScreenshot("right-header-comments.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + await page.keyboard.press("Escape"); + + await header.getByTitle("Zoom").click(); + await expect(page.getByText("Reset")).toBeVisible(); + await page.mouse.move(640, 300); + await expect(page).toHaveScreenshot("right-header-zoom.png", { + clip: { x: 900, y: 0, width: 380, height: 260 }, + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Share button", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Share button in the right header", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + // A shared team where the user is admin, so the header shows Share. + await workspace.mockRPC("get-teams", "workspace/get-teams-shared.json"); + await workspace.goToWorkspace(); + + const header = page.locator('[class*="workspace-header-right"]'); + const share = header + .locator("[class*='share-btn']") + .or(header.getByRole("button", { name: "Share" })); + await expect(share).toBeVisible(); + await page.mouse.move(640, 300); + await expect(header).toHaveScreenshot(shot); + + await share.hover(); + await page.waitForTimeout(1000); + await expect(header).toHaveScreenshot(shot); + }); +}); + +test.describe("Undo history", () => { + test("Shows the actions list with an entry expanded", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC( + "update-file?id=*", + "workspace/update-file-empty.json", + ); + await workspace.mockRPC( + "get-file-snapshots?file-id=*", + "workspace/versions-snapshot-1.json", + ); + await workspace.goToWorkspace(); + + await page.keyboard.press("r"); + await workspace.clickWithDragViewportAt(128, 128, 200, 100); + + await page.keyboard.press("Control+Alt+h"); + await page.getByRole("tab", { name: "Actions" }).click(); + const panel = page.getByRole("tabpanel", { name: "Actions" }); + const entry = panel.getByText("New rectangle"); + // The short id and the relative time change on every run. + const mask = [page.locator('[class*="history-entry-meta"]')]; + + // The expand arrow only shows while the entry is hovered. + await entry.hover(); + await expect(panel).toHaveScreenshot({ mask }); + + // The data attribute exists before and after the DS migration. + await panel.locator('[data-has-entry="true"]').click(); + await expect(panel.getByText("Rectangle", { exact: true })).toBeVisible(); + await entry.hover(); + await expect(panel).toHaveScreenshot({ mask }); + }); +}); + test.describe("Palette", () => { test("Shows the bottom palette expanded and collapsed", async ({ page }) => { const workspace = new WorkspacePage(page); @@ -163,6 +947,467 @@ test.describe("Palette", () => { ), ).toBeVisible(); }); + + test("Shows the palette toggles selected", async ({ page }) => { + const workspace = new WorkspacePage(page); + const { fileId, pageId } = await setupFileWithAssets(workspace); + + await workspace.goToWorkspace({ fileId, pageId }); + + await workspace.palette + .getByRole("button", { name: "Typographies" }) + .click(); + await expect( + workspace.palette.getByText("Source Sans Pro Regular"), + ).toBeVisible(); + await page.mouse.move(640, 300); + // The toggles are a small part of the palette: compare every pixel. + await expect(workspace.palette).toHaveScreenshot("palette-text.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await workspace.palette + .getByRole("button", { name: "Color Palette" }) + .click(); + await expect( + workspace.palette.getByText( + "There are no color styles in your library yet", + ), + ).toBeVisible(); + await page.mouse.move(640, 300); + await expect(workspace.palette).toHaveScreenshot("palette-color.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Plugins", () => { + const setupPlugins = async (workspace, profile) => { + await workspace.setupEmptyFile(); + await workspace.mockRPC("get-team?id=*", "workspace/get-team-plugins.json"); + if (profile) { + await workspace.mockRPC("get-profile", profile); + } + await workspace.goToWorkspace(); + await workspace.page.keyboard.press("Control+Alt+P"); + }; + + test("Shows the plugins manager without plugins", async ({ page }) => { + const workspace = new WorkspacePage(page); + await setupPlugins(workspace); + + await expect(page.getByText("No plugins installed yet")).toBeVisible(); + await expect(page.getByRole("button", { name: "Close" })).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); + + test("Shows the plugins manager and the permissions dialog", async ({ + page, + }) => { + const workspace = new WorkspacePage(page); + await page.route("https://plugin.example.com/manifest.json", (route) => + route.fulfill({ + json: { + name: "Contrast checker", + description: "Check the contrast of the selected layers.", + code: "plugin.js", + permissions: ["content:write", "user:read", "library:write"], + }, + }), + ); + await setupPlugins( + workspace, + "logged-in-user/get-profile-with-plugins.json", + ); + + await expect(page.getByText("Lorem Ipsum generator")).toBeVisible(); + await expect(page).toHaveScreenshot(); + + await page + .getByRole("textbox") + .fill("https://plugin.example.com/manifest.json"); + await page.getByRole("button", { name: "Install" }).click(); + await expect(page.getByText("CONTRAST CHECKER")).toBeVisible(); + await expect(page).toHaveScreenshot(); + }); +}); + +test.describe("Nudge modal", () => { + test("Shows the nudge amount modal", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + + await page.getByRole("button", { name: "Main menu" }).click(); + await page.locator("#file-menu-preferences").click(); + await page.locator("#file-menu-nudge").click(); + + await expect(page.getByText("Nudge amount")).toBeVisible(); + await page.mouse.move(0, 0); + await expect(workspace.page).toHaveScreenshot("nudge-modal.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Text palette", () => { + test("Shows the scroll arrows and the palette menu", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC( + /get\-file\?/, + "workspace/get-file-many-typographies.json", + ); + await workspace.goToWorkspace({ + fileId: "c1249a66-fce0-8175-8004-7433fe4be8bc", + pageId: "c1249a66-fce0-8175-8004-7433fe4be8bd", + }); + + await workspace.palette + .getByRole("button", { name: "Typographies" }) + .click(); + await expect( + workspace.palette.getByText("Type 01 Regular 12"), + ).toBeVisible(); + await page.mouse.move(640, 300); + // The arrows are a small part of the palette: compare every pixel. + await expect(workspace.palette).toHaveScreenshot("text-palette.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + const rightArrow = workspace.palette.locator('[class*="right-arrow"]'); + await rightArrow.hover(); + await expect(workspace.palette).toHaveScreenshot( + "text-palette-arrow-hover.png", + { maxDiffPixelRatio: 0, threshold: 0 }, + ); + + await rightArrow.click(); + await page.mouse.move(640, 300); + await expect(workspace.palette).toHaveScreenshot( + "text-palette-scrolled.png", + { maxDiffPixelRatio: 0, threshold: 0 }, + ); + + await workspace.palette.locator('[class*="palette-actions"]').click(); + await expect(page.getByText("File library")).toBeVisible(); + await page.mouse.move(640, 300); + await expect(page).toHaveScreenshot("text-palette-menu.png", { + clip: { x: 300, y: 450, width: 700, height: 270 }, + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Selected colors", () => { + test("Shows the more colors button", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9066.json"); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.clickToggableLayer("Board"); + await page.keyboard.press("Control+A"); + + await expect( + workspace.rightSidebar.getByRole("button", { name: "More colors" }), + ).toBeVisible(); + // The button is a small part of the sidebar: compare every pixel. + await expect(workspace.rightSidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Imported SVG attributes", () => { + test("Shows the attributes of an imported SVG shape", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9930.json"); + await workspace.goToWorkspace({ + fileId: "8f843b59-7fbb-81ce-8005-aa6d47ae3111", + pageId: "fb9798e7-a547-80ae-8005-9ffda4a13e2c", + }); + + // The fourth "workspace" board holds the imported SVG groups. + await workspace.clickToggableLayer("workspace", {}, 3); + await workspace.clickToggableLayer("Canvas"); + await workspace.clickLeafLayer("Rectangle 760 (Stroke)"); + + const section = workspace.rightSidebar.getByText("Imported SVG Attributes"); + await section.scrollIntoViewIfNeeded(); + await expect(section).toBeVisible(); + // The attributes are a small part of the sidebar: compare every pixel. + await expect(workspace.rightSidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Grid cell options", () => { + test("Shows the grid cell options when selecting a cell", async ({ + page, + }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9042.json"); + await workspace.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-fragment-9042.json", + ); + await workspace.goToWorkspace({ + fileId: "af2494d0-39ba-8184-8005-230696f6df5c", + pageId: "af2494d0-39ba-8184-8005-230696f6df5d", + }); + + await workspace.clickLeafLayer("Board"); + await page + .getByTestId("inspect-layout") + .getByRole("button", { name: "Edit grid" }) + .click(); + + // Click the first cell of the grid editor. + const cell = page.locator("g.cell-editor > rect").first(); + const box = await cell.boundingBox(); + await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2); + + await expect(workspace.rightSidebar.getByText("Grid cell")).toBeVisible(); + // The cell options are a small part of the sidebar: compare every pixel. + await expect(workspace.rightSidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Export modal", () => { + test("Shows the export frames modal with every selection state", async ({ + page, + }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC( + /get\-file\?/, + "workspace/get-file-export-frames.json", + ); + await workspace.mockRPC( + "get-file-object-thumbnails?file-id=*", + "workspace/get-file-object-thumbnails-blank.json", + ); + await workspace.mockRPC( + "get-file-fragment?file-id=*", + "workspace/get-file-fragment-blank.json", + ); + await workspace.goToWorkspace({ + fileId: "8d102c33-b98f-81f6-8006-fdd4ea9780d7", + pageId: "8d102c33-b98f-81f6-8006-fdd4ea9780d8", + }); + + await page.getByRole("button", { name: "Main menu" }).click(); + await page.getByText("file").last().click(); + await page.locator("#file-menu-export-frames").click(); + const modal = page.getByText("Export as PDF").locator("../.."); + await expect(page.getByText("2 of 2 elements selected")).toBeVisible(); + await page.mouse.move(0, 0); + // Every row checked. + await expect(modal).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await page.getByRole("button", { name: "Board 1" }).click(); + await expect(page.getByText("1 of 2 elements selected")).toBeVisible(); + await page.mouse.move(0, 0); + // Mixed: the header checkbox shows the "some selected" mark. + await expect(modal).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await page.getByRole("button", { name: "Board 2" }).click(); + await expect(page.getByText("0 of 2 elements selected")).toBeVisible(); + await page.mouse.move(0, 0); + // Nothing checked: the export button is disabled. + await expect(modal).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + +test.describe("Unpublish library modal", () => { + test("Shows the close, cancel and unpublish buttons", async ({ page }) => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-library.json"); + await workspace.mockRPC( + "get-library-file-references?file-id=*", + "workspace/get-library-file-references-empty.json", + ); + await workspace.goToWorkspace({ + fileId: "c1249a66-fce0-8175-8004-7433fe4be8bc", + pageId: "c1249a66-fce0-8175-8004-7433fe4be8bd", + }); + + await page.getByRole("button", { name: "Main menu" }).click(); + await page.locator("#file-menu-file").click(); + await page.locator("#file-menu-remove-shared").click(); + + const modal = page.locator("[class*='delete_shared__modal-container']"); + await expect(modal).toBeVisible(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + + await modal.locator("[class*='modal-header'] button").hover(); + await expect(modal).toHaveScreenshot(shot); + }); +}); + +test.describe("Assets tab controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Add library button", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + await workspace.clickAssets(); + + const add = workspace.sidebar.getByTestId("libraries"); + await expect(add).toBeVisible(); + await page.mouse.move(640, 600); + await expect(add).toHaveScreenshot(shot); + await add.hover(); + await expect(add).toHaveScreenshot(shot); + }); + + test("Libraries, filter and group modal", async ({ page }) => { + const workspace = new WorkspacePage(page); + const { fileId, pageId } = await setupFileWithAssets(workspace); + await workspace.goToWorkspace({ fileId, pageId }); + await workspace.clickAssets(); + + const header = workspace.sidebar + .getByTestId("libraries") + .locator("xpath=.."); + await expect(workspace.sidebar.getByTestId("libraries")).toBeVisible(); + await page.mouse.move(640, 600); + await expect(header).toHaveScreenshot(shot); + + await workspace.sidebar.getByTestId("libraries").hover(); + await expect(header).toHaveScreenshot(shot); + + const filter = workspace.sidebar.getByTitle("Filter"); + await filter.hover(); + await expect(header).toHaveScreenshot(shot); + + await filter.click(); + await page.mouse.move(640, 600); + await expect(header).toHaveScreenshot(shot); + await page.keyboard.press("Escape"); + + await workspace.sidebar.getByRole("button", { name: "Colors" }).click(); + await workspace.sidebar + .locator("[class*='colors'] [class*='asset-list-item']") + .first() + .click({ button: "right" }); + await page.getByText("Group", { exact: true }).click(); + const modal = page.locator("[class*='groups__modal-container']"); + await expect(modal).toBeVisible(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + }); +}); + +test.describe("Comments panel filter", () => { + test("Shows the filter trigger and its menu", async ({ page }) => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + await workspace.showComments(); + + const trigger = workspace.rightSidebar.getByRole("button", { + name: "Show all comments", + }); + await expect(trigger).toBeVisible(); + await page.mouse.move(640, 600); + await expect(trigger).toHaveScreenshot(shot); + + await trigger.hover(); + await expect(trigger).toHaveScreenshot(shot); + + await trigger.click(); + const menu = page.locator("[class*='comment-mode-dropdown']"); + await expect(menu).toBeVisible(); + await page.mouse.move(640, 600); + await expect(menu).toHaveScreenshot(shot); + }); +}); + +test.describe("Gradient type select", () => { + test("Shows the gradient type select closed, open and changed", async ({ + page, + }) => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-not-empty.json"); + await workspace.goToWorkspace({ + fileId: "6191cd35-bb1f-81f7-8004-7cc63d087374", + pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375", + }); + await workspace.clickLeafLayer("Rectangle"); + await page.getByRole("button", { name: "#B1B2B5" }).click(); + await page.getByText("Solid").click(); + await page.getByText("Gradient").click(); + + const typeSelect = workspace.colorpicker + .getByRole("combobox") + .filter({ hasText: "Linear" }); + await expect(typeSelect).toBeVisible(); + const pickerBox = await workspace.colorpicker.boundingBox(); + const selectBox = await typeSelect.boundingBox(); + // The gradient bar above the select and room below it for the open list. + const clip = { + x: Math.floor(pickerBox.x), + y: Math.floor(selectBox.y) - 60, + width: Math.ceil(pickerBox.width), + height: 200, + }; + await page.mouse.move(0, 0); + await expect(page).toHaveScreenshot({ clip, ...shot }); + + await typeSelect.hover(); + await expect(page).toHaveScreenshot({ clip, ...shot }); + + await typeSelect.click(); + const radial = page.getByRole("option", { name: "Radial" }); + await expect(radial).toBeVisible(); + await page.mouse.move(0, 0); + await expect(page).toHaveScreenshot({ clip, ...shot }); + + await radial.click(); + await expect( + workspace.colorpicker.getByRole("combobox").filter({ hasText: "Radial" }), + ).toBeVisible(); + await page.mouse.move(0, 0); + await expect(page).toHaveScreenshot({ clip, ...shot }); + + // The whole gradient panel: preview bar, stops and the stop list. + const panel = workspace.colorpicker.locator("[class*='gradient-panel']"); + await expect(panel).toHaveScreenshot(shot); + }); }); test.describe("Shortcuts panel", () => { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index eef54f21b2..9f290ceb3f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' @@ -210,9 +210,6 @@ importers: '@penpot/text-editor': specifier: link:text-editor version: link:text-editor - '@penpot/tokenscript': - specifier: link:packages/tokenscript - version: link:packages/tokenscript '@penpot/ua-parser': specifier: penpot/ua-parser#1.0.0 version: https://codeload.github.com/penpot/ua-parser/tar.gz/90b970f39f2dc08378b975a0f01045b4ec8e89a4 @@ -445,12 +442,6 @@ importers: packages/mousetrap: {} - packages/tokenscript: - dependencies: - '@tokens-studio/tokenscript-interpreter': - specifier: ^0.26.0 - version: 0.26.0 - packages/ui: dependencies: react: @@ -547,12 +538,6 @@ packages: '@adobe/css-tools@4.5.0': resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} - '@ark/schema@0.56.6': - resolution: {integrity: sha512-OmLv80BCF2ib+zjecrXDosip/CSnZSYBGjblhqWYZWFOzdLQka6QymHToE9u7Z/HZW2KA8Hwc8MCNXoI3uGSIA==} - - '@ark/util@0.56.6': - resolution: {integrity: sha512-mHqq0f0HyDd9YadpFl25w0QECAP7d8HGIUpEC85duD+L+xr4i/CwfOid/Z7zJFFF/47oLt6+pZB9RbqWpBYTYg==} - '@asamuzakjp/css-color@7.1.3': resolution: {integrity: sha512-1t1U8Cm3RBl9vh3RAZhjkPaduzLSvQJRToRrxpGR/eDRW99mOREleuu+mWfiFa795bUzjwht7NgphCKTs2ianQ==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} @@ -1902,11 +1887,6 @@ packages: peerDependencies: style-dictionary: ^5.0.0 - '@tokens-studio/tokenscript-interpreter@0.26.0': - resolution: {integrity: sha512-dGjvUJnXRspWYp98FZw43l4cN+0ey/cF5sEJjL3coKc5C7DY7MsKgkmOONizmaZqf13GUIzklTEas3gt3jvrOQ==} - engines: {node: '>=16.0.0'} - hasBin: true - '@tokens-studio/types@0.5.2': resolution: {integrity: sha512-rzMcZP0bj2E5jaa7Fj0LGgYHysoCrbrxILVbT0ohsCUH5uCHY/u6J7Qw/TE0n6gR9Js/c9ZO9T8mOoz0HdLMbA==} @@ -2134,12 +2114,6 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} - arkregex@0.0.12: - resolution: {integrity: sha512-sOxL0eX50pnRYO4OyajKZChGuuj/iHwC5tysiAaGOPBu+zJwJQckWn7/DRH2++ol5uaKpyRn7N0hKT7nDqGmgg==} - - arktype@2.2.7: - resolution: {integrity: sha512-AbgM5SQvwCTURSR+ENfPtOG7cL5VZ0hqOVtoWyXKw2SviFddZhCBEUAjjVJYKCNFlPPR1VX5LGUKxsSqqc4Kdw==} - array-buffer-byte-length@1.0.2: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} @@ -3873,9 +3847,6 @@ packages: resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - picocolors@0.2.1: resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} @@ -4174,10 +4145,6 @@ packages: resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} engines: {node: '>= 20.19.0'} - readline-sync@1.4.10: - resolution: {integrity: sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==} - engines: {node: '>= 0.8.0'} - recast@0.23.21: resolution: {integrity: sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==} engines: {node: '>= 4'} @@ -5203,10 +5170,6 @@ packages: resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} - yauzl@3.4.0: - resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==} - engines: {node: '>=12'} - yocto-queue@1.2.2: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} @@ -5215,12 +5178,6 @@ snapshots: '@adobe/css-tools@4.5.0': {} - '@ark/schema@0.56.6': - dependencies: - '@ark/util': 0.56.6 - - '@ark/util@0.56.6': {} - '@asamuzakjp/css-color@7.1.3': dependencies: '@csstools/css-calc': 3.4.3(@csstools/css-parser-algorithms@4.0.2(@csstools/css-tokenizer@4.0.2))(@csstools/css-tokenizer@4.0.2) @@ -6461,13 +6418,6 @@ snapshots: is-mergeable-object: 1.1.1 style-dictionary: 5.5.5 - '@tokens-studio/tokenscript-interpreter@0.26.0': - dependencies: - arktype: 2.2.7 - commander: 14.0.3 - readline-sync: 1.4.10 - yauzl: 3.4.0 - '@tokens-studio/types@0.5.2': {} '@tybys/wasm-util@0.10.4': @@ -6717,16 +6667,6 @@ snapshots: aria-query@5.3.2: {} - arkregex@0.0.12: - dependencies: - '@ark/util': 0.56.6 - - arktype@2.2.7: - dependencies: - '@ark/schema': 0.56.6 - '@ark/util': 0.56.6 - arkregex: 0.0.12 - array-buffer-byte-length@1.0.2: dependencies: call-bound: 1.0.4 @@ -8555,8 +8495,6 @@ snapshots: pathval@2.0.1: {} - pend@1.2.0: {} - picocolors@0.2.1: {} picocolors@1.1.1: {} @@ -8897,8 +8835,6 @@ snapshots: readdirp@5.1.1: {} - readline-sync@1.4.10: {} - recast@0.23.21: dependencies: ast-types: 0.16.3 @@ -10018,8 +9954,4 @@ snapshots: y18n: 5.0.8 yargs-parser: 22.0.0 - yauzl@3.4.0: - dependencies: - pend: 1.2.0 - yocto-queue@1.2.2: {} diff --git a/frontend/resources/images/icons/handlers-equal.svg b/frontend/resources/images/icons/handlers-equal.svg index 645e773d8c..c1b3cb33aa 100644 --- a/frontend/resources/images/icons/handlers-equal.svg +++ b/frontend/resources/images/icons/handlers-equal.svg @@ -1,3 +1,3 @@ - + diff --git a/frontend/resources/images/icons/handlers-independent.svg b/frontend/resources/images/icons/handlers-independent.svg index 36e1c06917..cb782fbfe2 100644 --- a/frontend/resources/images/icons/handlers-independent.svg +++ b/frontend/resources/images/icons/handlers-independent.svg @@ -1,3 +1,3 @@ - + diff --git a/frontend/resources/images/icons/handlers-mirror.svg b/frontend/resources/images/icons/handlers-mirror.svg index 78f71c740e..1e8ac8868c 100644 --- a/frontend/resources/images/icons/handlers-mirror.svg +++ b/frontend/resources/images/icons/handlers-mirror.svg @@ -1,3 +1,3 @@ - + diff --git a/frontend/resources/images/icons/loader.svg b/frontend/resources/images/icons/loader.svg deleted file mode 100644 index ee1d9b96bf..0000000000 --- a/frontend/resources/images/icons/loader.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/resources/images/icons/share.svg b/frontend/resources/images/icons/share.svg index 2c0d41de63..0677a583d3 100644 --- a/frontend/resources/images/icons/share.svg +++ b/frontend/resources/images/icons/share.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/frontend/resources/images/icons/snap-nodes.svg b/frontend/resources/images/icons/snap-nodes.svg deleted file mode 100644 index 6ce0a90d67..0000000000 --- a/frontend/resources/images/icons/snap-nodes.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/resources/images/icons/snap.svg b/frontend/resources/images/icons/snap.svg index 0acdce1f20..b2f88cb835 100644 --- a/frontend/resources/images/icons/snap.svg +++ b/frontend/resources/images/icons/snap.svg @@ -1,3 +1,3 @@ - + diff --git a/frontend/resources/styles/common/refactor/common-dashboard.scss b/frontend/resources/styles/common/refactor/common-dashboard.scss deleted file mode 100644 index ba38cd0b78..0000000000 --- a/frontend/resources/styles/common/refactor/common-dashboard.scss +++ /dev/null @@ -1,159 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) KALEIDOS SUBSIDIARY SL - -@use "refactor/common-refactor" as *; - -.dashboard-header { - align-items: center; - display: flex; - height: $s-64; - max-height: $s-64; - justify-content: space-between; - padding: $s-4 $s-16 $s-4 $s-8; - position: relative; - user-select: none; - - &.team { - display: grid; - grid-template-columns: 20% 1fr 20%; - max-height: $s-64; - } - - .element-name { - margin-right: $s-8; - } - - .btn-secondary { - flex-shrink: 0; - height: $s-32; - - svg { - height: $s-16; - width: $s-16; - } - } - - nav { - display: flex; - align-items: flex-end; - justify-content: center; - - ul { - display: flex; - font-size: $fs-14; - justify-content: center; - margin: 0; - } - - li { - a { - display: flex; - align-items: center; - flex-basis: $s-140; - border-bottom: $s-3 solid transparent; - color: var(--color-foreground-secondary); - height: $s-40; - padding: $s-4 $s-24; - font-weight: $fw400; - - &:hover { - color: var(--color-background-secondary); - text-decoration: none; - } - } - } - } - - .dashboard-title { - display: flex; - align-items: center; - margin-left: $s-12; - - h1 { - color: var(--color-foreground-primary); - display: block; - flex-shrink: 0; - font-size: $fs-24; - font-weight: $fw400; - max-width: $s-712; - overflow: hidden; - text-overflow: ellipsis; - user-select: all; - white-space: nowrap; - width: 100%; - } - - .context-menu.is-open { - margin-top: $s-8; - } - } - - .icon { - display: flex; - align-items: center; - cursor: pointer; - - svg { - stroke: var(--color-foreground-secondary); - fill: none; - width: $s-16; - height: $s-16; - - &:hover { - stroke: var(--color-accent-tertiary); - fill: none; - } - } - } - - .dashboard-buttons { - display: flex; - justify-content: flex-end; - align-items: center; - } - - .dashboard-header-actions { - display: flex; - column-gap: $s-16; - } - - .dashboard-header-options { - li { - a { - font-size: $s-16; - color: var(--color-foreground-secondary); - border-color: transparent; - - &:hover { - color: var(--color-foreground-primary); - } - } - - &.active { - a { - color: var(--color-foreground-primary); - } - } - } - } -} - -.btn-primary { - @extend %button-primary; - - text-transform: uppercase; - font-size: $fs-14; - font-weight: $fw400; -} - -.btn-secondary { - @extend %button-secondary; - - color: var(--color-foreground-primary); - font-size: $fs-12; - text-transform: uppercase; - padding: 0 $s-16; -} diff --git a/frontend/scripts/_helpers.js b/frontend/scripts/_helpers.js index 9a6bf14ec8..2b59eb9038 100644 --- a/frontend/scripts/_helpers.js +++ b/frontend/scripts/_helpers.js @@ -328,7 +328,7 @@ export async function compileTranslations() { "nl", // this happens when file does not matches correct // iso code for the language. - ["ja_jp", "jpn_JP"], + ["ja", "jpn_JP"], ["uk", "ukr_UA"], "ha", ]; diff --git a/frontend/scripts/check-translations/words.es.txt b/frontend/scripts/check-translations/words.es.txt index 009f4c1c4e..5111ebe320 100644 --- a/frontend/scripts/check-translations/words.es.txt +++ b/frontend/scripts/check-translations/words.es.txt @@ -262,6 +262,8 @@ eliminarla eliminarlo eliminarlos ella +ellos +ese eso esos este @@ -270,6 +272,7 @@ estándar expirada funcionalidades granulares +guardarlos hay hubo importada @@ -295,6 +298,7 @@ puesto regenerada restaurarlo restaurarlos +sal seleccionada selector servidores diff --git a/frontend/scripts/check-translations/words.ru.txt b/frontend/scripts/check-translations/words.ru.txt new file mode 100644 index 0000000000..430deaa40a --- /dev/null +++ b/frontend/scripts/check-translations/words.ru.txt @@ -0,0 +1,352 @@ +# Word catalog for check-translations.js (Russian). +# Comment lines start with #. Blank lines are ignored. +# [function] short function words; a token starting with one of +# these is likely missing a space (ивкладка style). +# [common] ordinary words used to judge the other half of a split. +# [ok] correct words the splitter would flag; never report +# these. Add new valid words here when the gate trips. +# [brands] extra locale-specific brands/terms kept as-is. +# [elision] letters elided with an apostrophe (Russian has none). +# +# NOTE (Russian): Russian inflects (проект -> проекта, файл -> файла), +# so one-letter words (в, с, к, о, у, а, и) and common endings/particles +# (на, по, не, ли, же, бы, его, их, ...) are DELIBERATELY absent from +# [function]: listing them flags every inflected noun as a glued word. +# The gate still catches placeholder/plural/punctuation errors plus +# long content-word glues (review warnings). + +[elision] + +[function] +без +для +как +что +чтобы +чтоб +если +когда +хотя +пусть +даже +лишь +только +тоже +также +уже +ведь +вот +между +через +перед +после +возле +вокруг +вдоль +внутри +вне +кроме +мимо +около +против +среди +сверху +снизу +слева +справа +вы +мы +он +она +оно +они +меня +тебя +себя +нас +вас +это +этот +эта +эти +тот +та +здесь +там +тут +где +куда +откуда +почему +зачем +сколько +какой +какая +какое +какие + +[common] +создать +удалить +удаление +удалён +добавить +сохранить +сохранён +открыть +закрыть +изменить +выбрать +выбрано +показать +скрыть +переместить +перемещён +копировать +вставить +отменить +вернуть +дублировать +обновить +загрузить +загрузка +скачать +найти +искать +поиск +поделиться +пригласить +приглашение +приглашения +покинуть +восстановить +восстановлены +закрепить +открепить +опубликовать +настроить +настройки +отправить +получить +проверить +использовать +перейти +продолжить +начать +выйти +войти +файл +файлы +файлов +файла +проект +проекты +проектов +проекта +команда +команды +участник +участники +участников +библиотека +библиотеки +библиотек +компонент +компоненты +цвет +цвета +цветов +слой +слои +слоёв +доска +доски +досок +кадр +кадры +контур +обводка +заливка +размытие +тень +тени +сетка +сетки +макет +градиент +шрифт +шрифты +шрифтов +текст +текста +изображение +комментарий +комментарии +уведомление +уведомления +пароль +почта +почту +аккаунт +профиль +роль +доступ +ошибка +ошибки +версия +история +экспорт +импорт +тема +темы +набор +наборы +токен +токены +токенов +панель +окно +вкладка +кнопка +поле +список +таблица +строка +столбец +строки +высота +ширина +размер +имя +название +описание +время +дата +право +права +владелец +администратор +редактор +наблюдатель +гость +новый +новая +новые +общий +общая +последний +следующий +предыдущий +выбранное +непрозрачность +выравнивание +выровнять +направляющие +линейки +пиксельный +масштаб +приблизить +отдалить +группировать +разгруппировать +маска +узел +узлы +фигура +фигуры +элемент +элементы +свойства +стиль +эффект +ссылка +ссылку +вектор +растр +код +плагин +вебхук +шаблон +шаблоны +черновик +корзина +папка +область +холст +фон +граница +угол +центр +край +верх +низ +право +лево +горизонталь +вертикаль +сверху +снизу +слева +справа +внутрь +наружу +сплошной +пунктирный +точечный +круг +квадрат +треугольник +стрелка +эллипс +прямоугольник +многоугольник +звезда +кривая +путь + +[ok] +автосохранение +автовысота +автоширина +медиафайл +цветовой +командой +чтобы +чтоб +какой +какая +какое +какие +какого +каких +каком +какую +горизонтально +вертикально +какой-либо +адресата +последний +последняя +последнее +последние +последней +последнего +последнем +последнюю +доступна +автомакета +запросили +верхняя +верхнее +верхние +верхнего +верхней +верхнем +верхнюю +библиотекой +автоверсии +подорганизации +прочитать +просмотреть + +[brands] diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index dbd3deb96c..047807464c 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -16,6 +16,7 @@ [app.main.data.event :as ev] [app.main.data.profile :as dp] [app.main.data.websocket :as ws] + [app.main.data.workspace.mcp :as mcp] [app.main.errors :as errors] [app.main.features :as feat] [app.main.rasterizer :as thr] @@ -66,9 +67,10 @@ [] (ptk/reify ::initialize-rasterizer ptk/EffectEvent - (effect [_ _ _] - ;; The rasterizer is used for the dashboard thumbnails - (thr/init!)))) + (effect [_ state _] + ;; Only the SVG renderer uses the rasterizer + (when-not (feat/active-feature? state "render-wasm/v1") + (thr/init!))))) (defn initialize [] @@ -81,6 +83,7 @@ (watch [_ _ stream] (rx/merge (rx/of (ev/initialize) + (mcp/initialize) (dp/refresh-profile)) ;; Watch for profile deletion events @@ -105,9 +108,8 @@ (rx/map #(ws/initialize))) (->> stream - (rx/filter (ptk/type? ::feat/initialize)) - (rx/take 1) - (rx/map #(initialize-rasterizer))))))) + (rx/filter (comp #{::feat/initialize ::feat/recompute-features} ptk/type)) + (rx/map initialize-rasterizer)))))) (defn ^:export init [options] diff --git a/frontend/src/app/main/data/changes.cljs b/frontend/src/app/main/data/changes.cljs index 0baea3a2ab..e114ed26b7 100644 --- a/frontend/src/app/main/data/changes.cljs +++ b/frontend/src/app/main/data/changes.cljs @@ -16,6 +16,9 @@ [app.common.uuid :as uuid] [app.main.data.event :as ev] [app.main.data.helpers :as dsh] + [app.main.data.persistence.commits :as pcm] + [app.main.data.persistence.lock :as plk] + [app.main.data.workspace :as-alias dw] [app.main.features :as features] [app.main.worker :as mw] [app.render-wasm.api :as wasm.api] @@ -68,12 +71,6 @@ (rx/empty))) (rx/ignore)))))) -(defn- get-pending-commits - [{:keys [persistence]}] - (->> (:queue persistence) - (map (d/getf (:index persistence))) - (not-empty))) - (def ^:private xf:map-page-id ;; Component-only changes have no :page-id; keep drops those nils so ;; update-object-indices does not create a nil key in :pages-index. @@ -135,14 +132,14 @@ (if (and (not ignore-wasm?) (features/active-feature? state "render-wasm/v1")) ;; Update the wasm model - (let [shape-changes (volatile! {}) + (let [shape-changes (js/Map.) state (binding [cts/*shape-changes* shape-changes] (update-in state [:files file-id :data] apply-changes))] (let [objects (dm/get-in state [:files file-id :data :pages-index (:current-page-id state) :objects])] - (wasm.shape/process-shape-changes! objects @shape-changes)) + (wasm.shape/process-shape-changes! objects (es6-iterator-seq (.entries shape-changes)))) state) @@ -162,7 +159,7 @@ "Create a commit event instance" [{:keys [commit-id redo-changes undo-changes origin save-undo? features file-id file-revn file-vern undo-group tags stack-undo? source ignore-wasm? - selected-before translation? skip-component-sync?]}] + selected-before translation? skip-component-sync? undo-index]}] (assert (cpc/check-changes redo-changes) "expect valid vector of changes for redo-changes") @@ -191,7 +188,9 @@ :ignore-wasm? ignore-wasm? :selected-before selected-before :translation? translation? - :skip-component-sync? skip-component-sync?}] + :skip-component-sync? skip-component-sync? + ;; Undo history index an undo or redo commit moved away from + :undo-index undo-index}] (ptk/reify ::commit cljs.core/IDeref @@ -200,7 +199,7 @@ ptk/WatchEvent (watch [_ state _] (let [pending (when-not local? - (get-pending-commits state))] + (not-empty (pcm/pending-commits (:persistence state) file-id)))] (rx/concat (rx/of (apply-changes-localy commit pending)) (if pending @@ -212,6 +211,54 @@ (rx/map (fn [commit] (update-indexes commit :redo-changes))))) (rx/of (update-indexes commit :redo-changes))))))))) +(defn rebase-commits + "Undoes `undo`, applies `remote` and redoes `redo` on the local data of + `file-id`, commits oldest first. Nothing is saved or added to the undo + history; if the changes fail to apply, the file is reloaded. + + Not a commit, so commit watchers miss it: once it has run, its deref + gives `{:file-id :changes :failed}` (`:failed` true when the file + reloads instead)." + [file-id {:keys [undo remote redo]}] + (let [commit {:file-id file-id + :redo-changes (-> [] + (into (mapcat :undo-changes) (reverse undo)) + (into remote) + (into (mapcat :redo-changes) redo))} + event (apply-changes-localy commit nil) + failed? (volatile! false)] + (ptk/reify ::rebase-commits + IDeref + (-deref [_] + {:file-id file-id + :changes (:redo-changes commit) + :failed @failed?}) + + ptk/UpdateEvent + (update [_ state] + (if (dsh/lookup-file-data state file-id) + (try + (ptk/update event state) + (catch :default cause + (log/error :hint "unable to rebase commits" :file-id (str file-id) :cause cause) + (vreset! failed? true) + state)) + state)) + + ptk/WatchEvent + (watch [_ state stream] + (cond + @failed? + (rx/of (ptk/event ::dw/reload-current-file)) + + (dsh/lookup-file-data state file-id) + (rx/merge + (or (ptk/watch event state stream) (rx/empty)) + (rx/of (update-indexes commit :redo-changes))) + + :else + (rx/empty)))))) + (defn- resolve-file-revn [state file-id] (:revn (dsh/lookup-file state file-id))) @@ -227,13 +274,17 @@ Options: - save-undo?: if set to false, do not add undo changes. - undo-group: if some consecutive changes (or even transactions) share the same - undo-group, they will be undone or redone in a single step + undo-group, they will be undone or redone in a single step. Without + it, a commit made while an undo transaction is open joins the + transaction's group, and any other gets a new one. + - ignore-save-lock: commit even while the save-failure lock holds. Only for + commits that are not user edits: the undo of rolled-back + commits and data derived from commits already made. " [{:keys [redo-changes undo-changes save-undo? undo-group tags stack-undo? file-id - translation? skip-component-sync?] + translation? skip-component-sync? ignore-save-lock] :or {save-undo? true stack-undo? false - undo-group (uuid/next) tags #{}} :as params}] (ptk/reify ::commit-changes @@ -249,13 +300,19 @@ ;; Historical previews must not create edits to the live file. Check ;; this when creating commits so previously queued edits can still save. + ;; While saves fail, a user edit would queue behind the failed save. ;; Refusals answer with an empty stream (never nil) so callers can ;; uniformly subscribe and observe termination. (if (and (:can-edit permissions) - (not (dm/get-in state [:workspace-global :preview-id]))) + (not (dm/get-in state [:workspace-global :preview-id])) + (or ignore-save-lock (not (plk/locked? state)))) (do (log/trace :hint "commit-changes" :redo-changes redo-changes) - (let [selected (dm/get-in state [:workspace-local :selected])] + (let [selected (dm/get-in state [:workspace-local :selected]) + tx (dm/get-in state [:workspace-undo :transaction]) + ;; Inside an undo transaction, the group of its history entry + ;; (taken from its first commit), or the one it was opened with + undo-group (or undo-group (:undo-group tx) (:commit-group tx) (uuid/next))] (rx/of (-> params (assoc :undo-group undo-group) (assoc :features features) diff --git a/frontend/src/app/main/data/common.cljs b/frontend/src/app/main/data/common.cljs index b04228694c..c2a7de99af 100644 --- a/frontend/src/app/main/data/common.cljs +++ b/frontend/src/app/main/data/common.cljs @@ -18,6 +18,7 @@ [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.data.persistence :as-alias dps] + [app.main.data.team :as dtm] [app.main.repo :as rp] [app.main.router :as rt] [app.main.store :as st] @@ -250,7 +251,11 @@ Updates local team organization data and redirects to SSO when required. `team` and `notification` come from the server-published message (see backend app.rpc.notifications/notify-team-change); `notification` is a translation - key resolved here, dynamic by design." + key resolved here, dynamic by design. + + The message is broadcast to every connected user, so it cannot carry the + per-profile `:restrictions-met`: the last known value is kept and the teams + are fetched again to get the current profile's one." [{:keys [team notification]}] (ptk/reify ::handle-change-team-organization ptk/WatchEvent @@ -259,11 +264,14 @@ organization (:organization team) team-name (or (:name team) (dm/get-in state [:teams (:id team) :name])) - current-team? (= (:id team) current-team-id)] + current-team? (= (:id team) current-team-id) + known-team? (contains? (:teams state) (:id team))] (when (and (contains? cf/flags :admin-console) - current-team?) + (or current-team? known-team?)) (rx/concat - (when notification + (when known-team? + (rx/of (dtm/fetch-teams))) + (when (and current-team? notification) ;; Execution time translation strings (keys sent by the backend): ;; (tr "dashboard.team-belong-organization") ;; (tr "dashboard.team-no-longer-belong-organization") @@ -274,8 +282,9 @@ :type :toast :level :info :timeout nil}))) - (when (:id organization) - (check-team-sso current-team-id)))))) ptk/UpdateEvent + (when (and current-team? (:id organization)) + (check-team-sso current-team-id)))))) + ptk/UpdateEvent (update [_ state] (if (contains? cf/flags :admin-console) (let [team-id (:id team) @@ -283,8 +292,14 @@ organization (:organization team)] (d/update-in-when state [:teams team-id] (fn [team] - (cond-> (cto/apply-organization team organization) - team-name (assoc :name team-name))))) + (let [same-organization? (= (:id organization) + (dm/get-in team [:organization :id])) + organization (cond-> organization + same-organization? + (assoc :restrictions-met + (dm/get-in team [:organization :restrictions-met])))] + (cond-> (cto/apply-organization team organization) + team-name (assoc :name team-name)))))) state)))) (defn handle-organization-change-sso diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index b0e47eafea..ab2ace94da 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -23,6 +23,7 @@ [app.main.data.helpers :as dsh] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] + [app.main.data.persistence :as dps] [app.main.data.team :as dtm] [app.main.data.websocket :as dws] [app.main.repo :as rp] @@ -282,6 +283,13 @@ ;; --- EVENT: duplicate-project +(defn project-with-file-count + "Loads the file count missing from a duplicated project's RPC response." + [{:keys [id] :as project}] + (->> (rp/cmd! :get-project-files {:project-id id}) + (rx/map (fn [files] + (assoc project :count (count files) :is-pinned false))))) + (defn project-duplicated [{:keys [id] :as project}] (ptk/reify ::project-duplicated @@ -312,6 +320,7 @@ (str " " copy-count)))) new-name (cfh/generate-unique-name name unames :suffix-fn suffix-fn)] (->> (rp/cmd! :duplicate-project {:project-id id :name new-name}) + (rx/mapcat project-with-file-count) (rx/tap on-success) (rx/map project-duplicated) (rx/catch on-error)))))) @@ -527,29 +536,60 @@ (rx/map #(with-meta (file-created %) (meta it))) (rx/catch on-error)))))) -;; --- EVENT: duplicate-file +;; --- EVENT: duplicate-files -(defn duplicate-file - [{:keys [id name] :as params}] - (dm/assert! (uuid? id)) - (dm/assert! (string? name)) - (ptk/reify ::duplicate-file +;; Longest wait for the pending changes of a file to be saved before copying it. +(def ^:private save-wait-timeout-ms 10000) + +(defn- notify-duplicated + "Tells the user the `n` copies exist. With `unsaved`, warns instead that + they miss the changes not saved yet." + [n unsaved] + (if unsaved + (ntf/show {:content (tr "dashboard.duplicate-unsaved-changes" (i18n/c n)) + :type :toast + :level :warning + :timeout 8000}) + (ntf/success (tr "dashboard.success-duplicate-file" (i18n/c n))))) + +(defn- copy-files + "Copies `files` on the server, then tells the user. A copy that fails + ends the stream with its error, so nothing is told." + [files] + (ptk/reify ::copy-files ptk/WatchEvent (watch [_ state _] - (let [{:keys [on-success on-error] - :or {on-success identity - on-error rx/throw}} (meta params) - unames (cfh/get-used-names (get state :files)) + (let [file-ids (into #{} (map :id) files) + ;; The copies hold the server's data, so what is unsaved now is missing + unsaved (dps/unsaved-changes? state file-ids) + unames (cfh/get-used-names (get state :files)) suffix-fn (fn [copy-count] (str/concat " " (tr "dashboard.copy-suffix") (when (> copy-count 1) - (str " " copy-count)))) - new-name (cfh/generate-unique-name name unames :suffix-fn suffix-fn)] - (->> (rp/cmd! :duplicate-file {:file-id id :name new-name}) - (rx/tap on-success) - (rx/map file-created) - (rx/catch on-error)))))) + (str " " copy-count))))] + (rx/concat + (->> (rx/from files) + (rx/merge-map + (fn [{:keys [id name]}] + (let [new-name (cfh/generate-unique-name name unames :suffix-fn suffix-fn)] + (->> (rp/cmd! :duplicate-file {:file-id id :name new-name}) + (rx/map file-created)))))) + (rx/of (notify-duplicated (count files) unsaved))))))) + +(defn duplicate-files + "Sends the pending changes of `files`, copies them and tells the user + once every copy exists." + [files] + (dm/assert! (every? (comp uuid? :id) files)) + (dm/assert! (every? (comp string? :name) files)) + (ptk/reify ::duplicate-files + ptk/WatchEvent + (watch [_ _ _] + ;; The copy is made from the server's data: send the pending changes first. + (rx/concat + (dps/force-persist-files-and-wait (into #{} (map :id) files) save-wait-timeout-ms) + (rx/of (copy-files files)))))) ;; --- EVENT: move-files diff --git a/frontend/src/app/main/data/event.cljs b/frontend/src/app/main/data/event.cljs index c381583e12..64070e1e85 100644 --- a/frontend/src/app/main/data/event.cljs +++ b/frontend/src/app/main/data/event.cljs @@ -8,7 +8,6 @@ (:require ["@penpot/ua-parser" :as ua] [app.common.data :as d] - [app.common.data.macros :as dm] [app.common.json :as json] [app.common.logging :as l] [app.common.math :as math] @@ -316,14 +315,14 @@ 0))) (add-libraries-counters [state files] - (reduce (fn [state library-id] - (let [data (dm/get-in files [library-id :data])] - (-> state - (update :total-components + (count (:components data))) - (update :total-colors + (count (:colors data))) - (update :total-typographies + (count (:typographies data)))))) - state - (refs/select-libraries files file-id)))] + (reduce-kv (fn [state _ library] + (let [data (:data library)] + (-> state + (update :total-components + (count (:components data))) + (update :total-colors + (count (:colors data))) + (update :total-typographies + (count (:typographies data)))))) + state + (refs/select-libraries files file-id)))] (ptk/reify ::snapshot-performance-info ptk/UpdateEvent diff --git a/frontend/src/app/main/data/exports/assets.cljs b/frontend/src/app/main/data/exports/assets.cljs index 5ca4f3e983..f041557921 100644 --- a/frontend/src/app/main/data/exports/assets.cljs +++ b/frontend/src/app/main/data/exports/assets.cljs @@ -6,10 +6,10 @@ (ns app.main.data.exports.assets (:require + [app.common.files.helpers :as cfh] [app.common.time :as ct] [app.common.uuid :as uuid] [app.main.data.event :as ev] - [app.main.data.exports.wasm :as wasm.exports] [app.main.data.helpers :as dsh] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] @@ -18,9 +18,11 @@ [app.main.features :as features] [app.main.repo :as rp] [app.main.store :as st] + [app.main.worker :as mw] [app.render-wasm.api :as wasm.api] [app.util.dom :as dom] [app.util.i18n :refer [tr]] + [app.util.webapi :as wapi] [app.util.websocket :as ws] [beicon.v2.core :as rx] [cuerdas.core :as str] @@ -36,13 +38,13 @@ (into #{} (keep :file-id) exports)) (defn- warn-unsaved-changes - "Warns that the export may miss the changes of `file-ids` not saved yet." - [file-ids] + "Shows `message` when some changes of `file-ids` are not saved yet." + [file-ids message] (ptk/reify ::warn-unsaved-changes ptk/WatchEvent (watch [_ state _] (when (dwp/unsaved-changes? state file-ids) - (rx/of (ntf/show {:content (tr "workspace.export.unsaved-changes") + (rx/of (ntf/show {:content message :type :toast :level :warning :timeout 8000})))))) @@ -236,17 +238,22 @@ (update [_ state] (let [time-diff (ct/diff-ms (get-in state [:export :last-update]) (ct/now)) healthy? (< time-diff 6000) - ;; The legacy path has no server-side figures to track; it keeps - ;; reporting progress over the client's own list. - job? (some? (get-in state [:export :job-id]))] + ;; Local WASM jobs also store a client job-id so the widget can + ;; show cancelling/cancelled (it keys those labels on job-id). + job? (some? (get-in state [:export :job-id])) + current (get-in state [:export :status]) + ;; Cancel settles the widget immediately; a late worker + ;; `:progress` must not flip status back to "running". + terminal? (contains? #{"cancelled" "cancelling" "ended" "error"} current) + apply? (or (not= status "running") (not terminal?))] (cond-> state - job? + (and job? apply?) (update :export assoc :status status) - (and job? (some? total)) + (and job? apply? (some? total)) (update :export assoc :total total) - (= status "running") + (and apply? (= status "running")) (update :export assoc :progress done :last-update (ct/now) :healthy? healthy?) (= status "error") @@ -266,24 +273,37 @@ (when (= status "ended") (dom/trigger-download-uri filename mtype resource-uri))))) -(defn cancel-export - "Stops the running export. Only reachable on the job API path, where the - exporter can actually abort the work. +(def ^:private local-export-job-id* + "Job id of the in-flight worker multi-export, if any." + (atom nil)) - The widget settles from here rather than from the job's `cancelled` message: - the outcome is known once the request returns, and waiting on a round trip - through redis and the websocket would leave it stuck whenever that message is - missed." +(defn cancel-export + "Stops the running export. On the job API path the exporter aborts; on the + local WASM path the worker is told to stop and the widget settles here. + + Local cancel settles on the client: if the worker is stuck before the first + cancel check, waiting for a `:cancelled` reply would leave the UI hung." [] (ptk/reify ::cancel-export ptk/WatchEvent (watch [_ state _] - (when-let [job-id (get-in state [:export :job-id])] - (let [resource-id (get-in state [:export :resource-id]) - settle (rx/concat - (rx/of (update-export-status {:status "cancelled"})) - (->> (rx/of (clear-export-state resource-id)) - (rx/delay default-timeout)))] + (let [backend (get-in state [:export :backend]) + job-id (get-in state [:export :job-id]) + resource-id (get-in state [:export :resource-id]) + settle (rx/concat + (rx/of (update-export-status {:status "cancelled"})) + (->> (rx/of (clear-export-state resource-id)) + (rx/delay default-timeout)))] + (cond + (= "local" backend) + (do + (when-let [local-job-id @local-export-job-id*] + (mw/emit! {:cmd :exports/cancel :job-id local-job-id})) + ;; Mark cancelled so a late `:done` from the worker is ignored. + (reset! local-export-job-id* nil) + settle) + + (some? job-id) (rx/concat ;; Stopping is not instantaneous: the request has to reach the ;; exporter and the work has to unwind. @@ -302,6 +322,13 @@ (features/active-feature? state "render-wasm/v1")) (defn- export-target + "Where a single-shape export runs. + + With render-wasm off → `:exporter` (server). + With render-wasm on → never the server API: always `:client` when the + format is supported, except true `:too-large` (beyond raster limits — + the exporter would fail too). Oversized-for-GPU rasters still run + locally via the CPU path in `export-bytes`." [state {:keys [type object-id scale]}] (cond (not (and (wasm-export-enabled? state) @@ -311,18 +338,14 @@ (contains? #{:pdf :svg} type) :client + ;; Raster: only refuse when WASM reports true `:too-large`. The legacy + ;; `:exporter` code means “use CPU raster” — we do that locally. :else - (or (wasm.api/export-target object-id (or scale 1) type) :exporter))) + (if (= :too-large (wasm.api/export-target object-id (or scale 1) type)) + :too-large + :client))) -(defn- request-simple-export-wasm - [export] - (ptk/reify ::request-simple-export-wasm - ptk/EffectEvent - (effect [_ _ _] - (case (:type export) - :pdf (wasm.exports/export-pdf export) - :svg (wasm.exports/export-svg export) - (wasm.exports/export-image export))))) +(declare request-local-export-wasm) (defn request-simple-export [{:keys [export]}] @@ -339,8 +362,16 @@ ptk/WatchEvent (watch [_ state _] (case @target + ;; Drawn from the local data, which a rejected save may still change :client - (rx/of (request-simple-export-wasm export)) + (rx/concat + (dwp/force-persist-files-and-wait (export-file-ids [export]) save-wait-timeout-ms) + (rx/of (warn-unsaved-changes (export-file-ids [export]) + (tr "workspace.export.unsaved-changes-included")) + (request-local-export-wasm {:exports [export] + :cmd :export-shapes + :name (:name export) + :kind :single}))) :too-large (rx/of (ntf/error (tr "errors.export-too-large"))) @@ -353,7 +384,8 @@ :is-wasm (wasm-export-enabled? state)})] (rx/concat (dwp/force-persist-files-and-wait (export-file-ids [export]) save-wait-timeout-ms) - (rx/of (warn-unsaved-changes (export-file-ids [export]))) + (rx/of (warn-unsaved-changes (export-file-ids [export]) + (tr "workspace.export.unsaved-changes"))) (->> (rp/cmd! :export params) (rx/map (fn [{:keys [filename mtype uri]}] @@ -363,95 +395,223 @@ (rx/of (clear-export-state uuid/zero) (export-failed cause)))))))))))) -(defn request-multiple-export +(defn- export-file-page-ids + [state exports] + (let [file-id (or (:file-id (first exports)) (:current-file-id state)) + page-id (or (:page-id (first exports)) (:current-page-id state))] + [file-id page-id])) + +(defn collect-export-objects + "Live page objects needed for `exports`: each root plus its descendants. + Falls back to the full page when the closure is empty (missing ids)." + [state exports] + (let [[file-id page-id] (export-file-page-ids state exports) + objects (or (dsh/lookup-page-objects state file-id page-id) {}) + root-ids (into #{} (map :object-id) exports) + ids (into #{} + (mapcat #(cfh/get-children-ids-with-self objects %)) + root-ids) + selected (select-keys objects ids)] + (if (seq selected) + selected + objects))) + +(defn- export-page-background + [state exports] + (let [[file-id page-id] (export-file-page-ids state exports) + page (dsh/lookup-page state file-id page-id)] + (:background page))) + +(defn- worker-export-entries + "Payload for the worker: drop `:shape` and other UI-only keys." + [exports] + (mapv (fn [{:keys [object-id type scale name suffix]}] + {:object-id object-id + :type type + :scale (or scale 1) + :name name + :suffix suffix}) + exports)) + +(defn- request-local-export-wasm + "Client-side export on the shared worker WASM module: one file (`:single`), + a ZIP of shapes (`:zip`), or a multi-page boards PDF (`:pdf-frames`)." + [{:keys [exports cmd name kind] + :or {cmd :export-shapes}}] + (ptk/reify ::request-local-export-wasm + ptk/WatchEvent + (watch [_ state _] + (let [resource-id (uuid/next) + job-id (uuid/next) + total (count exports) + kind (or kind + (if (= cmd :export-frames) :pdf-frames :zip)) + objects (collect-export-objects state exports) + background (export-page-background state exports) + entries (worker-export-entries exports) + clear-later (->> (rx/of (clear-export-state resource-id)) + (rx/delay default-timeout))] + (reset! local-export-job-id* job-id) + (swap! st/ongoing-tasks conj :export) + + (rx/concat + (rx/of (initialize-export-status exports cmd + {:id resource-id} + {:job-id job-id + :total total + :status "running" + :backend "local"})) + (->> (mw/ask-many! + {:cmd :exports/run-local + :job-id job-id + :kind kind + :exports entries + :objects objects + :background background + :name name}) + (rx/mapcat + (fn [{:keys [type] :as message}] + ;; Cancel clears `local-export-job-id*` and settles the + ;; widget locally. Ignore every later worker message so a + ;; stray `:progress` cannot flip the UI back to "running". + (if (nil? @local-export-job-id*) + (rx/empty) + (case type + :progress + (rx/of (update-export-status {:status "running" + :done (:done message) + :total (:total message)})) + + :done + (let [uri (wapi/create-uri (:blob message))] + (rx/concat + (rx/of (update-export-status + {:status "ended" + :done total + :filename (:filename message) + :mtype (:mtype message) + :resource-uri uri})) + clear-later)) + + :cancelled + (rx/concat + (rx/of (update-export-status {:status "cancelled"})) + clear-later) + + :error + (rx/of (export-widget-failed + exports cmd + (ex-info (or (:hint message) "worker export failed") + (or (:data message) + {:type :internal + :code :worker-export-error})))) + + (rx/empty))))) + (rx/catch + (fn [cause] + (if (= :export-cancelled (:type (ex-data cause))) + (rx/concat + (rx/of (update-export-status {:status "cancelled"})) + clear-later) + (rx/of (export-widget-failed exports cmd cause))))) + (rx/finalize + (fn [] + (reset! local-export-job-id* nil) + (swap! st/ongoing-tasks disj :export))))))))) + +(defn- request-multiple-export-server [{:keys [exports cmd name] + :or {cmd :export-shapes}}] + (ptk/reify ::request-multiple-export-server + ptk/WatchEvent + (watch [_ state _] + (let [resource-id (volatile! nil) + ;; Emits when the export request fails. + failed-s (rx/subject) + profile-id (:profile-id state) + ws-conn (:ws-conn state) + params (cond-> + {:exports exports + :cmd cmd + :profile-id profile-id + :force-multiple true + :is-wasm (wasm-export-enabled? state)} + (some? name) + (assoc :name name)) + + progress-stream + (->> (ws/get-rcv-stream ws-conn) + (rx/filter ws/message-event?) + (rx/map :payload) + (rx/filter #(= :export-update (:type %))) + (rx/filter #(= @resource-id (:resource-id %))) + (rx/take-until failed-s) + (rx/share)) + + stopper + (rx/filter #(or (= "ended" (:status %)) + (= "error" (:status %)) + (= "cancelled" (:status %))) + progress-stream) + + file-ids + (export-file-ids exports)] + + (swap! st/ongoing-tasks conj :export) + + (rx/concat + (dwp/force-persist-files-and-wait file-ids save-wait-timeout-ms) + (rx/of (warn-unsaved-changes file-ids (tr "workspace.export.unsaved-changes"))) + (rx/merge + ;; Updates the export state with each progress message, until + ;; shortly after the export ends or fails. + (->> progress-stream + (rx/map update-export-status) + (rx/take-until (rx/delay 500 stopper)) + (rx/finalize (fn [] + (swap! st/ongoing-tasks disj :export)))) + + ;; Clears the export state a while after the export ends or is + ;; cancelled. + (->> progress-stream + (rx/filter #(or (= "ended" (:status %)) + (= "cancelled" (:status %)))) + (rx/take 1) + (rx/delay default-timeout) + (rx/map #(clear-export-state @resource-id)) + (rx/take-until (rx/delay 6000 stopper))) + + ;; Launches the exportation process and stores the resource id + ;; locally. Goes last so a failure reaches the progress listeners + ;; above: the push to `failed-s` releases the leave-page warning + ;; even though no progress message will ever arrive, and nothing + ;; is raised to the global error handler, which would replace the + ;; workspace with an error page. + (->> (rp/cmd! :export params) + (rx/map (fn [{:keys [id] :as resource}] + (vreset! resource-id id) + (initialize-export-status exports cmd resource nil))) + (rx/catch (fn [cause] + (rx/push! failed-s cause) + (rx/of (export-widget-failed exports cmd cause))))))))))) + +(defn request-multiple-export + [{:keys [exports cmd] :or {cmd :export-shapes} :as params}] - (let [exports (normalize-exports exports)] + (let [exports (normalize-exports exports) + params (assoc params :exports exports :cmd cmd)] (ptk/reify ::request-multiple-export ptk/WatchEvent (watch [_ state _] - (let [resource-id (volatile! nil) - ;; Emits when the export request fails. - failed-s (rx/subject) - profile-id (:profile-id state) - ws-conn (:ws-conn state) - params (cond-> - {:exports exports - :cmd cmd - :profile-id profile-id - :force-multiple true - :is-wasm (wasm-export-enabled? state)} - (some? name) - (assoc :name name)) - - progress-stream - (->> (ws/get-rcv-stream ws-conn) - (rx/filter ws/message-event?) - (rx/map :payload) - (rx/filter #(= :export-update (:type %))) - (rx/filter #(= @resource-id (:resource-id %))) - (rx/take-until failed-s) - (rx/share)) - - stopper - (rx/filter #(or (= "ended" (:status %)) - (= "error" (:status %)) - (= "cancelled" (:status %))) - progress-stream) - - file-ids - (export-file-ids exports)] - - (swap! st/ongoing-tasks conj :export) - - (rx/concat - (dwp/force-persist-files-and-wait file-ids save-wait-timeout-ms) - (rx/of (warn-unsaved-changes file-ids)) - (rx/merge - ;; Updates the export state with each progress message, until - ;; shortly after the export ends or fails. - (->> progress-stream - (rx/map update-export-status) - (rx/take-until (rx/delay 500 stopper)) - (rx/finalize (fn [] - (swap! st/ongoing-tasks disj :export)))) - - ;; Clears the export state a while after the export ends or is - ;; cancelled. - (->> progress-stream - (rx/filter #(or (= "ended" (:status %)) - (= "cancelled" (:status %)))) - (rx/take 1) - (rx/delay default-timeout) - (rx/map #(clear-export-state @resource-id)) - (rx/take-until (rx/delay 6000 stopper))) - - ;; Launches the exportation process and stores the resource id - ;; locally. With wasm export active the job API is used instead: - ;; it answers with the exporter's own object count and gives a - ;; handle to cancel. It goes last so a failure reaches the - ;; progress listeners above: the push to `failed-s` releases the - ;; leave-page warning even though no progress message will ever - ;; arrive, and nothing is raised to the global error handler, - ;; which would replace the workspace with an error page. - (->> (if (wasm-export-enabled? state) - (->> (rp/cmd! :create-export-job params) - (rx/map (fn [{job-id :id :keys [total] :as job}] - (vreset! resource-id (:resource-id job)) - (initialize-export-status exports cmd - {:id (:resource-id job)} - {:job-id job-id - :total total - :status (:state job) - :backend (:backend job)})))) - (->> (rp/cmd! :export params) - (rx/map (fn [{:keys [id] :as resource}] - (vreset! resource-id id) - (initialize-export-status exports cmd resource nil))))) - (rx/catch (fn [cause] - (rx/push! failed-s cause) - (rx/of (export-widget-failed exports cmd cause)))))))))))) + (if (wasm-export-enabled? state) + ;; Drawn from the local data, which a rejected save may still change + (let [file-ids (export-file-ids exports)] + (rx/concat + (dwp/force-persist-files-and-wait file-ids save-wait-timeout-ms) + (rx/of (warn-unsaved-changes file-ids (tr "workspace.export.unsaved-changes-included")) + (request-local-export-wasm params)))) + (rx/of (request-multiple-export-server params))))))) (defn request-export [{:keys [exports] :as params}] diff --git a/frontend/src/app/main/data/exports/files.cljs b/frontend/src/app/main/data/exports/files.cljs index 03c19be412..905716897c 100644 --- a/frontend/src/app/main/data/exports/files.cljs +++ b/frontend/src/app/main/data/exports/files.cljs @@ -10,10 +10,11 @@ [app.common.data :as d] [app.common.schema :as sm] [app.main.data.event :as ev] + [app.main.data.jobs :as dj] [app.main.data.modal :as modal] [app.main.data.persistence :as dps] [app.main.repo :as rp] - [app.util.sse :as sse] + [app.main.store :as st] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -77,28 +78,79 @@ (rx/reduce conj []) (rx/map (partial show-export-dialog team-id)))))))))) +(defn- export-file + "One file, one job: the file is exported on its own, so a failure does + not touch the others, and the artifact is read from the result of the + job once it is over. + + Every step of the job becomes a message for the caller: the milestone + under `:progress`, the artifact as `:uri` when the job completed, and + the public error of the job when it failed. The queue of a file is + announced by `export-files` before any job exists and only ends with a + milestone: a worker picking the job up is not one. + + `on-job` is an optional callback invoked with `{:job-id ... :file-id + ...}` for the created job, so the caller can track it per file and + cancel it while it runs." + [ws-conn type file on-job] + (->> (rp/cmd! :create-export-binfile-job + {:params {:file-ids #{(:id file)} + :export-type type}}) + (rx/mapcat (fn [{job-id :id}] + (when (fn? on-job) + (on-job {:job-id job-id :file-id (:id file)})) + (->> (dj/watch-job ws-conn job-id) + (rx/mapcat (fn [{:keys [kind status result error] :as emission}] + (cond + (= "completed" status) + (rx/of {:file-id (:id file) + :uri (:resource-uri result) + :filename (:name file) + :size (:size result)}) + + (= "failed" status) + (rx/of {:file-id (:id file) + :error error}) + + (= "cancelled" status) + (rx/of {:file-id (:id file) + :cancelled true}) + + (= :progress kind) + (rx/of {:file-id (:id file) + :progress (:payload emission)}) + + :else + (rx/empty))))))) + ;; a transport failure carries no job error: a nil `ex-data` would + ;; make the dialog drop the message and leave the row hanging, so a + ;; synthetic error takes its place and the row reports it + (rx/catch (fn [cause] + (rx/of {:file-id (:id file) + :error (or (ex-data cause) + {:type :internal + :code :unknown-error})}))))) + (defn export-files - "Start files exportation process" - [& {:keys [type files]}] + "Start files exportation process. + + Every file is queued up front, even the ones whose job is only created + once the previous one is over, so a multiple export shows all of them + waiting from its first moment. + + The optional `:on-job` callback is invoked with `{:job-id ... + :file-id ...}` for every file job created, so the caller can track + them per file and cancel them while they run." + [& {:keys [type files on-job]}] (assert (check-export-files files) "expected a sequence of files") (assert (valid-types type) "expected valid export type") - (->> (rx/from files) - (rx/mapcat - (fn [file] - (->> (rp/cmd! ::sse/export-binfile {:file-id (:id file) - :version 3 - :type type}) - (rx/filter sse/end-of-stream?) - (rx/map sse/get-payload) - (rx/map (fn [uri] - {:file-id (:id file) - :uri uri - :filename (:name file)})) - (rx/catch (fn [cause] - (let [error (ex-data cause)] - (rx/of {:file-id (:id file) - :error error}))))))))) + (let [ws-conn (:ws-conn @st/state)] + (rx/concat + (rx/from (map (fn [file] {:file-id (:id file) :queued true}) files)) + (->> (rx/from files) + (rx/mapcat (fn [file] + (export-file ws-conn type file on-job))))))) ;;;;;;;;;;;;;;;;;;;;;; ;; Team Request diff --git a/frontend/src/app/main/data/exports/wasm.cljs b/frontend/src/app/main/data/exports/wasm.cljs index 696b8ed716..b23556474a 100644 --- a/frontend/src/app/main/data/exports/wasm.cljs +++ b/frontend/src/app/main/data/exports/wasm.cljs @@ -9,51 +9,113 @@ [app.common.media :refer [format->mtype]] [app.render-wasm.api :as wasm.api] [app.util.dom :as dom] - [app.util.webapi :as wapi])) + [app.util.webapi :as wapi] + [cuerdas.core :as str])) + +;; Regex shared with the exporter so ZIP entry names stay compatible. +(def ^:private sanitize-file-regex #"[\\/:*?\"<>|]") + +(defn export-extension + [type] + (case type + :png ".png" + :jpeg ".jpg" + :webp ".webp" + :svg ".svg" + :pdf ".pdf" + :zip ".zip" + "")) + +(defn sanitize-filename + "Exporter-compatible filename: strip reserved chars, then name+suffix+ext + with numeric de-dupe against `used`." + [{:keys [name suffix type]} used] + (let [base (-> (str (or name "") (or suffix "")) + (str/replace sanitize-file-regex "_"))] + (loop [index 0] + (let [candidate (str base + (when (pos? index) + (str "-" (inc index))) + (export-extension type))] + (if (contains? used candidate) + (recur (inc index)) + candidate))))) + +(defn export-bytes + "Encoded bytes for one export entry. Returns nil when WASM is not ready. + Used by the worker local export path (single / ZIP) and by main-thread + helpers (plugins, clipboard) that still need a sync URI. + + Raster formats use the GPU surface when it fits; otherwise the CPU + raster path (same as the headless exporter) so oversized shapes still + export locally without calling the server API." + [{:keys [type scale object-id] :as _export}] + (let [scale (or scale 1)] + (case type + :pdf (wasm.api/render-shape-pdf object-id) + :svg (wasm.api/render-shape-svg object-id scale) + (if (= :client (wasm.api/export-target object-id scale type)) + (wasm.api/render-shape-pixels object-id scale type) + (wasm.api/render-shape-raster object-id scale type))))) (defn export-image-uri - [{:keys [type scale object-id]}] + [{:keys [type] :as params}] ;; The export type doubles as the encoder format, so the bytes always match ;; the mtype we wrap them in. `:svg` never reaches here — it needs vector ;; markup and `exports.assets/wasm-export-types` keeps it off this path. - (let [bytes (wasm.api/render-shape-pixels object-id scale type) + (let [bytes (export-bytes params) mtype (format->mtype type) - blob (wapi/create-blob bytes mtype)] + blob (wapi/create-blob bytes mtype)] (wapi/create-uri blob))) (defn export-image [{:keys [type name] :as params}] - (let [url (export-image-uri params) + (let [url (export-image-uri params) mtype (format->mtype type)] (dom/trigger-download-uri name mtype url) (wapi/revoke-uri url) nil)) (defn export-pdf-uri - [{:keys [object-id]}] - (let [bytes (wasm.api/render-shape-pdf object-id) - blob (wapi/create-blob bytes "application/pdf")] + [params] + (let [bytes (export-bytes (assoc params :type :pdf)) + blob (wapi/create-blob bytes "application/pdf")] (wapi/create-uri blob))) (defn export-pdf [{:keys [name] :as params}] - (let [url (export-pdf-uri params) + (let [url (export-pdf-uri params) filename (str name ".pdf")] (dom/trigger-download-uri filename "application/pdf" url) (js/queueMicrotask #(wapi/revoke-uri url)) nil)) (defn export-svg-uri - [{:keys [scale object-id]}] - (let [bytes (wasm.api/render-shape-svg object-id (or scale 1)) - blob (wapi/create-blob bytes "image/svg+xml")] + [params] + (let [bytes (export-bytes (assoc params :type :svg)) + blob (wapi/create-blob bytes "image/svg+xml")] (wapi/create-uri blob))) (defn export-svg [{:keys [name] :as params}] - (let [url (export-svg-uri params) + (let [url (export-svg-uri params) filename (str name ".svg")] (dom/trigger-download-uri filename "image/svg+xml" url) (js/queueMicrotask #(wapi/revoke-uri url)) nil)) +(defn export-frames-pdf-uri + "One multi-page PDF for the given object ids (page order = id order). + Synchronous main-thread helper; multi-export prefers the worker session." + [{:keys [object-ids]}] + (let [bytes (wasm.api/render-shapes-pdf object-ids) + blob (wapi/create-blob bytes "application/pdf")] + (wapi/create-uri blob))) + +(defn export-frames-pdf + [{:keys [name] :as params}] + (let [url (export-frames-pdf-uri params) + filename (str (or name "export") ".pdf")] + (dom/trigger-download-uri filename "application/pdf" url) + (js/queueMicrotask #(wapi/revoke-uri url)) + nil)) diff --git a/frontend/src/app/main/data/imports.cljs b/frontend/src/app/main/data/imports.cljs new file mode 100644 index 0000000000..6fd079b015 --- /dev/null +++ b/frontend/src/app/main/data/imports.cljs @@ -0,0 +1,569 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.main.data.imports + "Reading a penpot package and importing it as a job. + + Both steps run in the main thread: reading the manifest of the package + is cheap (it scales with the number of entries, not with the size of the + file) and the work itself belongs to the server, so the only thing the + client has to be near is the websocket that reports the progress of the + job." + (:require + [app.common.exceptions :as ex] + [app.common.json :as json] + [app.common.schema :as sm] + [app.common.uuid :as uuid] + [app.main.data.jobs :as dj] + [app.main.data.uploads :as uploads] + [app.main.repo :as rp] + [app.main.store :as st] + [app.util.http :as http] + [app.util.i18n :as i18n :refer [tr]] + [app.util.zip :as uz] + [beicon.v2.core :as rx] + [cuerdas.core :as str] + [promesa.core :as p])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; MESSAGES OF A FAILURE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- resolve-hint + "The hint or the explanation of an error, trimmed, or nil." + [error] + (let [data (if (map? error) error (ex-data error))] + (or (some-> (:hint data) str/trim not-empty) + (some-> (:explain data) str/trim not-empty)))) + +(defn- cause-message + "The message of a failure of the transport: the hint the server sent, + then the explanation, then the message of the cause and finally the + generic one. The wrapper of an aborted stream says nothing useful, so it + is skipped." + [cause default] + (let [message (some-> (ex-message cause) str/trim)] + (or (resolve-hint cause) + (when-not (or (str/blank? message) (= message "stream exception")) + message) + default))) + +(defn- job-message + "The message of a job that failed." + [error] + (or (resolve-hint error) (tr "labels.error"))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; READING A PACKAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- slurp-uri + [uri response-type] + (->> (http/send! {:uri uri :response-type response-type :method :get}) + (rx/map :body))) + +(defn- parse-mtype + "The mime type behind the first bytes of a file, guessed the same way the + browser would: only a zip can be a penpot package of version 3." + [ba] + (let [u8 (js/Uint8Array. ba 0 4) + sg (areduce u8 i ret "" (str ret (if (zero? i) "" " ") (.toString (aget u8 i) 8)))] + (case sg + "120 113 3 4" "application/zip" + "1 13 32 206" "application/octet-stream" + "other"))) + +(def ^:private schema:manifest + [:map {:title "Manifest"} + [:type :string] + [:files + [:vector + [:map + [:id ::sm/uuid] + [:name :string]]]]]) + +(def ^:private decode-manifest + (sm/decoder schema:manifest sm/json-transformer)) + +(defn- read-zip-manifest + [zip-reader] + (->> (rx/from (uz/get-entry zip-reader "manifest.json")) + (rx/mapcat (fn [entry] + (if (nil? entry) + (rx/throw (ex/error :type :validation + :code :invalid-penpot-file + :hint "Not a valid Penpot file: manifest.json is missing")) + (uz/read-as-text entry)))) + (rx/map json/decode))) + +(defn- segment-of + "The segment of a zip path that follows `prefix`, up to the next slash, + or nil when the path does not start with it." + [prefix path] + (when (str/starts-with? path prefix) + (let [rest (subs path (count prefix)) + idx (str/index-of rest "/")] + (if (some? idx) (subs rest 0 idx) rest)))) + +(defn- file-of-entry + "The file of a container a zip entry belongs to. A container keeps every + file of a package under its own prefix: `files/.json` for its data + and `files//…` for the rest." + [path] + (some-> (segment-of "files/" path) + (str/replace #"\.json$" ""))) + +(defn- object-of-entry + "The storage object a zip entry holds. A container keeps the media and + the thumbnails of its files shared under `objects/`, and both the + metadata (`objects/.json`) and the binary (`objects/.`) + belong to the same object." + [path] + (when-some [segment (segment-of "objects/" path)] + (let [dot (str/index-of segment ".")] + (if (some? dot) (subs segment 0 dot) segment)))) + +(defn- reference-entry? + "Whether an entry of a file is the metadata of one of the media or the + thumbnails that file references, the only place that links a file to the + objects it occupies." + [path] + (or (str/includes? path "/media/") + (str/includes? path "/thumbnails/"))) + +(defn- index-container + "Read the entries of a container in one pass: what every file of the + package occupies by itself, what every storage object occupies, and the + entries that say which objects a file references." + [entries] + (reduce + (fn [index entry] + (let [path (.-filename ^js entry) + size (.-compressedSize ^js entry) + file-id (file-of-entry path)] + (cond + (some? file-id) + (cond-> (update-in index [:files file-id] (fnil + 0) size) + (reference-entry? path) + (update :references conj [file-id entry])) + + :else + (if-some [object-id (object-of-entry path)] + (update-in index [:objects object-id] (fnil + 0) size) + index)))) + {:files {} :objects {} :references []} + entries)) + +(defn- read-objects + "The objects every file of a container references, read from the + metadata of its media and its thumbnails." + [references] + (->> (map (fn [[file-id entry]] + (->> (uz/read-as-text entry) + (p/fmap (fn [text] [file-id (json/decode text)])))) + references) + (p/all) + (p/fmap + (fn [decoded] + (reduce (fn [result [file-id data]] + (->> [:media-id :thumbnail-id] + (keep #(get data %)) + (reduce (fn [result object-id] + (update result file-id (fnil conj #{}) object-id)) + result))) + {} + decoded))))) + +(defn- file-sizes + "How much every file of a container occupies inside it: its own entries + plus the storage objects it references, which the container keeps shared + and which are only linked from the file. The manifest carries neither a + size, so the parts of a package are the only account of what it + occupies." + [entries] + (let [{:keys [files objects references]} (index-container entries)] + (->> (read-objects references) + (p/fmap + (fn [referenced] + (reduce-kv (fn [sizes file-id object-ids] + (update sizes file-id (fnil + 0) + (reduce + 0 (keep objects object-ids)))) + files + referenced)))))) + +(defn- analyze-file + "The entries of one file the user dropped: one per file of a version 3 + package, one for a version 1 file, and an error for anything else. + + A container carries its own weight and the weight of every file in it: + the entry of a file of a version 3 package sizes the file inside its + container, and keeps what the container itself occupies in + `:container-size`, which is what the upload of the package is measured + on." + [{:keys [uri size] :as file}] + (let [stream (->> (slurp-uri uri :buffer) + (rx/merge-map + (fn [body] + (let [mtype (parse-mtype body)] + (cond + (= "application/zip" mtype) + (let [zip-reader (uz/reader body)] + (->> (read-zip-manifest zip-reader) + (rx/merge-map + (fn [manifest] + (if (= (:type manifest) "penpot/export-files") + (->> (uz/get-entries zip-reader) + (p/mcat file-sizes) + (rx/from) + (rx/map (fn [sizes] + (-> file + (assoc :type :binfile-v3) + (assoc :container-size size) + (assoc :files (:files (decode-manifest manifest))) + (assoc :sizes sizes))))) + (rx/of (assoc file :type :unknown))))) + (rx/finalize (partial uz/close zip-reader)))) + + (= "application/octet-stream" mtype) + (rx/of (assoc file :type :binfile-v1 :container-size size)) + + :else + (rx/of (assoc file :type :unknown)))))) + (rx/share))] + + (->> (rx/merge + (->> stream + (rx/filter (fn [entry] (= :binfile-v1 (:type entry)))) + (rx/map (fn [entry] + (let [file-id (uuid/next)] + (-> entry + (assoc :file-id file-id) + (assoc :name (:name file)) + (assoc :status :success)))))) + + (->> stream + (rx/filter (fn [entry] (= :binfile-v3 (:type entry)))) + (rx/merge-map (fn [{:keys [files sizes] :as entry}] + (->> (rx/from files) + (rx/map (fn [file] + (-> entry + (dissoc :files :sizes) + (assoc :name (:name file)) + (assoc :file-id (:id file)) + ;; the size the file itself + ;; occupies inside the + ;; container, never the + ;; container's + (assoc :size (get sizes (str (:id file)))) + (assoc :status :success)))))))) + + (->> stream + (rx/filter (fn [data] (= :unknown (:type data)))) + (rx/map (fn [_] + {:uri (:uri file) + :name (:name file) + :size (:size file) + :container-size (:size file) + :type :unknown + :status :error + :error (tr "dashboard.import.analyze-error")})))) + + (rx/catch (fn [cause] + (let [error (cause-message cause (tr "dashboard.import.analyze-error"))] + (rx/of (assoc file :error error :status :error)))))))) + +(defn analyze + "Read the manifest of every file the user dropped and answer with the + entries the wizard shows: one per file of a package." + [files] + (->> (rx/from files) + (rx/merge-map analyze-file))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IMPORTING A PACKAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- packages + "The packages to import: a version 1 file is one package of one entry, + and the entries of a version 3 file share their upload, which the uri + of the dropped file identifies." + [entries] + (concat + (->> entries + (filter #(= :binfile-v1 (:type %))) + (map (fn [entry] + {:uri (:uri entry) + :version 1 + :name (str/replace (:name entry) #".penpot$" "") + :entries [entry]}))) + + (->> entries + (filter #(= :binfile-v3 (:type %))) + (group-by :uri) + (map (fn [[uri entries]] + {:uri uri + :version 3 + :name (:name (first entries)) + :entries entries}))))) + +(defn- entry-messages + "The message of every entry of a package: the job works on the package, + and each of its entries shows the same step." + [entries message] + (map (fn [entry] (assoc message :file-id (:file-id entry))) entries)) + +(defn- upload-milestone + "The milestone of the upload phase: the chunks done out of the total. + It is built on the client, never comes from the backend, and reuses + the milestone shape so the wizard shows it like any other step." + [{:keys [current total]}] + {:stage :upload :counters {:upload {:current current :total total}}}) + +(defn- import-package + "One package as a job: the blob is uploaded in chunks, the job is created + with the upload and followed, and its outcome becomes the messages of + the wizard. + + `on-job` is an optional callback invoked with the id of the created + job, so the caller can cancel it while it runs." + [ws-conn project-id resolutions {:keys [uri version name entries]} on-job] + (let [upload-events (rx/subject) + upload-ended? (atom false) + finish-upload! (fn [] + (when (compare-and-set! upload-ended? false true) + (rx/end! upload-events))) + report-upload! (fn [progress] + (doseq [message (entry-messages entries {:status :progress + :progress (upload-milestone progress)})] + (rx/push! upload-events message)))] + (rx/merge + upload-events + (->> (slurp-uri uri :blob) + (rx/mapcat (fn [blob] + (->> (uploads/upload-blob-chunked blob :on-progress report-upload!) + (rx/tap (fn [_] (finish-upload!)))))) + (rx/mapcat (fn [{:keys [session-id]}] + (rp/cmd! :create-import-binfile-job + {:params {:project-id project-id + :name name + :version version} + :upload-id session-id}))) + (rx/mapcat (fn [{job-id :id}] + (when (fn? on-job) + (on-job job-id)) + ;; the job exists but may wait for a worker: tell + ;; every entry it is queued before following it + (rx/concat + (rx/from (entry-messages entries {:status :queued})) + (->> (dj/watch-job ws-conn job-id) + (rx/mapcat (fn [{:keys [kind status result error] :as emission}] + (cond + (= "completed" status) + (do + (when-let [resolution (not-empty (:resolution result))] + (vswap! resolutions merge resolution)) + (rx/from (entry-messages entries {:status :finish}))) + + (= "failed" status) + (rx/from (entry-messages entries {:status :error + :error (job-message error)})) + + (= "cancelled" status) + (rx/from (entry-messages entries {:status :error + :error (tr "jobs.import-cancelled")})) + + (= :progress kind) + (rx/from (entry-messages entries {:status :progress + :progress (:payload emission)})) + + ;; a worker picked it up (or will + ;; retry it): no milestone yet + (contains? #{:start :retry} kind) + (rx/from (entry-messages entries {:status :started})) + + :else + (rx/empty)))))))) + (rx/catch (fn [cause] + (finish-upload!) + (rx/from (entry-messages entries {:status :error + :error (cause-message cause (tr "labels.error"))})))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; WIZARD STEP, UPLOAD AND FILE SIZE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn format-file-size + "Human size of a dropped file, like `234MB` or `512KB`. + Returns nil when the size is unknown, so the row shows no subtitle." + [size] + (when (and (some? size) (number? size) (>= size 0)) + (let [kb 1024 + mb (* kb 1024) + gb (* mb 1024)] + (cond + (< size kb) + (str (js/Math.round size) "B") + + (< size mb) + (let [v (/ size kb)] + (str (if (< v 10) + (-> (.toFixed v 1) + (str/replace #"\.0$" "")) + (str (js/Math.round v))) + "KB")) + + (< size gb) + (let [v (/ size mb)] + (str (if (< v 10) + (-> (.toFixed v 1) + (str/replace #"\.0$" "")) + (str (js/Math.round v))) + "MB")) + + :else + (let [v (/ size gb)] + (str (if (< v 10) + (-> (.toFixed v 1) + (str/replace #"\.0$" "")) + (str (js/Math.round v))) + "GB")))))) + +(defn- upload-ratio + "The fraction of the container of an entry already sent: the chunks + uploaded over the chunks the file has, or nil while its upload has not + started." + [entry] + (let [{:keys [current total]} (get-in entry [:progress :counters :upload])] + (when (and (some? current) (some? total) (pos? total)) + (/ current total)))) + +(defn- uploaded-ratio + "The fraction of the container of an entry already uploaded. A container + is uploaded whole before its job exists, so from the moment its entries + leave the upload stage they count as sent: only an entry still sending + chunks, or one that has not started, says less than everything." + [entry] + (cond + (= :upload (get-in entry [:progress :stage])) (or (upload-ratio entry) 0) + (contains? #{:analyze :import-ready} (:status entry)) 0 + :else 1)) + +(defn upload-done? + "Whether the container of an entry finished uploading: the chunks sent + reached the chunks the file has." + [entry] + (let [{:keys [current total]} (get-in entry [:progress :counters :upload])] + (and (some? current) (some? total) (= current total)))) + +(defn upload-pct + "The percentage of the whole upload: the bytes already sent over the + bytes of every container. The containers are weighted by their size, + never averaged, because a `.penpot` is uploaded whole: a container of + 200MB moves the bar as much as four of 50MB." + [entries] + (let [containers (map (comp first :entries) (packages entries)) + total (reduce + 0 (keep :container-size containers))] + (when (pos? total) + (let [sent (reduce + 0 (map (fn [{:keys [container-size] :as entry}] + (* container-size (uploaded-ratio entry))) + containers))] + (js/Math.round (* 100 (/ sent total))))))) + +(defn- files-done + "How many files of a container are already rebuilt. A container that + finished leaves all of its files behind; while it runs, its milestone + says which file it is working on, one based, so the ones before it are + done." + [entries] + (if (every? #(= :import-success (:status %)) entries) + (count entries) + (let [{:keys [current total]} (get-in (first entries) [:progress :counters :files]) + total (or total (count entries))] + (-> (or current 1) dec (max 0) (min total))))) + +(defn imported-count + "How many of the files being imported are already rebuilt: the files of + the containers that finished plus the ones a running container left + behind. A container rebuilds its files one by one, so counting only the + finished containers (or only their files) would leave the number still + for the whole import." + [entries] + (reduce + 0 (map (comp files-done :entries) (packages entries)))) + +(defn waiting-slot? + "Whether every file of the wizard waits for a free slot: the jobs of the + containers exist, but no worker picked them up yet." + [entries] + (and (pos? (count entries)) + (every? #(= :import-queued (:status %)) entries))) + +(defn- pending-upload? + "Whether a file is still to be uploaded, or is being uploaded right now: + the wizard is in its upload step while any file is pending." + [entry] + (or (= :import-ready (:status entry)) + (= :upload (get-in entry [:progress :stage])))) + +(defn import-step + "The wizard step the header paints: `:check` while the entries are + analyzed, `:upload` until every file of every container is uploaded, and + `:import` once the jobs run or the wizard reaches its outcome. + + A file leaves the upload behind when its job exists: the queued message + carries no milestone, and the upload one disappears with it, so the + presence of a milestone is not enough to keep the step in the upload." + [status entries] + (case status + (:analyze :import-ready) + :check + + :import-progress + (if (some pending-upload? entries) + :upload + :import) + + (:import-success :import-error :library-resolution :library-summary) + :import + + :check)) + +(defn awaiting-analysis? + "Whether the wizard hides the rows and shows the checking placeholder + instead: the dialog is still analyzing and no entry failed yet. Entries + that fail during analyze take the error-summary path instead of rows." + [status errors?] + (and (= :analyze status) (not errors?))) + +(defn ready-to-import? + "Whether the wizard can offer its import step: the analysis is over and + every file it found can be imported. A ready file is not enough on its + own: until the analysis ends the wizard does not know how many files the + dropped containers carry, and the ones still to arrive would never be + imported." + [entries analysis-ended?] + (and analysis-ended? + (pos? (count entries)) + (every? #(= :import-ready (:status %)) entries))) + +(defn import-files + "Import the entries the wizard analyzed, one job per package. + + Answers with the messages the wizard already understands: the step of + the job for every entry (progress, finish or error) and, once every + package is over, the resolution of the libraries they brought. + + The optional `:on-job` callback is invoked with the id of every + package job created, so the caller can cancel them while they run." + [{:keys [project-id entries on-job]}] + (let [ws-conn (:ws-conn @st/state) + resolutions (volatile! {}) + importing (->> (rx/from (packages entries)) + (rx/merge-map (fn [package] + (import-package ws-conn project-id resolutions package on-job))))] + (rx/concat importing + (->> (rx/defer #(rx/of @resolutions)) + (rx/map (fn [resolution] + {:libraries-resolution resolution})))))) diff --git a/frontend/src/app/main/data/jobs.cljs b/frontend/src/app/main/data/jobs.cljs new file mode 100644 index 0000000000..9ffc801512 --- /dev/null +++ b/frontend/src/app/main/data/jobs.cljs @@ -0,0 +1,94 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.main.data.jobs + "Following a job the client created. + + A running job publishes its progress as `:job-event` messages on the + websocket of the profile and keeps its outcome in its own row. This + namespace joins both: `watch-job` emits every event of the job and one + final outcome, read from the row and not from the event, because the + closing event carries only the outcome." + (:require + [app.main.repo :as rp] + [app.util.websocket :as ws] + [beicon.v2.core :as rx])) + +(def ^:private terminal-statuses + #{"completed" "failed" "cancelled"}) + +(defn- events-of + "The events of one job, as they arrive on the websocket, with the + milestone of the event under `:payload`." + [ws-conn job-id] + (->> (ws/get-rcv-stream ws-conn) + (rx/filter ws/message-event?) + (rx/map :payload) + (rx/filter #(and (= :job-event (:type %)) + (= job-id (:job-id %)))) + (rx/map (fn [event] + {:kind (keyword (:kind event)) + :payload (:payload event)})))) + +(defn- outcome-of + "The row of a job as the outcome of the stream, nil while it is not + over. A job that failed answers with the public error of the row." + [job] + (when (contains? terminal-statuses (:status job)) + (cond-> {:status (:status job)} + (some? (:result job)) + (assoc :result (:result job)) + + (some? (:error job)) + (assoc :error (:error job))))) + +(defn- read-outcome + [job-id] + (->> (rp/cmd! :get-job {:id job-id}) + (rx/map outcome-of) + (rx/filter some?))) + +(defn watch-job + "The lifecycle of `job-id` as an observable: every event it publishes + and one final outcome, after which the stream ends. + + The outcome is read from the row and not from the closing event: it is + read once when the stream starts and again on every reconnect of the + websocket, so a job that ended before this stream existed, or while the + socket was down, still answers. A read that fails reaches the caller, + which is the only one that can tell a transient failure from a job that + is gone. + + Emissions: + + - `{:kind :progress :payload {:stage ... :counters {...}}}` per event, + with the kind of the event (`:progress`, `:start`, `:retry`) + - `{:status \"completed\" :result {...}}` or `{:status \"failed\" + :error {...}}` or `{:status \"cancelled\"}` once, and then the + stream completes" + [ws-conn job-id] + (let [events (events-of ws-conn job-id) + progress (->> events + (rx/filter #(not= :end (:kind %)))) + triggers (rx/merge (->> (ws/get-rcv-stream ws-conn) + (rx/filter ws/opened-event?)) + (->> events + (rx/filter #(= :end (:kind %))))) + outcomes (->> (rx/merge (rx/of ::start) triggers) + (rx/mapcat (fn [_] (read-outcome job-id))) + (rx/take 1) + (rx/share))] + (rx/merge (rx/take-until outcomes progress) + outcomes))) + +(defn cancel-job + "Ask the server to cancel a job the caller created. Fire and forget: + failures are ignored because the job may have just finished on its + own." + [job-id] + (->> (rp/cmd! :cancel-job {:id job-id}) + (rx/catch (fn [_] (rx/of nil))) + (rx/subs! (constantly nil) (constantly nil) (constantly nil)))) diff --git a/frontend/src/app/main/data/nitrate.cljs b/frontend/src/app/main/data/nitrate.cljs index 67ddad852e..1fbbce503c 100644 --- a/frontend/src/app/main/data/nitrate.cljs +++ b/frontend/src/app/main/data/nitrate.cljs @@ -362,8 +362,7 @@ (let [source-organization (:organization team) can-move? (cto/allowed? :move-team - {:organization-perms {:owner-id (:owner-id source-organization) - :permissions (:permissions source-organization)} + {:organization-perms source-organization :profile-id profile-id :team-perms (:permissions team) :target-organization-same-owner? false})] @@ -414,7 +413,18 @@ :subscription-status subscription-status}) pending-id (str (uuid/next)) callback-url (dom/append-query-param (rt/get-current-href) - :pending-action-id pending-id)] + :pending-action-id pending-id) + ;; The permissions may have changed in Nitrate since the modal + ;; was opened: refresh them and explain why the action failed. + on-error + (fn [cause] + (if (= :not-allowed (-> cause ex-data :code)) + (rx/of (dt/fetch-teams) + (modal/show :no-permission-modal + {:type (if (= team-previous-organization-status "other-organization") + :no-organizations-change + :no-organizations-create)})) + (rx/throw cause)))] (rx/concat (when-not skip-audit? (rx/of audit-event)) @@ -423,7 +433,8 @@ (fn [{:keys [authorized redirect-uri]}] (if authorized (->> (rp/cmd! ::add-team-to-organization {:team-id team-id :organization-id organization-id}) - (rx/map (fn [_] (modal/hide)))) + (rx/map (fn [_] (modal/hide))) + (rx/catch on-error)) (if redirect-uri (do (ss/save-pending-action! pending-id {:type :add-team-to-organization @@ -491,10 +502,9 @@ (fn [teams] (let [all-organizations (map dt/team->organization (filter #(and (:is-default %) (:organization %)) teams)) - organizations (filter (fn [organization] - (let [perm (dm/get-in organization [:permissions :create-teams]) - is-own? (= profile-id (:owner-id organization))] - (or (= perm "any") is-own?))) all-organizations) + organizations (filter #(cto/allowed? :create-team + {:organization-perms % + :profile-id profile-id}) all-organizations) team (first (filter #(= (:id %) team-id) teams)) current-organization (:organization team) on-confirm (fn [organization-id] @@ -551,22 +561,17 @@ team (first (filter #(= (:id %) team-id) teams)) source-organization (:organization team) current-organization-id (:id source-organization) - move-perm (dm/get-in source-organization [:permissions :move-teams]) source-owner-id (:owner-id source-organization) + can-move? (fn [organization] + (cto/allowed? :move-team + {:organization-perms source-organization + :profile-id profile-id + :target-organization-same-owner? (= source-owner-id (:owner-id organization))})) can-create? (fn [organization] - (let [perm (dm/get-in organization [:permissions :create-teams]) - is-own? (= profile-id (:owner-id organization))] - (or (= perm "any") is-own?))) - organizations-by-move (case move-perm - "never" - [] - - "myOrganizations" - (filter #(= source-owner-id (:owner-id %)) all-organizations) - - ;; Default to always-allowed behavior. - all-organizations) - organizations (filter can-create? organizations-by-move) + (cto/allowed? :create-team + {:organization-perms organization + :profile-id profile-id})) + organizations (filter #(and (can-move? %) (can-create? %)) all-organizations) selectable-organizations (remove #(= current-organization-id (:id %)) organizations) on-confirm (fn [organization-id] (st/emit! (add-team-to-organization {:team-id team-id diff --git a/frontend/src/app/main/data/persistence.cljs b/frontend/src/app/main/data/persistence.cljs index 7f08500144..245afddf88 100644 --- a/frontend/src/app/main/data/persistence.cljs +++ b/frontend/src/app/main/data/persistence.cljs @@ -16,8 +16,14 @@ [app.main.data.common :as-alias dc] [app.main.data.event :as ev] [app.main.data.helpers :as dsh] + [app.main.data.modal :as-alias modal] [app.main.data.notifications :as ntf] + [app.main.data.persistence.commits :as pcm] + [app.main.data.persistence.lock :as plk] [app.main.data.workspace :as-alias dw] + [app.main.data.workspace.edition :as dwe] + [app.main.data.workspace.path.state :as path.state] + [app.main.data.workspace.undo :as dwu] [app.main.errors :as errors] [app.main.refs :as refs] [app.main.repo :as rp] @@ -30,6 +36,8 @@ (declare ^:private persist-commit) (declare ^:private resume-persistence) (declare ^:private slow-retry-cycle) +(declare ^:private flush-buffer) +(declare ^:private commit-persisted) (log/set-level! :warn) @@ -58,6 +66,9 @@ sending it. Fast failures, such as being offline, never count." 3) +;; Wait for the changes stored before a saved commit, then reload the file. +(def ^:private unseen-changes-timeout-ms 10000) + (def ^:private saving-stall-timeout-ms (* 5 60 1000)) (def ^:private saving-check-interval-ms 30000) (def ^:private save-wait-timeout-ms (* 2 60 1000)) @@ -66,12 +77,15 @@ (defn terminal-status? "True when a persistence snapshot releases waiters: a failed save, or - a settled (`nil` / `:saved`) empty queue. Anything else — `:pending`, - `:saving` and the `:retrying` episode — keeps waiting." - [{:keys [status queue]}] + a settled (`nil` / `:saved`) empty queue and buffer. Anything else — + `:pending`, `:saving`, the `:retrying` episode, or commits still in the + buffer (the status turns `:saved` as soon as the queue empties) — keeps + waiting." + [{:keys [status queue buffer]}] (boolean (or (= status :error) (and (empty? queue) + (empty? buffer) (or (nil? status) (= status :saved)))))) (defn wait-persisted-or-error @@ -145,7 +159,20 @@ (and (= from :retrying) (#{:pending :saving} to)) from :else to)) +(defn- count-unsaved-commits + "Local commits not saved yet, queued or buffered, of every file." + [state] + (let [{:keys [queue index buffer]} (:persistence state)] + (+ (count (mapcat #(get-in index [% :commits]) queue)) + (count buffer)))) + +(defn- nothing-to-save? + [{:keys [buffer queue]}] + (and (empty? buffer) (empty? queue))) + (defn- update-status + "Moves persistence to `status`. A delayed :pending notice is ignored + when nothing is left to save (e.g. a rollback already dropped the edit)." [status] (ptk/reify ::update-status ptk/UpdateEvent @@ -155,7 +182,9 @@ (log/trc :hint "update-status" :from (:status pstate) :to status) - (let [status (next-status (:status pstate) status)] + (let [status (if (and (= :pending status) (nothing-to-save? pstate)) + (:status pstate) + (next-status (:status pstate) status))] (cond-> (assoc pstate :status status) (#{:pending :saving} status) (update :last-progress-at d/nilv (inst-ms (ct/now))) @@ -219,6 +248,13 @@ (retry-window-open? pstate) (< (or slow-failures 0) max-slow-failures))) +(defn save-retrying? + "True while the slow cycle keeps resending a failed queue, including + while one of its attempts is in flight." + [pstate] + (or (true? (:recovering pstate)) + (slow-retry-pending? pstate))) + (defn- report-stalled-persistence [now] (ptk/reify ::report-stalled-persistence @@ -276,6 +312,98 @@ (effect [_ _ _] (swap! revn-data update file-id (fnil max 0) revn)))) +(defn- record-remote-changes + "Numbers a remote commit, keeps the highest revision received per file + (`:remote-revn`) and, while `file-id` has pending commits, keeps its + changes for a rollback to apply again." + [file-id revn changes] + (ptk/reify ::record-remote-changes + IDeref + (-deref [_] {:file-id file-id :revn revn}) + + ptk/UpdateEvent + (update [_ state] + (update state :persistence + (fn [pstate] + (let [number (inc (:remote-seq pstate 0))] + (cond-> (-> pstate + (assoc :remote-seq number) + (update-in [:remote-revn file-id] (fnil max revn) revn)) + (seq (pcm/pending-commits pstate file-id)) + (update-in [:remote-changes file-id] (fnil conj []) + {:seq number :changes changes})))))))) + +(defn- stale-commit? + "True when `commit` was made on another version of its file than the + loaded one, which a peer's restore replaced: its changes are not in the + local data and the server refuses them. A file loaded with no version + known is never stale." + [state {:keys [file-id file-vern]}] + (let [vern (:vern (dsh/lookup-file state file-id))] + (and (some? vern) + (not= vern file-vern)))) + +(defn- last-unseen-revn + "The highest revision above `seen` (the highest one received) of the rows + other sessions stored in `lagged`, the rows a save answer lists after + the revision it was sent with. Nil when there is none, or when the save + stored nothing (the last row is not this session's)." + [lagged session-id seen] + (let [own? #(= session-id (:session-id %))] + (when (own? (last lagged)) + (transduce (comp (remove own?) + (map :revn) + (filter #(> % seen))) + (completing (fnil max 0)) + nil + lagged)))) + +(defn- settle-saved-commit + "Acknowledges the saved `commit` once this tab received every change the + server stored before it (from `lagged`, the save answer's rows), such as + the repair of a damaged file. Until then its commits stay pending, so a + late change is applied under them, in the server's order. A change that + does not arrive in time reloads the file." + [commit lagged] + (let [needed (volatile! nil)] + (ptk/reify ::settle-saved-commit + ptk/UpdateEvent + (update [_ state] + (let [file-id (:file-id commit) + seen (dm/get-in state [:persistence :remote-revn file-id] -1) + revn (when (and (= file-id (:current-file-id state)) + (not (stale-commit? state commit))) + (last-unseen-revn lagged (:session-id state) seen))] + (vreset! needed revn) + (cond-> state + (some? revn) + (d/update-in-when [:persistence :index (:id commit)] assoc ::awaiting revn)))) + + ptk/WatchEvent + (watch [_ _ stream] + (if-let [revn @needed] + (let [file-id (:file-id commit) + arrived-s (->> stream + (rx/filter (ptk/type? ::record-remote-changes)) + (rx/map deref) + (rx/filter #(and (= file-id (:file-id %)) + (>= (:revn %) revn))) + (rx/map (constantly ::arrived))) + left-s (->> stream + (rx/filter (ptk/type? ::dw/finalize-workspace)) + (rx/map (constantly ::left))) + timeout-s (->> (rx/timer unseen-changes-timeout-ms) + (rx/map (constantly ::timeout)))] + (->> (rx/merge arrived-s left-s timeout-s) + (rx/take 1) + (rx/mapcat (fn [outcome] + (rx/concat + (rx/of (commit-persisted commit)) + (if (= ::timeout outcome) + (rx/of (ptk/event ::dw/reload-current-file)) + (rx/empty))))))) + (rx/of (commit-persisted commit))))))) + (defn- discard-commit [commit-id] (ptk/reify ::discard-commit @@ -288,11 +416,28 @@ (pop queue) (throw (ex-info "invalid state" {}))))) (update :index dissoc commit-id) + (pcm/keep-saved-commits (get-in pstate [:index commit-id])) + (pcm/prune-remote-changes (get-in pstate [:index commit-id :file-id])) (assoc :last-progress-at (inst-ms (ct/now))) (dissoc :stall-reported :failing-since :recovering :slow-failures :attempts :retry-token :retry-for))))))) +(defn- without-commits + "`buffer` without `commits`." + [buffer commits] + (let [ids (into #{} (map :id) commits)] + (into [] (remove #(contains? ids (:id %))) buffer))) + +(defn- buffer-commit + "Keeps a local commit in the buffer until the next flush." + [commit] + (ptk/reify ::buffer-commit + ptk/UpdateEvent + (update [_ state] + (let [number (get-in state [:persistence :remote-seq] 0)] + (update-in state [:persistence :buffer] (fnil conj []) (assoc commit :remote-seq number)))))) + (defn- append-commit "Event used internally to append the current change to the persistence queue." @@ -307,6 +452,7 @@ (-> pstate (cond-> (not= :error (:status pstate)) (update :run-id d/nilv run-id)) + (update :buffer without-commits (:commits commit)) (update :queue queue-conj id) (update :index assoc id commit))))) @@ -355,6 +501,14 @@ :retrying (slow-retry-pending? pstate) :queued-commits (count queue)})) +(defn- end-stopped-slow-cycle + "Clears `:recovering` when the slow cycle no longer resends the failure: + the limit of slow failures is reached or the retry window closed." + [pstate] + (cond-> pstate + (not (slow-retry-pending? pstate)) + (dissoc :recovering))) + (defn- persistence-failed [commit-id cause] (ptk/reify ::persistence-failed @@ -374,7 +528,8 @@ ::errors/handled? true)) (update :failing-since d/nilv (inst-ms (ct/now))) (dissoc :run-id :last-progress-at :stall-reported - :attempts :retry-token :retry-for)))))) + :attempts :retry-token :retry-for) + (end-stopped-slow-cycle)))))) ptk/WatchEvent (watch [_ state stream] @@ -399,14 +554,17 @@ ;; navigate away and lose the retained changes. (let [pstate (:persistence state)] (when (notify-failure? pstate) - (errors/flash-persistence cause :retrying (slow-retry-pending? pstate))))))) + (errors/flash-persistence cause + :retrying (slow-retry-pending? pstate) + :busy (busy-server-error? (ex-data cause)))))))) (defn- persistence-transient-failure "Transient save failure: the head commit stays queued and a retry is scheduled with backoff instead of parking the save in `:error`. Once the - budget (`retry-delays-ms`) is exhausted, or when the failed attempt was - one of the slow cycle, the failure falls through to the `persistence-failed` - path, which schedules the next slow attempt. While retries are suspended + budget (`retry-delays-ms`) is exhausted, the commit has `max-slow-failures` + slow failures, or the failed attempt was one of the slow cycle, the + failure falls through to the `persistence-failed` path, which schedules + the next slow attempt while `slow-retry-pending?`. While retries are suspended nothing is scheduled: the episode waits for a file to be opened again. `elapsed-ms` is how long the failed attempt took." [commit-id cause elapsed-ms] @@ -431,9 +589,11 @@ ptk/WatchEvent (watch [_ state _] - (let [{:keys [attempts recovering suspended]} (:persistence state)] + (let [{:keys [attempts recovering suspended slow-failures]} (:persistence state)] (cond - (or recovering (> attempts (count retry-delays-ms))) + (or recovering + (> attempts (count retry-delays-ms)) + (>= (or slow-failures 0) max-slow-failures)) (rx/of (persistence-failed commit-id cause)) suspended @@ -444,7 +604,9 @@ ;; One notice per episode: shown on the first attempt, ;; re-showing would only replace the identical toast. (when (= 1 attempts) - (rx/of (ntf/show {:content (tr "errors.save-retrying") + (rx/of (ntf/show {:content (if (busy-server-error? (ex-data cause)) + (tr "errors.save-retrying-busy") + (tr "errors.save-retrying")) :type :toast :level :warning :tag reconnecting-tag}))) @@ -465,6 +627,245 @@ (d/update-in-when state [:persistence :index (:id commit)] assoc ::acknowledged true)))) +(def rejected-tag + "Tag of the notice that changes to the open file were rejected." + :persistence-rejected) + +(def ^:private other-file-rejected-tag + "Tag of the notice that changes to a file not open were rejected." + :persistence-rejected-other-file) + +(def file-repaired-tag + "Tag of the notice that the backend repaired the open file." + :file-repaired) + +(def repaired-rejected-tag + "Tag of the notice that tells both of a repair and of rejected changes." + :file-repaired-rejected) + +(def ^:private rejected-timeout-ms + "How long the rejected changes notice stays; the report is sent anyway." + 10000) + +(defn notify-repaired + "Tells the user the backend repaired the open file. The store holds one + toast, so a notice of rejected changes to that file still on screen is + folded into it, keeping its report link." + [] + (ptk/reify ::notify-repaired + ptk/WatchEvent + (watch [_ state _] + (let [{:keys [tag links]} (:notification state)] + (rx/of + (ntf/show + (if (contains? #{rejected-tag repaired-rejected-tag} tag) + (cond-> {:content (tr "workspace.notifications.file-repaired-changes-undone") + :type :toast + :level :warning + :timeout nil + :tag repaired-rejected-tag} + (seq links) + (assoc :links links)) + {:content (tr "workspace.notifications.file-repaired") + :type :toast + :level :info + :timeout nil + :tag file-repaired-tag}))))))) + +(defn- rejection-notice + "Text, tag and timeout of the notice that the changes `rejected` in a + save of `file-id` were undone. A file other than the open one is named; + a repair notice of the open file still on screen is folded into it." + [state file-id {:keys [preexisting-damage]}] + (let [file-name (dm/get-in state [:files file-id :name]) + current? (= file-id (:current-file-id state)) + repaired? (contains? #{file-repaired-tag repaired-rejected-tag} + (dm/get-in state [:notification :tag]))] + (cond + (and (not current?) (some? file-name)) + {:hint (tr "errors.save-rejected-changes-undone-in-file" file-name) + :tag other-file-rejected-tag + :timeout rejected-timeout-ms} + + (and current? repaired?) + {:hint (tr "workspace.notifications.file-repaired-changes-undone") + :tag repaired-rejected-tag + :timeout nil} + + (and current? preexisting-damage) + {:hint (tr "errors.save-rejected-damaged-data") + :tag rejected-tag + :timeout rejected-timeout-ms} + + :else + {:hint (tr "errors.save-rejected-changes-undone") + :tag (if current? rejected-tag other-file-rejected-tag) + :timeout rejected-timeout-ms}))) + +(defn- discard-path-edit + "Forgets the path editor's state for `id`, so leaving the edition saves + nothing from it." + [id] + (ptk/reify ::discard-path-edit + ptk/UpdateEvent + (update [_ state] + (update-in state [:workspace-local :edit-path] dissoc id)))) + +(defn- prune-removed-shapes + "Deselects the shapes missing from the current page, closing the color + picker that edits them, and leaves edition mode if the edited one is + missing. A path the rollback changed (`edited` is the edited shape before + it) leaves edition too: the editor works on its own copy of the shape, + which still holds the reverted content. Text editors stay open and show + the reverted text." + [edited] + (ptk/reify ::prune-removed-shapes + ptk/UpdateEvent + (update [_ state] + (let [objects (dsh/lookup-page-objects state) + selected (dm/get-in state [:workspace-local :selected]) + kept (some->> selected (into (empty selected) (filter #(contains? objects %))))] + (cond-> state + (some? selected) + (assoc-in [:workspace-local :selected] kept) + + (and (not= (count selected) (count kept)) + (= :colorpicker (dm/get-in state [::modal/modal :type]))) + (dissoc ::modal/modal)))) + + ptk/WatchEvent + (watch [_ state _] + (let [edition (dm/get-in state [:workspace-local :edition]) + shape (get (dsh/lookup-page-objects state) edition)] + (cond + (nil? edition) + (rx/empty) + + (nil? shape) + (rx/of (dwe/clear-edition-mode)) + + (and (path.state/editing? state) + (not= edited shape)) + (rx/of (discard-path-edit edition) + (dwe/clear-edition-mode)) + + :else + (rx/empty)))))) + +(defn- compensate-commits + "Undoes the saved `commits` of `file-id`, newest first. A loaded file gets + a commit, applied locally; the undo of a file that is not loaded goes to + the queue as is." + [origin state file-id commits] + (let [changes (pcm/compensation-changes commits)] + (if (dsh/lookup-file-data state file-id) + (dch/commit-changes (assoc changes + :origin origin + :file-id file-id + :save-undo? false + ;; Its own group, never that of an open undo transaction + :undo-group (uuid/next) + ;; Rollbacks also run while saves fail + :ignore-save-lock true)) + (let [{:keys [file-revn file-vern features]} (peek commits)] + (append-commit + (pcm/queue-entry + [(assoc changes + :id (uuid/next) + :created-at (ct/now) + :origin (ptk/type origin) + :file-id file-id + :file-revn file-revn + :file-vern file-vern + :features features + :remote-seq (dm/get-in state [:persistence :remote-seq] 0))])))))) + +(defn- reloaded-s + "Ends once the reload that follows a failed rebase of `file-ids` has put + the server data of those files in the state." + [stream current-file-id file-ids] + (let [loaded? (if (every? #(= current-file-id %) file-ids) + (ptk/type? ::dw/bundle-fetched) + (ptk/type? ::dw/all-libraries-resolved))] + (->> stream + (rx/filter loaded?) + (rx/take 1) + (rx/ignore) + (rx/take-until (rx/filter (ptk/type? ::dw/finalize-workspace) stream))))) + +(defn- notify-rolled-back + "Emits `::rolled-back` with `targets` once the reverted data is in the + state: at once, or after the reload when a rebase of `rebases` failed. + The reload replaces the whole file data, so other shapes may change too." + [stream state rebases targets] + (rx/defer + (fn [] + (let [failed (into #{} (comp (map deref) (filter :failed) (map :file-id)) rebases) + event (ptk/data-event ::rolled-back targets)] + (if (empty? failed) + (rx/of event) + (rx/concat (reloaded-s stream (:current-file-id state) failed) + (rx/of event))))))) + +(defn- apply-rollback-plan + "Events that revert the local data as `plan` says: pending commits are + undone, saved ones undone by a new commit, and their groups leave the + undo history." + [origin state stream {:keys [files compensate groups undo-index]}] + (let [rebases (mapv (fn [[fid plan]] (dch/rebase-commits fid plan)) files)] + (rx/concat + (rx/from rebases) + (rx/from (map (fn [[fid commits]] (compensate-commits origin state fid commits)) + (group-by :file-id compensate))) + (rx/of (dwu/forget-undo-groups groups undo-index) + (prune-removed-shapes (get (dsh/lookup-page-objects state) + (dm/get-in state [:workspace-local :edition])))) + ;; Tells listeners (a library sync, plugins) which files, pages and + ;; shapes lost commits + (notify-rolled-back stream state rebases + (pcm/rollback-targets {:files files :compensate compensate}))))) + +(defn- rollback-rejected + "Reverts the commits `rejected` in the save of `entry`, the later commits + of its file and the rest of their undo groups. Pending commits are undone + and dropped, saved ones undone by a new commit; the groups leave the undo + history and the user gets a warning." + [{:keys [file-id] :as entry} rejected] + (let [plan (volatile! nil)] + (ptk/reify ::rollback-rejected + ptk/UpdateEvent + (update [_ state] + ;; A save made on a version a restore replaced: the reloaded data + ;; holds none of it, so there is nothing to revert + (if (stale-commit? state entry) + state + (let [plan' (pcm/rollback-plan (:persistence state) entry rejected)] + (vreset! plan plan') + (update state :persistence pcm/without-rolled-back entry plan')))) + + ptk/WatchEvent + (watch [it state stream] + (if-let [plan @plan] + (apply-rollback-plan it state stream plan) + (rx/empty))) + + ptk/EffectEvent + (effect [_ state _] + (when-let [plan @plan] + (let [{:keys [error commit-ids]} rejected + {:keys [hint tag timeout]} (rejection-notice state file-id rejected)] + (errors/flash :cause (ex-info "The server rejected some changes" + (merge error + {:file-id file-id + :commit-ids commit-ids} + (pcm/rollback-summary plan))) + :type :unhandled + :level :warning + :timeout timeout + :tag tag + :report-link? true + :hint hint))))))) + (defn- update-file-request "Issues the `update-file` request, tracked as active for its lifetime." [request-id params] @@ -491,22 +892,55 @@ (nil? commit) :missing-commit (::acknowledged commit) :acknowledged (contains? @active-requests (::request-id commit)) :in-flight + ;; Saved, waiting for the changes stored before it + (::awaiting commit) :in-flight + (stale-commit? state commit) :stale (not (dm/get-in state [:permissions :can-edit])) :permission-denied :else :ready))) +(defn- with-changes + "Adds the changes of a queue entry to the save params, one group per + commit so the backend can reject them one by one." + [params {:keys [commits]}] + (assoc params :changes-with-metadata + (mapv (fn [{:keys [id origin redo-changes]}] + (cond-> {:commit-id id :changes (vec redo-changes)} + (keyword? origin) + (assoc :hint-origin origin))) + commits))) + +(defn- save-attempt-failed + "Routes a failed attempt to send `commit-id`, which took `elapsed-ms`. A + commit made on a version a restore replaced is dropped instead: the + reloaded file does not hold it and no resend can save it." + [commit-id cause elapsed-ms] + (ptk/reify ::save-attempt-failed + ptk/WatchEvent + (watch [_ state _] + (let [commit (dm/get-in state [:persistence :index commit-id])] + (rx/of (cond + (and (some? commit) (stale-commit? state commit)) + (commit-persisted commit) + + (transient-error? (ex-data cause)) + (persistence-transient-failure commit-id cause elapsed-ms) + + :else + (persistence-failed commit-id cause))))))) + (defn- send-queued-commit "Sends one queued commit and maps its outcome to persistence events." - [request-id session-id {:keys [id file-id file-revn file-vern changes features] :as commit}] + [request-id session-id {:keys [id file-id file-revn file-vern features] :as commit}] (let [sent-at (inst-ms (ct/now)) - params {:id file-id - :revn (max file-revn (get @revn-data file-id 0)) - :vern file-vern - :session-id session-id - :origin (:origin commit) - :created-at (:created-at commit) - :commit-id id - :changes (vec changes) - :features features}] + params (-> {:id file-id + :revn (max file-revn (get @revn-data file-id 0)) + :vern file-vern + :session-id session-id + :origin (:origin commit) + :created-at (:created-at commit) + :commit-id id + :features features} + (with-changes commit))] ;; UI read-only mode does not invalidate already queued edits. (->> (update-file-request request-id params) ;; Fails an unanswered request as a transient network error, so the @@ -520,19 +954,21 @@ ;; A response that carries no revision, including one that never ;; arrived, is treated as a failed save rather than a saved file. (rx/if-empty nil) - (rx/mapcat (fn [{:keys [revn]}] + (rx/mapcat (fn [{:keys [revn rejected lagged]}] (if (and (int? revn) (<= 0 revn)) - (rx/of (update-file-revn file-id revn) - (commit-persisted commit)) + (rx/concat + (rx/of (update-file-revn file-id revn)) + ;; Reverts the rejected commits before the queue moves on + (if (some? rejected) + (rx/of (rollback-rejected commit rejected)) + (rx/empty)) + (rx/of (settle-saved-commit commit lagged))) (rx/throw (ex-info "The save response has no valid revision" {:type :persistence :code :invalid-save-response :file-id file-id}))))) (rx/catch (fn [cause] - (rx/of (if (transient-error? (ex-data cause)) - (persistence-transient-failure - id cause (- (inst-ms (ct/now)) sent-at)) - (persistence-failed id cause)))))))) + (rx/of (save-attempt-failed id cause (- (inst-ms (ct/now)) sent-at)))))))) (defn- persist-commit "Sends the queued commit, stamping the attempt first. A commit whose @@ -563,7 +999,8 @@ (ex-info hint {:type :persistence :code code :commit-id commit-id - :file-id (:file-id commit)}))))] + :file-id (:file-id commit) + :unsaved-commits (count-unsaved-commits state)}))))] (if (and (some? token) (not= token (dm/get-in state [:persistence :retry-token]))) ;; Stale retry timer: its episode was superseded. Stay silent. @@ -574,6 +1011,8 @@ :acknowledged (rx/of (commit-persisted commit)) ;; The replacement runner listens for the original request's result. :in-flight (rx/empty) + ;; Made on a version a restore replaced: never sent + :stale (rx/of (commit-persisted commit)) :permission-denied (fail :save-permission-denied "Edit permission was lost before changes could be saved") :ready (send-queued-commit request-id (:session-id state) commit)))))))) @@ -675,17 +1114,35 @@ rejecting: callers use it to avoid snapshotting a file whose edits were never saved. What must not survive is the halt. Left in place, the failed commits sit at the head of the queue forever and, because the queue is - shared, they also block the pending edits of every other file." - [] - (ptk/reify ::release-failed-file - ptk/UpdateEvent - (update [_ state] - (let [commit-id (get-in state [:persistence :error :commit-id]) - file-id (get-in state [:persistence :index commit-id :file-id])] - (update state :persistence - #(-> % + shared, they also block the pending edits of every other file. + + With `revert?` the file is still open with the released edits applied, + so they are rolled back as a rejection would be: undone on screen and + cut from the undo history." + [revert?] + (let [plan (volatile! nil)] + (ptk/reify ::release-failed-file + ptk/UpdateEvent + (update [_ state] + (let [pstate (:persistence state) + commit-id (get-in pstate [:error :commit-id]) + file-id (get-in pstate [:index commit-id :file-id]) + plan' (when (and revert? file-id) + (pcm/release-plan pstate file-id))] + (vreset! plan plan') + (assoc state :persistence + (-> (if plan' + (pcm/without-rolled-back pstate {:file-id file-id} plan') + pstate) (drop-file-commits file-id) - (dissoc :failing-since :slow-failures))))))) + (pcm/prune-remote-changes file-id) + (dissoc :failing-since :slow-failures))))) + + ptk/WatchEvent + (watch [it state stream] + (if-let [plan @plan] + (apply-rollback-plan it state stream plan) + (rx/empty)))))) (defn- queue-needs-a-runner? "True when the queue still holds commits that nobody is sending. @@ -700,23 +1157,41 @@ (not= :error status) (not= :retrying status))) +(defn forget-other-versions + "Forgets the commits of the freshly loaded `file` made on another version + (`pcm/without-other-versions`), so no rollback or rebase replays them on + the loaded data. A pending status with nothing left to save turns saved." + [state {:keys [id vern]}] + (update state :persistence + (fn [pstate] + (let [pstate (pcm/without-other-versions pstate id vern)] + (cond-> pstate + (and (= :pending (:status pstate)) + (nothing-to-save? pstate)) + (assoc :status :saved)))))) + (defn pending-file-changes - "Queued edits to restore into a freshly loaded file, in queue order. - Only edits of the loaded version belong in its data. A permanently - failed file is excluded because recovery releases its edits." + "Queued and buffered edits to restore into a freshly loaded file, oldest + first. Only edits of the loaded version belong in its data. A + permanently failed file is excluded because recovery releases its edits." [{:keys [persistence]} {:keys [id vern]}] - (let [{:keys [queue index status error]} persistence - failed-file-id (get-in index [(:commit-id error) :file-id])] + (let [{:keys [queue index buffer status error]} persistence + failed-file-id (get-in index [(:commit-id error) :file-id]) + of-file? #(and (= id (:file-id %)) + (= vern (:file-vern %)))] (if (and (= :error status) (= id failed-file-id) (not (slow-retry-pending? persistence))) [] - (into [] - (comp (map #(get index %)) - (filter #(and (= id (:file-id %)) - (= vern (:file-vern %)))) - (mapcat :redo-changes)) - queue)))) + (-> [] + (into (comp (map #(get index %)) + (filter of-file?) + (mapcat :commits) + (mapcat :redo-changes)) + queue) + (into (comp (filter of-file?) + (mapcat :redo-changes)) + buffer))))) (defn- initialize-persistence-state "Brings persistence to a coherent starting shape when the workspace @@ -730,20 +1205,36 @@ (ptk/reify ::initialize-persistence-state ptk/WatchEvent (watch [_ state _] - (let [{:keys [queue status run-id]} (:persistence state)] - (cond - ;; A commit left queued by a previous session, with no runner. - (queue-needs-a-runner? (:persistence state)) - (rx/of (resume-persistence)) + (let [{:keys [queue status run-id buffer]} (:persistence state)] + (rx/concat + (cond + ;; A commit left queued by a previous session, with no runner. + (queue-needs-a-runner? (:persistence state)) + (rx/of (resume-persistence)) - ;; Nothing queued and nothing in flight: there is nothing to save. - (and (empty? queue) - (not= status :error) - (or run-id (#{:pending :saving} status))) - (rx/of (update-status :saved)) + ;; Nothing queued and nothing in flight: there is nothing to save. + (and (empty? queue) + (empty? buffer) + (not= status :error) + (or run-id (#{:pending :saving} status))) + (rx/of (update-status :saved)) - :else - (rx/empty)))))) + :else + (rx/empty)) + ;; Queues the commits a previous session left buffered + (if (seq buffer) + (rx/of (flush-buffer)) + (rx/empty))))))) + +(defn- notify-kept-failure + "Shows the save failure the queue keeps again, so the count of unsaved + changes replaces the notice the role change shows." + [] + (ptk/reify ::notify-kept-failure + ptk/EffectEvent + (effect [_ state _] + (let [error (dm/get-in state [:persistence :error])] + (errors/flash-persistence (ex-info (:hint error) error)))))) (defn- recover-persistence "Lifts a halted queue when a file is opened again or the role changes. @@ -762,7 +1253,8 @@ A lost permission is not a temporary condition. The backend raises `:not-found` for it, and replaying the save to find that out routes the - user to the exception page." + user to the exception page. A role change that leaves the user a viewer + keeps those edits; the one that brings edit rights back releases them." [opened-file?] (ptk/reify ::recover-persistence ptk/WatchEvent @@ -784,8 +1276,17 @@ (slow-retry-pending? (:persistence state)) (rx/of (resume-persistence true)) + ;; A viewer's edits wait for edit rights to come back, still + ;; queued and on screen. + (and (not opened-file?) + (= :save-permission-denied (dm/get-in state [:persistence :error :code])) + (not (dm/get-in state [:permissions :can-edit]))) + (rx/of (notify-kept-failure)) + + ;; An opened file was loaded without the released edits; one + ;; still open shows them, so they are reverted. (= :error status) - (rx/of (release-failed-file) (resume-persistence)) + (rx/of (release-failed-file (not opened-file?)) (resume-persistence)) (queue-needs-a-runner? (:persistence state)) (rx/of (resume-persistence)) @@ -796,22 +1297,22 @@ :else (rx/empty))))))) -(def ^:private xf-mapcat-undo - (mapcat :undo-changes)) - -(def ^:private xf-mapcat-redo - (mapcat :redo-changes)) - (defn- merge-commit + "One queue entry per file and version: a save carries one version." [buffer] - (->> (rx/from (group-by :file-id buffer)) - (rx/map (fn [[_ [item :as commits]]] - (let [uchg (into [] xf-mapcat-undo commits) - rchg (into [] xf-mapcat-redo commits)] - (-> item - (assoc :undo-changes uchg) - (assoc :redo-changes rchg) - (assoc :changes rchg))))))) + (->> (rx/from (group-by (juxt :file-id :file-vern) buffer)) + (rx/map (comp pcm/queue-entry val)))) + +(defn- flush-buffer + "Queues the buffered commits, merged into one queue entry per file." + [] + (ptk/reify ::flush-buffer + ptk/WatchEvent + (watch [_ state _] + (if-let [buffer (seq (dm/get-in state [:persistence :buffer]))] + (->> (merge-commit buffer) + (rx/map append-commit)) + (rx/empty))))) (defn- resume-on-online "Re-enters the runner for a retrying episode when the browser reports @@ -830,14 +1331,25 @@ (defn- sync-unload-guard "Turns the browser's leave-page warning on while edits are unsaved: buffered, queued, or retained after a failed save." - [buffered?] + [] (ptk/reify ::sync-unload-guard ptk/EffectEvent (effect [_ state _] - (if (or buffered? (seq (dm/get-in state [:persistence :queue]))) + (if (or (seq (dm/get-in state [:persistence :buffer])) + (seq (dm/get-in state [:persistence :queue]))) (swap! st/ongoing-tasks conj :persistence) (swap! st/ongoing-tasks disj :persistence))))) +(defn- persist-buffered + "Forces a save when local commits wait in the buffer." + [] + (ptk/reify ::persist-buffered + ptk/WatchEvent + (watch [_ state _] + (if (seq (dm/get-in state [:persistence :buffer])) + (rx/of ::force-persist) + (rx/empty))))) + (defn leave-signals "Emits when the tab is hidden or the window loses focus: another tab or app, a plugin iframe, a closed laptop. Empty outside the browser." @@ -857,9 +1369,6 @@ (log/debug :hint "initialize persistence") (let [stoper-s (rx/filter (ptk/type? ::initialize-persistence) stream) - ;; True while local commits wait in the buffer for the next flush. - buffered? (volatile! false) - local-commits-s (->> stream (rx/filter dch/commit?) @@ -909,23 +1418,30 @@ ;; Sends the buffered edits when the user leaves the tab or window. (->> (leave-signals) - (rx/filter (fn [_] @buffered?)) - (rx/map (fn [_] ::force-persist)) + (rx/map (fn [_] (persist-buffered))) (rx/take-until stoper-s)) - ;; Syncs the leave-page warning on every local commit and on every - ;; queue change (append after a flush, discard after a save, - ;; release of a failed file). + ;; Syncs the leave-page warning on every buffer and queue change (->> (rx/merge - (rx/of false) - (->> local-commits-s - (rx/map (fn [_] (vreset! buffered? true)))) + (rx/of nil) (->> stream - (rx/filter #(or (ptk/type? ::append-commit %) + (rx/filter #(or (ptk/type? ::buffer-commit %) + (ptk/type? ::append-commit %) (ptk/type? ::discard-commit %) - (ptk/type? ::release-failed-file %))) - (rx/map (fn [_] @buffered?)))) - (rx/map sync-unload-guard) + (ptk/type? ::rollback-rejected %) + (ptk/type? ::release-failed-file %) + (ptk/type? ::dw/bundle-fetched %))))) + (rx/map (fn [_] (sync-unload-guard))) + (rx/take-until stoper-s)) + + ;; Locks the workspace while saves fail and the user can edit + (->> stream + (rx/filter #(or (ptk/type? ::update-status %) + (ptk/type? ::persistence-failed %) + (ptk/type? ::resume-persistence %) + (ptk/type? ::discard-commit %) + (ptk/type? ::dc/change-team-role %))) + (rx/map (fn [_] (plk/sync-failure-lock))) (rx/take-until stoper-s)) (->> local-commits-s @@ -934,15 +1450,14 @@ (update-status :pending))) (rx/take-until stoper-s)) - ;; Here we watch for local commits, buffer them in a small - ;; chunks (very near in time commits) and append them to the - ;; persistence queue + ;; Buffers local commits; each notification queues the buffer (->> local-commits-s + (rx/map buffer-commit) + (rx/take-until stoper-s)) + + (->> notifier-s + (rx/map (fn [_] (flush-buffer))) (rx/take-until stoper-s) - (rx/buffer-until notifier-s) - (rx/tap (fn [_] (vreset! buffered? false))) - (rx/mapcat merge-commit) - (rx/map append-commit) (rx/finalize (fn [] (log/debug :hint "finalize persistence: changes watcher")))) @@ -952,6 +1467,7 @@ (rx/filter dch/commit?) (rx/map deref) (rx/filter #(= :remote (:source %))) - (rx/mapcat (fn [{:keys [file-id file-revn] :as commit}] - (rx/of (update-file-revn file-id file-revn)))) + (rx/mapcat (fn [{:keys [file-id file-revn redo-changes]}] + (rx/of (record-remote-changes file-id file-revn redo-changes) + (update-file-revn file-id file-revn)))) (rx/take-until stoper-s))))))) diff --git a/frontend/src/app/main/data/persistence/commits.cljs b/frontend/src/app/main/data/persistence/commits.cljs new file mode 100644 index 0000000000..a1f5e3d253 --- /dev/null +++ b/frontend/src/app/main/data/persistence/commits.cljs @@ -0,0 +1,255 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.main.data.persistence.commits + "Pure functions over the commits of the persistence state: buffered, + queued and saved ones, and the plan to roll some of them back." + (:require + [app.common.data :as d])) + +(def ^:private entry-keys + "Keys a queue entry keeps of the first commit merged into it." + [:id :created-at :origin :features :file-id :file-revn :file-vern]) + +(def ^:private commit-keys + "Keys a queue entry keeps of each of its commits." + [:id :origin :undo-group :redo-changes :undo-changes :remote-seq :undo-index]) + +(def ^:private saved-keys + "Keys a saved commit takes from its queue entry." + [:file-id :file-revn :file-vern :features]) + +(defn queue-entry + "Merges `commits` of one file, oldest first, into a queue entry." + [[commit :as commits]] + (-> (select-keys commit entry-keys) + (assoc :commits (mapv #(select-keys % commit-keys) commits)))) + +(defn pending-commits + "Unsaved commits of `file-id`, oldest first: queued, then buffered." + [pstate file-id] + (let [file? #(= file-id (:file-id %)) + xf:queued (comp (map #(get-in pstate [:index %])) + (filter file?) + (mapcat :commits))] + (-> [] + (into xf:queued (:queue pstate)) + (into (filter file?) (:buffer pstate))))) + +(defn- pending-files + "Files with unsaved commits." + [pstate] + (-> #{} + (into (map #(get-in pstate [:index % :file-id])) (:queue pstate)) + (into (map :file-id) (:buffer pstate)))) + +(defn prune-remote-changes + "Forgets the remote changes of `file-id` older than its oldest pending + commit, which a rollback never applies again." + [pstate file-id] + (if-let [since (some-> (first (pending-commits pstate file-id)) :remote-seq)] + (d/update-in-when pstate [:remote-changes file-id] + (fn [entries] (into [] (filter #(> (:seq %) since)) entries))) + (update pstate :remote-changes dissoc file-id))) + +(defn without-other-versions + "`pstate` without the commits of `file-id` made on a version other than + `vern`, the one just loaded: a restore replaced it, so they are not in + the data and the server refuses them. Their queue entries stay, emptied, + for the runner to settle the one in flight and skip the rest." + [pstate file-id vern] + (let [other? #(and (= file-id (:file-id %)) + (not= vern (:file-vern %))) + emptied (into {} + (keep (fn [id] + (let [entry (get-in pstate [:index id])] + (when (and (other? entry) (seq (:commits entry))) + [id (assoc entry :commits [])])))) + (:queue pstate))] + (if (or (seq emptied) + (some other? (:buffer pstate)) + (some other? (:saved pstate))) + (-> pstate + (update :index merge emptied) + (update :buffer #(into [] (remove other?) %)) + (update :saved #(into [] (remove other?) %)) + (prune-remote-changes file-id)) + pstate))) + +(defn- saved-commits + "The `commits` of `entry` as saved commits, which carry their file." + [entry commits] + (let [file (select-keys entry saved-keys)] + (map #(merge % file) commits))) + +(defn keep-saved-commits + "Keeps the saved commits, `entry`'s included, whose undo group a pending + commit shares: if that commit is rejected they are undone too." + [pstate {:keys [commits] :as entry}] + (let [groups (into #{} + (keep :undo-group) + (mapcat #(pending-commits pstate %) (pending-files pstate)))] + (assoc pstate :saved + (into [] + (filter #(contains? groups (:undo-group %))) + (concat (:saved pstate) (saved-commits entry commits)))))) + +(defn- reverted-commits + "Pending commits to revert per file: from the first one that is rejected + in `file-id`, or in an undo group of `groups`, to the last." + [pending file-id rejected kept groups] + (let [reverted? + (fn [fid {:keys [id undo-group]}] + (and (not (contains? kept id)) + (or (and (= fid file-id) (contains? rejected id)) + (contains? groups undo-group)))) + + from-first-reverted + (fn [[fid commits]] + (when-let [i (d/index-of-pred commits (partial reverted? fid))] + [fid (subvec commits i)]))] + (into {} (keep from-first-reverted) pending))) + +(defn rollback-plan + "How to roll back the save of `entry` that came back `rejected`. Whole + undo groups are reverted, across files and saves. + + Each file in `:files` undoes all its pending commits, applies again the + remote changes that came after them and redoes the commits that stay, so + a peer's later value for an attribute survives. Saved commits of a + reverted group go to `:compensate`, to be undone by a new commit. + `:undo-index` is the undo history index before the oldest undo or redo + commit it reverts, if any." + [pstate {:keys [file-id commits] :as entry} {:keys [commit-ids]}] + (let [rejected (set commit-ids) + accepted (into [] (remove #(contains? rejected (:id %))) commits) + kept (into #{} (map :id) accepted) + pending (into {} (map (juxt identity #(pending-commits pstate %))) (pending-files pstate)) + + [reverted groups] + (loop [groups #{}] + (let [reverted (reverted-commits pending file-id rejected kept groups) + groups' (into groups (comp (mapcat val) (keep :undo-group)) reverted)] + (if (= groups groups') + [reverted groups] + (recur groups')))) + + saved (into (vec (:saved pstate)) (saved-commits entry accepted)) + + compensate + (into [] (filter #(contains? groups (:undo-group %))) saved) + + file-plan + (fn [[fid commits]] + (let [all (get pending fid) + since (:remote-seq (first all) 0) + xf:remote (comp (filter #(> (:seq %) since)) + (mapcat :changes))] + [fid {:undo all + :remote (into [] xf:remote (get-in pstate [:remote-changes fid])) + :redo (subvec all 0 (- (count all) (count commits))) + :reverted commits}]))] + + {:files (into {} (map file-plan) reverted) + :compensate compensate + :groups groups + ;; Saved commits are older than pending ones + :undo-index (some :undo-index (concat compensate (mapcat val reverted)))})) + +(defn release-plan + "How to roll back every pending commit of `file-id`, as if the server had + rejected them all, together with the rest of their undo groups." + [pstate file-id] + (rollback-plan pstate + {:file-id file-id :commits []} + {:commit-ids (map :id (pending-commits pstate file-id))})) + +(defn rollback-summary + "Counts the commits `plan` reverts and the files they belong to." + [{:keys [files compensate]}] + {:reverted (+ (transduce (map (comp count :reverted val)) + files) + (count compensate)) + :files (count (into (set (keys files)) (map :file-id) compensate))}) + +(def ^:private page-change-types + "Changes whose `:id` is a page id." + #{:add-page :mod-page :del-page :mov-page}) + +(def ^:private shape-change-types + "Changes whose `:id` is a shape id." + #{:add-obj :mod-obj :del-obj :fix-obj}) + +(defn- change-page-id + [{:keys [type] :as change}] + (if (contains? page-change-types type) + (:id change) + (:page-id change))) + +(defn- change-shape-ids + "Ids of the page shapes `change` touches; none for component shapes." + [{:keys [type page-id] :as change}] + (when (some? page-id) + (cond + (contains? shape-change-types type) + [(:id change)] + + (contains? #{:mov-objects :reorder-children} type) + (:shapes change)))) + +(defn rollback-targets + "The files of the commits `plan` reverts, and the pages, page shapes and + comment thread positions their changes touch." + [{:keys [files compensate]}] + (let [commits (into (vec compensate) (mapcat (comp :reverted val)) files) + changes (into [] (mapcat :redo-changes) commits)] + {:file-ids (into (set (keys files)) (map :file-id) compensate) + :page-ids (into #{} (keep change-page-id) changes) + :shape-ids (into #{} (mapcat change-shape-ids) changes) + :comment-thread-ids (into #{} + (comp (filter #(= :set-comment-thread-position (:type %))) + (map :comment-thread-id)) + changes)})) + +(defn- drop-commits + "Removes the commits in `ids` from the queue and the buffer; an entry left + empty leaves the queue." + [pstate ids] + (let [dropped? (comp ids :id) + trim (fn [id] + [id (update (get-in pstate [:index id]) :commits + #(into [] (remove dropped?) %))]) + trimmed (into {} + (comp (filter #(some dropped? (get-in pstate [:index % :commits]))) + (map trim)) + (:queue pstate)) + emptied (into #{} (comp (filter (comp empty? :commits val)) (map key)) trimmed)] + (-> pstate + (update :queue #(into (empty %) (remove emptied) %)) + (update :index #(apply dissoc (merge % trimmed) emptied)) + (update :buffer #(into [] (remove dropped?) %))))) + +(defn without-rolled-back + "Removes from `pstate` the commits `plan` reverts or compensates. The + in-flight `entry` keeps only the commits that stay saved." + [pstate {:keys [id file-id commits]} {:keys [files compensate]}] + (let [head (into #{} (map :id) commits) + reverted (into #{} (comp (mapcat (comp :reverted val)) (map :id)) files) + compensated (into #{} (map :id) compensate) + unsaved (into reverted compensated)] + (as-> pstate $ + (drop-commits $ (into #{} (remove head) reverted)) + (d/update-in-when $ [:index id :commits] + (fn [commits] + (into [] (remove (comp unsaved :id)) commits))) + (update $ :saved (fn [saved] (into [] (remove (comp compensated :id)) saved))) + (update $ :remote-changes dissoc file-id) + (reduce prune-remote-changes $ (disj (set (keys files)) file-id))))) + +(defn compensation-changes + "Changes that undo the saved `commits` of one file, newest first." + [commits] + {:redo-changes (into [] (mapcat :undo-changes) (reverse commits)) + :undo-changes (into [] (mapcat :redo-changes) commits)}) diff --git a/frontend/src/app/main/data/persistence/lock.cljs b/frontend/src/app/main/data/persistence/lock.cljs new file mode 100644 index 0000000000..95e7ddba92 --- /dev/null +++ b/frontend/src/app/main/data/persistence/lock.cljs @@ -0,0 +1,105 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.main.data.persistence.lock + "Read-only lock of the workspace while saves fail." + (:require + [app.common.data.macros :as dm] + [app.main.data.workspace.edition :as dwe] + [app.util.mouse :as mse] + [beicon.v2.core :as rx] + [potok.v2.core :as ptk])) + +(defn failing? + "True while saves fail: the queue is halted, or an attempt of the slow + cycle is in flight (status :saving until it lands or fails again)." + [pstate] + (or (= :error (:status pstate)) + (true? (:recovering pstate)))) + +(defn locked? + "True while the workspace is read-only because saves fail." + [state] + (some? (dm/get-in state [:workspace-global :persistence-lock]))) + +(defn cancel-gestures? + "True for the event that cancels the gestures in progress when saves + start failing; it comes before the `:interrupt` that stops them, so a + drag puts its shapes back instead of committing its partial move." + [event] + (ptk/type? ::cancel-gestures event)) + +(defn gesture-stopper + "Stops a drag as `mse/drag-stopper` does, and also when the gestures in + progress are cancelled because saves fail; `cancelled?` turns true + before that stop reaches the drag. A drag that sees it ends without + committing anything." + ([stream cancelled?] + (gesture-stopper stream cancelled? nil)) + ([stream cancelled? opts] + (rx/merge (mse/drag-stopper stream opts) + (->> stream + (rx/filter cancel-gestures?) + (rx/tap (fn [_] (vreset! cancelled? true))))))) + +(defn- lock-on-failure + "Puts the workspace in read-only mode and keeps the read-only value it + had, which `unlock-after-failure` brings back. Gestures in progress are + cancelled, so they commit nothing." + [] + (ptk/reify ::lock-on-failure + ptk/UpdateEvent + (update [_ state] + (if (dm/get-in state [:workspace-global :persistence-lock]) + state + (update state :workspace-global + (fn [wglobal] + (assoc wglobal + :persistence-lock {:read-only? (true? (:read-only? wglobal))} + :read-only? true))))) + + ptk/WatchEvent + (watch [_ _ _] + (rx/of (ptk/data-event ::cancel-gestures) + :interrupt + (dwe/clear-edition-mode))))) + +(defn- unlock-after-failure + "Brings back the read-only value the workspace had before saves failed. + While the render context is lost the workspace stays read-only and the + value goes to the one `context-restored` brings back." + [] + (ptk/reify ::unlock-after-failure + ptk/UpdateEvent + (update [_ state] + (if-let [lock (dm/get-in state [:workspace-global :persistence-lock])] + (let [read-only? (:read-only? lock)] + (if (dm/get-in state [:render-state :lost]) + (-> state + (update :workspace-global dissoc :persistence-lock) + (assoc-in [:render-state :pre-context-lost-read-only?] read-only?)) + (update state :workspace-global + (fn [wglobal] + (-> wglobal + (dissoc :persistence-lock) + (assoc :read-only? read-only?)))))) + state)))) + +(defn sync-failure-lock + "Locks the workspace when saves start failing and unlocks it when they + recover. A user who cannot edit is never locked: the workspace is + already read-only for them, and the failure toast says why." + [] + (ptk/reify ::sync-failure-lock + ptk/WatchEvent + (watch [_ state _] + (let [lock? (and (failing? (:persistence state)) + (true? (dm/get-in state [:permissions :can-edit]))) + locked? (some? (dm/get-in state [:workspace-global :persistence-lock]))] + (cond + (and lock? (not locked?)) (rx/of (lock-on-failure)) + (and locked? (not lock?)) (rx/of (unlock-after-failure)) + :else (rx/empty)))))) diff --git a/frontend/src/app/main/data/plugins.cljs b/frontend/src/app/main/data/plugins.cljs index 33d898cf69..e9b15e0849 100644 --- a/frontend/src/app/main/data/plugins.cljs +++ b/frontend/src/app/main/data/plugins.cljs @@ -58,7 +58,7 @@ (update-in state [:workspace-local :open-plugins] (fnil disj #{}) id)))) (defn start-plugin! - [{:keys [plugin-id name version description host code permissions allow-background]} ^js extensions] + [{:keys [plugin-id name version description host code permissions allow-background scope]} ^js extensions] (let [load-plugin (unchecked-get ug/global "ɵloadPlugin")] (if (fn? load-plugin) (-> (load-plugin @@ -69,6 +69,7 @@ :host host :code code :allowBackground (boolean allow-background) + :scope scope :permissions (apply array permissions)} nil extensions) @@ -82,7 +83,7 @@ :action "start-plugin!")))) (defn- load-plugin! - [{:keys [plugin-id name version description host code icon permissions]}] + [{:keys [plugin-id name version description host code icon permissions scope]}] (st/emit! (pflag/initialize plugin-id version) (save-current-plugin plugin-id)) @@ -96,6 +97,7 @@ :host host :code code :icon icon + :scope scope :permissions (apply array permissions)} (fn [] (st/emit! (remove-current-plugin plugin-id)))) diff --git a/frontend/src/app/main/data/style_dictionary.cljs b/frontend/src/app/main/data/style_dictionary.cljs index 9b37274b5f..8021298f2c 100644 --- a/frontend/src/app/main/data/style_dictionary.cljs +++ b/frontend/src/app/main/data/style_dictionary.cljs @@ -10,7 +10,6 @@ ["style-dictionary$default" :as sd] [app.common.files.tokens :as cfo] [app.common.logging :as l] - [app.common.schema :as sm] [app.common.time :as ct] [app.common.types.token :as cto] [app.common.types.tokens-lib :as ctob] @@ -86,8 +85,7 @@ (let [number? (or (number? value) (numeric-string? value)) parsed-value (cfo/parse-token-value value) - out-of-bounds (or (>= (:value parsed-value) sm/max-safe-int) - (<= (:value parsed-value) sm/min-safe-int))] + out-of-bounds (cto/number-out-of-bounds? (:value parsed-value))] (cond (and parsed-value (not out-of-bounds) number?) @@ -112,8 +110,7 @@ If the `value` is not parseable and/or has missing references returns a map with `:errors`." [value] (let [parsed-value (cfo/parse-token-value value) - out-of-bounds (or (>= (:value parsed-value) sm/max-safe-int) - (<= (:value parsed-value) sm/min-safe-int))] + out-of-bounds (cto/number-out-of-bounds? (:value parsed-value))] (if (and parsed-value (not out-of-bounds)) parsed-value (if out-of-bounds @@ -185,7 +182,7 @@ If the `value` is not parseable and/or has missing references returns a map with `:errors`." [value] (let [normalized-value (str/lower (str/trim value)) - valid? (contains? #{"none" "uppercase" "lowercase" "capitalize"} normalized-value) + valid? (contains? cto/text-case-values normalized-value) references (seq (cto/find-token-value-references value))] (cond valid? diff --git a/frontend/src/app/main/data/team.cljs b/frontend/src/app/main/data/team.cljs index 081f41102d..e8afbfc994 100644 --- a/frontend/src/app/main/data/team.cljs +++ b/frontend/src/app/main/data/team.cljs @@ -124,8 +124,7 @@ in-organization? (and (contains? cf/flags :admin-console) organization) can-create? (if in-organization? (cto/allowed? :create-team - {:organization-perms {:owner-id (:owner-id organization) - :permissions (:permissions organization)} + {:organization-perms organization :profile-id profile-id :team-perms (:permissions team)}) true)] @@ -150,8 +149,7 @@ in-organization? (and (contains? cf/flags :admin-console) organization) can-delete? (if in-organization? (cto/allowed? :delete-team - {:organization-perms {:owner-id (:owner-id organization) - :permissions (:permissions organization)} + {:organization-perms organization :profile-id profile-id :team-perms (:permissions team)}) (boolean (dm/get-in team [:permissions :is-owner]))) diff --git a/frontend/src/app/main/data/tokenscript.cljs b/frontend/src/app/main/data/tokenscript.cljs index 22e07efc66..9a5a3a6b74 100644 --- a/frontend/src/app/main/data/tokenscript.cljs +++ b/frontend/src/app/main/data/tokenscript.cljs @@ -1,170 +1,360 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + (ns app.main.data.tokenscript + "Resolves the workspace tokens with the tokenscript interpreter + (`tokenscript.core`, from penpot/tokenscript-interpreter). + + `resolve-tokens` takes the usual map of token name to token and returns + the same map with `:resolved-value` (a value ready for shape attributes: + numbers in pixels, colors as hex strings, composites as maps), + `:resolved-symbol` (the tokenscript value, see + `tokenscript.symbols`) and `:errors` (Penpot error maps, see + `app.main.data.workspace.tokens.errors`) added to each token." (:require - ["@penpot/tokenscript" :refer [BaseSymbolType - ColorSymbol - ListSymbol NumberSymbol - NumberWithUnitSymbol - ProcessorError - processTokens - TokenSymbol - makeConfig]] + [app.common.data :as d] [app.common.logging :as l] [app.common.time :as ct] - [app.main.data.workspace.tokens.errors :as wte])) + [app.common.types.token :as cto] + [app.main.data.workspace.tokens.errors :as wte] + [cuerdas.core :as str] + [tokenscript.core :as tsc] + [tokenscript.errors :as tse] + [tokenscript.symbols :as sym])) -(l/set-level! :debug) +;; Value conversion ----------------------------------------------------------- -;; Config ---------------------------------------------------------------------- +(defn- color->hex-string + "Hex string of a color symbol (`#rrggbbaa` when it has alpha)." + [color] + (sym/to-string (tsc/color->hex color))) -(def config (makeConfig)) +(defn- symbol->value + "Converts a resolved tokenscript value into the shape used by shape + attributes and the token forms: -;; Class predicates ------------------------------------------------------------ -;; Predicates to get information about the tokenscript interpreter symbol type -;; Or to determine the error + - colors: hex strings (alpha as `#rrggbbaa`) + - `rem` values: pixels (16px root font size), `%` values: fractions + - other numbers with unit: the number + - structured tokens (typography, shadows): maps with keyword keys and + vectors + - null: nil" + [v] + (let [entries->map (fn [entries] + (into {} (map (fn [[k v]] [(keyword k) (symbol->value v)])) entries))] + (cond + (nil? v) nil + (sym/null? v) nil + (sym/token? v) (let [content (sym/value v)] + (if (map? content) + (entries->map content) + (mapv symbol->value content))) + (sym/list? v) (mapv symbol->value (sym/value v)) + (sym/dictionary? v) (entries->map (sym/value v)) + (sym/color? v) (color->hex-string v) + (sym/number-with-unit? v) (let [n (sym/value v)] + (case (:unit v) + "rem" (* n 16) + "%" (/ n 100) + n)) + (sym/symbol? v) (sym/value v) + :else v))) -(defn tokenscript-symbol? [v] - (instance? BaseSymbolType v)) +;; Value validation ----------------------------------------------------------- +;; +;; Tokenscript resolves any expression; these are the Penpot level checks +;; on the resolved value of each token type (ranges, allowed keywords, +;; composite shapes) that decide whether a token can be applied. -(defn structured-token? [v] - (instance? TokenSymbol v)) +(defn- words->string + "Values like `700 Italic` resolve to a list of words; font weights are + strings." + [v] + (if (sequential? v) (str/join " " (map str v)) v)) -(defn structured-record-token? [^js v] - (and (structured-token? v) (instance? js/Map (.-value v)))) - -(defn structured-array-token? [^js v] - (and (structured-token? v) (instance? js/Array (.-value v)))) - -(defn number-with-unit-symbol? [v] - (instance? NumberWithUnitSymbol v)) - -(defn number-symbol? [v] - (instance? NumberSymbol v)) - -(defn list-symbol? [v] - (instance? ListSymbol v)) - -(defn color-symbol? [v] - (instance? ColorSymbol v)) - -(defn processor-error? [err] - (instance? ProcessorError err)) - -;; Conversion Tools ------------------------------------------------------------ -;; Helpers to convert tokenscript symbols to penpot accepted formats - -(defn color-symbol->hex-string [^js v] - (when (color-symbol? v) - (.toString (.to v "hex")))) - -(defn color-alpha [^js v] - (if (.isHex v) - 1 - (or (.getAttribute v "alpha") 1))) - -(defn color-symbol->penpot-color [^js v] - {:color (color-symbol->hex-string v) - :opacity (color-alpha v)}) - -(defn rem-number-with-unit? [v] - (and (number-with-unit-symbol? v) - (= (.-unit v) "rem"))) - -(defn percent-number-with-unit? [v] - (and (number-with-unit-symbol? v) - (= (.-unit v) "%"))) - -(defn rem->px [^js v] - (* (.-value v) 16)) - -(declare tokenscript-symbols->penpot-unit) - -(defn structured-token->penpot-map - "Converts structured token (record or array) to penpot map format. - Structured tokens are non-primitive token types like `typography` or `box-shadow`." - [^js token-symbol] - (if (instance? js/Array (.-value token-symbol)) - (mapv tokenscript-symbols->penpot-unit (.-value token-symbol)) - (let [entries (es6-iterator-seq (.entries (.-value token-symbol)))] - (into {} (map (fn [[k v :as V]] - [(keyword k) (tokenscript-symbols->penpot-unit v)]) - entries))))) - -(defn tokenscript-symbols->penpot-unit [^js v] +(defn- font-family->vector + "Font families are always a flat vector of family names, also when the + value is a single name or references a font family token." + [v] (cond - (nil? v) nil - (structured-token? v) (structured-token->penpot-map v) - (list-symbol? v) (structured-token->penpot-map v) - (color-symbol? v) (.-value (.to v "hex")) - (rem-number-with-unit? v) (rem->px v) - (percent-number-with-unit? v) (/ (.-value v) 100) - :else (.-value v))) + (string? v) [v] + (sequential? v) (vec (flatten v)) + :else v)) -;; Processors ------------------------------------------------------------------ -;; The processor resolves tokens -;; resolved/error tokens get put back into a clojure structure directly during build time -;; For updating tokens we use the `TokenResolver` crud methods from the processing result -;; The `TokenResolver` has detailed information for each tokens dependency graph +(defn- line-height->ratio + "Line heights in pixels are stored relative to the font size. Returns + the ratio or an error when the font size is not available." + [line-height font-size] + (if (and (sym/number-with-unit? line-height) (= "px" (:unit line-height))) + (if (number? font-size) + {:value (/ (sym/value line-height) font-size)} + {:error (wte/error-with-value :error.style-dictionary/composite-line-height-needs-font-size font-size)}) + {:value (symbol->value line-height)})) -(defn create-token-builder - "Collects resolved tokens during build time into a clojure structure. - Returns Tokenscript Symbols in `:resolved-value` key." - [tokens] - (let [output (volatile! tokens) +(defn- typography->value + "Converts a resolved typography composite, adjusting the fields the + text attributes expect. With `line-height-ratio` a line height in + pixels is converted relative to the font size." + [s line-height-ratio] + (let [value (symbol->value s)] + (if-not (map? value) + {:value value} + (let [value (cond-> value + (contains? value :font-family) (update :font-family font-family->vector) + (contains? value :font-weight) (update :font-weight words->string)) + line-height (when (and line-height-ratio (sym/token? s)) + (get (sym/value s) "line-height"))] + (if (some? line-height) + (let [{ratio :value error :error} (line-height->ratio line-height (:font-size value))] + (if error + {:error error} + {:value (assoc value :line-height ratio)})) + {:value value}))))) - ;; When a token is resolved (No parsing / reference errors) we assing `:resolved-value` for the original token - on-resolve - (fn [^js/String token-name ^js/Symbol resolved-symbol] - (vswap! output assoc-in [token-name :resolved-value] resolved-symbol)) +(defn- convert-value + "Converts the resolved symbol of a token to the value shape expected by + its type. Returns `{:value v}` or `{:error e}`. Options: - ;; When a token contains any errors we assing `:errors` for the original token - on-error - (fn [^js/String token-name ^js/Error _error ^js/String _original-value] - (let [value (get tokens token-name) - default-error [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]] - (vswap! output assoc-in [token-name :errors] default-error))) + :line-height-ratio convert typography line heights in pixels to a + ratio of the font size (default true)" + ([type s] (convert-value type s nil)) + ([type s {:keys [line-height-ratio] :or {line-height-ratio true}}] + (case type + :typography (typography->value s line-height-ratio) + :font-family {:value (font-family->vector (symbol->value s))} + :font-weight {:value (words->string (symbol->value s))} + {:value (symbol->value s)}))) - ;; Extract the atom value - get-result - (fn [] @output)] - #js {:onResolve on-resolve - :onError on-error - :getResult get-result})) +(defn- convert-token-value + "`convert-value` for a token: a failing conversion (e.g. a color that + cannot be written as hex) is an error of that token, not of the whole + resolution." + [{:keys [type value]} s] + (try + (convert-value type s) + (catch :default _ + {:error (wte/error-with-value :error.style-dictionary/invalid-token-value value)}))) -(defn clj->token->tokenscript-token - "Convert penpot token into a format that tokenscript can handle." +(defn plugin-value + "Resolved value of a token as exposed by the plugin API: like + `:resolved-value` but with typography line heights left as resolved." + [{:keys [type resolved-symbol]}] + (when (some? resolved-symbol) + (:value (convert-value type resolved-symbol {:line-height-ratio false})))) + +(defn- validate-value + "Returns a Penpot error for the resolved `value` of `token`, or nil when + it is acceptable. The interpreter validates the kind of value of each + type; these are the Penpot specific limits on top." + [{:keys [type]} value] + (if (and (number? value) (cto/number-out-of-bounds? value)) + (wte/error-with-value :error.token/number-too-large value) + (case type + :text-decoration + (when-not (and (string? value) (cto/valid-text-decoration value)) + (wte/error-with-value :error.style-dictionary/invalid-token-value-text-decoration value)) + + :font-weight + (when-not (cto/valid-font-weight-variant (str value)) + (wte/error-with-value :error.style-dictionary/invalid-token-value-font-weight value)) + + :font-family + (when-not (or (string? value) (and (sequential? value) (every? string? value))) + (wte/error-with-value :error.style-dictionary/invalid-token-value-font-family value)) + + nil))) + +;; Errors --------------------------------------------------------------------- + +(defn- missing-reference-of + "Missing token name behind an issue, if the root cause is a missing + token." + [{:keys [code data]}] + (case code + :processor/token-not-found (:token-name data) + :interpreter/unknown-reference (:reference data) + :processor/dependency-error (let [cause (:cause data)] + (when (= :processor/token-not-found (:code cause)) + (get-in cause [:data :token-name]))) + nil)) + +(defn- circular? + [{:keys [code data]}] + (or (= :processor/circular-dependency code) + (= :processor/circular-dependency (get-in data [:cause :code])))) + +(defn- issue->error + "Maps a tokenscript issue to a Penpot token error. The interpreter only + reports codes and data; the messages are Penpot's. Codes without a + message of their own are an invalid value." + [{:keys [code data] :as issue} value] + (cond + (circular? issue) + (wte/error-with-value :error.token/circular-reference nil) + + (missing-reference-of issue) + (wte/error-with-value :error.style-dictionary/missing-reference [(missing-reference-of issue)]) + + :else + (case code + (:operations/division-by-zero :functions/division-by-zero) + (wte/error-with-value :error.token/division-by-zero nil) + + :operations/cannot-mix-units + (wte/error-with-value :error.token/incompatible-units (:units data)) + + :operations/cannot-compare-units + (wte/error-with-value :error.token/incompatible-units [(:unit-a data) (:unit-b data)]) + + :interpreter/unknown-function + (wte/error-with-value :error.token/unknown-function (:function-name data)) + + :processor/token-name-collision + (wte/error-with-value :error.token/name-collision (:token-name data)) + + (:functions/argument-out-of-range :functions/invalid-base) + (wte/error-with-value :error.token/function-argument-out-of-range (:function-name data)) + + (wte/error-with-value :error.style-dictionary/invalid-token-value value)))) + +(defn- validation-error + "Maps a failed token type validation of the interpreter (an issue with + an `INVALID__` code) to the Penpot error of the token's + type." + [{:keys [code]} {:keys [type value]}] + (case type + :opacity (wte/error-with-value :error.style-dictionary/invalid-token-value-opacity value) + :stroke-width (wte/error-with-value :error.style-dictionary/invalid-token-value-stroke-width value) + :text-case (wte/error-with-value :error.style-dictionary/invalid-token-value-text-case value) + :text-decoration (wte/error-with-value :error.style-dictionary/invalid-token-value-text-decoration value) + :font-weight (wte/error-with-value :error.style-dictionary/invalid-token-value-font-weight value) + :font-family (wte/error-with-value :error.style-dictionary/invalid-token-value-font-family value) + :typography (wte/error-with-value :error.style-dictionary/invalid-token-value-typography value) + :shadow (wte/error-with-value :error.style-dictionary/invalid-token-value-shadow value) + (if (= "INVALID_NUMBER_UNIT" code) + (wte/error-with-value :error.style-dictionary/value-with-units value) + (wte/error-with-value :error.style-dictionary/invalid-token-value value)))) + +(defn- token-errors + [issues token] + (->> issues + (keep (fn [issue] + (cond + (tse/error-map? issue) (issue->error issue (:value token)) + (= :tokenscript/issue (:type issue)) (validation-error issue token)))) + (seq))) + +;; Resolution ----------------------------------------------------------------- + +(defn- quote-font-family + "Quotes a font family name so tokenscript reads it as one string instead + of parsing its words (`Red` would become a color, `2P` a number and a + word). Entries with a token reference or already quoted are kept." + [family] + (cond + (or (not (string? family)) + (seq (cto/find-token-value-references family)) + (re-matches #"^(['\"]).*\1$" family)) + family + + (not (str/includes? family "\"")) + (str "\"" family "\"") + + (not (str/includes? family "'")) + (str "'" family "'") + + :else + family)) + +(defn- quote-font-families + [value] + (if (sequential? value) + (mapv quote-font-family value) + value)) + +(defn- token->input + "Token data for the interpreter. Types are passed by their DTCG names, + which the interpreter's bundled token types validate." [{:keys [type value]}] - #js {"$type" (name type) - "$value" (clj->js value)}) + (cond-> {:value (case type + :font-family (quote-font-families value) + :typography (cond-> value + (map? value) (d/update-when :font-family quote-font-families)) + value)} + (some? type) (assoc :type (get cto/token-type->dtcg-token-type type (name type))))) -(defn clj-tokens->tokenscript-tokens - "Convert penpot map of tokens into tokenscript map structure. - tokenscript accepts a map of [token-name {\"$type\": string, \"$value\": any}]" +(defn- empty-value? + [token] + (nil? (:value token))) + +(defn- resolved-token + "Adds the result of the interpreter to `token`: `:resolved-value` and + `:resolved-symbol` for the resolved symbol `s`, or `:errors` for its + `issues`, a failing conversion or a value Penpot doesn't accept." + [token issues s] + (if-let [errors (token-errors issues token)] + (assoc token :errors (vec errors)) + (let [{:keys [value error]} (convert-token-value token s)] + (if-let [error (or error (validate-value token value))] + (assoc token :errors [error]) + (assoc token + :resolved-value value + :resolved-symbol s))))) + +(defn- resolve-tokens* [tokens] - (let [token-map (js/Map.)] - (doseq [[k token] tokens] - (.set token-map k (clj->token->tokenscript-token token))) - token-map)) + (let [tpoint (ct/tpoint-ms) + inputs (into {} + (comp (remove (fn [[_ token]] (empty-value? token))) + (map (fn [[name token]] [name (token->input token)]))) + tokens) + result (tsc/build inputs) + resolved (:tokens result) + issues (:issues result) + output (reduce-kv + (fn [acc name token] + (assoc acc name + (if (empty-value? token) + (assoc token :errors [(wte/get-error-code :error.token/empty-input)]) + (resolved-token token (get issues name) (get resolved name))))) + {} + tokens)] + (l/dbg :hint "tokenscript/resolve-tokens" :elapsed (tpoint)) + output)) -(defn process-tokens - "Builds tokens using `tokenscript`." +;; Several components resolve the same token maps on every change (the +;; sidebar, the forms and the propagation each compute the active tokens +;; separately), so the results of the last few maps are kept. Entries are +;; matched by identity first and by equality second: comparing two maps +;; of a few hundred tokens is far cheaper than resolving them again. +(def ^:private cache-size 8) + +(defonce ^:private results-cache (volatile! [])) + +(defn- cached-result [tokens] - (let [input (clj-tokens->tokenscript-tokens tokens) - result (processTokens input #js {:config config - :builder (create-token-builder tokens)})] - result)) + (let [entries @results-cache] + (or (some (fn [[t result]] (when (identical? t tokens) result)) entries) + (some (fn [[t result]] (when (= t tokens) result)) entries)))) -(defn update-token +(defn- remember-result + [tokens result] + (vswap! results-cache (fn [entries] (vec (take cache-size (cons [tokens result] entries)))))) + +(defn resolve-tokens + "Resolves `tokens` (a map of token name to token). Returns the tokens + with `:resolved-value` or `:errors` added." + [tokens] + (or (cached-result tokens) + (let [result (resolve-tokens* tokens)] + (remember-result tokens result) + result))) + +(defn resolve-token + "Resolves `token` against `tokens` (the token replaces any token with + the same name). Returns the resolved token." [tokens token] - (let [result (process-tokens tokens) - resolver (.-resolver result)] - (.updateToken resolver #js {:tokenPath (:name token) - :tokenData (clj->token->tokenscript-token token)}))) - -;; Main ------------------------------------------------------------------------ - -(defn resolve-tokens [tokens] - (let [tpoint (ct/tpoint-ms) - result (process-tokens tokens) - elapsed (tpoint)] - (l/dbg :hint "tokenscript/resolve-tokens" :elapsed elapsed) - (.-output result))) + (get (resolve-tokens (assoc tokens (:name token) token)) (:name token))) diff --git a/frontend/src/app/main/data/uploads.cljs b/frontend/src/app/main/data/uploads.cljs index 5ae47c477a..f18956bd7f 100644 --- a/frontend/src/app/main/data/uploads.cljs +++ b/frontend/src/app/main/data/uploads.cljs @@ -11,7 +11,7 @@ by any feature that needs to upload large binary blobs: 1. create-upload-session – obtain a session-id - 2. upload-chunk – upload each slice (max-parallel-chunk-uploads in-flight) + 2. upload-chunk – upload each slice, one at a time 3. caller-specific step – e.g. assemble-file-media-object or import-binfile `upload-blob-chunked` drives steps 1 and 2 and emits the completed @@ -24,18 +24,13 @@ [app.main.repo :as rp] [beicon.v2.core :as rx])) -(def ^:private max-parallel-chunk-uploads - "Maximum number of chunk upload requests that may be in-flight at the - same time within a single chunked upload session." - 2) - (defn upload-blob-chunked "Uploads `blob` via the three-step chunked session API. Steps performed: 1. Creates an upload session (`create-upload-session`). - 2. Slices `blob` and uploads every chunk (`upload-chunk`), - with at most `max-parallel-chunk-uploads` concurrent requests. + 2. Slices `blob` and uploads every chunk (`upload-chunk`), one at a + time so the progress of the file is a straight line. Returns an observable that emits exactly one map: `{:session-id }` @@ -43,8 +38,11 @@ The caller is responsible for the final step (assemble / import). The optional `opts` map accepts: - `:chunk-size` – size in bytes of each chunk (default: `cf/upload-chunk-size`, 25 MiB)." - [blob & {:keys [chunk-size] :or {chunk-size cf/upload-chunk-size}}] + `:chunk-size` – size in bytes of each chunk (default: `cf/upload-chunk-size`, 25 MiB). + `:on-progress` – a fn called after every uploaded chunk with + `{:current :total }`, so the + caller can show the upload while it runs." + [blob & {:keys [chunk-size on-progress] :or {chunk-size cf/upload-chunk-size}}] (let [total-size (.-size blob) total-chunks (js/Math.ceil (/ total-size chunk-size))] (->> (rp/cmd! :create-upload-session @@ -53,17 +51,22 @@ (fn [{raw-session-id :session-id}] (let [session-id (cond-> raw-session-id (string? raw-session-id) uuid/uuid) + uploaded (atom 0) chunk-uploads (->> (range total-chunks) (map (fn [idx] (let [start (* idx chunk-size) end (min (+ start chunk-size) total-size) chunk (.slice blob start end)] - (rp/cmd! :upload-chunk - {:session-id session-id - :index idx - :content (list chunk (dm/str "chunk-" idx))})))))] + (->> (rp/cmd! :upload-chunk + {:session-id session-id + :index idx + :content (list chunk (dm/str "chunk-" idx))}) + (rx/tap (fn [_] + (when (fn? on-progress) + (on-progress {:current (swap! uploaded inc) + :total total-chunks})))))))))] (->> (rx/from chunk-uploads) - (rx/merge-all max-parallel-chunk-uploads) + (rx/concat-all) (rx/last) (rx/map (fn [_] {:session-id session-id}))))))))) diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index 683bbcf8b9..adc1d01265 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -24,6 +24,7 @@ [app.main.data.fonts :as df] [app.main.data.helpers :as dsh] [app.main.features :as features] + [app.main.features.pointer-map :as fpmap] [app.main.repo :as rp] [app.main.router :as rt] [app.render-wasm.api :as wasm.api] @@ -57,7 +58,7 @@ [:map {:title "initialize"} [:file-id ::sm/uuid] [:share-id {:optional true} [:maybe ::sm/uuid]] - [:page-id {:optional true} ::sm/uuid]]) + [:page-id {:optional true} [:maybe ::sm/uuid]]]) (defn initialize [{:keys [file-id share-id] :as params}] @@ -116,10 +117,17 @@ (def ^:private schema:fetch-bundle [:map {:title "fetch-bundle"} - [:page-id ::sm/uuid] + [:page-id {:optional true} ::sm/uuid] [:file-id ::sm/uuid] [:share-id {:optional true} ::sm/uuid]]) +(defn resolve-viewer-fragment + "Resolves one pointer-mapped entry of the view-only bundle into a + [key data] pair. Thin wrapper over the shared `fpmap/resolve-pointer` + with share-link scoping; see it for the failure contract." + [file-id share-id entry] + (fpmap/resolve-pointer file-id share-id entry)) + (defn- fetch-bundle [{:keys [file-id share-id] :as params}] @@ -140,14 +148,7 @@ (uuid? share-id) (assoc :share-id share-id)) - resolve (fn [[key pointer]] - (let [params {:file-id file-id :fragment-id @pointer} - params (cond-> params - (uuid? share-id) - (assoc :share-id share-id))] - (->> (rp/cmd! :get-file-fragment params) - (rx/map :data) - (rx/map #(vector key %)))))] + resolve (partial resolve-viewer-fragment file-id share-id)] (->> (rp/cmd! :get-view-only-bundle params') (rx/mapcat @@ -305,19 +306,27 @@ index (some-> (rt/get-query-param qparams :index) parse-long) frame-id (some-> (:frame-id qparams) uuid/parse) page-id (some-> (rt/get-query-param qparams :page-id) uuid/parse) - file-id (some-> (rt/get-query-param qparams :file-id) uuid/parse)] + file-id (some-> (rt/get-query-param qparams :file-id) uuid/parse) + first-page-id (-> file :data :pages first)] - (rx/merge - (rx/of (case (:zoom qparams) - "fit" zoom-to-fit - "fill" zoom-to-fill - nil)) - (rx/of - (update-page-position-data file-id page-id) - (cond - (some? frame-id) (go-to-frame frame-id) - (some? index) (go-to-frame-by-index index) - :else (go-to-frame-auto))))))))) + (if (and (nil? page-id) (some? first-page-id)) + ;; Every viewer event reads the page from the route, so a URL + ;; without page-id names the first page; the viewer then + ;; initializes again on the completed route. + (rx/of (rt/nav :viewer + (assoc (rt/get-params state) :page-id first-page-id) + {::rt/replace true})) + (rx/merge + (rx/of (case (:zoom qparams) + "fit" zoom-to-fit + "fill" zoom-to-fill + nil)) + (rx/of + (update-page-position-data file-id page-id) + (cond + (some? frame-id) (go-to-frame frame-id) + (some? index) (go-to-frame-by-index index) + :else (go-to-frame-auto)))))))))) (defn fetch-comment-threads [{:keys [file-id page-id share-id] :as params}] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 74fe1252be..7581866dc6 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -56,7 +56,6 @@ [app.main.data.workspace.layers :as dwly] [app.main.data.workspace.layout :as layout] [app.main.data.workspace.libraries :as dwl] - [app.main.data.workspace.mcp :as mcp] [app.main.data.workspace.notifications :as dwn] [app.main.data.workspace.pages :as dwpg] [app.main.data.workspace.path :as dwdp] @@ -78,7 +77,6 @@ [app.main.refs :as refs] [app.main.repo :as rp] [app.main.router :as rt] - [app.plugins.register :as preg] [app.render-wasm :as wasm] [app.render-wasm.api :as wasm.api] [app.util.dom :as dom] @@ -146,11 +144,11 @@ (log/inf :hint "resolve file" :file-id (str (:id file)) :features (str/join " " (:features file))) + ;; fpmap/resolve-file guarantees resolved data or an error; there is + ;; no leftover-pointer case to handle here. (->> (fpmap/resolve-file file) (rx/map :data) - (rx/map - (fn [data] - (assoc file :data (d/removem (comp t/pointer? val) data)))))) + (rx/map #(assoc file :data %)))) (defn- check-libraries-synchronization [file-id libraries] @@ -246,7 +244,9 @@ :save-undo? false :origin it :tags #{:position-data} - :skip-component-sync? true})) + :skip-component-sync? true + ;; Derived from the text, so it follows it while saves fail + :ignore-save-lock true})) (rx/empty))))))) (defn- workspace-initialized @@ -260,15 +260,8 @@ ptk/WatchEvent (watch [_ _ _] - (rx/merge - (rx/of (dp/check-open-plugin) - (fdf/fix-deleted-fonts-for-local-library file-id)) - (if (contains? cf/flags :mcp) - ;; We wait the plugin runtime to be ready before launch the - ;; mcp initialization - (->> (rx/from (preg/wait-for-runtime)) - (rx/map (fn [_] (mcp/init)))) - (rx/empty)))))) + (rx/of (dp/check-open-plugin) + (fdf/fix-deleted-fonts-for-local-library file-id))))) (defn- compute-shape-stats "Compute shape statistics in a single pass over pages-index. @@ -351,7 +344,8 @@ (let [file (restore-pending-changes state file)] (-> state (assoc :thumbnails (d/update-vals thumbnails (fn [uri] {:uri uri :rendered-at nil}))) - (update :files assoc file-id file)))))) + (update :files assoc file-id file) + (dps/forget-other-versions file)))))) (defn zoom-to-frame [] @@ -426,7 +420,10 @@ ptk/WatchEvent (watch [_ state stream] - (let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream) + (let [;; A reload starts a new `initialize-workspace`, which replaces these streams + stoper-s (rx/filter #(or (ptk/type? ::finalize-workspace %) + (ptk/type? ::initialize-workspace %)) + stream) rparams (rt/get-params state) features (features/get-enabled-features state team-id) render-wasm-enabled? (features/active-feature? state "render-wasm/v1") @@ -586,7 +583,9 @@ {:redo-changes changes :undo-changes [] :save-undo? false :tags #{:position-data} - :skip-component-sync? true}))))) + :skip-component-sync? true + ;; Derived from commits already made, rollbacks included + :ignore-save-lock true}))))) (rx/take-until stoper-s))) (->> stream @@ -626,12 +625,14 @@ :workspace-tokens :workspace-undo :workspace-versions) - (update :workspace-global dissoc :read-only? :preview-id :default-font) + (update :workspace-global dissoc :read-only? :preview-id :default-font + :persistence-lock) (assoc-in [:workspace-global :options-mode] :design) (update :files d/update-vals #(dissoc % :data)))) ptk/WatchEvent (watch [_ state _] + (t/clear-uuid-cache) (let [project-id (:current-project-id state)] (rx/of (dwn/finalize file-id) (dpj/finalize-project project-id) diff --git a/frontend/src/app/main/data/workspace/clipboard.cljs b/frontend/src/app/main/data/workspace/clipboard.cljs index d763e48e2e..877e535f75 100644 --- a/frontend/src/app/main/data/workspace/clipboard.cljs +++ b/frontend/src/app/main/data/workspace/clipboard.cljs @@ -53,12 +53,15 @@ [app.main.router :as rt] [app.main.store :as st] [app.main.streams :as ms] + [app.render-wasm.text-paste :as text-paste] [app.util.clipboard :as clipboard] [app.util.code-gen.markup-svg :as svg] + [app.util.code-gen.markup-svg-wasm :as svg.wasm] [app.util.code-gen.style-css :as css] [app.util.globals :as ug] [app.util.http :as http] [app.util.i18n :as i18n :refer [tr]] + [app.util.text.clipboard :as text-clipboard] [app.util.text.content :as tc] [app.util.webapi :as wapi] [beicon.v2.core :as rx] @@ -218,13 +221,18 @@ file (dsh/lookup-file state file-id) version (get file :version) - initial {:type :copied-shapes - :features features - :version version - :file-id file-id - :selected selected - :objects {} - :images #{}} + geometry (dwtxt/copied-text-geometry state objects selected) + + initial (cond-> {:type :copied-shapes + :features features + :version version + :file-id file-id + :team-id (:current-team-id state) + :selected selected + :objects {} + :images #{}} + (some? geometry) + (assoc :text-geometry geometry)) shapes (->> (cfh/selected-with-children objects selected) (keep (d/getf objects)))] @@ -272,9 +280,15 @@ (declare ^:private paste-svg-text) (declare ^:private paste-shapes) -(def ^:private default-options +(defn- v3-html-paste? + [state] + (and (features/active-feature? state "text-editor-wasm/v1") + (features/active-feature? state "text-editor-wasm/v1-html-paste"))) + +(defn- clipboard-options + [state] #js {:decodeTransit t/decode-str - :allowHTMLPaste (features/active-feature? @st/state "text-editor/v2-html-paste")}) + :allowHTMLPaste (v3-html-paste? state)}) (defn- create-paste-from-blob [in-viewport? replace?] @@ -339,7 +353,7 @@ ptk/WatchEvent (watch [_ state _] (if (page-ready? (dsh/lookup-page-objects state)) - (->> (clipboard/from-navigator default-options) + (->> (clipboard/from-navigator (clipboard-options state)) (rx/mapcat (create-paste-from-blob false (boolean replace?))) (rx/take 1) (rx/catch on-clipboard-permission-error)) @@ -360,7 +374,7 @@ ;; Pastes arriving before the page is loaded are ignored as well. (if (or is-editing? (not (page-ready? objects))) (rx/empty) - (->> (clipboard/from-synthetic-clipboard-event event default-options) + (->> (clipboard/from-synthetic-clipboard-event event (clipboard-options state)) (rx/mapcat (create-paste-from-blob in-viewport? false)))))))) (defn copy-selected-svg @@ -379,7 +393,10 @@ (gsh/translate-to-frame % (get objects parent-frame-id))) shapes (mapv maybe-translate selected) - svg-formatted (svg/generate-formatted-markup objects shapes)] + ;; With render-wasm active, never call classic SVG codegen. + svg-formatted (if (features/active-feature? state "render-wasm/v1") + (svg.wasm/get-formatted-svg-markup (mapv :id shapes)) + (svg/generate-formatted-markup objects shapes))] (-> (clipboard/to-clipboard-multi {"image/svg+xml" svg-formatted "text/plain" svg-formatted}) @@ -540,7 +557,7 @@ (rx/empty)))))] (if (page-ready? (dsh/lookup-page-objects state)) - (->> (clipboard/from-navigator default-options) + (->> (clipboard/from-navigator (clipboard-options state)) (rx/mapcat #(.text %)) (rx/map decode-entry) (rx/take 1) @@ -581,10 +598,12 @@ [:features ::sm/set-of-strings] [:version :int] [:file-id ::sm/uuid] + [:team-id {:optional true} ::sm/uuid] [:selected ::sm/set-of-uuid] [:objects [:map-of ::sm/uuid :map]] [:images [:set :map]] + [:text-geometry {:optional true} [:map-of ::sm/uuid :map]] [:position {:optional true} ::gpt/point]]) (def ^:private @@ -886,7 +905,7 @@ (assoc change :index (get map-ids (:old-id change))) change))) - (process-shape [valid-file-ids frame-id parent-id shape] + (process-shape [valid-file-ids other-team? frame-id parent-id shape] (cond-> shape :always (assoc :frame-id frame-id :parent-id parent-id) @@ -897,7 +916,11 @@ (assoc :shapes []) (cfh/text-shape? shape) - (ctt/remove-external-typographies valid-file-ids)))] + (ctt/remove-external-typographies valid-file-ids) + + ;; Custom font ids differ between teams. + (and other-team? (cfh/text-shape? shape)) + (update :content text-clipboard/replace-custom-fonts)))] (ptk/reify ::paste-shapes ptk/WatchEvent @@ -950,7 +973,11 @@ valid-file-ids (conj (set (keys libraries)) file-id) - objects (update-vals objects (partial process-shape valid-file-ids frame-id parent-id)) + ;; Shapes copied before the team was recorded count as same team. + other-team? (and (some? (:team-id pdata)) + (not= (:team-id pdata) (:current-team-id state))) + + objects (update-vals objects (partial process-shape valid-file-ids other-team? frame-id parent-id)) all-objects (merge page-objects objects) @@ -1068,11 +1095,21 @@ (ptk/reify ::paste-html-text ptk/WatchEvent (watch [_ state _] - (let [style (deref refs/workspace-clipboard-style) - root (dwtxt/create-root-from-html html style (features/active-feature? @st/state "text-editor/v2-html-paste")) - text (.-textContent root) - content (tc/dom->cljs root)] - (when (types.text/valid-content? content) + (let [payload (when (v3-html-paste? state) + (text-clipboard/html->payload html)) + [text content] + (if (v3-html-paste? state) + (if (some? payload) + (let [content (dwtxt/payload-content state payload)] + [(txt/content->text content) content]) + (when-let [fragment (some-> (text-clipboard/html->fragment html) + (text-clipboard/without-overrides))] + [(text-paste/fragment->text fragment) + (text-paste/fragment->content fragment (txt/get-default-text-attrs))])) + (let [style (deref refs/workspace-clipboard-style) + root (dwtxt/create-root-from-html html style (features/active-feature? @st/state "text-editor/v2-html-paste"))] + [(.-textContent root) (tc/dom->cljs root)]))] + (when (and (some? content) (types.text/valid-content? content)) (let [id (uuid/next) width (max 8 (min (* 7 (count text)) 700)) height 16 @@ -1094,7 +1131,8 @@ (dwsh/create-and-add-shape :text x y shape (when skip-edition? {:skip-edition? true}))) (if skip-edition? - (rx/of (dwwt/resize-wasm-text-debounce id {:undo-group id + (rx/of (dwtxt/move-new-text-fills id payload) + (dwwt/resize-wasm-text-debounce id {:undo-group id :undo-id undo-id})) (rx/of (dwu/commit-undo-transaction undo-id)))))))))) diff --git a/frontend/src/app/main/data/workspace/colors.cljs b/frontend/src/app/main/data/workspace/colors.cljs index 3bbc099711..9150e651a5 100644 --- a/frontend/src/app/main/data/workspace/colors.cljs +++ b/frontend/src/app/main/data/workspace/colors.cljs @@ -1348,6 +1348,43 @@ :shape-id (:shape-id fill) :index (:index fill)}))) +(defn shape-colors + [shape file-id libraries] + (let [applied-tokens (:applied-tokens shape) + applied-fill (get applied-tokens :fill) + applied-stroke (get applied-tokens :stroke-color) + fills (:fills shape) + strokes (:strokes shape) + shadows (:shadow shape) + shape-id (:id shape) + + fills* (map-indexed + (fn [index fill] + (cond-> (assoc fill :shape-id shape-id :index index) + (and (zero? index) applied-fill) + (assoc :has-token-applied true + :token-name applied-fill))) + fills) + + strokes* (map-indexed + (fn [index stroke] + (cond-> (assoc stroke :shape-id shape-id :index index) + (and (zero? index) applied-stroke) + (assoc :has-token-applied true + :token-name applied-stroke))) + strokes) + + shadows* (map-indexed #(assoc %2 :shape-id shape-id :index %1) shadows)] + (if (= :text (:type shape)) + (-> [] + (into (keep #(stroke->color-att % file-id libraries)) strokes*) + (into (map #(shadow->color-attr % file-id libraries)) shadows*) + (into (extract-text-colors shape file-id libraries))) + (-> [] + (into (keep #(fill->color-att % file-id libraries)) fills*) + (into (keep #(stroke->color-att % file-id libraries)) strokes*) + (into (map #(shadow->color-attr % file-id libraries)) shadows*))))) + (defn extract-all-colors "Extracts color information from a list of shapes, including fills, strokes, and shadows. If a shape has applied tokens of type :fill or :stroke-color, the first fill or stroke @@ -1363,41 +1400,4 @@ Returns: A vector of color attribute maps with metadata for each shape." [shapes file-id libraries] - (reduce - (fn [result shape] - (let [applied-tokens (:applied-tokens shape) - applied-fill (get applied-tokens :fill) - applied-stroke (get applied-tokens :stroke-color) - fills (:fills shape) - strokes (:strokes shape) - shadows (:shadow shape) - shape-id (:id shape) - - fills* (map-indexed - (fn [index fill] - (cond-> (assoc fill :shape-id shape-id :index index) - (and (zero? index) applied-fill) - (assoc :has-token-applied true - :token-name applied-fill))) - fills) - - strokes* (map-indexed - (fn [index stroke] - (cond-> (assoc stroke :shape-id shape-id :index index) - (and (zero? index) applied-stroke) - (assoc :has-token-applied true - :token-name applied-stroke))) - strokes) - - shadows* (map-indexed #(assoc %2 :shape-id shape-id :index %1) shadows)] - (if (= :text (:type shape)) - (-> result - (into (keep #(stroke->color-att % file-id libraries)) strokes*) - (into (map #(shadow->color-attr % file-id libraries)) shadows*) - (into (extract-text-colors shape file-id libraries))) - (-> result - (into (keep #(fill->color-att % file-id libraries)) fills*) - (into (keep #(stroke->color-att % file-id libraries)) strokes*) - (into (map #(shadow->color-attr % file-id libraries)) shadows*))))) - [] - shapes)) + (into [] (mapcat #(shape-colors % file-id libraries)) shapes)) diff --git a/frontend/src/app/main/data/workspace/comments.cljs b/frontend/src/app/main/data/workspace/comments.cljs index 79d804c50b..656fdaa651 100644 --- a/frontend/src/app/main/data/workspace/comments.cljs +++ b/frontend/src/app/main/data/workspace/comments.cljs @@ -20,6 +20,7 @@ [app.main.data.event :as ev] [app.main.data.helpers :as dsh] [app.main.data.notifications :as ntf] + [app.main.data.persistence :as-alias dps] [app.main.data.workspace.common :as dwco] [app.main.data.workspace.drawing :as dwd] [app.main.data.workspace.edition :as dwe] @@ -219,21 +220,62 @@ (rx/ignore)) (rx/empty))))) +(defn- restore-rolled-back-threads + "After a rollback, puts the threads in `comment-thread-ids` back where the + restored page data has them, in the rendered threads and the backend. + Threads left on a frame the rollback removed (in `shape-ids`) move to the + page root, as deleting the frame does." + [{:keys [comment-thread-ids shape-ids]}] + (ptk/reify ::restore-rolled-back-threads + ptk/WatchEvent + (watch [_ state _] + (let [threads (:comment-threads state) + + restored + (into {} + (keep (fn [id] + (when-let [{:keys [page-id]} (get threads id)] + (when-let [{:keys [position frame-id]} + (-> (dsh/lookup-page state page-id) + (dm/get-in [:comment-thread-positions id]))] + [id {:comment-thread-id id + :position position + :frame-id frame-id}])))) + comment-thread-ids) + + orphaned? + (fn [{:keys [id page-id] :as thread}] + (let [frame-id (or (dm/get-in restored [id :frame-id]) (:frame-id thread))] + (and (contains? shape-ids frame-id) + (nil? (get (dsh/lookup-page-objects state page-id) frame-id)))))] + + (rx/concat + (rx/from (map sync-comment-thread-position (vals restored))) + (->> (vals threads) + (filter orphaned?) + (map dcmt/update-comment-thread-frame) + (rx/from))))))) + (defn watch-comment-thread-position-changes - "Syncs rendered threads and the backend when an undo/redo changes a comment position." + "Syncs rendered threads and the backend when an undo/redo changes a comment + position, and when a rollback reverts a change of positions or frames." [stopper] (ptk/reify ::watch-comment-thread-position-changes ptk/WatchEvent (watch [_ _ stream] - (->> stream - (rx/filter dwc/commit?) - (rx/map deref) - (rx/filter #(contains? undo-origins (:origin %))) - (rx/mapcat (fn [commit] - (->> (:redo-changes commit) - (filter #(= :set-comment-thread-position (:type %))) - (rx/from)))) - (rx/map sync-comment-thread-position) + (->> (rx/merge + (->> stream + (rx/filter dwc/commit?) + (rx/map deref) + (rx/filter #(contains? undo-origins (:origin %))) + (rx/mapcat (fn [commit] + (->> (:redo-changes commit) + (filter #(= :set-comment-thread-position (:type %))) + (rx/from)))) + (rx/map sync-comment-thread-position)) + (->> stream + (rx/filter (ptk/type? ::dps/rolled-back)) + (rx/map (comp restore-rolled-back-threads deref)))) (rx/take-until stopper))))) (defn frame-pin-transform diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index d682bbfe0e..e9911688b2 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -6,6 +6,7 @@ (ns app.main.data.workspace.common (:require + [app.common.data.macros :as dm] [app.common.logging :as log] [app.main.data.profile :as du] [app.main.data.workspace.layout :as dwl] @@ -70,11 +71,15 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn set-workspace-read-only + "Sets the read-only mode. While failing saves lock the workspace, the + value is kept for when the lock is lifted." [read-only?] (ptk/reify ::set-workspace-read-only ptk/UpdateEvent (update [_ state] - (update state :workspace-global assoc :read-only? read-only?)) + (if (dm/get-in state [:workspace-global :persistence-lock]) + (assoc-in state [:workspace-global :persistence-lock :read-only?] read-only?) + (update state :workspace-global assoc :read-only? read-only?))) ptk/WatchEvent (watch [_ _ _] diff --git a/frontend/src/app/main/data/workspace/drawing/box.cljs b/frontend/src/app/main/data/workspace/drawing/box.cljs index 18f510f722..3699229bd7 100644 --- a/frontend/src/app/main/data/workspace/drawing/box.cljs +++ b/frontend/src/app/main/data/workspace/drawing/box.cljs @@ -21,11 +21,11 @@ [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] [app.main.data.helpers :as dsh] + [app.main.data.persistence.lock :as plk] [app.main.data.workspace.drawing.common :as common] [app.main.snap :as snap] [app.main.streams :as ms] [app.util.array :as array] - [app.util.mouse :as mse] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -93,7 +93,8 @@ (ptk/reify ::handle-drawing ptk/WatchEvent (watch [_ state stream] - (let [stopper (mse/drag-stopper stream) + (let [cancelled? (volatile! false) + stopper (plk/gesture-stopper stream cancelled?) layout (get state :workspace-layout) zoom (dm/get-in state [:workspace-local :zoom] 1) @@ -149,5 +150,7 @@ (fn [[_ shift? mod? point]] #(update-drawing % initial (cond-> point snap-pixel? (gpt/round-step 1)) shift? mod? snap-pixel?)))))) - (->> (rx/of (common/handle-finish-drawing)) + (->> (rx/defer #(rx/of (if @cancelled? + (common/cancel-drawing) + (common/handle-finish-drawing)))) (rx/delay 100))))))) diff --git a/frontend/src/app/main/data/workspace/drawing/common.cljs b/frontend/src/app/main/data/workspace/drawing/common.cljs index 930d91a280..bb54665da3 100644 --- a/frontend/src/app/main/data/workspace/drawing/common.cljs +++ b/frontend/src/app/main/data/workspace/drawing/common.cljs @@ -132,3 +132,16 @@ ;; Let the current mouse event finish before clearing drawing state. (->> (rx/of (clear-drawing {:preserve-tool? (= tool :curve)})) (rx/delay 0))))))) + +(defn cancel-drawing + "Ends the drawing in progress without adding its shape, as when saves + start failing mid-gesture." + [] + (ptk/reify ::cancel-drawing + ptk/UpdateEvent + (update [_ state] + (update state :workspace-drawing dissoc :object)) + + ptk/WatchEvent + (watch [_ _ _] + (rx/of (handle-finish-drawing))))) diff --git a/frontend/src/app/main/data/workspace/drawing/curve.cljs b/frontend/src/app/main/data/workspace/drawing/curve.cljs index efc670b492..f15636a5f5 100644 --- a/frontend/src/app/main/data/workspace/drawing/curve.cljs +++ b/frontend/src/app/main/data/workspace/drawing/curve.cljs @@ -17,9 +17,9 @@ [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] [app.main.data.helpers :as dsh] + [app.main.data.persistence.lock :as plk] [app.main.data.workspace.drawing.common :as common] [app.main.streams :as ms] - [app.util.mouse :as mse] [app.util.path.simplify-curve :as ups] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -104,19 +104,22 @@ (ptk/reify ::handle-drawing ptk/WatchEvent (watch [_ _ stream] - (let [stopper (mse/drag-stopper stream) - mouse (rx/sample 10 ms/mouse-position) - shape (cts/setup-shape {:type :path - :initialized? true - :frame-id uuid/zero - :parent-id uuid/zero - ::points []})] + (let [cancelled? (volatile! false) + stopper (plk/gesture-stopper stream cancelled?) + mouse (rx/sample 10 ms/mouse-position) + shape (cts/setup-shape {:type :path + :initialized? true + :frame-id uuid/zero + :parent-id uuid/zero + ::points []})] (rx/concat (rx/of #(update % :workspace-drawing assoc :object shape)) (->> mouse (rx/map insert-point) (rx/take-until stopper)) - (rx/of - (setup-frame) - (finish-drawing) - (common/handle-finish-drawing))))))) + (rx/defer + #(if @cancelled? + (rx/of (common/cancel-drawing)) + (rx/of (setup-frame) + (finish-drawing) + (common/handle-finish-drawing))))))))) diff --git a/frontend/src/app/main/data/workspace/drawing/line.cljs b/frontend/src/app/main/data/workspace/drawing/line.cljs index 10fc76f6b7..355862d3f3 100644 --- a/frontend/src/app/main/data/workspace/drawing/line.cljs +++ b/frontend/src/app/main/data/workspace/drawing/line.cljs @@ -27,9 +27,9 @@ [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] [app.main.data.helpers :as dsh] + [app.main.data.persistence.lock :as plk] [app.main.data.workspace.drawing.common :as common] [app.main.streams :as ms] - [app.util.mouse :as mse] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -128,17 +128,18 @@ (ptk/reify ::handle-drawing ptk/WatchEvent (watch [_ state stream] - (let [stopper (mse/drag-stopper stream) - zoom (dm/get-in state [:workspace-local :zoom] 1) - start @ms/mouse-position - shape (cts/setup-shape {:type :path - :initialized? true - :click-draw? true - :frame-id uuid/zero - :parent-id uuid/zero - :strokes (arrow-strokes arrow?) - :fills [] - :content (path/points->content [start start])})] + (let [cancelled? (volatile! false) + stopper (plk/gesture-stopper stream cancelled?) + zoom (dm/get-in state [:workspace-local :zoom] 1) + start @ms/mouse-position + shape (cts/setup-shape {:type :path + :initialized? true + :click-draw? true + :frame-id uuid/zero + :parent-id uuid/zero + :strokes (arrow-strokes arrow?) + :fills [] + :content (path/points->content [start start])})] (rx/concat (rx/of #(update % :workspace-drawing assoc :object shape)) @@ -152,7 +153,9 @@ (rx/map (partial update-endpoint start)) (rx/take-until stopper)) - (rx/of - (setup-frame) - (finalize) - (common/handle-finish-drawing)))))))) + (rx/defer + #(if @cancelled? + (rx/of (common/cancel-drawing)) + (rx/of (setup-frame) + (finalize) + (common/handle-finish-drawing)))))))))) diff --git a/frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs b/frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs index 7a69602045..5d2b3e4de0 100644 --- a/frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs +++ b/frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs @@ -21,78 +21,68 @@ ;; - Moving files from one team to another in the same instance ;; - Custom fonts are explicitly deleted in the team area -(defn- calculate-alternative-font-id - [value] - (let [fonts (deref fonts/fontsdb)] - (reduce-kv (fn [_ _ font] - (if (= (:family font) value) - (reduced (:id font)) - nil)) - nil - fonts))) +(defn- index-fonts-by-family + [fonts] + (reduce-kv (fn [index id font] + (let [family (:family font)] + (if (contains? index family) + index + (assoc index family id)))) + {} + fonts)) (defn- has-invalid-font-family? - [node] - (let [fonts (deref fonts/fontsdb) - font-family (:font-family node)] - (and (some? font-family) - (nil? (get fonts (:font-id node)))))) + [fonts node] + (and (some? (:font-family node)) + (nil? (get fonts (:font-id node))))) -(defn- shape-has-invalid-font-family?? - [shape] - (and (cfh/text-shape? shape) - (some has-invalid-font-family? - (txt/node-seq txt/is-text-node? (:content shape))))) +(defn- fixable-font? + [fonts families node] + (and (has-invalid-font-family? fonts node) + (contains? families (:font-family node)))) (defn- fix-deleted-font - [node] - (if-let [alternative-font-id (calculate-alternative-font-id (:font-family node))] - (assoc node :font-id alternative-font-id) - node)) - -(defn- fix-shape-content - [shape] - (txt/transform-nodes has-invalid-font-family? fix-deleted-font - (:content shape))) - -(defn- fix-typography - [typography] - (if-let [alternative-font-id (calculate-alternative-font-id (:font-family typography))] - (assoc typography :font-id alternative-font-id) - typography)) + [families node] + (assoc node :font-id (get families (:font-family node)))) (defn- generate-page-changes [{:keys [objects id]}] - (reduce-kv (fn [changes shape-id shape] - (if (shape-has-invalid-font-family?? shape) - (let [fixed-content (fix-shape-content shape)] - (if (not= fixed-content (:content shape)) + (let [fonts (deref fonts/fontsdb) + families (index-fonts-by-family fonts) + fixable? (partial fixable-font? fonts families)] + (reduce-kv (fn [changes shape-id shape] + (let [content (:content shape)] + (if (and (cfh/text-shape? shape) + (some (partial has-invalid-font-family? fonts) + (txt/node-seq txt/is-text-node? content)) + (some fixable? (txt/node-seq txt/is-node? content))) (conj changes {:type :mod-obj :id shape-id :page-id id :operations [{:type :set :attr :content - :val fixed-content} + :val (txt/transform-nodes + fixable? + (partial fix-deleted-font families) + content)} {:type :set :attr :position-data :val nil}]}) - changes)) - changes)) - [] - objects)) + changes))) + [] + objects))) (defn- generate-library-changes [fdata] - (reduce-kv (fn [changes _ typography] - (if (has-invalid-font-family? typography) - (let [fixed-typography (fix-typography typography)] - (if (not= fixed-typography typography) - (conj changes {:type :mod-typography - :typography fixed-typography}) - changes)) - changes)) - [] - (:typographies fdata))) + (let [fonts (deref fonts/fontsdb) + families (index-fonts-by-family fonts)] + (reduce-kv (fn [changes _ typography] + (if (fixable-font? fonts families typography) + (conj changes {:type :mod-typography + :typography (fix-deleted-font families typography)}) + changes)) + [] + (:typographies fdata)))) (defn fix-deleted-fonts-for-local-library "Looks the file local library for deleted fonts and emit changes if diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 8d2aec1597..97ff3bd1a3 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -37,6 +37,7 @@ [app.main.data.helpers :as dsh] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] + [app.main.data.persistence :as dps] [app.main.data.workspace :as-alias dw] [app.main.data.workspace.groups :as dwg] [app.main.data.workspace.notifications :as-alias dwn] @@ -107,6 +108,7 @@ (map extract-change changes))) (declare sync-file) +(declare notify-sync-file) (defn extract-path-if-missing [item] @@ -614,7 +616,7 @@ in the given file library. Then selects the newly created instance." ([file-id component-id position] (instantiate-component file-id component-id position nil)) - ([file-id component-id position {:keys [start-move? initial-point id-ref origin]}] + ([file-id component-id position {:keys [start-move? initial-point id-ref origin frame-id]}] (dm/assert! (uuid? file-id)) (dm/assert! (uuid? component-id)) (dm/assert! (gpt/point? position)) @@ -637,7 +639,11 @@ component-id position page - libraries) + libraries + nil + frame-id + frame-id + {:force-frame? (some? frame-id)}) component (ctn/get-component-from-shape new-shape libraries) undo-id (js/Symbol)] @@ -904,7 +910,9 @@ ptk/WatchEvent (watch [it state _] (log/info :msg "UPDATE-COMPONENT of shape" :id (str id) :undo-group undo-group) - (let [page-id (:current-page-id state) + (let [;; One undo group for the commits in both files + undo-group (or undo-group (uuid/next)) + page-id (:current-page-id state) libraries (dsh/lookup-libraries state) file (dsh/lookup-file state) @@ -961,11 +969,12 @@ component (ctkl/get-component data component-id) page-id (:main-instance-page component) root-id (:main-instance-id component)] - (if (and (= tag "component") - (features/active-feature? state "render-wasm/v1")) + (if (features/active-feature? state "render-wasm/v1") ;; WASM: render immediately, UI only — server persist happens - ;; on the debounced path (update-component-thumbnail) - (dwt.wasm/render-thumbnail file-id page-id root-id) + ;; on the debounced path (update-component-thumbnail). Frame + ;; thumbnails are only shown by the SVG viewport. + (when (= tag "component") + (dwt.wasm/render-thumbnail file-id page-id root-id)) (dwt/update-thumbnail file-id page-id root-id tag "update-component-thumbnail-sync")))) (defn- render-component-thumbnail-event @@ -986,7 +995,9 @@ page (dsh/lookup-page state) shape (ctn/get-shape page shape-id) component-id (:component-id shape) - undo-id (js/Symbol)] + undo-id (js/Symbol) + ;; One undo group for the update and the syncs, in any file + undo-group (or undo-group (uuid/next))] (rx/of (dwu/start-undo-transaction undo-id) (update-component shape-id undo-group) @@ -1009,7 +1020,9 @@ ptk/WatchEvent (watch [_ state _] (let [current-file-id (:current-file-id state) - undo-id (js/Symbol)] + undo-id (js/Symbol) + ;; One undo group for the syncs of both files + undo-group (or undo-group (uuid/next))] (rx/of (dwu/start-undo-transaction undo-id) (sync-file current-file-id file-id :components component-id undo-group) @@ -1251,6 +1264,30 @@ (when-not (= (:frame-id shape) uuid/zero) (dwt/clear-thumbnail file-id (:page-id shape) (:frame-id shape) "frame"))))))))))) +(defn- mark-library-synced + "Tells the backend that `file-id` is synced with `library-id`. Only the + update notice depends on it, so a failure (e.g. edit rights lost + meanwhile) is logged and ignored." + [file-id library-id] + (->> (rp/cmd! :update-file-library-sync-status + {:file-id file-id + :library-id library-id}) + (rx/catch (fn [cause] + (log/warn :hint "unable to mark the library as synced" + :file-id (str file-id) + :library-id (str library-id) + :cause cause) + (rx/empty))))) + +(defn- restore-library-synced-at + "Puts back the sync date of `library-id` a rejected sync moved forward, + so its update notice can show again." + [library-id synced-at] + (ptk/reify ::restore-library-synced-at + ptk/UpdateEvent + (update [_ state] + (d/update-in-when state [:files library-id] assoc :synced-at synced-at)))) + (defn sync-file "Synchronize the given file from the given library. Walk through all shapes in all pages in the file that use some color, typography or @@ -1271,70 +1308,101 @@ (contains? valid-asset-types asset-type))) (dm/assert! (or (nil? asset-id) (uuid? asset-id))) - (ptk/reify ::sync-file - ptk/UpdateEvent - (update [_ state] - (if (and (not= library-id (:current-file-id state)) - (nil? asset-id)) - (d/update-in-when state [:files library-id] assoc :synced-at (ct/now)) - state)) + (let [;; The sync date this sync replaced, if it replaced one + synced-at (volatile! ::unchanged)] + (ptk/reify ::sync-file + ptk/UpdateEvent + (update [_ state] + (if (and (not= library-id (:current-file-id state)) + (nil? asset-id)) + (do (vreset! synced-at (dm/get-in state [:files library-id :synced-at])) + (d/update-in-when state [:files library-id] assoc :synced-at (ct/now))) + state)) - ptk/WatchEvent - (watch [it state _] - (when (and (some? file-id) (some? library-id)) ; Prevent race conditions while navigating out of the file - (log/info :msg "SYNC-FILE" - :file (debug-pretty-file file-id state) - :library (debug-pretty-file library-id state) - :asset-type asset-type - :asset-id asset-id - :undo-group undo-group) - (let [ldata (dsh/lookup-file-data state file-id) - libraries (dsh/lookup-libraries state) - current-file-id (:current-file-id state) + ptk/WatchEvent + (watch [it state stream] + (when (and (some? file-id) (some? library-id)) ; Prevent race conditions while navigating out of the file + (log/info :msg "SYNC-FILE" + :file (debug-pretty-file file-id state) + :library (debug-pretty-file library-id state) + :asset-type asset-type + :asset-id asset-id + :undo-group undo-group) + (let [ldata (dsh/lookup-file-data state file-id) + libraries (dsh/lookup-libraries state) + current-file-id (:current-file-id state) - changes (cll/generate-sync-file-changes - (pcb/empty-changes it) - undo-group - asset-type - file-id - asset-id - library-id - libraries - current-file-id) + changes (cll/generate-sync-file-changes + (pcb/empty-changes it) + undo-group + asset-type + file-id + asset-id + library-id + libraries + current-file-id) - find-frames (fn [change] - (->> (ch/frames-changed ldata change) - (map #(assoc %1 :page-id (:page-id change))))) + find-frames (fn [change] + (->> (ch/frames-changed ldata change) + (map #(assoc %1 :page-id (:page-id change))))) - updated-frames (->> changes - :redo-changes - (mapcat find-frames) - distinct) + updated-frames (->> changes + :redo-changes + (mapcat find-frames) + distinct) - pending-ids (sync-file-pending-ids file-id changes) + pending-ids (sync-file-pending-ids file-id changes) - frontend-sync - (sync-file-frontend-events - file-id changes updated-frames undo-group)] + frontend-sync + (sync-file-frontend-events + file-id changes updated-frames undo-group)] - (log/debug :msg "SYNC-FILE finished" :js/rchanges (log-changes - (:redo-changes changes) - ldata)) - (rx/concat - ;; Keep the sync pending until its layout work starts. - (wrf/with-pending :sync-file pending-ids frontend-sync) + (log/debug :msg "SYNC-FILE finished" :js/rchanges (log-changes + (:redo-changes changes) + ldata)) + (rx/concat + ;; Keep the sync pending until its layout work starts. + (wrf/with-pending :sync-file pending-ids frontend-sync) - (when (not= file-id library-id) - ;; When we have just updated the library file, give some time for the - ;; update to finish, before marking this file as synced. - ;; TODO: look for a more precise way of syncing this. - ;; Maybe by using the stream (second argument passed to watch) - ;; to wait for the corresponding changes-committed and then proceed - ;; with the :update-file-library-sync-status mutation. - (rx/concat (rx/timer 3000) - (rp/cmd! :update-file-library-sync-status - {:file-id file-id - :library-id library-id})))))))))) + (when (not= file-id library-id) + (let [stopper-s (rx/filter (ptk/type? ::dw/finalize-workspace) stream) + rolled-back-s (->> stream + (rx/filter (ptk/type? ::dps/rolled-back)) + (rx/filter #(contains? (:file-ids (deref %)) file-id))) + restore (if (= ::unchanged @synced-at) + (rx/empty) + (rx/of (restore-library-synced-at library-id @synced-at))) + ;; Checked once the rollback has undone the sync locally + notify #(->> (dps/wait-persisted) + (rx/map (fn [_] (notify-sync-file))))] + (->> (rx/merge + ;; The server rejected the sync: its commits were undone + (rx/map (constantly ::rolled-back) rolled-back-s) + ;; The flush of the sync commits starts within 3 s; a save + ;; that fails for good ends the wait with no value + (->> (rx/timer 3000) + (rx/mapcat (fn [_] (dps/wait-persisted))) + (rx/map (constantly ::saved)) + (rx/if-empty ::failed))) + (rx/take 1) + (rx/take-until stopper-s) + (rx/mapcat (fn [outcome] + (case outcome + ::saved + (mark-library-synced file-id library-id) + + ;; Never saved: the update notice can show again + ;; once the release of the failed save undoes the sync + ::failed + (rx/concat + restore + (->> rolled-back-s + (rx/take 1) + (rx/take-until stopper-s) + (rx/mapcat (fn [_] (notify))))) + + ::rolled-back + (rx/concat restore (notify)))))))))))))))) ;; FIXME: the data should be set on the backend for clock consistency @@ -1459,7 +1527,9 @@ (rx/of (dch/commit-changes {:origin it :redo-changes (:redo-changes changes) :undo-changes [] - :save-undo? false})))))) + :save-undo? false + ;; Follows a change already made, a rollback included + :ignore-save-lock true})))))) (defn component-changed "Notify that the component with the given id has changed, so it needs to be updated @@ -1598,7 +1668,31 @@ notifier-s (->> component-events-s (rx/debounce 5000) - (rx/tap #(log/trc :hint "buffer initialized")))] + (rx/tap #(log/trc :hint "buffer initialized"))) + + ;; A rollback is a rebase, not a commit: no component change + ;; is detected, so the thumbnails of the mains it reverted are + ;; rendered and uploaded here, each rebase checked against the + ;; file data right before it + rolled-back-thumbnails-s + (->> stream + (rx/filter (ptk/type? ::dch/rebase-commits)) + (rx/map deref) + (rx/filter (complement :failed)) + ;; Its own data pairing: `workspace-data-s` is shared and + ;; does not replay the current data to a late subscriber + (rx/with-latest-from + (->> (rx/from-atom refs/workspace-data {:emit-current-value? true}) + (rx/concat (rx/of nil)) + (rx/buffer 2 1) + (rx/map first))) + (rx/mapcat + (fn [[{:keys [file-id changes]} old-data]] + (if (= file-id (:id old-data)) + (->> (into #{} (mapcat (partial ch/components-changed old-data)) changes) + (rx/from) + (rx/map #(update-component-thumbnail % file-id))) + (rx/empty)))))] (when (or (contains? cf/flags :component-thumbnails) (features/active-feature? state "render-wasm/v1")) @@ -1630,7 +1724,9 @@ (->> component-events-s (rx/filter (ptk/type? ::touch-component)) (rx/map deref) - (rx/map render-component-thumbnail-event))) + (rx/map render-component-thumbnail-event)) + + rolled-back-thumbnails-s) (rx/take-until stopper-s))))))) diff --git a/frontend/src/app/main/data/workspace/mcp.cljs b/frontend/src/app/main/data/workspace/mcp.cljs index bc7ef868eb..07f766ab61 100644 --- a/frontend/src/app/main/data/workspace/mcp.cljs +++ b/frontend/src/app/main/data/workspace/mcp.cljs @@ -12,13 +12,15 @@ [app.common.uri :as u] [app.config :as cf] [app.main.broadcast :as mbc] + [app.main.data.auth :as-alias auth] [app.main.data.plugins :as dp] [app.main.data.profile :as du] - [app.main.data.workspace :as-alias dw] [app.main.store :as st] [app.plugins.register :as preg] + [app.util.globals :as globals] [app.util.timers :as ts] [beicon.v2.core :as rx] + [beicon.v2.operators :as rxo] [potok.v2.core :as ptk])) (def reconnect-fallback-interval 60000) @@ -36,21 +38,61 @@ :plugin-id preg/mcp-plugin-id :description "This plugin enables interaction with the Penpot MCP server" :allow-background true + :scope "global" :permissions - #{"library:read" "library:write" - "comment:read" "comment:write" - "content:write" "content:read"}}) + preg/mcp-permissions}) (defonce interval-sub (atom nil)) +;; Resolves the promise that keeps the session Web Lock held; nil when none is. +(defonce ^:private session-lock-release + (atom nil)) + +(defn- hold-session-lock + "Holds a shared Web Lock while the MCP connection is requested. Chrome + does not freeze a page that holds a Web Lock, and a frozen page cannot + run the plugin." + [] + (when-let [locks (and (nil? @session-lock-release) + (unchecked-get globals/navigator "locks"))] + (let [released (js/Promise. (fn [resolve] (reset! session-lock-release resolve))) + release @session-lock-release] + (-> (.request locks "penpot-mcp-session" #js {:mode "shared"} (constantly released)) + (.catch (fn [cause] + ;; lets a later connection request try again, unless a + ;; newer lock request has already replaced this one + (compare-and-set! session-lock-release release nil) + (log/warn :hint "unable to hold the MCP session lock" :cause cause))))))) + +(defn- release-session-lock + [] + (when-let [release @session-lock-release] + (reset! session-lock-release nil) + (release))) + (defn connect-mcp [] (ptk/reify ::connect-mcp + ptk/UpdateEvent + (update [_ state] + (if (and (get-in state [:mcp :enabled]) + (get-in state [:mcp :token-valid])) + (update state :mcp assoc + :connection-requested true + :connection-status "connecting") + state)) + ptk/WatchEvent - (watch [_ _ _] - (rx/of (mbc/event :mcp/force-disconnect {}) - (ptk/data-event ::connect))))) + (watch [_ state _] + (if (get-in state [:mcp :connection-requested]) + (rx/of (ptk/data-event ::connect)) + (rx/empty))) + + ptk/EffectEvent + (effect [_ state _] + (when (get-in state [:mcp :connection-requested]) + (hold-session-lock))))) (defn- start-reconnect-watcher [] @@ -63,8 +105,9 @@ ;; Slow app-level fallback. The plugin owns normal WebSocket ;; reconnects; this only restarts it if the app remains in a ;; failed connection state. - (when (contains? reconnect-fallback-statuses - (-> @st/state :mcp :connection-status)) + (when (and (get-in @st/state [:mcp :connection-requested]) + (contains? reconnect-fallback-statuses + (-> @st/state :mcp :connection-status))) (.log js/console "Reconnecting to MCP...") (st/emit! (ptk/data-event ::connect)))))))) @@ -74,6 +117,8 @@ (rx/dispose! @interval-sub) (reset! interval-sub nil))) +(declare user-disconnect-mcp) + ;; This event will arrive when the mcp is enabled in the dashboard (defn update-mcp-status [value] @@ -86,42 +131,72 @@ ptk/WatchEvent (watch [_ _ _] - (case value - true (rx/of (connect-mcp)) - false (rx/of (ptk/data-event ::disconnect)) - nil)))) + (if (false? value) + (rx/of (user-disconnect-mcp)) + (rx/empty))))) (defn update-mcp-connection-status - [value] - (ptk/reify ::update-mcp-plugin-connection - ptk/UpdateEvent - (update [_ state] - (update state :mcp assoc :connection-status value)) - - ptk/WatchEvent - (watch [_ _ _] - ;; Only one MCP plugin instance may be active across browser tabs. - ;; When this tab becomes connected, tell every other tab to - ;; disconnect (which also stops their reconnect watcher). Otherwise - ;; several tabs stay connected at once and the MCP server reports - ;; "multiple instances connected" and the agent fails. - (when (= "connected" value) - (rx/of (mbc/event :mcp/force-disconnect {})))))) + ([value] + (update-mcp-connection-status value nil)) + ([value session-id] + (ptk/reify ::update-mcp-plugin-connection + ptk/UpdateEvent + (update [_ state] + (if (get-in state [:mcp :connection-requested]) + (update state :mcp assoc + :connection-status value + :session-id session-id) + state))))) ;; This event will arrive when the user selects disconnect on the menu -;; or there is a broadcast message for disconnection (defn user-disconnect-mcp [] (ptk/reify ::user-disconnect-mcp + ptk/UpdateEvent + (update [_ state] + (update state :mcp assoc + :connection-requested false + :connection-status "disconnected" + :session-id nil)) + ptk/WatchEvent (watch [_ _ _] - (rx/of (ptk/data-event ::disconnect) - (update-mcp-connection-status "disconnected"))) + (rx/of (ptk/data-event ::disconnect))) ptk/EffectEvent (effect [_ _ _] + (release-session-lock) (stop-reconnect-watcher!)))) +(defn- extension-hello? + "Checks if a window message is a browser extension's request for the + MCP session, posted on this page by its content script." + [event] + (and (identical? (unchecked-get event "source") globals/window) + (= (unchecked-get event "origin") (.-origin globals/location)) + (= "penpot-mcp-extension:hello" + (some-> (unchecked-get event "data") (unchecked-get "type"))))) + +(defn extension-status + "Returns the connection status to announce to a browser extension. The + plugin also reports `disconnected` while it reconnects, so the + extension, which forgets a session on `disconnected`, only hears it + once the user has stopped the connection." + [status connection-requested?] + (if (and (= status "disconnected") connection-requested?) + "connecting" + status)) + +(defn- announce-session + "Posts the MCP session of this page to a browser extension, which keeps + this tab from being discarded." + [status session-id] + (.postMessage globals/window + #js {:type "penpot-mcp:session" + :sessionId session-id + :status status} + (.-origin globals/location))) + (defn- init-mcp-plugin "Waits the plugin runtime and initializes the bundled MCP plugin." [{:keys [token]}] @@ -133,31 +208,68 @@ (assoc :host (str (u/join cf/public-uri "plugins/mcp/")))) stopper-s (rx/merge - (rx/filter (ptk/type? ::dw/finalize-workspace) stream) - (rx/filter (ptk/type? ::stop-mcp-plugin) stream)) + (rx/filter (ptk/type? ::auth/logged-out) stream) + (rx/filter (ptk/type? ::stop-mcp-plugin) stream) + (rx/filter (ptk/type? ::init) stream)) + + active? (atom true) + + ;; Whether a browser extension asked for the session; nothing is + ;; posted on the page before that. + announce? (atom false) + + on-message + (fn [event] + (when (extension-hello? event) + (reset! announce? true) + (let [{:keys [connection-status connection-requested session-id]} (get @st/state :mcp)] + (announce-session (extension-status connection-status connection-requested) + session-id)))) extension #js {:getToken (constantly token) :getServerUrl #(str cf/mcp-ws-uri) + :isConnectionRequested #(and @active? + (get-in @st/state [:mcp :connection-requested])) :setMcpStatus - (fn [status] - (when (= status "connected") - (start-reconnect-watcher)) - (st/emit! (update-mcp-connection-status status)) - (log/info :hint "MCP STATUS" :status status)) + (fn [status session-id] + (when @active? + (when (and (= status "connected") + (get-in @st/state [:mcp :connection-requested])) + (start-reconnect-watcher)) + (st/emit! (update-mcp-connection-status status session-id)) + (when @announce? + (announce-session (extension-status status (get-in @st/state [:mcp :connection-requested])) + session-id)) + (log/info :hint "MCP STATUS" :status status))) :on (fn [event cb] (when-let [event - (case event - "disconnect" ::disconnect - "connect" ::connect - nil)] + (when @active? + (case event + "disconnect" ::disconnect + "connect" ::connect + nil))] (->> stream (rx/filter (ptk/type? event)) (rx/take-until stopper-s) (rx/subs! (fn [_] (cb))))))}] + (->> stopper-s + (rx/take 1) + (rx/subs! (fn [_] + (reset! active? false) + (.removeEventListener globals/window "message" on-message) + (when @announce? + (announce-session "disconnected" nil)) + (release-session-lock) + (stop-reconnect-watcher!) + (dp/close-plugin! default-manifest)))) + (.addEventListener globals/window "message" on-message) + ;; A content script that said hello before this listener existed + ;; says it again + (.postMessage globals/window #js {:type "penpot-mcp:ready"} (.-origin globals/location)) (dp/start-plugin! manifest #js {:mcp extension}))))) (defn- stop-mcp-plugin @@ -198,15 +310,18 @@ (update [_ state] (let [profile (get state :profile) mcp-enabled? (-> profile :props :mcp-enabled boolean)] - (update state :mcp assoc :enabled mcp-enabled?))) + (update state :mcp assoc + :enabled mcp-enabled? + :connection-requested false + :connection-status "disconnected" + :session-id nil))) ptk/WatchEvent (watch [_ state stream] (let [stopper-s (rx/merge - (rx/filter (ptk/type? ::dw/finalize-workspace) stream) + (rx/filter (ptk/type? ::auth/logged-out) stream) (rx/filter (ptk/type? ::init) stream)) - session-id (get state :session-id) mcp-state (get state :mcp)] (->> (rx/merge @@ -229,19 +344,10 @@ (rx/map init-mcp-plugin)) (rx/empty)) - (->> mbc/stream - (rx/filter (mbc/type? :mcp/force-disconnect)) - (rx/filter (fn [{:keys [id]}] - (not= session-id id))) - (rx/map deref) - (rx/map (fn [] (user-disconnect-mcp)))) - (->> mbc/stream (rx/filter (mbc/type? :mcp/enable)) (rx/mapcat (fn [_] - ;; Re-init so the force-disconnect - ;; listener is set up now that MCP - ;; is enabled. + ;; initialize the idle plugin now that MCP is enabled (rx/of (update-mcp-status true) (init))))) @@ -253,3 +359,25 @@ (stop-mcp-plugin)))))) (rx/take-until stopper-s)))))) + +(defn initialize + "Initialize the MCP controller for each authenticated app session." + [] + (ptk/reify ::initialize + ptk/WatchEvent + (watch [_ _ stream] + (if (contains? cf/flags :mcp) + (->> stream + (rx/filter #(or (ptk/type? ::du/set-profile %) + (ptk/type? ::auth/logged-out %))) + (rx/map #(when (ptk/type? ::du/set-profile %) + (:id (deref %)))) + (rx/pipe (rxo/distinct-contiguous)) + (rx/switch-map + (fn [profile-id] + (if profile-id + (->> (rx/from (preg/wait-for-runtime)) + (rx/map (fn [_] (init)))) + (rx/empty)))) + (rx/take-until (rx/filter (ptk/type? ::initialize) stream))) + (rx/empty))))) diff --git a/frontend/src/app/main/data/workspace/media.cljs b/frontend/src/app/main/data/workspace/media.cljs index f3a395a504..71ac10d86a 100644 --- a/frontend/src/app/main/data/workspace/media.cljs +++ b/frontend/src/app/main/data/workspace/media.cljs @@ -487,10 +487,14 @@ (watch [_ _ _] (->> (svg->clj [name svg-string]) (rx/take 1) - (rx/map #(svg/add-svg-shapes id % position {:ignore-selection? true + (rx/map #(svg/add-svg-shapes id % position {:frame-id uuid/zero + :ignore-selection? true :change-selection? false})))))) (defn create-svg-shape-with-images - [file-id id name svg-string position on-success on-error] + "Uploads the images of `svg-string` and adds it as shape `id`. + `check-writable` runs once the images are uploaded, before the shape is + added; when it throws, no shape is added and `on-error` gets the error." + [file-id id name svg-string position {:keys [check-writable on-success on-error]}] (ptk/reify ::create-svg-shape-with-images ptk/WatchEvent (watch [_ _ _] @@ -500,13 +504,15 @@ (fn [svg-data] (->> (svg/upload-images svg-data file-id) (rx/map #(assoc svg-data :image-data %))))) + (rx/tap (fn [_] (check-writable))) (rx/map (fn [svg-data] (svg/add-svg-shapes id svg-data position - {:ignore-selection? true + {:frame-id uuid/zero + :ignore-selection? true :change-selection? false}))) (rx/tap on-success) (rx/catch on-error))))) diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index e2cfc06963..82e3d9cc72 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -17,10 +17,10 @@ [app.common.geom.shapes :as gsh] [app.common.logging :as log] [app.common.math :as mth] + [app.common.render-wasm.api.select :as wselect] [app.common.types.component :as ctk] [app.common.types.container :as ctn] [app.common.types.modifiers :as ctm] - [app.common.types.path :as path] [app.common.types.shape-tree :as ctst] [app.common.types.shape.attrs :refer [editable-attrs]] [app.common.types.shape.layout :as ctl] @@ -233,55 +233,78 @@ (map #(get objects %)) (reduce get-ignore-tree nil)))) +(defn- cached-transform + [transforms] + (let [cache (js/Map.)] + (fn [shape] + (let [id (dm/get-prop shape :id) + cached (.get cache id)] + (if (and (some? cached) (identical? shape (aget cached 0))) + (aget cached 1) + (let [result (gsh/apply-transform shape (get transforms id))] + (.set cache id #js [shape result]) + result)))))) + (defn calculate-ignore-tree-wasm "Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers" - [transforms objects] + ([transforms objects] + (calculate-ignore-tree-wasm transforms objects (cached-transform transforms))) + ([transforms objects transform] + (let [order (into {} (map-indexed (fn [i id] [id i])) (keys transforms)) - (letfn [(get-ignore-tree - ([ignore-tree shape] - (let [shape-id (dm/get-prop shape :id) - transformed-shape (gsh/apply-transform shape (get transforms shape-id)) + component-shape + (fn [shape parent parent-component] + (cond + (cfh/root? shape) nil + (ctk/instance-root? shape) shape + (and (ctk/instance-head? shape) (not (ctk/in-component-copy? parent))) shape + :else parent-component)) - root - (if (:component-root shape) - shape - (ctn/get-component-shape objects shape {:allow-main? true})) + walk + (fn walk [ignore-tree shape component latest] + (let [index (get order (dm/get-prop shape :id)) + latest (if (and (some? index) (or (nil? latest) (> index (nth latest 0)))) + (if (some? component) + [index component (transform component)] + latest) + latest) + ignore-tree + (if (and (some? latest) (ctk/in-component-copy? shape)) + (let [[_ root transformed-root] latest] + (assoc ignore-tree + (dm/get-prop shape :id) + (check-delta shape root (transform shape) transformed-root))) + ignore-tree)] + (reduce (fn [ignore-tree child] + (walk ignore-tree child (component-shape child shape component) latest)) + ignore-tree + (map (d/getf objects) (:shapes shape))))) - transformed-root - (if (:component-root shape) - transformed-shape - (gsh/apply-transform root (get transforms (:id root))))] + under-transform (volatile! {}) - (get-ignore-tree ignore-tree shape transformed-shape root transformed-root))) - - ([ignore-tree shape root transformed-root] - (let [shape-id (dm/get-prop shape :id) - transformed-shape (gsh/apply-transform shape (get transforms shape-id))] - (get-ignore-tree ignore-tree shape transformed-shape root transformed-root))) - - ([ignore-tree shape transformed-shape root transformed-root] - (let [shape-id (dm/get-prop shape :id) - - ignore-tree - (cond-> ignore-tree - (and (some? root) (ctk/in-component-copy? shape)) - (assoc - shape-id - (check-delta shape root transformed-shape transformed-root))) - - set-child - (fn [ignore-tree child] - (get-ignore-tree ignore-tree child root transformed-root))] - - (->> (:shapes shape) - (map (d/getf objects)) - (reduce set-child ignore-tree)))))] - - ;; we check twice because we want only to search parents of components but once the - ;; tree is traversed we only want to process the objects in components - (->> (keys transforms) - (map #(get objects %)) - (reduce get-ignore-tree nil)))) + under-transform? + (fn under-transform? [id] + (let [parent-id (dm/get-in objects [id :parent-id])] + (cond + (or (nil? parent-id) (= parent-id id)) false + (contains? order parent-id) true + :else + (let [cached (get @under-transform parent-id ::none)] + (if (= cached ::none) + (let [result (under-transform? parent-id)] + (vswap! under-transform assoc parent-id result) + result) + cached)))))] + (->> (keys transforms) + (remove under-transform?) + (keep #(get objects %)) + (reduce (fn [ignore-tree shape] + (let [parent (get objects (:parent-id shape))] + (walk ignore-tree + shape + (component-shape shape parent (ctn/get-component-shape objects parent {:allow-main? true})) + nil))) + nil))))) (defn assoc-position-data [shape position-data old-shape] @@ -703,6 +726,22 @@ (vreset! cache (translate-selrect computed (- tx) (- ty))) computed)))) +(defn- without-inherited-transforms + "Drops the pure-move entries that repeat their parent's transform. Only for WASM, which + applies a parent's move to the subtree; the UI looks transforms up per shape. WASM does not + expand into bool operands nor masks, so those are kept." + [objects modifiers] + (let [transforms (into {} modifiers)] + (into [] + (remove (fn [[id transform]] + (let [parent (get objects (dm/get-in objects [id :parent-id]))] + (and (= transform (get transforms (:id parent))) + (gmt/move? transform) + (not (cfh/bool-shape? parent)) + (not (and (cfh/mask-shape? parent) + (= id (first (:shapes parent))))))))) + modifiers))) + #_:clj-kondo/ignore (defn set-wasm-modifiers [modif-tree & {:keys [ignore-constraints ignore-snap-pixel snap-ignore-axis @@ -764,7 +803,9 @@ (let [propagated (wasm.api/propagate-modifiers geometry-entries snap-pixel? snap-ignore-axis)] (if (seq propagated) propagated root-modifiers)))] (when wasm-ready? - (wasm.api/set-modifiers modifiers)) + (wasm.api/set-modifiers + (cond->> modifiers + translation? (without-inherited-transforms (dsh/lookup-page-objects state))))) (let [ids (into [] xf:map-key geometry-entries) selrect (when wasm-ready? (if (and translation? (not snap-pixel?) selection-rect-cache (seq modifiers)) @@ -816,7 +857,7 @@ ;; propagating geometry, so propagate_modifiers sees the updated state. (doseq [[id {:keys [property value]}] (extract-property-changes modif-tree)] (when (= property :grow-type) - (wasm.api/use-shape id) + (wselect/use-shape id) (wasm.api/set-shape-grow-type value))) (let [objects (dsh/lookup-page-objects state) @@ -869,8 +910,11 @@ :else (into {} (wasm.api/propagate-modifiers geometry-entries snap-pixel? snap-ignore-axis))) + transform-shape + (cached-transform transforms) + ignore-tree - (calculate-ignore-tree-wasm transforms objects) + (calculate-ignore-tree-wasm transforms objects transform-shape) options (-> params @@ -887,22 +931,23 @@ ids (into (set (keys modif-tree)) xf:without-uuid-zero (keys transforms)) + options + (cond-> options + translation? + (assoc :resize-ids + (into [] + (remove (fn [id] + (let [parent-id (dm/get-in objects [id :parent-id])] + (and (contains? ids parent-id) + (= (get transforms id) + (get transforms parent-id)))))) + ids))) + update-shape (fn [shape] - (let [shape-id (dm/get-prop shape :id) - transform (get transforms shape-id) - modifiers (dm/get-in modif-tree [shape-id :modifiers])] - (-> shape - (gsh/apply-transform transform) - (ctm/apply-structure-modifiers modifiers)))) - - bool-ids - (into #{} - (comp - (mapcat (partial cfh/get-parents-with-self objects)) - (filter cfh/bool-shape?) - (map :id)) - ids) + (-> shape + (transform-shape) + (ctm/apply-structure-modifiers (dm/get-in modif-tree [(dm/get-prop shape :id) :modifiers])))) undo-id (js/Symbol)] @@ -914,16 +959,7 @@ (clear-local-transform) (ptk/event ::dwg/move-frame-guides {:ids ids :transforms transforms}) (ptk/event ::dwcm/move-frame-comment-threads transforms) - (dwsh/update-shapes ids update-shape options) - - ;; The update to the bool path needs to be in a different operation because it - ;; needs to have the updated children info. - ;; `update-layout? false`: recalculating a bool path can never change - ;; `:hidden`, and the layout check would recompute the whole boolean - ;; path in WASM once per bool shape just to find that out. - (dwsh/update-shapes bool-ids path/update-bool-shape (assoc options - :with-objects? true - :update-layout? false))) + (dwsh/update-shapes ids update-shape options)) (if undo-transation? (rx/of (dwu/commit-undo-transaction undo-id)) @@ -936,34 +972,6 @@ (filter #(:rotation (get editable-attrs (:type %)))) (map :id))) -;; Rotation use different algorithm to calculate children -;; modifiers (and do not use child constraints). -(defn set-wasm-rotation-modifiers - ([angle shapes] - (set-wasm-rotation-modifiers angle shapes (-> shapes gsh/shapes->rect grc/rect->center))) - - ([angle shapes center] - (ptk/reify ::set-wasm-rotation-modifiers - ptk/EffectEvent - (effect [_ state _] - (ensure-interactive-transform-start!) - (let [objects (dsh/lookup-page-objects state) - ids (sequence xf-rotation-shape shapes) - - get-modifier - (fn [shape] - (ctm/rotation-modifiers shape center angle)) - - modif-tree - (build-modif-tree ids objects get-modifier) - - modifiers - (mapv (fn [[id {:keys [modifiers]}]] - (d/vec2 id (ctm/modifiers->transform modifiers))) - modif-tree)] - - (wasm.api/set-modifiers modifiers)))))) - (defn set-rotation-modifiers ([angle shapes] (set-rotation-modifiers angle shapes (-> shapes gsh/shapes->rect grc/rect->center))) diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index 6945fb00a9..459e4e70b9 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -16,7 +16,7 @@ [app.main.data.common :as dc] [app.main.data.helpers :as dsh] [app.main.data.modal :as modal] - [app.main.data.notifications :as ntf] + [app.main.data.persistence :as dps] [app.main.data.plugins :as dpl] [app.main.data.websocket :as dws] [app.main.data.workspace :as-alias dw] @@ -27,7 +27,6 @@ [app.main.data.workspace.texts :as dwt] [app.main.router :as rt] [app.util.globals :refer [global]] - [app.util.i18n :refer [tr]] [app.util.mouse :as mse] [app.util.object :as obj] [app.util.rxops :as rxs] @@ -47,12 +46,29 @@ (declare handle-pointer-send) (declare handle-export-update) +(defn- resubscribed? + "True when `event` is a new subscription to `file-id`, which a reload of + the open file starts." + [file-id event] + (and (ptk/type? ::initialize event) + (= file-id (:file-id (deref event))))) + (defn initialize + "Subscribes to the notifications of `file-id` and its team until the file + is left or a new subscription starts. A new subscription to the same file + replaces this one, so a reload never doubles the handled messages." [team-id file-id] (ptk/reify ::initialize + IDeref + (-deref [_] {:file-id file-id}) + ptk/WatchEvent (watch [_ state stream] - (let [stopper (rx/filter (ptk/type? ::finalize) stream) + (let [stopped-by (volatile! nil) + stopper (->> stream + (rx/filter #(or (ptk/type? ::finalize %) + (ptk/type? ::initialize %))) + (rx/tap #(vreset! stopped-by %))) profile-id (:profile-id state) organization-id (dm/get-in state [:teams team-id :organization :id]) @@ -106,7 +122,13 @@ (rx/take-until stopper))] - (rx/concat stream (rx/of (dws/send endmsg))))))) + (rx/concat + stream + ;; The server keeps one file subscription per connection: an + ;; unsubscribe sent after the new subscribe would end it + (rx/defer #(if (resubscribed? file-id @stopped-by) + (rx/empty) + (rx/of (dws/send endmsg))))))))) (defn- handle-change-team-role [{:keys [role] :as msg}] @@ -279,10 +301,7 @@ :redo-changes (vec changes) :undo-changes []})) (if (:repaired msg) - (rx/of (ntf/show {:content (tr "workspace.notifications.file-repaired") - :type :toast - :level :info - :timeout nil})) + (rx/of (dps/notify-repaired)) (rx/empty)))))) (defn handle-file-deleted @@ -302,13 +321,27 @@ [:map {:title "handle-file-restored"} [:type :keyword] [:file-id ::sm/uuid] - [:vern :int]]) + [:vern :int] + [:profile-id {:optional true} ::sm/uuid] + [:label {:optional true} :string] + [:created-at {:optional true} ::ct/inst]]) (def ^:private check-file-restored-params (sm/check-fn schema:handle-file-restored)) +(defn- restorer-fullname + "Name of the user who restored the file, when known: by `profile-id`, or + else by the presence of the session that restored it." + [state profile-id session-id] + (when-let [profile-id (or profile-id + (dm/get-in state [:workspace-presence session-id :profile-id]))] + (dm/get-in state [:profiles profile-id :fullname]))) + (defn handle-file-restored - [{:keys [file-id vern] :as msg}] + "Reloads the file a peer restored to an earlier version and tells the + user who restored which version, and whether their edits not saved yet + were dropped with the replaced version." + [{:keys [file-id vern session-id profile-id label created-at] :as msg}] (assert (check-file-restored-params msg) "expected valid parameters") @@ -320,9 +353,16 @@ file (dsh/lookup-file state curr-file-id) curr-vern (:vern file)] - (when (and (= file-id curr-file-id) - (not= vern curr-vern)) - (rx/of (ptk/event ::dw/reload-current-file))))))) + (if (and (= file-id curr-file-id) + (not= vern curr-vern)) + (rx/of (ptk/event ::dw/reload-current-file) + (modal/show :file-restored + {:fullname (restorer-fullname state profile-id session-id) + :label label + :created-at created-at + ;; Made on the replaced version, so the reload drops them + :unsaved (boolean (seq (dps/pending-file-changes state file)))})) + (rx/empty)))))) (def ^:private schema:handle-library-change [:map {:title "handle-library-change"} diff --git a/frontend/src/app/main/data/workspace/pages.cljs b/frontend/src/app/main/data/workspace/pages.cljs index c8b8180c41..ea76aa091c 100644 --- a/frontend/src/app/main/data/workspace/pages.cljs +++ b/frontend/src/app/main/data/workspace/pages.cljs @@ -147,7 +147,12 @@ :workspace-focus-selected))] (cond-> state - exit? (dissoc :workspace-drawing)))))) + exit? (dissoc :workspace-drawing)))) + + ptk/EffectEvent + (effect [_ _ _] + ;; Free the worker's copy of the page and its indexes + (mw/emit! {:cmd :index/finalize :page-id page-id})))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Workspace Page CRUD diff --git a/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs b/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs index 7ea9afe315..fb522f6839 100644 --- a/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs +++ b/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs @@ -132,7 +132,7 @@ :fills [(stroke->fill stroke)] :strokes []} (:even-odd? result) - (assoc :svg-attrs {:fillRule "evenodd"}))))))) + (assoc :svg-attrs {:fill-rule "evenodd"}))))))) (:strokes shape))) (defn convert-selected-strokes-to-path diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index aaab6e6802..ae0831f5f6 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -580,21 +580,57 @@ (let [hover-value (if value #{id} #{})] (assoc-in state [:workspace-local :hover] hover-value))))) +(defn- leave-focus-mode + "Leaves focus mode, bringing back the viewport and the selection from + before it. Shapes removed since then are left out of the selection." + [state] + (let [objects (dsh/lookup-page-objects state) + local (:workspace-pre-focus state) + selected (:selected local)] + (-> state + (assoc :workspace-local + (cond-> local + (some? selected) + (assoc :selected (into (empty selected) (filter #(contains? objects %)) selected)))) + (dissoc :workspace-focus-selected) + (dissoc :workspace-pre-focus)))) + (defn update-focus-shapes + "Adds the `added` shapes to the focus set and takes the `removed` ones + out. Focus mode ends when no focused shape is left, so a later shape + never brings it back on its own." [added removed] - (ptk/reify ::update-focus-shapes - ptk/UpdateEvent - (update [_ state] + (let [left? (volatile! false)] + (ptk/reify ::update-focus-shapes + ptk/UpdateEvent + (update [_ state] + (let [objects (dsh/lookup-page-objects state) + current (:workspace-focus-selected state) + focus (-> current + (set/union added) + (set/difference removed)) + focus (cfh/clean-loops objects focus)] + (cond + (empty? current) + state - (let [objects (dsh/lookup-page-objects state) + (empty? focus) + (do (vreset! left? true) + (leave-focus-mode state)) - focus (-> (:workspace-focus-selected state) - (set/union added) - (set/difference removed)) - focus (cfh/clean-loops objects focus)] + :else + (assoc state :workspace-focus-selected focus)))) - (-> state - (assoc :workspace-focus-selected focus)))))) + ptk/WatchEvent + (watch [_ _ _] + (if @left? + (rx/of (ptk/data-event ::focus-mode-left)) + (rx/empty))) + + ptk/EffectEvent + (effect [_ state _] + (when @left? + (dwvw/maybe-sync-workspace-local-viewport! state)))))) (defn toggle-focus-mode "Zoom in on and center viewport on selection; @@ -615,10 +651,7 @@ (if in-mode? ;; Exit focus, restoring previous viewport, selection, etc - (-> state - (assoc :workspace-local (:workspace-pre-focus state)) - (dissoc :workspace-focus-selected) - (dissoc :workspace-pre-focus)) + (leave-focus-mode state) (if have-selection? ;; Enter focus and save viewport, selection, etc (-> state @@ -633,6 +666,7 @@ ptk/WatchEvent (watch [_ state stream] (let [stopper (rx/filter #(or (= ::toggle-focus-mode (ptk/type %)) + (= ::focus-mode-left (ptk/type %)) (= ::dwpg/finalize-page (ptk/type %))) stream)] (when (d/not-empty? (:workspace-focus-selected state)) (->> (rx/merge diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index cbf974c929..68a34dad95 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -151,7 +151,10 @@ (ptk/reify ::initialize-shape-layout ptk/WatchEvent (watch [_ _ stream] - (let [stopper (rx/filter (ptk/type? ::finalize-shape-layout) stream)] + (let [;; A workspace reload starts a new one, which replaces this one + stopper (rx/filter #(or (ptk/type? ::finalize-shape-layout %) + (ptk/type? ::initialize-shape-layout %)) + stream)] (->> stream (rx/filter (ptk/type? :layout/update)) ;; Keeps each update's page and the shapes that can be laid out; diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 491f7a2c2d..82691dfad9 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -110,7 +110,7 @@ ([ids update-fn {:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id ignore-touched undo-group with-objects? changed-sub-attr changed-item-index - translation? skip-grid-reassignment? skip-component-sync?] + translation? skip-grid-reassignment? skip-component-sync? resize-ids] :or {reg-objects? false save-undo? true stack-undo? false @@ -153,7 +153,7 @@ :ignore-touched ignore-touched :with-objects? with-objects? :skip-grid-reassignment? skip-grid-reassignment?}) - (cond-> reg-objects? (pcb/resize-parents ids)) + (cond-> reg-objects? (pcb/resize-parents (or resize-ids ids))) (pcb/set-translation? translation?) (pcb/set-skip-component-sync? skip-component-sync?))))] ;; Check buffered text candidates when the buffer is committed. @@ -184,6 +184,26 @@ ::update-shapes-buffer-event)))) (rx/empty))))))) +(defn- any-group-like? + "Whether any of `ids` or their ancestors is group-like." + [objects ids] + (let [cache (js/Map.) + group-like? + (fn group-like? [id] + (let [cached (.get cache id)] + (if (some? cached) + cached + (let [shape (get objects id) + parent-id (:parent-id shape) + result (boolean + (or (cfh/group-like-shape? shape) + (and (some? parent-id) + (not= parent-id id) + (group-like? parent-id))))] + (.set cache id result) + result))))] + (boolean (some group-like? ids)))) + (defn update-shapes ([ids update-fn] (update-shapes ids update-fn nil)) @@ -191,7 +211,7 @@ {:as props :keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id ignore-touched undo-group with-objects? changed-sub-attr changed-item-index translation? - skip-grid-reassignment? skip-component-sync?] + skip-grid-reassignment? skip-component-sync? resize-ids] :or {reg-objects? false save-undo? true stack-undo? false @@ -212,10 +232,15 @@ objects (dsh/lookup-page-objects state page-id) ids (into [] (filter some?) ids) + ;; Parent resizing only reads the builder objects for groups + skip-local? + (and translation? (not (any-group-like? objects ids))) + changes (-> (pcb/empty-changes it page-id) (pcb/set-save-undo? save-undo?) (pcb/set-stack-undo? stack-undo?) + (pcb/skip-local skip-local?) (cls/generate-update-shapes ids update-fn objects @@ -250,7 +275,7 @@ (rx/empty)) (if (seq (:redo-changes changes)) - (let [changes (cond-> changes reg-objects? (pcb/resize-parents ids))] + (let [changes (cond-> changes reg-objects? (pcb/resize-parents (or resize-ids ids)))] (rx/of (dch/commit-changes changes))) (rx/empty)) diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index 230a242a93..9b8eb6bd09 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -19,6 +19,7 @@ [app.main.data.workspace.comments :as dwcm] [app.main.data.workspace.drawing :as dwd] [app.main.data.workspace.drawing.common :as dwdc] + [app.main.data.workspace.history :as dwh] [app.main.data.workspace.layers :as dwly] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.shape-layout :as dwsl] @@ -655,7 +656,8 @@ :subsections [:panels] :section [:workspace] :fn #(emit-when-no-readonly - (dw/toggle-layout-flag :document-history))} + (-> (dwh/initialize-history) + (vary-meta assoc ::ev/origin "workspace:shortcuts")))} :toggle-colorpalette {:tooltip (ds/alt "P") :label (fn [] (tr "shortcuts.toggle-colorpalette")) diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index a972edb3d9..48a84f0b30 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -70,7 +70,7 @@ ([svg-data position] (add-svg-shapes nil svg-data position nil)) - ([id svg-data position {:keys [change-selection? ignore-selection?] + ([id svg-data position {:keys [change-selection? ignore-selection? frame-id] :or {ignore-selection? false change-selection? true}}] (ptk/reify ::add-svg-shapes ptk/WatchEvent @@ -88,10 +88,11 @@ base-id (:parent-id base) - frame-id (if (or selected-frame? (empty? selected) - (not= :frame (dm/get-in objects [base-id :type]))) - (ctst/top-nested-frame objects position) - base-id) + frame-id (or frame-id + (if (or selected-frame? (empty? selected) + (not= :frame (dm/get-in objects [base-id :type]))) + (ctst/top-nested-frame objects position) + base-id)) parent-id (if (or selected-frame? (empty? selected) (nil? base)) frame-id diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index e66b378f54..0464cee98b 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -10,15 +10,19 @@ [app.common.attrs :as attrs] [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.exceptions :as ex] [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] [app.common.geom.point :as gpt] [app.common.geom.rect :as grc] [app.common.geom.shapes :as gsh] [app.common.math :as mth] + [app.common.render-wasm.api.select :as wselect] + [app.common.transit :as t] [app.common.types.fills :as types.fills] [app.common.types.modifiers :as ctm] [app.common.types.shape.layout :as ctl] + [app.common.types.shape.text :as cts] [app.common.types.text :as txt] [app.common.uuid :as uuid] [app.main.data.changes :as dch] @@ -42,8 +46,10 @@ [app.render-wasm.api :as wasm.api] [app.render-wasm.api.fonts :as wasm.fonts] [app.render-wasm.text-editor :as wasm.text-editor] + [app.render-wasm.text-paste :as text-paste] [app.util.clipboard :as clipboard] [app.util.text-editor :as ted] + [app.util.text.clipboard :as text-clipboard] [app.util.text.content :as tc] [app.util.text.content.styles :as styles] [app.util.timers :as ts] @@ -90,7 +96,10 @@ (ptk/reify ::initialize-text-reflow ptk/WatchEvent (watch [_ _ stream] - (let [stopper (rx/filter (ptk/type? ::finalize-text-reflow) stream) + (let [;; A workspace reload starts a new one, which replaces this one + stopper (rx/filter #(or (ptk/type? ::finalize-text-reflow %) + (ptk/type? ::initialize-text-reflow %)) + stream) page-stopper (rx/filter page-finalize? stream)] (->> stream (rx/filter (ptk/type? :text/reflow)) @@ -443,6 +452,30 @@ result (if (empty? result) txt/default-text-attrs result)] result)) +(defn- v2-editor-live? + "False for a missing or disposed v2 editor. A component can still hold + the instance for one render after closing the editor disposed it, and + reading its style then throws." + [editor] + (and (some? editor) + (try + (.-currentStyle ^js editor) + true + (catch :default _ false)))) + +(defn- styled-runs + "The text runs of `content` with their styles, without node ids or the + style keys set to nil." + [content] + (mapv (fn [[style text]] [(d/without-nils style) text]) + (txt/content->text+styles content))) + +(defn same-styled-text? + "True when text contents `a` and `b` hold the same text with the same + styles." + [a b] + (= (styled-runs a) (styled-runs b))) + (defn v2-current-text-values [{:keys [editor-instance attrs]}] (let [result (-> (.-currentStyle editor-instance) @@ -462,7 +495,7 @@ [{:keys [editor-styles editor-state editor-instance attrs shape] :as options}] (cond (some? editor-styles) (v3-current-text-values options) - (some? editor-instance) (v2-current-text-values options) + (v2-editor-live? editor-instance) (v2-current-text-values options) (some? editor-state) (v1-current-paragraph-values options) :else (shape-current-values shape txt/is-paragraph-node? attrs))) @@ -479,7 +512,7 @@ [{:keys [editor-styles editor-state editor-instance attrs shape] :as options}] (cond (some? editor-styles) (v3-current-text-values options) - (some? editor-instance) (v2-current-text-values options) + (v2-editor-live? editor-instance) (v2-current-text-values options) (some? editor-state) (v1-current-text-values options) :else (shape-current-values shape txt/is-text-node? attrs))) @@ -1313,7 +1346,15 @@ (ptk/reify ::v2-update-text-shape-content ptk/WatchEvent (watch [it state _] - (if (features/active-feature? state "render-wasm/v1") + (cond + ;; The shape is gone (e.g. a rollback): only forget the edit session + (not (contains? (dsh/lookup-page-objects state) id)) + (rx/of (fn [state] + (-> state + (update :workspace-new-text-shapes disj id) + (update :workspace-text-session-geom (fnil dissoc {}) id)))) + + (features/active-feature? state "render-wasm/v1") (let [;; v3 editor always passes :finalize? from keyword opts; when absent ;; that binds nil and :or defaults do not apply — coerce so undo flags ;; stay strict booleans for changes-builder schema validation. @@ -1437,6 +1478,7 @@ (update :workspace-new-text-shapes disj id) (update :workspace-text-session-geom (fnil dissoc {}) id))))))))) + :else (let [modifiers (get-in state [:workspace-text-modifier id]) new-shape? (contains? (:workspace-new-text-shapes state) id)] (rx/of @@ -1453,6 +1495,17 @@ (gsh/transform-shape (ctm/change-size shape width height)))))) {:undo-group (when new-shape? id)})))))))) +(defn- editor-content-update + "Event saving `content` from the WASM editor, renaming the shape after its text." + [shape-id content & {:keys [finalize?]}] + (let [text (txt/content->text content) + name (when (not= text "") + (txt/generate-shape-name text))] + (v2-update-text-shape-content shape-id content + :update-name? true + :name name + :finalize? finalize?))) + (defn- layout-item "The ancestor of `shape` (or `shape` itself) that is a direct child of its board." [objects shape] @@ -1504,13 +1557,7 @@ "Commits `content` from the WASM editor to the text `shape-id` and, while editing, previews its new size in the layout." [shape-id content & {:keys [finalize?]}] - (let [text (txt/content->text content) - name (when (not= text "") - (txt/generate-shape-name text)) - update-event (v2-update-text-shape-content shape-id content - :update-name? true - :name name - :finalize? finalize?)] + (let [update-event (editor-content-update shape-id content :finalize? finalize?)] (if finalize? update-event (ptk/reify ::v3-update-editor-content @@ -1526,6 +1573,17 @@ (when-let [{:keys [shape-id content]} (wasm.text-editor/text-editor-sync-content)] (v3-update-editor-content shape-id content :finalize? finalize?))) +(defn v3-apply-pending-caret-styles + "Event saving an insertion made at `before` that consumed the pending caret style: + the inserted text gets that style as its own span. Nil when nothing changed." + [shape-id before] + (let [range (wasm.text-editor/typed-range before (wasm.text-editor/caret-position))] + ;; Sync first so the cached content stays index-aligned with WASM. + (wasm.text-editor/text-editor-sync-content) + (if-let [{:keys [content]} (wasm.api/apply-pending-caret-styles! shape-id range)] + (v3-update-editor-content shape-id content) + (v3-sync-editor-content)))) + (defn- sync-editor-content-stream "Stream of the sync event for `reason`, after asking WASM to repaint." [reason] @@ -1535,19 +1593,27 @@ (rx/of event) (rx/empty)))) -(defn- editor-selected-text - "Plain text of the current WASM editor selection, or nil when there is none." - [] +(defn editor-selection-clipboard-data + "Clipboard items for the WASM editor selection, or nil when nothing is selected. + The HTML also carries the styled content for pasting it in Penpot." + [state] (when (and (wasm.text-editor/text-editor-has-focus?) (wasm.text-editor/text-editor-has-selection?)) (let [text (wasm.text-editor/text-editor-export-selection)] - (when (seq text) text)))) - -(defn- write-selection-to-clipboard - "Write `text` as plain text and HTML; Windows apps often prefer CF_HTML." - [text] - (clipboard/to-clipboard-multi {"text/plain" text - "text/html" (clipboard/plain-text->html text)})) + (when (seq text) + (let [shape-id (wasm.text-editor/text-editor-get-active-shape-id) + content (when (features/active-feature? state "text-editor-wasm/v1-html-paste") + (wasm.text-editor/selection-content shape-id)) + geometry (when (some? content) + (wasm.text-editor/content-geometry shape-id content + (wasm.text-editor/selection-start))) + payload (when (some? content) + (text-clipboard/payload->html content + (:current-file-id state) + (:current-team-id state) + geometry))] + {"text/plain" text + "text/html" (clipboard/plain-text->html text payload)}))))) (defn- on-clipboard-error [cause] @@ -1565,9 +1631,9 @@ [] (ptk/reify ::v3-copy-selection ptk/WatchEvent - (watch [_ _ _] - (if-let [text (editor-selected-text)] - (->> (rx/from (write-selection-to-clipboard text)) + (watch [_ state _] + (if-let [data (editor-selection-clipboard-data state)] + (->> (rx/from (clipboard/to-clipboard-multi data)) (rx/ignore) (rx/catch on-clipboard-error)) (rx/empty))))) @@ -1577,9 +1643,9 @@ [] (ptk/reify ::v3-cut-selection ptk/WatchEvent - (watch [_ _ _] - (if-let [text (editor-selected-text)] - (->> (rx/from (write-selection-to-clipboard text)) + (watch [_ state _] + (if-let [data (editor-selection-clipboard-data state)] + (->> (rx/from (clipboard/to-clipboard-multi data)) (rx/mapcat (fn [_] ;; Delete only once the text is safely on the clipboard, ;; so a refused clipboard cannot lose the selection. @@ -1588,23 +1654,210 @@ (rx/catch on-clipboard-error)) (rx/empty))))) +(defn payload-content + "The content of a Penpot text `payload` without what the current file cannot reach. + A payload without team counts as coming from the current team." + [state {:keys [content file-id team-id]}] + (let [current-file-id (:current-file-id state)] + (text-clipboard/clean-content + content + {:valid-file-ids (conj (set (keys (dsh/lookup-libraries state))) current-file-id) + :same-file? (= file-id current-file-id) + :same-team? (or (nil? team-id) (= team-id (:current-team-id state)))}))) + +(defn- decode-copied-shapes + "The copied shapes data in clipboard `text`, or nil when it holds something else." + [text] + (when (str/starts-with? (str/trim (or text "")) "{") + (let [data (ex/ignoring (t/decode-str text))] + (when (and (map? data) + (= :copied-shapes (:type data)) + (map? (:objects data)) + (set? (:selected data))) + data)))) + +(defn copied-text-geometry + "The geometry (see `text-editor/content-geometry`) of each selected text in + `objects` with gradient or image spans, by id, for a shape copy; nil when none." + [state objects selected] + (when (features/active-feature? state "text-editor-wasm/v1-html-paste") + (not-empty + (into {} + (keep (fn [id] + (let [{:keys [content] :as shape} (get objects id)] + (when (cfh/text-shape? shape) + (some->> (wasm.text-editor/content-geometry id content {:para 0 :offset 0}) + (vector id)))))) + selected)))) + +(defn- text-origins + "The span origins of `text` in `text-geometry`, nil for each span without one." + [text-geometry {:keys [id content]}] + (or (get-in text-geometry [id :origins]) + (mapv #(mapv (constantly nil) (:children %)) + (-> content :children first :children)))) + +(defn- pasteable-text? + "True when a copied text shape, which other apps or plugins may have written, can + be pasted into the text editor." + [{:keys [x y content]}] + (and (number? x) (number? y) (cts/valid-content? content))) + +(defn- copied-shapes->payload + "A text payload joining the content of the copied text shapes, top to bottom and + left to right; nil when none of the copied shapes is a text or it is not valid." + [{:keys [objects selected file-id team-id text-geometry]}] + (let [texts (->> selected + (keep #(get objects %)) + (filter cfh/text-shape?))] + (when (and (seq texts) (every? pasteable-text? texts)) + (let [texts (sort-by (juxt :y :x) texts) + payload (cond-> {:file-id file-id + :team-id team-id + :content (assoc-in (:content (first texts)) + [:children 0 :children] + (into [] (mapcat #(-> % :content :children first :children)) texts))} + (some? text-geometry) + (assoc :geometry {:origins (into [] (mapcat (partial text-origins text-geometry)) texts)}))] + (when (text-clipboard/valid-payload? payload) + payload))))) + +(defn- penpot-paste + [state payload] + (let [content (payload-content state payload)] + {:penpot? true + :content content + :geometry (:geometry payload) + :fragment (text-paste/content->fragment content)})) + +(defn- clipboard->paste + "What to paste for clipboard `html` and `text`, or nil: text and text shapes copied + in Penpot keep their styles; any other text takes the caret style." + [state html text] + (let [html-paste? (features/active-feature? state "text-editor-wasm/v1-html-paste") + payload (when html-paste? (text-clipboard/html->payload html)) + shapes (when (and html-paste? (nil? payload)) (decode-copied-shapes text))] + (cond + (some? payload) + (penpot-paste state payload) + + (some? shapes) + (some->> (copied-shapes->payload shapes) (penpot-paste state)) + + :else + (when-let [fragment (or (when (and html-paste? (< 0 (count html) text-clipboard/max-paste-html-length)) + (some-> (text-clipboard/html->fragment html) + (text-clipboard/without-overrides))) + (text-clipboard/text->fragment text))] + {:penpot? false + :fragment fragment})))) + +(defn- target-geometry + "Where the gradient and image spans of `pasted`, inserted at `start`, begin in `shape` + with `content`, laid over the box it gets once resized to its text; nil when none." + [{:keys [id grow-type]} content pasted start] + (when-let [geometry (wasm.text-editor/content-geometry id pasted start)] + (if (= :fixed grow-type) + geometry + ;; Auto-sized boxes only fit their text when resized (for the editor, when it + ;; closes), which drops the vertical-align offset in the measured origins. + (let [{:keys [width height]} (wasm.api/get-text-dimensions id) + fit (fn [{:keys [box] :as origin}] + (let [offset (case (some-> (:vertical-align content) name) + "center" (/ (- (:height box) height) 2) + "bottom" (- (:height box) height) + 0)] + (-> origin + (update :y - offset) + (assoc :box {:width (if (= :auto-width grow-type) width (:width box)) + :height height}))))] + (update geometry :origins (partial mapv (partial mapv #(some-> % fit)))))))) + +(defn- move-pasted-fills + "`content`, the shape's after restyling the paste, with the pasted gradients and + images keeping their place on their spans. Pushes the result to WASM." + [shape content {:keys [geometry] :as paste} start] + (let [pasted (:content paste) + target (when (some? geometry) (target-geometry shape content pasted start))] + (if (some? target) + (let [content (text-paste/move-span-fills content pasted start geometry target)] + (wasm.api/set-shape-text-content (:id shape) content) + content) + content))) + +(defn move-new-text-fills + "Event moving the gradients and images of `payload`, just pasted on the canvas as the + text `id`, so they keep their place on their spans." + [id {:keys [geometry]}] + (ptk/reify ::move-new-text-fills + ptk/WatchEvent + (watch [_ state _] + (let [{:keys [content] :as shape} (dsh/lookup-shape state id) + start {:para 0 :offset 0} + target (when (and (some? shape) (some? geometry) (wasm.api/initialized?)) + (wselect/use-shape id) + (wasm.api/set-shape-text-content id content) + (target-geometry shape content content start))] + (if (some? target) + (rx/of (dwsh/update-shapes + [id] + #(update % :content text-paste/move-span-fills content start geometry target) + {:attrs #{:content}})) + (rx/empty)))))) + +(defn- insert-fragment + "Insert the text of `fragment` at the caret and restyle it: with its own styles + or overrides, else with the pending caret style. Returns the event saving it." + [state {:keys [fragment penpot?] :as paste}] + (let [shape-id (wasm.text-editor/text-editor-get-active-shape-id) + start (wasm.text-editor/selection-start) + styled? (or penpot? (text-paste/styled? fragment)) + pending? (some? (wasm.text-editor/get-pending-caret-styles shape-id))] + (when styled? + (wasm.text-editor/clear-pending-caret-styles!)) + (wasm.text-editor/text-editor-insert-text (text-paste/fragment->text fragment)) + (cond + (nil? start) + (v3-sync-editor-content) + + styled? + (do + ;; Sync first so the cached content stays index-aligned with WASM. + (wasm.text-editor/text-editor-sync-content) + (if-let [{:keys [content]} (wasm.api/apply-paste-styles shape-id fragment start penpot?)] + (let [shape (dsh/lookup-shape state shape-id)] + (v3-update-editor-content shape-id (move-pasted-fills shape content paste start))) + (v3-sync-editor-content))) + + pending? + (v3-apply-pending-caret-styles shape-id start) + + :else + (v3-sync-editor-content)))) + +(defn v3-paste + "Paste clipboard `html` and `text` at the caret, replacing the selection." + [html text] + (ptk/reify ::v3-paste + ptk/WatchEvent + (watch [_ state _] + (if-let [paste (when (wasm.text-editor/text-editor-has-focus?) + (clipboard->paste state html text))] + (let [event (insert-fragment state paste)] + (wasm.api/request-render-preserving-target "text-paste") + (if (some? event) (rx/of event) (rx/empty))) + (rx/empty))))) + (defn v3-paste-text - "Insert the system clipboard text at the caret, replacing the selection." + "Paste the system clipboard at the caret, replacing the selection." [] (ptk/reify ::v3-paste-text ptk/WatchEvent (watch [_ _ _] (if-not (wasm.text-editor/text-editor-has-focus?) (rx/empty) - (->> (rx/from (clipboard/read-text)) - (rx/mapcat (fn [text] - (if (seq text) - (do - ;; Pasted text keeps the surrounding style. - (wasm.text-editor/clear-pending-caret-styles!) - (wasm.text-editor/text-editor-insert-text text) - (sync-editor-content-stream "text-paste")) - (rx/empty)))) + (->> (rx/from (clipboard/read-html-and-text)) + (rx/map (fn [{:keys [html text]}] (v3-paste html text))) (rx/catch on-clipboard-error)))))) (defn v3-select-all diff --git a/frontend/src/app/main/data/workspace/thumbnails.cljs b/frontend/src/app/main/data/workspace/thumbnails.cljs index b83d09842a..baf65f0ccc 100644 --- a/frontend/src/app/main/data/workspace/thumbnails.cljs +++ b/frontend/src/app/main/data/workspace/thumbnails.cljs @@ -361,10 +361,12 @@ (rx/buffer 2 1) (rx/share)) - ;; All commits stream, indepentendly of the source of the commit + ;; All commits stream, indepentendly of the source of the commit. + ;; A rollback's rebase is not a commit but changes the data too. all-commits-s (->> stream - (rx/filter dch/commit?) + (rx/filter #(or (dch/commit? %) + (ptk/type? ::dch/rebase-commits %))) (rx/map deref) (rx/observe-on :async) (rx/with-latest-from workspace-data-s) diff --git a/frontend/src/app/main/data/workspace/thumbnails_wasm.cljs b/frontend/src/app/main/data/workspace/thumbnails_wasm.cljs index 4bcfe857f6..b5b78b92c5 100644 --- a/frontend/src/app/main/data/workspace/thumbnails_wasm.cljs +++ b/frontend/src/app/main/data/workspace/thumbnails_wasm.cljs @@ -122,16 +122,25 @@ (catch :default err (rx/error! subs err))))))) + (get-subtree + [state] + (-> (dsh/lookup-page-objects state file-id page-id) + (cfh/get-children-with-self frame-id))) + (persist-to-server [data-uri] - (let [blob (wapi/data-uri->blob data-uri)] - (->> (rp/cmd! :create-file-object-thumbnail - {:file-id file-id - :object-id object-id - :media blob - :tag "component"}) - (rx/catch rx/empty) - (rx/ignore)))) + ;; A change during the render (a rollback, an edit) makes + ;; it stale; that change brings its own render + (if (= (get-subtree state) (get-subtree @st/state)) + (let [blob (wapi/data-uri->blob data-uri)] + (->> (rp/cmd! :create-file-object-thumbnail + {:file-id file-id + :object-id object-id + :media blob + :tag "component"}) + (rx/catch rx/empty) + (rx/ignore))) + (rx/empty))) (do-render-thumbnail [] diff --git a/frontend/src/app/main/data/workspace/tokens/application.cljs b/frontend/src/app/main/data/workspace/tokens/application.cljs index 13bfd42689..3ab4427ce6 100644 --- a/frontend/src/app/main/data/workspace/tokens/application.cljs +++ b/frontend/src/app/main/data/workspace/tokens/application.cljs @@ -735,9 +735,6 @@ any-variant? (->> shapes vals (some ctk/is-variant?) boolean) resolved-value (get-in resolved-tokens [(:name token) :resolved-value]) - resolved-value (if (contains? cf/flags :tokenscript) - (ts/tokenscript-symbols->penpot-unit resolved-value) - resolved-value) tokenized-attributes (cfo/attributes-map attributes token) type (:type token)] (rx/concat diff --git a/frontend/src/app/main/data/workspace/tokens/color.cljs b/frontend/src/app/main/data/workspace/tokens/color.cljs index a713e76d0f..7112ca8255 100644 --- a/frontend/src/app/main/data/workspace/tokens/color.cljs +++ b/frontend/src/app/main/data/workspace/tokens/color.cljs @@ -7,9 +7,7 @@ (ns app.main.data.workspace.tokens.color (:require [app.common.files.tokens :as cfo] - [app.config :as cf] - [app.main.data.tinycolor :as tinycolor] - [app.main.data.tokenscript :as ts])) + [app.main.data.tinycolor :as tinycolor])) (defn color-bullet-color [token-color-value] (when-let [tc (tinycolor/valid-color token-color-value)] @@ -19,8 +17,5 @@ (tinycolor/->hex-string tc)))) (defn resolved-token-bullet-color [{:keys [resolved-value] :as token}] - (if (contains? cf/flags :tokenscript) - (when (and resolved-value (ts/color-symbol? resolved-value)) - (ts/color-symbol->penpot-color resolved-value)) - (when (and resolved-value (cfo/color-token? token)) - (color-bullet-color resolved-value)))) + (when (and resolved-value (cfo/color-token? token)) + (color-bullet-color resolved-value))) diff --git a/frontend/src/app/main/data/workspace/tokens/errors.cljs b/frontend/src/app/main/data/workspace/tokens/errors.cljs index bb4d06ded7..daa27111c8 100644 --- a/frontend/src/app/main/data/workspace/tokens/errors.cljs +++ b/frontend/src/app/main/data/workspace/tokens/errors.cljs @@ -57,6 +57,22 @@ {:error/code :error.token/number-too-large :error/fn #(str (tr "errors.tokens.number-too-large" %))} + :error.token/division-by-zero + {:error/code :error.token/division-by-zero + :error/fn #(tr "errors.tokens.division-by-zero")} + + :error.token/incompatible-units + {:error/code :error.token/incompatible-units + :error/fn #(tr "errors.tokens.incompatible-units" (str/join ", " %))} + + :error.token/unknown-function + {:error/code :error.token/unknown-function + :error/fn #(tr "errors.tokens.unknown-function" %)} + + :error.token/function-argument-out-of-range + {:error/code :error.token/function-argument-out-of-range + :error/fn #(tr "errors.tokens.function-argument-out-of-range" %)} + ;; Surfaced when a token name conflicts with a token-group prefix ;; across the active sets (e.g. one set defines `a`, another set ;; defines `a.b`). DTCG/StyleDictionary cannot represent both as diff --git a/frontend/src/app/main/data/workspace/tokens/format.cljs b/frontend/src/app/main/data/workspace/tokens/format.cljs index ada74c1df8..759399c3d1 100644 --- a/frontend/src/app/main/data/workspace/tokens/format.cljs +++ b/frontend/src/app/main/data/workspace/tokens/format.cljs @@ -1,6 +1,11 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + (ns app.main.data.workspace.tokens.format (:require - [app.main.data.tokenscript :as ts] [cuerdas.core :as str])) (def category-dictionary @@ -35,39 +40,10 @@ (str/join "\n") (str "\n"))) -(defn- format-structured-token - "Formats tokenscript Token" - [token-symbol] - (->> (.-value token-symbol) - (.entries) - (es6-iterator-seq) - (format-map-entries))) - -(defn format-tokenscript-symbol - [^js tokenscript-symbol] - (cond - (ts/rem-number-with-unit? tokenscript-symbol) - (str (ts/rem->px tokenscript-symbol) "px") - - (ts/color-symbol? tokenscript-symbol) - (ts/color-symbol->hex-string tokenscript-symbol) - - (ts/structured-record-token? tokenscript-symbol) - (format-structured-token tokenscript-symbol) - - (ts/structured-array-token? tokenscript-symbol) - (str/join "\n" (map format-tokenscript-symbol (.-value tokenscript-symbol))) - - :else - (.toString tokenscript-symbol))) - (defn format-token-value "Converts token value of any shape to a string." [token-value] (cond - (ts/tokenscript-symbol? token-value) - (format-tokenscript-symbol token-value) - (map? token-value) (format-map-entries token-value) diff --git a/frontend/src/app/main/data/workspace/tokens/propagation.cljs b/frontend/src/app/main/data/workspace/tokens/propagation.cljs index 04472fd046..2027e5be42 100644 --- a/frontend/src/app/main/data/workspace/tokens/propagation.cljs +++ b/frontend/src/app/main/data/workspace/tokens/propagation.cljs @@ -6,7 +6,6 @@ (ns app.main.data.workspace.tokens.propagation (:require - [app.common.data :as d] [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] [app.common.files.tokens :as cfo] @@ -217,8 +216,7 @@ (when (and tokens-lib tokens-status) (when-let [tokens-tree (cfo/get-tokens-in-active-sets tokens-status tokens-lib)] (->> (if (contains? cf/flags :tokenscript) - (rx/of (-> (ts/resolve-tokens tokens-tree) - (d/update-vals #(update % :resolved-value ts/tokenscript-symbols->penpot-unit)))) + (rx/of (ts/resolve-tokens tokens-tree)) (sd/resolve-tokens tokens-tree)) (rx/mapcat (fn [sd-tokens] diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 640a9fc11f..3e5f8ced3a 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -21,6 +21,7 @@ [app.common.geom.shapes.grid-layout :as gslg] [app.common.logic.shapes :as cls] [app.common.math :as mth] + [app.common.render-wasm.api.select :as wselect] [app.common.types.component :as ctk] [app.common.types.container :as ctn] [app.common.types.modifiers :as ctm] @@ -34,6 +35,7 @@ [app.main.data.changes :as dch] [app.main.data.event :as ev] [app.main.data.helpers :as dsh] + [app.main.data.persistence.lock :as plk] [app.main.data.workspace.collapse :as dwc] [app.main.data.workspace.modifiers :as dwm] [app.main.data.workspace.selection :as dws] @@ -47,7 +49,6 @@ [app.util.array :as array] [app.util.dom :as dom] [app.util.keyboard :as kbd] - [app.util.mouse :as mse] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -148,6 +149,16 @@ (effect [_ _ _] (ms/clear-transform-preview!)))) +(defn cancel-transform + "Drops the preview of a cancelled drag, so its shapes stay where they + were and nothing is committed." + [] + (ptk/reify ::cancel-transform + ptk/WatchEvent + (watch [_ _ _] + (rx/of (dwm/clear-local-transform) + (finish-transform))))) + ;; -- Resize -------------------------------------------------------- (defn- shape-has-image-fill? @@ -340,13 +351,14 @@ (rx/empty) (let [initial-position @ms/mouse-position - stopper (mse/drag-stopper stream) - layout (:workspace-layout state) - page-id (:current-page-id state) - focus (:workspace-focus-selected state) - zoom (dm/get-in state [:workspace-local :zoom] 1) - objects (dsh/lookup-page-objects state page-id) - shape-ids (filterv (comp not :blocked (d/getf objects)) ids)] + cancelled? (volatile! false) + stopper (plk/gesture-stopper stream cancelled?) + layout (:workspace-layout state) + page-id (:current-page-id state) + focus (:workspace-focus-selected state) + zoom (dm/get-in state [:workspace-local :zoom] 1) + objects (dsh/lookup-page-objects state page-id) + shape-ids (filterv (comp not :blocked (d/getf objects)) ids)] (if (empty? shape-ids) (rx/empty) @@ -383,6 +395,7 @@ (->> resize-events-stream (rx/take-until stopper) (rx/last) + (rx/filter (fn [_] (not @cancelled?))) (rx/map #(dwm/apply-wasm-modifiers (dwm/create-modif-tree shape-ids %) @@ -415,13 +428,17 @@ (rx/take-until stopper) (rx/mapcat (fn [] modifiers-stream))) - (if (features/active-feature? state "render-wasm/v1") - (rx/of - (finish-transform)) + (rx/defer + #(cond + @cancelled? + (rx/of (cancel-transform)) - (rx/of - (dwm/apply-modifiers) - (finish-transform)))))))))))) + (features/active-feature? state "render-wasm/v1") + (rx/of (finish-transform)) + + :else + (rx/of (dwm/apply-modifiers) + (finish-transform))))))))))))) (defn start-move-line-point "Drags one endpoint of a straight path while keeping the other fixed." @@ -440,7 +457,8 @@ start-point (path.helpers/segment->point (nth content index)) other-point (path.helpers/segment->point (nth content (if (zero? index) 1 0))) - stopper (mse/drag-stopper stream) + cancelled? (volatile! false) + stopper (plk/gesture-stopper stream cancelled?) ;; Shift constrains the endpoint around the fixed point. position-stream @@ -484,8 +502,10 @@ (rx/last) (rx/mapcat (fn [pos] - (rx/of (move-endpoint start-point false) - (move-endpoint pos true)))))) + (if @cancelled? + (rx/of (move-endpoint start-point false)) + (rx/of (move-endpoint start-point false) + (move-endpoint pos true))))))) (rx/of #(assoc-in % [:workspace-local :transform] nil))))))) (defn trigger-bounding-box-cloaking @@ -617,7 +637,8 @@ ptk/WatchEvent (watch [_ state stream] - (let [stopper (mse/drag-stopper stream) + (let [cancelled? (volatile! false) + stopper (plk/gesture-stopper stream cancelled?) group (gsh/shapes->rect shapes) group-center (grc/rect->center group) initial-angle (gpt/angle @ms/mouse-position group-center) @@ -656,10 +677,13 @@ (->> angle-stream (rx/take-until stopper) (rx/last) + (rx/filter (fn [_] (not @cancelled?))) (rx/map #(dwm/apply-wasm-modifiers (rotation-modifiers % shapes group-center) :ignore-snap-pixel true)))) - (rx/of (finish-transform))) + (rx/defer #(if @cancelled? + (rx/of (cancel-transform)) + (rx/of (finish-transform))))) (let [emit-modifiers (fn [angle] (dwm/set-rotation-modifiers angle shapes group-center))] @@ -675,8 +699,10 @@ (rx/take-until stopper) (rx/last) (rx/map emit-modifiers))) - (rx/of (dwm/apply-modifiers) - (finish-transform))))))))) + (rx/defer #(if @cancelled? + (rx/of (cancel-transform)) + (rx/of (dwm/apply-modifiers) + (finish-transform))))))))))) (defn increase-rotation "Rotate shapes a fixed angle, from a keyboard action." @@ -727,7 +753,7 @@ (watch [_ state stream] (let [initial (deref ms/mouse-position) - stopper (mse/drag-stopper stream {:interrupt? false}) + stopper (plk/gesture-stopper stream (volatile! false) {:interrupt? false}) zoom (get-in state [:workspace-local :zoom] 1) ;; We toggle the selection so we don't have to wait for the event @@ -780,7 +806,7 @@ [target-frame objects position] (if (features/active-feature? @st/state "render-wasm/v1") (do - (wasm.api/use-shape target-frame) + (wselect/use-shape target-frame) (let [cell (wasm.api/get-grid-coords position)] (when (not= cell [-1 -1]) cell))) (gslg/get-drop-cell target-frame objects position))) @@ -820,15 +846,17 @@ duplicate-move-started? (get-in state [:workspace-local :duplicate-move-started?] false) - stopper (mse/drag-stopper stream) - layout (get state :workspace-layout) - zoom (get-in state [:workspace-local :zoom] 1) - focus (:workspace-focus-selected state) + cancelled? (volatile! false) + stopper (plk/gesture-stopper stream cancelled?) + layout (get state :workspace-layout) + zoom (get-in state [:workspace-local :zoom] 1) + focus (:workspace-focus-selected state) exclude-frames (into #{} - (filter (partial cfh/frame-shape? objects)) - (cfh/selected-with-children objects selected)) + (comp (mapcat #(cfh/get-children-ids-with-self objects %)) + (filter (partial cfh/frame-shape? objects))) + selected) exclude-frames-siblings (into exclude-frames @@ -836,6 +864,21 @@ (filter (partial ctl/any-layout-immediate-child-id? objects))) selected) + ;; Frame lookups per pointer move only test frames that can be a target + frames + (ctst/get-frames objects) + + with-target-frames + (fn [excluded] + (vary-meta objects assoc ::ctst/index-frames + (into [] (remove #(contains? excluded (:id %))) frames))) + + target-objects + (with-target-frames exclude-frames) + + target-objects-siblings + (with-target-frames exclude-frames-siblings) + position (->> ms/mouse-position (rx/map #(gpt/to-vec from-position %))) @@ -879,8 +922,9 @@ (rx/map (fn [[move-vector mod?]] (let [position (gpt/add from-position move-vector) - exclude-frames (if mod? exclude-frames exclude-frames-siblings) - target-frame (ctst/top-nested-frame objects position exclude-frames) + target-frame (if mod? + (ctst/top-nested-frame target-objects position) + (ctst/top-nested-frame target-objects-siblings position)) [target-frame _] (ctn/find-valid-parent-and-frame-ids target-frame objects shapes false libraries parent-validation-cache) flex-layout? (ctl/flex-layout? objects target-frame) grid-layout? (ctl/grid-layout? objects target-frame) @@ -958,15 +1002,17 @@ (rx/mapcat (fn [[[_ target-frame drop-index drop-cell] [modifiers snap-ignore-axis]]] (let [undo-id (js/Symbol)] - (rx/of - (dwu/start-undo-transaction undo-id) - (dwm/apply-wasm-modifiers modifiers - :snap-ignore-axis snap-ignore-axis - :undo-transation? false - :subtree-ids-by-id subtree-ids-by-id) - (move-shapes-to-frame ids target-frame drop-index drop-cell) - (finish-transform) - (dwu/commit-undo-transaction undo-id)))))))) + (if @cancelled? + (rx/of (cancel-transform)) + (rx/of + (dwu/start-undo-transaction undo-id) + (dwm/apply-wasm-modifiers modifiers + :snap-ignore-axis snap-ignore-axis + :undo-transation? false + :subtree-ids-by-id subtree-ids-by-id) + (move-shapes-to-frame ids target-frame drop-index drop-cell) + (finish-transform) + (dwu/commit-undo-transaction undo-id))))))))) (rx/merge (->> modifiers-stream @@ -997,13 +1043,15 @@ (rx/mapcat (fn [[[_ target-frame drop-index drop-cell] [modifiers snap-ignore-axis]]] (let [undo-id (js/Symbol)] - (rx/of (dwu/start-undo-transaction undo-id) - ;; Apply the exact final modifiers; the preview may drop the last frame. - (dwm/set-modifiers modifiers false false {:snap-ignore-axis snap-ignore-axis}) - (dwm/apply-modifiers {:undo-transation? false}) - (move-shapes-to-frame ids target-frame drop-index drop-cell) - (finish-transform) - (dwu/commit-undo-transaction undo-id))))))))))))))) + (if @cancelled? + (rx/of (cancel-transform)) + (rx/of (dwu/start-undo-transaction undo-id) + ;; Apply the exact final modifiers; the preview may drop the last frame. + (dwm/set-modifiers modifiers false false {:snap-ignore-axis snap-ignore-axis}) + (dwm/apply-modifiers {:undo-transation? false}) + (move-shapes-to-frame ids target-frame drop-index drop-cell) + (finish-transform) + (dwu/commit-undo-transaction undo-id)))))))))))))))) (def valid-directions #{:up :down :right :left}) @@ -1511,7 +1559,8 @@ :element-type (cfh/get-selected-type objects ids) :moved moved-count})))) - (when (and (some? frame-id) (d/not-empty? changes)) + ;; Shapes that already belong to the frame leave nothing to move + (when (and (some? frame-id) (seq ids)) (rx/of (dch/commit-changes changes) (dwc/expand-collapse frame-id))) (when add-component-to-variant? diff --git a/frontend/src/app/main/data/workspace/undo.cljs b/frontend/src/app/main/data/workspace/undo.cljs index 156bc5b7cb..769f90a929 100644 --- a/frontend/src/app/main/data/workspace/undo.cljs +++ b/frontend/src/app/main/data/workspace/undo.cljs @@ -42,9 +42,11 @@ [app.common.schema :as sm] [app.common.time :as ct] [app.common.types.shape.layout :as ctl] + [app.common.uuid :as uuid] [app.main.data.changes :as dch] [app.main.data.common :as dcm] [app.main.data.helpers :as dsh] + [app.main.data.persistence.lock :as plk] [beicon.v2.core :as rx] [potok.v2.core :as ptk])) @@ -60,6 +62,8 @@ [:undo-changes [:vector cpc/schema:change]] [:redo-changes [:vector cpc/schema:change]] [:undo-group ::sm/uuid] + ;; Undo groups of every commit the entry holds + [:undo-groups {:optional true} [:set ::sm/uuid]] [:tags [:set :keyword]] [:selected-before {:optional true} [:maybe [:set ::sm/uuid]]] [:selected-after {:optional true} [:maybe [:set ::sm/uuid]]] @@ -136,7 +140,7 @@ exists, or creates a new entry if it doesn't. When stacking onto an existing entry, the entry's original timestamp is preserved so the history panel keeps showing when the action originated. Issue #7660." - [state {:keys [undo-changes redo-changes selected-after] :as entry}] + [state {:keys [undo-changes redo-changes undo-group selected-after] :as entry}] (let [index (get-in state [:workspace-undo :index] -1)] (if (>= index 0) (update-in state [:workspace-undo :items index] @@ -144,6 +148,7 @@ (-> item (update :undo-changes #(into undo-changes %)) (update :redo-changes #(into % redo-changes)) + (update :undo-groups (fnil conj #{}) undo-group) (assoc :selected-after selected-after)))) (add-undo-entry state entry)))) @@ -155,6 +160,7 @@ (update-in [:workspace-undo :transaction :redo-changes] #(into % redo-changes)) (cond-> (nil? (get-in state [:workspace-undo :transaction :undo-group])) (assoc-in [:workspace-undo :transaction :undo-group] undo-group)) + (update-in [:workspace-undo :transaction :undo-groups] (fnil conj #{}) undo-group) (cond-> (nil? (get-in state [:workspace-undo :transaction :selected-before])) (assoc-in [:workspace-undo :transaction :selected-before] selected-before)) (assoc-in [:workspace-undo :transaction :selected-after] selected-after) @@ -175,7 +181,9 @@ ptk/UpdateEvent (update [_ state] (let [selected-after (dm/get-in state [:workspace-local :selected]) - entry (assoc entry :selected-after selected-after)] + entry (assoc entry + :selected-after selected-after + :undo-groups #{(:undo-group entry)})] (cond (and (get-in state [:workspace-undo :transaction]) (or (not stack?) @@ -205,7 +213,9 @@ (update state :workspace-undo (fn [undo-state] (-> undo-state - (update :transaction #(d/nilv % empty-tx)) + ;; Commits made while it is open without an undo group of + ;; their own get this one + (update :transaction #(d/nilv % (assoc empty-tx :commit-group (uuid/next)))) (update :transactions-pending assoc id (ct/now)))))) ptk/WatchEvent @@ -237,7 +247,7 @@ (if (and (some? current-tx) (empty? pending-tx)) (-> state - (add-undo-entry current-tx) + (add-undo-entry (dissoc current-tx :commit-group)) (update :workspace-undo dissoc :transaction)) state))) @@ -251,6 +261,36 @@ (update-in [:workspace-undo :transactions-pending] dissoc id) (add-transaction-undo-entry))))) +(defn forget-undo-groups + "Drops the undo history from the first entry holding a commit of + `undo-groups` on, and empties the open transaction when the history is + cut or it holds one of them; its later commits keep the undo group of + its earlier ones. `undo-index`, when not nil, is the index the oldest + reverted undo or redo commit moved away from: the history points there + again." + [undo-groups undo-index] + (ptk/reify ::forget-undo-groups + ptk/UpdateEvent + (update [_ state] + (update state :workspace-undo + (fn [{:keys [items index transaction] :as undo}] + (let [reverted? #(some undo-groups (:undo-groups %)) + first-index (d/index-of-pred items reverted?) + last-index (dec (count items)) + index (if (some? undo-index) + (min undo-index last-index) + (d/nilv index last-index))] + (cond-> (assoc undo :index index) + (some? first-index) + (assoc :items (subvec items 0 first-index) + :index (min index (dec first-index))) + + (and (some? transaction) + (or (some? first-index) (reverted? transaction))) + (assoc :transaction + (assoc empty-tx :commit-group (or (:undo-group transaction) + (:commit-group transaction))))))))))) + (def reinitialize-undo "Clears the undo stack, removing all entries and transactions." (ptk/reify ::reset-undo @@ -292,27 +332,36 @@ (let [objects (dsh/lookup-page-objects state) edition (get-in state [:workspace-local :edition]) drawing (get state :workspace-drawing)] - (when-not (and (or (some? edition) (some? (:object drawing))) - (not (ctl/grid-layout? objects edition))) + ;; While saves fail the history stays still: its commit would be + ;; refused after the index moved + (when-not (or (plk/locked? state) + (and (or (some? edition) (some? (:object drawing))) + (not (ctl/grid-layout? objects edition)))) (let [undo (:workspace-undo state) items (:items undo) index (or (:index undo) (dec (count items)))] (when (and (some? items) (<= -1 dest-index (dec (count items)))) - (let [changes (vec (apply concat - (cond - (< dest-index index) - (->> (subvec items (inc dest-index) (inc index)) - (reverse) - (map :undo-changes)) - (> dest-index index) - (->> (subvec items (inc index) (inc dest-index)) - (map :redo-changes)) - :else [])))] - (when (seq changes) - (rx/of (materialize-undo changes dest-index) - (dch/commit-changes {:redo-changes changes - :undo-changes [] + (let [[redo-changes undo-changes] + (cond + (< dest-index index) + (let [entries (subvec items (inc dest-index) (inc index))] + [(into [] (mapcat :undo-changes) (reverse entries)) + (into [] (mapcat :redo-changes) entries)]) + + (> dest-index index) + (let [entries (subvec items (inc index) (inc dest-index))] + [(into [] (mapcat :redo-changes) entries) + (into [] (mapcat :undo-changes) (reverse entries))]) + + :else + [[] []])] + (when (seq redo-changes) + (rx/of (materialize-undo redo-changes dest-index) + ;; Undoable by a rollback, which also puts the index back + (dch/commit-changes {:redo-changes redo-changes + :undo-changes undo-changes + :undo-index index :origin it :save-undo? false}))))))))))) @@ -329,9 +378,10 @@ edition (get-in state [:workspace-local :edition]) drawing (get state :workspace-drawing)] - ;; Editors handle their own undo's - (when (or (and (nil? edition) (nil? (:object drawing))) - (ctl/grid-layout? objects edition)) + ;; Editors handle their own undo's; nothing is undone while saves fail + (when (and (not (plk/locked? state)) + (or (and (nil? edition) (nil? (:object drawing))) + (ctl/grid-layout? objects edition))) (let [undo (:workspace-undo state) items (:items undo) index (or (:index undo) (dec (count items)))] @@ -356,7 +406,8 @@ (restore-selection (:selected-before first-item)))) (rx/of (materialize-undo changes (dec index)) (dch/commit-changes {:redo-changes changes - :undo-changes [] + :undo-changes (:redo-changes item) + :undo-index index :save-undo? false :origin it}) (restore-selection (:selected-before item)) @@ -370,9 +421,10 @@ edition (get-in state [:workspace-local :edition]) drawing (get state :workspace-drawing)] - ;; Editors handle their own undo's - (when (or (and (nil? edition) (nil? (:object drawing))) - (ctl/grid-layout? objects edition)) + ;; Editors handle their own undo's; nothing is undone while saves fail + (when (and (not (plk/locked? state)) + (or (and (nil? edition) (nil? (:object drawing))) + (ctl/grid-layout? objects edition))) (let [undo (:workspace-undo state) items (:items undo) index (or (:index undo) (dec (count items)))] @@ -394,7 +446,8 @@ (restore-selection (:selected-after last-item)))) (rx/of (materialize-undo changes (inc index)) (dch/commit-changes {:redo-changes changes - :undo-changes [] + :undo-changes (:undo-changes item) + :undo-index index :origin it :save-undo? false}) (restore-selection (:selected-after item)))))))))))) diff --git a/frontend/src/app/main/data/workspace/versions.cljs b/frontend/src/app/main/data/workspace/versions.cljs index 5e376d6a98..30fab0de28 100644 --- a/frontend/src/app/main/data/workspace/versions.cljs +++ b/frontend/src/app/main/data/workspace/versions.cljs @@ -22,8 +22,16 @@ [app.main.repo :as rp] [app.util.i18n :refer [tr]] [beicon.v2.core :as rx] + [cuerdas.core :as str] [potok.v2.core :as ptk])) +(defn repair-snapshot? + "True when `snapshot` is the copy the backend keeps of a file before it + repairs it." + [{:keys [label]}] + (and (string? label) + (str/starts-with? label "internal/repair/"))) + (defonce default-state {:status :loading :data nil @@ -77,7 +85,13 @@ (rx/mapcat (fn [{:keys [id]}] (rx/of (update-versions-state {:editing id}) - (fetch-versions)))))))))) + (fetch-versions)))) + ;; A version of the file without the unsaved changes would + ;; lose them, so none is made and the user is told why + (rx/catch (fn [cause] + (if (= :persistence (:type (ex-data cause))) + (rx/of (ntf/warn (tr "workspace.versions.create-failed-unsaved"))) + (rx/throw cause)))))))))) (defn rename-version [id label] @@ -297,6 +311,10 @@ ;; localized date, instead of the internal snapshot ;; label (e.g. `internal/snapshot/20`). label (cond + (repair-snapshot? snapshot) + (tr "workspace.versions.repair.version" + (ct/format-inst (:created-at snapshot) :localized-date)) + (= "system" (:created-by snapshot)) (tr "workspace.versions.autosaved.version" (ct/format-inst (:created-at snapshot) :localized-date)) diff --git a/frontend/src/app/main/data/workspace/wasm_text.cljs b/frontend/src/app/main/data/workspace/wasm_text.cljs index 435c8082c4..9ff79af325 100644 --- a/frontend/src/app/main/data/workspace/wasm_text.cljs +++ b/frontend/src/app/main/data/workspace/wasm_text.cljs @@ -14,6 +14,7 @@ [app.common.files.helpers :as cfh] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] + [app.common.render-wasm.api.select :as wselect] [app.common.types.modifiers :as ctm] [app.main.data.helpers :as dsh] [app.main.data.workspace :as-alias dw] @@ -39,7 +40,7 @@ ;; text shape is being edited): there is no design state to query, and ;; returning nil makes callers skip the WASM resize/modifier path. (when (and id (wasm.api/initialized?)) - (wasm.api/use-shape id) + (wselect/use-shape id) ;; While the WASM text editor is actively editing it already holds the live ;; content and layout. Re-pushing the content here calls `_clear_shape_text` ;; + `_update_shape_text_layout`, which resets the editor and drops every diff --git a/frontend/src/app/main/errors.cljs b/frontend/src/app/main/errors.cljs index 666a4d846b..5f48fd0c9e 100644 --- a/frontend/src/app/main/errors.cljs +++ b/frontend/src/app/main/errors.cljs @@ -23,7 +23,7 @@ [app.util.dom :as dom] [app.util.globals :as g] [app.util.http :as http] - [app.util.i18n :refer [tr]] + [app.util.i18n :as i18n :refer [tr]] [app.util.timers :as ts] [app.util.webapi :as wapi] [beicon.v2.core :as rx] @@ -506,7 +506,8 @@ (defn flash "Show error notification banner and emit error report. A nil timeout keeps the notification visible until dismissed or replaced. - A `:tag` lets the notification be hidden later by that tag. + A `:tag` lets the notification be hidden later by that tag. `:level` + defaults to `:error`. The payload format is derived from the cause: environment failures get a compact report. The audit event name is the canonical one requested by @@ -537,8 +538,8 @@ synchronously from inside an error handler creates a re-entrant event-processing cycle that can exhaust the JS call stack (RangeError: Maximum call stack size exceeded)." - [& {:keys [type hint cause timeout tag report-link? links] - :or {type :handled timeout 5000}}] + [& {:keys [type hint cause timeout tag report-link? links level] + :or {type :handled timeout 5000 level :error}}] (js/Promise. (fn [resolve _reject] (ts/schedule @@ -563,7 +564,7 @@ (ntf/show (cond-> {:content (or ^boolean hint (tr "errors.generic")) :type :toast - :level :error + :level level :timeout timeout} (some? tag) (assoc :tag tag) @@ -621,8 +622,10 @@ (def ^:private delegated-persistence-types "Save failure causes routed to their own error handler: retaining the - changes cannot resolve them." - #{:authentication :not-found}) + changes cannot resolve them. A save answered not-found (file gone, edit + rights lost, missing data) is not here: it keeps the workspace open and + notifies like any other failed save." + #{:authentication}) (defn- delegated-persistence-failure? [{:keys [type cause-type code]}] @@ -672,8 +675,9 @@ (defn flash-persistence "Notifies a save failure. With `:retrying` the failure is transient and the queue keeps trying in the background, so the toast says the changes - are kept and offers no reload, which would lose them." - [cause & {:keys [retrying]}] + are kept and offers no reload, which would lose them. `:busy` tells that + the server was too busy, not out of reach." + [cause & {:keys [retrying busy]}] (let [data (ex-data cause)] (cond (delegated-persistence-failure? data) @@ -688,7 +692,18 @@ :timeout nil :tag persistence-failed-tag :report-link? true - :hint (tr "errors.save-still-retrying")) + :hint (if busy + (tr "errors.save-still-retrying-busy") + (tr "errors.save-still-retrying"))) + + (= :save-permission-denied (:code data)) + (flash :cause cause + :type (persistence-report-type data) + :timeout nil + :tag persistence-failed-tag + :report-link? true + :links [(reload-page-link)] + :hint (tr "errors.save-permission-lost" (i18n/c (:unsaved-commits data 0)))) :else (flash :cause cause diff --git a/frontend/src/app/main/features/pointer_map.cljs b/frontend/src/app/main/features/pointer_map.cljs index 176cbbb8c3..062998791c 100644 --- a/frontend/src/app/main/features/pointer_map.cljs +++ b/frontend/src/app/main/features/pointer_map.cljs @@ -7,27 +7,54 @@ (ns app.main.features.pointer-map "A frontend specific helpers for work with pointer-map feature" (:require + [app.common.logging :as log] [app.common.transit :as t] [app.main.repo :as rp] [beicon.v2.core :as rx])) +(defn- unresolved-pointers + [m] + (into [] (comp (filter (fn [[_ v]] (t/pointer? v))) (map key)) m)) + +(defn resolve-pointer + "Resolves a single [key pointer] entry into a [key data] pair against + `get-file-fragment`. A failing fragment rejects with the original + cause (logged with file-id and fragment-id) instead of resolving to + a silently pruned entry. Pass a share-id for share-link scoped + resolution, as the viewer does." + ([file-id entry] + (resolve-pointer file-id nil entry)) + ([file-id share-id [key pointer :as kv]] + (if (t/pointer? pointer) + (let [params (cond-> {:file-id file-id :fragment-id @pointer} + (uuid? share-id) + (assoc :share-id share-id))] + (->> (rp/cmd! :get-file-fragment params) + (rx/map :data) + (rx/map #(vector key %)) + (rx/catch (fn [cause] + (log/error :hint "unable to resolve file fragment" + :file-id (str file-id) + :fragment-id (str @pointer) + :cause cause) + (rx/throw cause))))) + (rx/of kv)))) + (defn resolve-file [{:keys [id data] :as file}] - (letfn [(resolve-pointer [[key val :as kv]] - (if (t/pointer? val) - (->> (rp/cmd! :get-file-fragment {:file-id id :fragment-id @val}) - (rx/map #(get % :data)) - (rx/map #(vector key %))) - (rx/of kv))) - - (resolve-pointers [coll] + (letfn [(resolve-pointers [coll] (->> (rx/from (seq coll)) - (rx/merge-map resolve-pointer) + (rx/merge-map (partial resolve-pointer id)) (rx/reduce conj {})))] (->> (rx/zip (resolve-pointers data) (resolve-pointers (:pages-index data))) (rx/take 1) (rx/map (fn [[data pages-index]] - (let [data (assoc data :pages-index pages-index)] + (let [data (assoc data :pages-index pages-index) + pending (concat (unresolved-pointers data) + (unresolved-pointers pages-index))] + (when (seq pending) + (throw (ex-info "unresolved file fragments after resolve" + {:file-id id :keys pending}))) (assoc file :data data))))))) diff --git a/frontend/src/app/main/rasterizer.cljs b/frontend/src/app/main/rasterizer.cljs index 9e32471c38..468fe60c82 100644 --- a/frontend/src/app/main/rasterizer.cljs +++ b/frontend/src/app/main/rasterizer.cljs @@ -25,6 +25,7 @@ (defonce ready? false) (defonce queue #js []) (defonce instance nil) +(defonce started? false) (defonce msgbus (rx/subject)) (defonce origin (-> cf/rasterizer-uri @@ -32,6 +33,7 @@ (dm/str))) (declare send-message!) +(declare init!) (defn- process-queued-messages! [] @@ -86,6 +88,8 @@ :scope "penpot/rasterizer" :payload payload}] + (init!) + (if ^boolean ready? (send-message! message) (queue-message! message)) @@ -108,39 +112,42 @@ (render {:data data :styles styles :width width :result result}))) (defn init! - "Initializes the rasterizer." + "Creates the rasterizer iframe once. It is only needed by the SVG + renderer, so sessions with render-wasm never load it." [] - (let [iframe (dom/create-element "iframe")] - (dom/set-attribute! iframe "id" "rasterizer") - (dom/set-attribute! iframe "src" origin) - (dom/set-attribute! iframe "hidden" true) - (.addEventListener js/window "message" on-message) - (->> (http/fetch {:method :head - :uri cf/rasterizer-uri - :mode :no-cors}) - (rx/map (fn [response] - (let [allowed? (not (.-redirected response))] - (when-not allowed? - (log/err :hint "rasterizer iframe blocked by adblocker" :origin origin)) - allowed?))) - (rx/catch (fn [cause] - (log/err :hint "rasterizer iframe blocked by adblocker" :origin origin :cause cause) - (rx/of false))) + (when-not ^boolean started? + (set! started? true) + (let [iframe (dom/create-element "iframe")] + (dom/set-attribute! iframe "id" "rasterizer") + (dom/set-attribute! iframe "src" origin) + (dom/set-attribute! iframe "hidden" true) + (.addEventListener js/window "message" on-message) + (->> (http/fetch {:method :head + :uri cf/rasterizer-uri + :mode :no-cors}) + (rx/map (fn [response] + (let [allowed? (not (.-redirected response))] + (when-not allowed? + (log/err :hint "rasterizer iframe blocked by adblocker" :origin origin)) + allowed?))) + (rx/catch (fn [cause] + (log/err :hint "rasterizer iframe blocked by adblocker" :origin origin :cause cause) + (rx/of false))) - (rx/subs! (fn [allowed?] - (if allowed? - (do - (dom/append-child! js/document.body iframe) - (set! instance iframe)) + (rx/subs! (fn [allowed?] + (if allowed? + (do + (dom/append-child! js/document.body iframe) + (set! instance iframe)) - (let [new-origin (-> cf/public-uri - (u/join "rasterizer.html") - (dm/str))] - (log/warn :hint "fallback to main domain" :origin new-origin) + (let [new-origin (-> cf/public-uri + (u/join "rasterizer.html") + (dm/str))] + (log/warn :hint "fallback to main domain" :origin new-origin) - (dom/set-attribute! iframe "src" new-origin) - (dom/append-child! js/document.body iframe) + (dom/set-attribute! iframe "src" new-origin) + (dom/append-child! js/document.body iframe) - (set! origin new-origin) - (set! cf/rasterizer-uri cf/public-uri) - (set! instance iframe)))))))) + (set! origin new-origin) + (set! cf/rasterizer-uri cf/public-uri) + (set! instance iframe))))))))) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index bb7fc0392a..61baf8e3af 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -335,6 +335,9 @@ (def workspace-read-only? (l/derived :read-only? workspace-global)) +(def persistence-lock + (l/derived :persistence-lock workspace-global)) + (def workspace-paddings-selected (l/derived :paddings-selected workspace-global)) diff --git a/frontend/src/app/main/render_viewer_wasm.cljs b/frontend/src/app/main/render_viewer_wasm.cljs index 9a6bca0d99..18b34fe5a1 100644 --- a/frontend/src/app/main/render_viewer_wasm.cljs +++ b/frontend/src/app/main/render_viewer_wasm.cljs @@ -9,6 +9,7 @@ (:require [app.common.data.macros :as dm] [app.common.exceptions :as ex] + [app.common.render-wasm.api.select :as wselect] [app.common.render-wasm.wasm :as wasm] [app.render-wasm.api :as wasm.api] [app.util.dom :as dom] @@ -83,14 +84,14 @@ (when (seq include-ids) (wasm.api/set-render-include-filter! include-ids)) (doseq [id clear-fills-ids] - (wasm.api/use-shape id) + (wselect/use-shape id) (wasm.api/clear-shape-fills!))) (defn- viewer-restore-layer-mask! [page-objects clear-fills-ids] (wasm.api/clear-render-include-filter!) (doseq [id clear-fills-ids] - (wasm.api/use-shape id) + (wselect/use-shape id) (wasm.api/set-shape-fills id (get-in page-objects [id :fills] []) false))) (defn- viewer-do-render! diff --git a/frontend/src/app/main/repo.cljs b/frontend/src/app/main/repo.cljs index 8a9e832429..c3dca3b882 100644 --- a/frontend/src/app/main/repo.cljs +++ b/frontend/src/app/main/repo.cljs @@ -211,6 +211,7 @@ {:stream? true} :export-binfile {:response-type :blob} + :retrieve-list-of-builtin-templates {:query-params :all}}) (defn- send! diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index bbae3fec99..5e2075082c 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -20,6 +20,7 @@ [app.main.ui.error-boundary :refer [error-boundary*]] [app.main.ui.exports.files] [app.main.ui.frame-preview :as frame-preview] + [app.main.ui.mcp-authorize :refer [mcp-authorize*]] [app.main.ui.nitrate.entry :as nitrate-entry] [app.main.ui.notifications :as notifications] [app.main.ui.onboarding.questions :refer [questions-modal]] @@ -118,6 +119,9 @@ :auth-verify-token [:? [:> verify-token-page* {:route route}]] + :mcp-authorize + [:> mcp-authorize* {:route route :profile profile}] + :nitrate-entry [:> nitrate-entry/nitrate-entry-page* {:profile profile}] diff --git a/frontend/src/app/main/ui/auth/login.cljs b/frontend/src/app/main/ui/auth/login.cljs index d200c3746d..b43a954986 100644 --- a/frontend/src/app/main/ui/auth/login.cljs +++ b/frontend/src/app/main/ui/auth/login.cljs @@ -70,7 +70,7 @@ [:string {:min 1}]]]) (mf/defc login-form* - [{:keys [params handle-redirect on-success-callback on-recovery-request origin] :as props}] + [{:keys [params handle-redirect on-success-callback on-recovery-request origin hide-recovery-link] :as props}] (let [initial (mf/with-memo [params] params) form (fm/use-form :schema schema:login-form @@ -193,7 +193,8 @@ :label (tr "auth.password") :class (stl/css :form-field)}]]) - (when (and (not= origin :viewer) + (when (and (not hide-recovery-link) + (not= origin :viewer) (or (contains? cf/flags :login) (contains? cf/flags :login-with-password))) [:div {:class (stl/css :form-row :forgot-password-row)} diff --git a/frontend/src/app/main/ui/auth/login_modal.cljs b/frontend/src/app/main/ui/auth/login_modal.cljs new file mode 100644 index 0000000000..e06974b048 --- /dev/null +++ b/frontend/src/app/main/ui/auth/login_modal.cljs @@ -0,0 +1,153 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.main.ui.auth.login-modal + (:require-macros [app.main.style :as stl]) + (:require + [app.config :as cf] + [app.main.data.auth :as da] + [app.main.store :as st] + [app.main.ui.auth.login :refer [login-dialog*]] + [app.main.ui.auth.recovery-request :refer [recovery-request-page*]] + [app.main.ui.auth.register :refer [register-methods* register-success-page* + register-validate-form* terms-service-privacy-policy*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [app.util.keyboard :as k] + [rumext.v2 :as mf])) + +(mf/defc login-register-modal* + [{:keys [params on-close on-register]}] + (let [user-email (mf/use-state "") + register-token (mf/use-state "") + + current-section* (mf/use-state :login) + current-section (deref current-section*) + + set-current-section + (mf/use-fn + #(reset! current-section* %)) + + set-section + (mf/use-fn + (fn [event] + (let [section (-> (dom/get-current-target event) + (dom/get-data "value") + (keyword))] + (set-current-section section)))) + + go-back-to-login + (mf/use-fn + #(set-current-section :login)) + + main-section (or + (= current-section :login) + (= current-section :register) + (= current-section :register-validate)) + close + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (on-close)) + + success-email-sent + (fn [email] + (reset! user-email email) + (set-current-section :email-sent)) + + success-login + (fn [& _] + (.reload js/window.location true)) + + success-register + (fn [data] + (cond + (:invitation-token data) + (st/emit! (da/login-from-register + {:invitation-token (:invitation-token data)})) + + (:is-active data) + (if on-register (on-register) (on-close)) + + :else + (do + (reset! user-email (:email data)) + (set-current-section :email-sent))))] + + [:div {:class (stl/css :modal-overlay) + :on-key-down #(when (k/esc? %) (close %))} + [:div {:class (stl/css :modal-container) + :role "dialog" + :aria-modal true + :aria-labelledby "login-modal-title"} + [:div {:class (stl/css :modal-header)} + [:h2 {:class (stl/css :modal-header-title) + :id "login-modal-title"} (tr "labels.continue-with-penpot")] + [:> icon-button* {:variant "ghost" + :class (stl/css :modal-close) + :aria-label (tr "labels.close") + :on-click close + :icon i/close}]] + + [:div {:class (stl/css :modal-content)} + (case current-section + :login + [:div {:class (stl/css :login-form)} + [:> login-dialog* {:params params + :on-success-callback success-login + :hide-recovery-link true}] + [:div {:class (stl/css :login-links)} + [:div + [:button {:type "button" + :class (stl/css :link) + :on-click set-section + :data-value "recovery-request"} + (tr "auth.forgot-password")]] + (when (contains? cf/flags :registration) + [:div + [:span + (tr "auth.register") " "] + [:button {:type "button" + :class (stl/css :link) + :on-click set-section + :data-value "register"} + (tr "auth.register-submit")]])]] + + :register + [:div {:class (stl/css :login-form)} + [:> register-methods* {:params params :on-success-callback success-register}] + [:div {:class (stl/css :login-links)} + [:div + [:span (tr "auth.already-have-account") " "] + [:button {:type "button" + :class (stl/css :link) + :on-click set-section + :data-value "login"} + (tr "auth.login-here")]]]] + + :register-validate + [:div {:class (stl/css :login-form)} + [:> register-validate-form* {:params {:token @register-token} + :on-success-callback success-email-sent}] + [:div {:class (stl/css :login-links)} + [:div + [:a {:on-click set-section + :data-value "register"} + (tr "labels.go-back")]]]] + + :recovery-request + [:> recovery-request-page* {:go-back-callback go-back-to-login + :on-success-callback success-email-sent}] + + :email-sent + [:div {:class (stl/css :login-form)} + [:> register-success-page* {:params {:email @user-email}}]]) + + (when main-section + [:div {:class (stl/css :login-links)} + [:> terms-service-privacy-policy*]])]]])) diff --git a/frontend/src/app/main/ui/viewer/login.scss b/frontend/src/app/main/ui/auth/login_modal.scss similarity index 82% rename from frontend/src/app/main/ui/viewer/login.scss rename to frontend/src/app/main/ui/auth/login_modal.scss index 92963aae79..28f06b8524 100644 --- a/frontend/src/app/main/ui/viewer/login.scss +++ b/frontend/src/app/main/ui/auth/login_modal.scss @@ -72,3 +72,21 @@ position: relative; color: var(--color-foreground-primary); } + +.link { + font: inherit; + font-weight: 500; + + // A button is a block in its line: the normal line height keeps the box of + // the inline link it replaces. + line-height: normal; + color: var(--color-accent-primary); + background: none; + border: none; + padding: 0; + cursor: pointer; + + &:hover { + text-decoration: underline; + } +} diff --git a/frontend/src/app/main/ui/components/copy_button.cljs b/frontend/src/app/main/ui/components/copy_button.cljs index b3d5c0f1f6..a4db5ddc65 100644 --- a/frontend/src/app/main/ui/components/copy_button.cljs +++ b/frontend/src/app/main/ui/components/copy_button.cljs @@ -9,7 +9,7 @@ (:require [app.common.data.macros :as dm] [app.main.data.event :as-alias ev] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.util.clipboard :as clipboard] [app.util.dom :as dom] [app.util.i18n :refer [tr]] @@ -43,6 +43,6 @@ :on-click on-click} children [:span {:class (stl/css :icon-btn)} - (if active? - deprecated-icon/tick - deprecated-icon/clipboard)]])) + [:> icon* {:icon-id (if active? i/tick i/clipboard) + :size "s" + :class (stl/css :icon)}]]])) diff --git a/frontend/src/app/main/ui/components/copy_button.scss b/frontend/src/app/main/ui/components/copy_button.scss index 3173698c92..cecd60f2f1 100644 --- a/frontend/src/app/main/ui/components/copy_button.scss +++ b/frontend/src/app/main/ui/components/copy_button.scss @@ -4,89 +4,93 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; .copy-button { - @include deprecated.button-style; - - width: 100%; - height: deprecated.$s-32; - border: deprecated.$s-1 solid transparent; - border-radius: deprecated.$br-8; + border: $b-1 solid transparent; + background: none; + cursor: pointer; + inline-size: 100%; + block-size: $sz-32; + border-radius: $br-8; background-color: transparent; box-sizing: border-box; .icon-btn { - @include deprecated.flex-center; + display: flex; + justify-content: center; + align-items: center; + block-size: $sz-32; + min-inline-size: $sz-28; + inline-size: $sz-28; - height: deprecated.$s-32; - min-width: deprecated.$s-28; - width: deprecated.$s-28; - - svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); + .icon { + flex-shrink: 0; + color: var(--color-foreground-secondary); } } &:hover { background-color: var(--color-background-tertiary); color: var(--color-foreground-primary); - border: deprecated.$s-1 solid var(--color-background-tertiary); + border: $b-1 solid var(--color-background-tertiary); - .icon-btn { - svg { - stroke: var(--button-tertiary-foreground-color-active); - } + .icon-btn .icon { + color: var(--color-accent-primary); } } &:focus, &:focus-visible { outline: none; - border: deprecated.$s-1 solid var(--button-tertiary-border-color-focus); + border: $b-1 solid var(--color-accent-primary); background-color: transparent; - color: var(--button-tertiary-foreground-color-focus); + color: var(--color-foreground-primary); - .icon-btn svg { - stroke: var(--button-tertiary-foreground-color-active); + .icon-btn .icon { + color: var(--color-accent-primary); } } } .copy-wrapper { - @include deprecated.button-style; - @include deprecated.copy-wrapper-base; - - width: 100%; - height: fit-content; - text-align: left; - border: deprecated.$s-1 solid transparent; + position: relative; + min-block-size: $sz-32; + inline-size: 100%; + block-size: fit-content; + padding: px2rem(7) 0 px2rem(7) px2rem(7); + border: $b-1 solid transparent; + border-radius: $br-8; + background: none; + box-sizing: border-box; + cursor: pointer; + text-align: start; .icon-btn { - @include deprecated.flex-center; - + display: flex; + justify-content: center; + align-items: center; position: absolute; - top: 0; - right: 0; - height: deprecated.$s-32; - width: deprecated.$s-28; + inset-block-start: 0; + inset-inline-end: 0; + block-size: $sz-32; + inline-size: $sz-28; - svg { - @extend %button-icon-small; - - stroke: var(--button-tertiary-foreground-color-focus); + .icon { + flex-shrink: 0; + color: var(--color-foreground-primary); display: none; } } &:hover { - background-color: var(--button-tertiary-background-color-focus); - color: var(--button-tertiary-foreground-color-focus); - border: deprecated.$s-1 solid var(--button-tertiary-background-color-focus); + background-color: var(--color-background-tertiary); + color: var(--color-foreground-primary); + border: $b-1 solid var(--color-background-tertiary); - .icon-btn svg { + .icon-btn .icon { display: flex; } } @@ -94,8 +98,8 @@ &:focus, &:focus-visible { outline: none; - border: deprecated.$s-1 solid var(--button-tertiary-border-color-focus); + border: $b-1 solid var(--color-accent-primary); background-color: transparent; - color: var(--button-tertiary-foreground-color-focus); + color: var(--color-foreground-primary); } } diff --git a/frontend/src/app/main/ui/components/editable_select.scss b/frontend/src/app/main/ui/components/editable_select.scss index 7257ee14f2..fc5bc19cf8 100644 --- a/frontend/src/app/main/ui/components/editable_select.scss +++ b/frontend/src/app/main/ui/components/editable_select.scss @@ -123,7 +123,6 @@ justify-content: center; align-items: center; fill: none; - stroke-width: 1.33px; visibility: hidden; color: var(--dropdown-element-icon-color); } diff --git a/frontend/src/app/main/ui/components/mcp_menu.cljs b/frontend/src/app/main/ui/components/mcp_menu.cljs new file mode 100644 index 0000000000..8e5091bfd3 --- /dev/null +++ b/frontend/src/app/main/ui/components/mcp_menu.cljs @@ -0,0 +1,152 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.main.ui.components.mcp-menu + (:require-macros [app.main.style :as stl]) + (:require + [app.config :as cf] + [app.main.data.event :as ev] + [app.main.data.notifications :as ntf] + [app.main.data.workspace.mcp :as mcp] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.components.dropdown-menu :refer [dropdown-menu* dropdown-menu-item*]] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.util.clipboard :as clipboard] + [app.util.dom :as dom] + [app.util.i18n :refer [tr]] + [app.util.timers :as ts] + [rumext.v2 :as mf])) + +(defn- cancel-timer! + [timer-ref*] + (when-let [timer (mf/ref-val timer-ref*)] + (ts/dispose! timer) + (mf/set-ref-val! timer-ref* nil))) + +(mf/defc mcp-menu* + {::mf/wrap [mf/memo]} + [{:keys [is-mcp-connected is-connection-requested session-id dashboard]}] + (let [copied-text (tr "workspace.toolbar.mcp-session-copied") + copy-error (tr "errors.clipboard-api-unavailable") + menu-open* (mf/use-state false) + menu-open? (deref menu-open*) + + open-timer* (mf/use-ref nil) + close-timer* (mf/use-ref nil) + + on-toggle-menu + (mf/use-fn + (fn [event] + (dom/stop-propagation event) + (cancel-timer! open-timer*) + (cancel-timer! close-timer*) + (swap! menu-open* not))) + + on-close-menu + (mf/use-fn + (fn [] + (cancel-timer! open-timer*) + (cancel-timer! close-timer*) + (reset! menu-open* false))) + + on-display-menu + (mf/use-fn + (fn [] + (cancel-timer! close-timer*) + (cancel-timer! open-timer*) + (mf/set-ref-val! + open-timer* + (ts/schedule 350 + #(do + (reset! menu-open* true) + (mf/set-ref-val! open-timer* nil)))))) + + on-hide-menu + (mf/use-fn + (fn [] + (cancel-timer! open-timer*) + (cancel-timer! close-timer*) + (mf/set-ref-val! + close-timer* + (ts/schedule 350 + #(do + (reset! menu-open* false) + (mf/set-ref-val! close-timer* nil)))))) + + on-connect + (mf/use-fn + (mf/deps dashboard) + #(st/emit! (mcp/connect-mcp) + (ev/event {::ev/name "connect-mcp-plugin" + ::ev/origin (if dashboard "dashboard:header" "workspace:toolbar")}))) + + on-disconnect + (mf/use-fn + #(st/emit! (mcp/user-disconnect-mcp))) + + on-copy-session + (mf/use-fn + (mf/deps session-id copied-text copy-error) + (fn [] + (-> (clipboard/to-clipboard session-id) + (.then #(st/emit! (ntf/info copied-text))) + (.catch #(st/emit! (ntf/error copy-error))))))] + + (mf/with-effect [] + (fn [] + (cancel-timer! open-timer*) + (cancel-timer! close-timer*))) + + [:div {:class (stl/css-case :mcp-tool true :dashboard dashboard) + :on-pointer-enter on-display-menu + :on-pointer-leave on-hide-menu} + [:> button* {:variant (if dashboard "secondary" "ghost") + :on-click on-toggle-menu + :aria-haspopup true + :aria-expanded menu-open? + :aria-pressed menu-open? + :data-tool "mcp" + :data-testid "mcp-btn"} + [:div {:class (stl/css-case :toolbar-mcp-button true + :selected menu-open?)} + [:span {:class (stl/css-case :toolbar-mcp-button-dot true + :connected is-mcp-connected)}] + [:span {:class (stl/css-case :toolbar-mcp-button-label true + :connected is-mcp-connected)} + (tr "workspace.toolbar.mcp")]]] + + [:div {:class (stl/css :toolbar-mcp-menu)} + [:> dropdown-menu* {:show menu-open? + :on-close on-close-menu + :class (stl/css :toolbar-mcp-dropdown)} + (when (or is-mcp-connected session-id) + [:li {:class (stl/css :toolbar-mcp-dropdown-info) + :role "presentation"} + (when is-mcp-connected + [:span (tr "workspace.toolbar.mcp-connected")]) + (when session-id + [:span (tr "workspace.toolbar.mcp-session-id" session-id)])]) + (when session-id + [:> dropdown-menu-item* {:class (stl/css :toolbar-mcp-dropdown-item) + :on-click on-copy-session} + (tr "workspace.toolbar.mcp-copy-session-id")]) + [:> dropdown-menu-item* {:class (stl/css :toolbar-mcp-dropdown-item) + :on-click (if is-connection-requested on-disconnect on-connect)} + (if is-connection-requested + (tr "workspace.header.menu.mcp.plugin.status.disconnect") + (tr "workspace.header.menu.mcp.plugin.status.connect"))]]]])) + +(mf/defc dashboard-mcp-menu* + [] + (let [mcp-state (mf/deref refs/mcp)] + (when (and (contains? cf/flags :mcp) + (:enabled mcp-state) + (:token-valid mcp-state)) + [:> mcp-menu* {:dashboard true + :is-mcp-connected (= "connected" (:connection-status mcp-state)) + :is-connection-requested (:connection-requested mcp-state) + :session-id (:session-id mcp-state)}]))) diff --git a/frontend/src/app/main/ui/components/mcp_menu.scss b/frontend/src/app/main/ui/components/mcp_menu.scss new file mode 100644 index 0000000000..fd466d4206 --- /dev/null +++ b/frontend/src/app/main/ui/components/mcp_menu.scss @@ -0,0 +1,127 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS SUBSIDIARY SL + +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/spacing.scss" as *; +@use "ds/typography.scss" as t; +@use "ds/z-index.scss" as *; + +.mcp-tool { + position: relative; + + &.dashboard { + --mcp-disconnected-dot-color: var(--color-foreground-secondary); + } +} + +.toolbar-mcp-button { + display: flex; + align-items: center; + gap: var(--sp-xs); +} + +.toolbar-mcp-button-label { + &.connected { + color: var(--color-accent-primary); + } +} + +.toolbar-mcp-button-dot { + position: relative; + display: flex; + flex-shrink: 0; + inline-size: px2rem(6); + block-size: px2rem(6); + border-radius: 50%; + background-color: var(--mcp-disconnected-dot-color, var(--color-background-disabled)); + + &.connected { + background-color: var(--color-accent-primary); + } + + // One-shot "blob" ripple that confirms the moment the tab becomes + // connected (e.g. after using "Switch here"). Triggered purely by the + // `.connected` class appearing, in sync with the color change. + &.connected::after { + content: ""; + position: absolute; + inset: 0; + border-radius: 50%; + background-color: var(--color-accent-primary); + opacity: 0; + animation: mcp-status-blob 0.5s ease-out; + } +} + +@keyframes mcp-status-blob { + from { + opacity: 0.5; + transform: scale(1); + } + + to { + opacity: 0; + transform: scale(2.5); + } +} + +.toolbar-mcp-menu { + position: absolute; + z-index: var(--z-index-dropdown); + left: 0; + top: $sz-36; +} + +// Same look as the workspace main menu (`.base-menu` in main_menu.scss). +// The toolbar overrides `--menu-background-color`, so restore the global +// menu value here. +.toolbar-mcp-dropdown { + --menu-background-color: var(--color-background-tertiary); + + display: flex; + flex-direction: column; + gap: var(--sp-xs); + margin: 0; + padding: var(--sp-xs); + border: $b-2 solid var(--panel-border-color); + border-radius: $br-8; + background-color: var(--menu-background-color); + box-shadow: 0 0 $sz-12 0 var(--menu-shadow-color); +} + +.toolbar-mcp-dropdown-info { + @include t.use-typography("body-small"); + + display: flex; + flex-direction: column; + gap: var(--sp-xs); + padding: var(--sp-s) var(--sp-m); + border-radius: $br-8; + color: var(--color-foreground-secondary); + background-color: var(--color-background-primary); + font-size: px2rem(11); + white-space: nowrap; +} + +.toolbar-mcp-dropdown-item { + @include t.use-typography("body-small"); + + cursor: pointer; + display: flex; + align-items: center; + padding: var(--sp-s) var(--sp-m); + border-radius: $br-8; + color: var(--menu-foreground-color); + background-color: var(--menu-background-color); + white-space: nowrap; + + &:hover { + --menu-foreground-color: var(--menu-foreground-color-hover); + --menu-background-color: var(--menu-background-color-hover); + } +} diff --git a/frontend/src/app/main/ui/components/progress.cljs b/frontend/src/app/main/ui/components/progress.cljs index 52fb0b2393..35ae9f3101 100644 --- a/frontend/src/app/main/ui/components/progress.cljs +++ b/frontend/src/app/main/ui/components/progress.cljs @@ -31,8 +31,8 @@ [] (let [state (mf/deref refs/progress) profile (mf/deref refs/profile) - theme (get profile :theme theme/default) - default-theme? (= theme/default theme) + theme (theme/use-theme profile) + dark-theme? (= "dark" theme) error? (:error state) healthy? (:healthy state) visible? (:visible state) @@ -48,13 +48,13 @@ color (cond error? clr/new-danger - healthy? (if default-theme? + healthy? (if dark-theme? clr/new-primary clr/new-primary-light) (not healthy?) clr/new-warning) background-clr - (if default-theme? + (if dark-theme? clr/background-quaternary clr/background-quaternary-light) diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs index 0f53bd0a96..c83a33e97d 100644 --- a/frontend/src/app/main/ui/dashboard.cljs +++ b/frontend/src/app/main/ui/dashboard.cljs @@ -8,7 +8,6 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data.macros :as dm] - [app.common.exceptions :as ex] [app.config :as cf] [app.main.data.dashboard :as dd] [app.main.data.dashboard.shortcuts :as sc] diff --git a/frontend/src/app/main/ui/dashboard/change_owner.cljs b/frontend/src/app/main/ui/dashboard/change_owner.cljs index b6eb800030..e4a1cc3498 100644 --- a/frontend/src/app/main/ui/dashboard/change_owner.cljs +++ b/frontend/src/app/main/ui/dashboard/change_owner.cljs @@ -12,7 +12,9 @@ [app.common.uuid :as uuid] [app.main.data.modal :as modal] [app.main.ui.components.forms :as fm] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.util.i18n :as i18n :refer [tr]] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -45,8 +47,11 @@ [:div {:class (stl/css :modal-container)} [:div {:class (stl/css :modal-header)} [:h2 {:class (stl/css :modal-title)} (tr "modals.leave-and-reassign.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-content)} [:p {:class (stl/css :modal-msg)} @@ -62,19 +67,16 @@ [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} - [:input {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click modal/hide!}] + [:> button* {:variant "secondary" + :type "button" + :on-click modal/hide!} + (tr "labels.cancel")] - [:input.accept-button - {:type "button" - :class (stl/css-case :accept-btn true - :danger (:valid @form) - :global/disabled (not (:valid @form))) - :disabled (not (:valid @form)) - :value (tr "modals.leave-and-reassign.promote-and-leave") - :on-click on-accept}]]]]])) + [:> button* {:variant (if (:valid @form) "destructive" "primary") + :type "button" + :disabled (not (:valid @form)) + :on-click on-accept} + (tr "modals.leave-and-reassign.promote-and-leave")]]]]])) @@ -149,8 +151,11 @@ [:div {:class (stl/css :modal-organization-container)} [:div {:class (stl/css :modal-header)} [:h2 {:class (stl/css :modal-organization-title)} (tr "modals.before-leave-organization.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-content)} (if (zero? num-teams-to-delete) @@ -169,16 +174,13 @@ [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} - [:input {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click modal/hide!}] + [:> button* {:variant "secondary" + :type "button" + :on-click modal/hide!} + (tr "labels.cancel")] - [:input.accept-button - {:type "button" - :class (stl/css-case :accept-btn true - :danger all-valid? - :global/disabled (not all-valid?)) - :disabled (not all-valid?) - :value (tr "modals.leave-and-reassign.promote-and-leave") - :on-click on-accept}]]]]])) + [:> button* {:variant (if all-valid? "destructive" "primary") + :type "button" + :disabled (not all-valid?) + :on-click on-accept} + (tr "modals.leave-and-reassign.promote-and-leave")]]]]])) diff --git a/frontend/src/app/main/ui/dashboard/change_owner.scss b/frontend/src/app/main/ui/dashboard/change_owner.scss index df51c6e86a..d066c372d4 100644 --- a/frontend/src/app/main/ui/dashboard/change_owner.scss +++ b/frontend/src/app/main/ui/dashboard/change_owner.scss @@ -4,8 +4,10 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*, to move with the +// modals refactor) and the input placeholder (%input-with-label, with the inputs). @use "refactor/common-refactor.scss" as deprecated; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/z-index.scss" as *; @@ -20,48 +22,34 @@ } .modal-header { - margin-bottom: deprecated.$s-24; + margin-block-end: var(--sp-xxl); } .modal-title { - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } +// Tooltip wrapper of the close button: it places it in the corner. .modal-close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - margin-bottom: deprecated.$s-24; -} - -.input-wrapper { - @extend %input-with-label; - @include deprecated.body-small-typography; + margin-block-end: var(--sp-xxl); } .action-buttons { @extend %modal-action-btns; } -.cancel-button { - @extend %modal-cancel-btn; -} - -.accept-btn { - @extend %modal-accept-btn; - - &.danger { - @extend %modal-danger-btn; - } -} - .modal-msg { - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); } .teams-container { @@ -75,34 +63,34 @@ display: grid; grid-template-columns: 1fr 2fr; align-items: center; - width: 100%; + inline-size: 100%; } .modal-organization-container { @extend %modal-container-base; overflow-y: auto; - max-height: $sz-512; + max-block-size: $sz-512; } .modal-organization-title { - @include t.use-typography("headline-large"); + @include use-typography("headline-large"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } .modal-organization-msg { - @include t.use-typography("body-large"); + @include use-typography("body-large"); - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); } .team-name { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); } .team-member { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); } diff --git a/frontend/src/app/main/ui/dashboard/comments.cljs b/frontend/src/app/main/ui/dashboard/comments.cljs index 7f0da6b00e..8f2d4b14a8 100644 --- a/frontend/src/app/main/ui/dashboard/comments.cljs +++ b/frontend/src/app/main/ui/dashboard/comments.cljs @@ -15,14 +15,10 @@ [app.main.ui.comments :as cmt] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] - [app.main.ui.ds.foundations.assets.icon :as i] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) -(def ^:private comments-icon-svg - (deprecated-icon/icon-xref :comments (stl/css :comments-icon))) - (mf/defc comments-icon* [{:keys [profile on-show-comments]}] @@ -116,5 +112,7 @@ :key (:page-id tgroup)}])] [:div {:class (stl/css :thread-groups-placeholder)} - comments-icon-svg + [:> icon* {:icon-id i/comments + :size "l" + :class (stl/css :comments-icon)}] (tr "labels.no-comments-available")])]]])) diff --git a/frontend/src/app/main/ui/dashboard/comments.scss b/frontend/src/app/main/ui/dashboard/comments.scss index 4c2b2b86a5..a703749b82 100644 --- a/frontend/src/app/main/ui/dashboard/comments.scss +++ b/frontend/src/app/main/ui/dashboard/comments.scss @@ -4,44 +4,39 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .dashboard-comments-section { - @include deprecated.flex-center; - + display: flex; + justify-content: center; + align-items: center; position: relative; - border-radius: deprecated.$br-8; + border-radius: $br-8; } .thread-groups { - height: calc(100% - deprecated.$s-32); - max-height: deprecated.$s-440; + block-size: calc(100% - #{$sz-32}); + max-block-size: px2rem(440); overflow: auto; } -.thread-group { - display: flex; - flex-direction: column; - font-size: deprecated.$fs-12; -} - .thread-groups-placeholder { align-items: center; display: flex; flex-direction: column; - font-size: deprecated.$fs-12; - padding: deprecated.$s-24; + font-size: px2rem(12); + padding: var(--sp-xxl); text-align: center; color: var(--color-foreground-secondary); } .comments-icon { - @extend %button-icon; - - stroke: var(--icon-foreground); - height: deprecated.$s-24; - width: deprecated.$s-24; - margin-bottom: deprecated.$s-24; + flex-shrink: 0; + color: var(--color-foreground-secondary); + margin-block-end: var(--sp-xxl); } .comment-button { @@ -49,57 +44,44 @@ .unread { position: absolute; - width: deprecated.$s-8; - height: deprecated.$s-8; - border: deprecated.$s-2 solid var(--color-background-tertiary); + inline-size: px2rem(8); + block-size: px2rem(8); + border: $b-2 solid var(--color-background-tertiary); border-radius: 50%; background: var(--color-foreground-error); - top: deprecated.$s-6; - right: deprecated.$s-6; + inset-block-start: px2rem(6); + inset-inline-end: px2rem(6); } } -.comments-icon-small { - @extend %button-icon; - - stroke: var(--comment-icon-small-foreground-color); -} - .dropdown { - @include deprecated.menu-shadow; - + box-shadow: 0 0 $sz-12 0 var(--color-shadow-dark); background-color: var(--color-background-tertiary); - border-radius: deprecated.$br-8; - border: deprecated.$s-1 solid transparent; - bottom: deprecated.$s-4; - height: 40vh; - max-height: deprecated.$s-480; - min-height: deprecated.$s-200; + border-radius: $br-8; + border: $b-1 solid transparent; + inset-block-end: var(--sp-xs); + block-size: 40vh; + max-block-size: $sz-480; + min-block-size: $sz-200; position: absolute; overflow: hidden; - width: 100%; - z-index: deprecated.$z-index-4; - - hr { - margin: 0; - border-color: var(--color-foreground-secondary); - } + inline-size: 100%; + z-index: var(--z-index-dropdown); } .header { display: flex; - height: deprecated.$s-40; + block-size: $sz-40; align-items: center; - padding: 0 deprecated.$s-12; - gap: deprecated.$s-4; + padding: 0 var(--sp-m); + gap: var(--sp-xs); } .header-title { + @include use-typography("headline-small"); + color: var(--color-foreground-secondary); - font-size: deprecated.$fs-11; - line-height: 1.28; flex-grow: 1; - text-transform: uppercase; } .notifications-button { @@ -108,7 +90,7 @@ } &.mark-all-as-read-button { - border-radius: deprecated.$s-8; - border: deprecated.$s-1 solid; + border-radius: $br-8; + border: $b-1 solid; } } diff --git a/frontend/src/app/main/ui/dashboard/common/header.scss b/frontend/src/app/main/ui/dashboard/common/header.scss new file mode 100644 index 0000000000..2baf23ca7b --- /dev/null +++ b/frontend/src/app/main/ui/dashboard/common/header.scss @@ -0,0 +1,91 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS SUBSIDIARY SL + +// Page header shared by the dashboard and settings pages. + +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; + +.dashboard-header { + display: flex; + align-items: center; + justify-content: space-between; + position: relative; + block-size: $sz-64; + max-block-size: $sz-64; + padding: var(--sp-xs) var(--sp-l) var(--sp-xs) var(--sp-s); + user-select: none; + + &.team { + display: grid; + grid-template-columns: 20% 1fr 20%; + } +} + +.dashboard-title { + display: flex; + align-items: center; + margin-inline-start: var(--sp-m); +} + +.dashboard-title-text { + display: block; + flex-shrink: 0; + inline-size: 100%; + max-inline-size: $sz-712; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + user-select: all; + color: var(--color-foreground-primary); +} + +.dashboard-header-actions { + display: flex; + column-gap: var(--sp-l); +} + +.dashboard-buttons { + display: flex; + justify-content: flex-end; + align-items: center; +} + +.dashboard-header-menu { + display: flex; + align-items: flex-end; + justify-content: center; +} + +.dashboard-header-options { + display: flex; + justify-content: center; + margin: 0; +} + +.dashboard-header-link { + @include use-typography("body-large"); + + --dashboard-header-link-color: var(--color-foreground-secondary); + + display: flex; + align-items: center; + block-size: $sz-40; + padding: var(--sp-xs) var(--sp-xxl); + border-block-end: px2rem(3) solid transparent; + color: var(--dashboard-header-link-color); + + &:hover { + --dashboard-header-link-color: var(--color-foreground-primary); + + text-decoration: none; + } + + &.active { + --dashboard-header-link-color: var(--color-foreground-primary); + } +} diff --git a/frontend/src/app/main/ui/dashboard/deleted.cljs b/frontend/src/app/main/ui/dashboard/deleted.cljs index 2f41c028e6..014d744734 100644 --- a/frontend/src/app/main/ui/dashboard/deleted.cljs +++ b/frontend/src/app/main/ui/dashboard/deleted.cljs @@ -16,13 +16,17 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.context-menu-a11y :refer [context-menu*]] + [app.main.ui.components.mcp-menu :refer [dashboard-mcp-menu*]] [app.main.ui.dashboard.grid :refer [grid*]] [app.main.ui.dashboard.layout-toggle :as lt :refer [layout-toggle*]] [app.main.ui.dashboard.subscription :refer [get-subscription-type]] [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]] [app.main.ui.hooks :as hooks] - [app.main.ui.icons :as deprecated-icon] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] @@ -32,9 +36,6 @@ (def ^:private ref:deleted-files (l/derived :deleted-files st/state)) -(def ^:private menu-icon - (deprecated-icon/icon-xref :menu (stl/css :menu-icon))) - (defn- on-restore-project [project] (let [on-accept #(st/emit! (dd/restore-project-immediately project))] @@ -62,8 +63,12 @@ [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-deleted-title {:class (stl/css :dashboard-title)} - [:h1 (tr "dashboard.projects-title")]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (tr "dashboard.projects-title")]] [:div {:class (stl/css :dashboard-header-actions)} + [:> dashboard-mcp-menu*] [:> layout-toggle* {:layout layout :on-change on-change}]]]) (mf/defc project-context-menu* @@ -156,13 +161,14 @@ [:div {:class (stl/css :info-wrapper)} [:div {:class (stl/css-case :project-actions true)} - [:button {:class (stl/css :options-btn) - :on-click on-menu-click - :title (tr "dashboard.options") - :aria-label (tr "dashboard.options") - :data-testid "project-options" - :on-key-down handle-menu-click} - menu-icon]] + [:> icon-button* {:variant "ghost" + :class (stl/css :options-btn) + :icon i/menu + :aria-label (tr "dashboard.options") + :aria-pressed (boolean (:menu-open @local)) + :data-testid "project-options" + :on-click on-menu-click + :on-key-down handle-menu-click}]] (when (:menu-open @local) [:> project-context-menu* diff --git a/frontend/src/app/main/ui/dashboard/deleted.scss b/frontend/src/app/main/ui/dashboard/deleted.scss index d6e9b376ea..48ca1cfd71 100644 --- a/frontend/src/app/main/ui/dashboard/deleted.scss +++ b/frontend/src/app/main/ui/dashboard/deleted.scss @@ -4,19 +4,19 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; -@use "common/refactor/common-dashboard"; -@use "ds/typography.scss" as t; +@use "dashboard/common/header"; +@use "ds/typography.scss" as *; @use "ds/spacing.scss" as *; @use "ds/z-index.scss" as *; @use "ds/_borders.scss" as *; @use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; .dashboard-container { flex: 1 0 0; - width: 100%; + inline-size: 100%; margin-inline-end: var(--sp-l); - border-top: $b-1 solid var(--panel-border-color); + border-block-start: $b-1 solid var(--color-background-quaternary); overflow-y: auto; padding-block-end: var(--sp-xxxl); } @@ -31,10 +31,10 @@ display: block; color: var(--color-foreground-secondary); - @include t.use-typography("body-large"); + @include use-typography("body-large"); line-height: 0.8; - height: var(--sp-xl); + block-size: var(--sp-xl); } .info-text-highlight { @@ -43,7 +43,7 @@ .deleted-options { display: flex; - gap: 5px; + gap: px2rem(5); flex-shrink: 0; } @@ -51,12 +51,12 @@ background: var(--color-background-default); padding: var(--sp-xxl) var(--sp-xxl) var(--sp-s) var(--sp-xxl); position: sticky; - top: 0; + inset-block-start: 0; z-index: var(--z-index-panels); } .nav-inside { - border-bottom: $b-1 solid var(--panel-border-color); + border-block-end: $b-1 solid var(--color-background-quaternary); display: flex; gap: var(--sp-l); justify-content: space-between; @@ -74,7 +74,7 @@ .selected { color: var(--color-foreground-primary); - border-bottom: $b-1 solid var(--color-foreground-primary); + border-block-end: $b-1 solid var(--color-foreground-primary); } .project { @@ -83,8 +83,8 @@ align-items: center; justify-content: space-between; gap: var(--sp-s); - width: 99%; - max-height: $sz-40; + inline-size: 99%; + max-block-size: $sz-40; padding: var(--sp-s) var(--sp-s) var(--sp-s) var(--sp-l); margin-block-start: var(--sp-l); border-radius: $br-4; @@ -94,19 +94,19 @@ display: flex; align-items: center; justify-content: flex-start; - width: 100%; - min-height: var(--sp-xxxl); + inline-size: 100%; + min-block-size: var(--sp-xxxl); margin-inline-start: var(--sp-s); } .project-name { - @include t.use-typography("body-large"); + @include use-typography("body-large"); - width: fit-content; + inline-size: fit-content; margin-inline-end: var(--sp-m); line-height: 0.8; - color: var(--title-foreground-color-hover); - height: var(--sp-l); + color: var(--color-foreground-primary); + block-size: var(--sp-l); } .project-actions { @@ -115,14 +115,8 @@ margin-inline-start: var(--sp-xxxl); } -.add-file-btn, .options-btn { - @extend %button-tertiary; - - height: var(--sp-xxxl); - width: var(--sp-xxxl); margin: 0 var(--sp-s); - padding: var(--sp-s); } .info-wrapper { @@ -130,10 +124,3 @@ align-items: center; gap: var(--sp-s); } - -.add-icon, -.menu-icon { - @extend %button-icon; - - stroke: var(--icon-foreground); -} diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index 7a0abddea5..2e5dfa1596 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -120,8 +120,7 @@ on-duplicate (fn [] - (apply st/emit! (map dd/duplicate-file files)) - (st/emit! (ntf/success (tr "dashboard.success-duplicate-file" (i18n/c file-count))))) + (st/emit! (dd/duplicate-files files))) on-delete-accept (fn [_] diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index b3318fc6f8..b2df3175e5 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -14,6 +14,7 @@ [app.main.data.project :as dpj] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.components.mcp-menu :refer [dashboard-mcp-menu*]] [app.main.ui.dashboard.grid :refer [grid*]] [app.main.ui.dashboard.import :as udi] [app.main.ui.dashboard.inline-edition :refer [inline-edition]] @@ -22,6 +23,8 @@ [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]] [app.main.ui.hooks :as hooks] [app.util.dom :as dom] @@ -83,7 +86,10 @@ :data-testid "dashboard-header"} (if (:is-default project) [:div#dashboard-drafts-title {:class (stl/css :dashboard-title)} - [:h1 (tr "labels.drafts")]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (tr "labels.drafts")]] (if (and (:edition @local) can-edit) [:& inline-edition @@ -96,12 +102,16 @@ (swap! local assoc :edition false))) :max-length 250}] [:div {:class (stl/css :dashboard-title)} - [:h1 {:on-double-click on-edit - :data-testid "project-title" - :id (:id project)} + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text) + :on-double-click on-edit + :data-testid "project-title" + :id (:id project)} (:name project)]])) [:div {:class (stl/css :dashboard-header-actions)} + [:> dashboard-mcp-menu*] [:> layout-toggle* {:layout layout :on-change on-change}] diff --git a/frontend/src/app/main/ui/dashboard/files.scss b/frontend/src/app/main/ui/dashboard/files.scss index 5ab0695cad..f8a5186c38 100644 --- a/frontend/src/app/main/ui/dashboard/files.scss +++ b/frontend/src/app/main/ui/dashboard/files.scss @@ -4,7 +4,7 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "ds/_borders.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_utils.scss" as *; diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs index 5574f21572..b42014ffba 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.cljs +++ b/frontend/src/app/main/ui/dashboard/fonts.cljs @@ -21,8 +21,13 @@ [app.main.store :as st] [app.main.ui.components.context-menu-a11y :refer [context-menu*]] [app.main.ui.components.file-uploader :refer [file-uploader]] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.product.loader :refer [loader*]] [app.main.ui.notifications.context-notification :refer [context-notification]] [app.util.dom :as dom] [app.util.http :as http] @@ -71,7 +76,10 @@ (use-page-title team section) [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-fonts-title {:class (stl/css :dashboard-title)} - [:h1 (tr "labels.fonts")]]]) + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (tr "labels.fonts")]]]) (mf/defc font-variant-display-name* {::mf/private true} @@ -195,13 +203,18 @@ [:div {:class (stl/css :dashboard-fonts-upload)} [:div {:class (stl/css :dashboard-fonts-hero)} [:div {:class (stl/css :desc)} - [:h2 (tr "labels.upload-custom-fonts")] - [:> i18n/tr-html* {:content (tr "dashboard.fonts.hero-text1")}] + [:> heading* {:level 2 + :typography t/title-large + :class (stl/css :hero-title)} + (tr "labels.upload-custom-fonts")] + [:> i18n/tr-html* {:content (tr "dashboard.fonts.hero-text1") + :class (stl/css :hero-text)}] - [:button {:class (stl/css :btn-primary) - :on-click on-click - :tab-index "0"} - [:span (tr "labels.add-custom-font")] + [:> button* {:variant "primary" + :type "button" + :class (stl/css :add-font-button) + :on-click on-click} + (tr "labels.add-custom-font") [:& file-uploader {:input-id "font-upload" :accept accept-font-types :multi true @@ -223,17 +236,19 @@ [:div {:class (stl/css :font-item :table-row)} [:span (tr "dashboard.fonts.fonts-added" (i18n/c (count fonts)))] [:div {:class (stl/css :table-field :options)} - [:button {:class (stl/css-case - :btn-primary true - :disabled disable-upload-all?) - :on-click on-upload-all - :data-testid "upload-all" - :disabled disable-upload-all?} - [:span (tr "dashboard.fonts.upload-all")]] - [:button {:class (stl/css :btn-secondary) - :on-click on-dismis-all - :data-testid "dismiss-all"} - [:span (tr "dashboard.fonts.dismiss-all")]]]]) + [:> button* {:variant "primary" + :type "button" + :class (stl/css :upload-button) + :on-click on-upload-all + :data-testid "upload-all" + :disabled disable-upload-all?} + (tr "dashboard.fonts.upload-all")] + [:> button* {:variant "secondary" + :type "button" + :class (stl/css :dismiss-button) + :on-click on-dismis-all + :data-testid "dismiss-all"} + (tr "dashboard.fonts.dismiss-all")]]]) (for [{:keys [id] :as item} (sort-by :font-family font-vals)] (let [uploading? (contains? uploading id) @@ -242,6 +257,7 @@ :key (dm/str id)} [:div {:class (stl/css :table-field :family)} [:input {:type "text" + :class (stl/css :name-input) :data-id (dm/str id) :on-blur on-blur-name :on-change on-change-name @@ -256,23 +272,23 @@ [:div {:class (stl/css :table-field :options)} (when (:height-warning? item) - [:span {:class (stl/css :icon :failure)} - deprecated-icon/msg-neutral]) + [:> icon* {:icon-id i/msg-neutral + :class (stl/css :warning-icon)}]) - [:button {:on-click on-upload - :data-id (dm/str id) - :class (stl/css-case - :btn-primary true - :upload-button true - :disabled disable-upload?) - :disabled disable-upload?} + [:> button* {:variant "primary" + :type "button" + :on-click on-upload + :data-id (dm/str id) + :class (stl/css :upload-button) + :disabled disable-upload?} (if ^boolean uploading? (tr "labels.uploading") (tr "labels.upload"))] - [:span {:class (stl/css :icon :close) - :data-id (dm/str id) - :on-click on-delete} - deprecated-icon/close]]]))]])) + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.remove") + :icon i/close + :data-id (dm/str id) + :on-click on-delete}]]]))]])) (mf/defc installed-font-context-menu* {::mf/private true} @@ -292,7 +308,7 @@ :show is-open :fixed false :min-width true - :top -15 + :top -21 :left -115 :options options}])) @@ -406,6 +422,7 @@ :is-edition edition?)} (if ^boolean edition? [:input {:type "text" + :class (stl/css :name-input) :auto-focus true :default-value font-family :on-key-down on-key-down @@ -420,29 +437,37 @@ [:span {:class (stl/css :label)} [:> font-variant-display-name* {:variant item}]] (when can-edit - [:span - {:class (stl/css :icon :close) - :data-id (dm/str id) - :on-click on-delete-variant} - deprecated-icon/add])])] + [:button {:type "button" + :class (stl/css :variant-delete) + :aria-label (tr "dashboard.fonts.delete-variant") + :title (tr "dashboard.fonts.delete-variant") + :data-id (dm/str id) + :on-click on-delete-variant} + [:> icon* {:icon-id i/close + :size "s" + :aria-hidden true}]])])] (if ^boolean edition? [:div {:class (stl/css :table-field :options)} - [:button - {:disabled (str/blank? font-family) - :on-click on-save - :class (stl/css-case :btn-primary true - :btn-disabled (str/blank? font-family))} + [:> button* {:variant "primary" + :type "button" + :class (stl/css :upload-button) + :disabled (str/blank? font-family) + :on-click on-save} (tr "labels.save")] - [:button {:class (stl/css :icon :close) - :on-click on-cancel} - deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.cancel") + :icon i/close + :on-click on-cancel}]] (when can-edit [:div {:class (stl/css :table-field :options)} - [:span {:class (stl/css :icon) - :on-click on-menu-open} - deprecated-icon/menu] + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.options") + :aria-haspopup "menu" + :aria-expanded menu-open? + :icon i/menu + :on-click on-menu-open}] [:> installed-font-context-menu* {:on-close on-menu-close @@ -468,12 +493,16 @@ (cond (seq fonts) [:* - [:h3 (tr "labels.installed-fonts")] + [:> heading* {:level 3 + :typography t/title-small + :class (stl/css :installed-fonts-title)} + (tr "labels.installed-fonts")] [:div {:class (stl/css :installed-fonts-header)} [:div {:class (stl/css :table-field :family)} (tr "labels.font-family")] [:div {:class (stl/css :table-field :variants)} (tr "labels.font-variants")] [:div {:class (stl/css :table-field :search-input)} - [:input {:placeholder (tr "labels.search-font") + [:input {:class (stl/css :search-font-input) + :placeholder (tr "labels.search-font") :default-value "" :on-change on-change}]]] (for [[font-id variants] (->> (vals fonts) @@ -486,13 +515,16 @@ (nil? fonts) [:div {:class (stl/css :fonts-placeholder)} - [:div {:class (stl/css :icon)} deprecated-icon/loader] + [:> loader* {:width 32 + :title (tr "labels.loading")}] [:div {:class (stl/css :label)} (tr "dashboard.loading-fonts")]] :else (if ^boolean can-edit [:div {:class (stl/css :fonts-placeholder)} - [:div {:class (stl/css :icon)} deprecated-icon/text] + [:> icon* {:icon-id i/text + :size "xl" + :class (stl/css :placeholder-icon)}] [:div {:class (stl/css :label)} (tr "dashboard.fonts.empty-placeholder")]] [:> empty-placeholder* diff --git a/frontend/src/app/main/ui/dashboard/fonts.scss b/frontend/src/app/main/ui/dashboard/fonts.scss index c8cbcdf808..cbf77c3966 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.scss +++ b/frontend/src/app/main/ui/dashboard/fonts.scss @@ -4,66 +4,58 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "common/refactor/common-dashboard"; @use "ds/_utils.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_borders.scss" as *; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/spacing.scss" as *; @use "ds/mixins.scss" as *; +@use "dashboard/common/header"; .dashboard-fonts { - border-top: $b-1 solid var(--color-background-quaternary); + border-block-start: $b-1 solid var(--color-background-quaternary); display: flex; flex-direction: column; - padding-left: px2rem(120); - padding-bottom: px2rem(120); + padding-inline-start: px2rem(120); + padding-block-end: px2rem(120); overflow-y: auto; - - .btn-primary { - font-size: px2rem(11); - height: $sz-32; - min-width: px2rem(100); - } } .dashboard-installed-fonts { - max-width: px2rem(1000); - width: 100%; + max-inline-size: px2rem(1000); + inline-size: 100%; display: flex; - margin-top: var(--sp-xxl); + margin-block-start: var(--sp-xxl); flex-direction: column; - h3 { - @include t.use-typography("title-small"); - - color: var(--color-foreground-secondary); - margin: var(--sp-xs); - } - .font-item { color: var(--color-background-secondary); } } +.installed-fonts-title { + color: var(--color-foreground-secondary); + margin: var(--sp-xs); +} + .installed-fonts-header { - @include t.use-typography("headline-small"); + @include use-typography("headline-small"); align-items: center; color: var(--color-foreground-secondary); display: flex; - height: $sz-40; - padding-left: var(--sp-xxl); + block-size: $sz-40; + padding-inline-start: var(--sp-xxl); > .family { @include two-line-text-ellipsis; - min-width: $sz-200; - width: $sz-200; + min-inline-size: $sz-200; + inline-size: $sz-200; } > .variants { - padding-left: var(--sp-m); + padding-inline-start: var(--sp-m); } } @@ -71,31 +63,31 @@ display: flex; flex-grow: 1; justify-content: flex-end; +} - input { - @include t.use-typography("body-medium"); +.search-font-input { + @include use-typography("body-medium"); - background-color: var(--color-background-tertiary); - border-radius: $br-8; - border: $b-1 solid transparent; - color: var(--color-foreground-primary); - height: $sz-32; - margin: 0; - padding: 0 var(--sp-s); - width: px2rem(152); + background-color: var(--color-background-tertiary); + border-radius: $br-8; + border: $b-1 solid transparent; + color: var(--color-foreground-primary); + block-size: $sz-32; + margin: 0; + padding: 0 var(--sp-s); + inline-size: px2rem(152); - &:focus { - outline: $b-1 solid var(--color-accent-primary); - } + &:focus { + outline: $b-1 solid var(--color-accent-primary); + } - &::placeholder { - color: var(--color-foreground-secondary); - } + &::placeholder { + color: var(--color-foreground-secondary); } } .font-item { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); align-items: center; background-color: var(--color-background-tertiary); @@ -103,13 +95,13 @@ color: var(--color-foreground-secondary); display: flex; justify-content: space-between; - margin-top: var(--sp-xs); - max-width: px2rem(1000); + margin-block-start: var(--sp-xs); + max-inline-size: px2rem(1000); padding: var(--sp-m) var(--sp-xxl); - width: 100%; + inline-size: 100%; - input { - @include t.use-typography("body-medium"); + .name-input { + @include use-typography("body-medium"); @include text-ellipsis; border: $b-1 solid transparent; @@ -129,8 +121,8 @@ > .family { @include two-line-text-ellipsis; - min-width: $sz-200; - width: $sz-200; + min-inline-size: $sz-200; + inline-size: $sz-200; &.is-edition { overflow: visible; @@ -140,16 +132,16 @@ > .filenames { @include text-ellipsis; - min-width: $sz-200; + min-inline-size: $sz-200; } > .variants { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); display: flex; flex-wrap: wrap; flex-grow: 1; - padding-left: var(--sp-l); + padding-inline-start: var(--sp-l); gap: $sz-6; .variant { @@ -160,25 +152,11 @@ cursor: pointer; gap: var(--sp-xs); - .icon { - display: flex; - align-items: center; - justify-content: center; - height: $sz-16; - width: $sz-16; - - svg { - fill: none; - width: $sz-12; - height: $sz-12; - transform: rotate(45deg); - } - } + // The delete button only shows its icon while the variant is hovered. + --variant-delete-color: transparent; &:hover { - .icon svg { - stroke: var(--color-foreground-secondary); - } + --variant-delete-color: var(--color-foreground-secondary); } } @@ -197,79 +175,76 @@ } .filenames { - @include t.use-typography("body-small"); + @include use-typography("body-small"); @include text-ellipsis; - min-width: $sz-400; - padding-left: var(--sp-xxxl); + min-inline-size: $sz-400; + padding-inline-start: var(--sp-xxxl); } .options { display: flex; justify-content: flex-end; - min-width: px2rem(180); - - .icon { - width: $sz-24; - cursor: pointer; - display: flex; - margin-left: var(--sp-m); - justify-content: center; - align-items: center; - - svg { - width: $sz-16; - height: $sz-16; - stroke: var(--color-foreground-secondary); - fill: none; - } - - &.failure { - margin-right: var(--sp-m); - - svg { - stroke: var(--element-foreground-warning); - } - } - - &.close { - background: none; - border: none; - - svg { - stroke: var(--color-foreground-secondary); - } - } - } + min-inline-size: px2rem(180); + align-items: center; + gap: var(--sp-s); } } +.variant-delete { + display: flex; + align-items: center; + justify-content: center; + block-size: $sz-16; + inline-size: $sz-16; + padding: 0; + border: none; + background: none; + cursor: pointer; + color: var(--variant-delete-color); + + &:focus-visible { + --variant-delete-color: var(--color-foreground-secondary); + } +} + +// With the 8px gap of .options it keeps 12px on each side. +.warning-icon { + margin-inline: var(--sp-xs); + color: var(--color-accent-warning); +} + +// A DS button keeps its label at the start when it grows. +.upload-button { + min-inline-size: px2rem(100); + justify-content: center; +} + .dashboard-fonts-upload { - max-width: px2rem(1000); - width: 100%; + max-inline-size: px2rem(1000); + inline-size: 100%; display: flex; flex-direction: column; - .upload-button { - width: px2rem(100); - } - - .btn-secondary { - margin-left: var(--sp-m); + .dismiss-button { + margin-inline-start: var(--sp-xs); } } .dashboard-fonts-hero { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); padding: var(--sp-xxxl) 0; - margin-top: px2rem(80); + margin-block-start: px2rem(80); display: flex; justify-content: space-between; - .btn-primary { - height: $sz-40; - width: 100%; + .add-font-button { + --button-height: #{$sz-40}; + + inline-size: 100%; + flex-shrink: 0; + justify-content: center; } .desc { @@ -277,23 +252,18 @@ flex-direction: column; gap: var(--sp-xxl); color: var(--color-background-secondary); - width: $sz-500; + inline-size: $sz-500; - h2 { + .hero-title { color: var(--color-foreground-primary); - font-weight: 400; } - p { - @include t.use-typography("body-large"); + .hero-text { + @include use-typography("body-large"); color: var(--color-foreground-secondary); } } - - .btn-primary { - flex-shrink: 0; - } } .fonts-placeholder { @@ -302,21 +272,18 @@ border: $b-1 solid var(--color-background-quaternary); display: flex; flex-direction: column; - height: $sz-160; + block-size: $sz-160; justify-content: center; - margin-top: var(--sp-l); - max-width: px2rem(1000); - width: 100%; + margin-block-start: var(--sp-l); + max-inline-size: px2rem(1000); + inline-size: 100%; - .icon svg { - stroke: var(--color-foreground-secondary); - fill: none; - width: $sz-32; - height: $sz-32; + .placeholder-icon { + color: var(--color-foreground-secondary); } .label { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-secondary); } diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs index b46a0c53c1..7673e94573 100644 --- a/frontend/src/app/main/ui/dashboard/grid.cljs +++ b/frontend/src/app/main/ui/dashboard/grid.cljs @@ -23,6 +23,7 @@ [app.main.refs :as refs] [app.main.render :as render] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.color-bullet :as bc] [app.main.ui.dashboard.file-menu :refer [file-menu* file-menu-items*]] @@ -293,22 +294,35 @@ library-view? (= origin :libraries) + on-open-new-tab + (mf/use-fn + (mf/deps file-id can-restore) + (fn [] + (when-not can-restore + (st/emit! (dcm/go-to-workspace :file-id file-id ::rt/new-window true))))) + on-select (mf/use-fn - (mf/deps selected? selected-num) + (mf/deps selected? selected-num on-open-new-tab) (fn [event] - (when (or (not selected?) (> selected-num 1)) - (dom/stop-propagation event) - (let [shift? (kbd/shift? event)] - (when-not shift? - (st/emit! (dd/clear-selected-files))) - (st/emit! (dd/toggle-file-select file)))))) + (if (kbd/mod? event) + (do + (dom/stop-propagation event) + (on-open-new-tab)) + (when (or (not selected?) (> selected-num 1)) + (dom/stop-propagation event) + (let [shift? (kbd/shift? event)] + (when-not shift? + (st/emit! (dd/clear-selected-files))) + (st/emit! (dd/toggle-file-select file))))))) on-navigate (mf/use-fn (mf/deps file-id can-restore) (fn [event] - (when-not can-restore + ;; A Ctrl/Cmd double click has already opened the file in new + ;; tabs through on-select; the current tab stays where it is. + (when-not (or can-restore (kbd/mod? event)) (let [menu-icon (mf/ref-val menu-ref) target (dom/get-target event)] (when-not (dom/child? target menu-icon) diff --git a/frontend/src/app/main/ui/dashboard/grid.scss b/frontend/src/app/main/ui/dashboard/grid.scss index 11f25f8f86..df8adf08d6 100644 --- a/frontend/src/app/main/ui/dashboard/grid.scss +++ b/frontend/src/app/main/ui/dashboard/grid.scss @@ -308,7 +308,8 @@ $thumbnail-default-height: px2rem(168); .library-name-block { color: var(--color-foreground-secondary); - inline-size: calc(100% - var(--sp-xxl) - var(--sp-s)); + flex: 1; + min-inline-size: 0; text-align: left; } @@ -381,7 +382,9 @@ $thumbnail-default-height: px2rem(168); block-size: px2rem(20); line-height: 1; margin-inline-end: var(--sp-xs); - inline-size: px2rem(20); + flex-shrink: 0; + min-inline-size: px2rem(20); + white-space: nowrap; overflow: hidden; } diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index dd459da077..048c0d7e73 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -13,6 +13,8 @@ [app.common.logging :as log] [app.main.data.dashboard :as dd] [app.main.data.event :as ev] + [app.main.data.imports :as imp] + [app.main.data.jobs :as dj] [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] [app.main.repo :as rp] @@ -27,9 +29,7 @@ [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.ds.foundations.typography.text :refer [text*]] [app.main.ui.ds.notifications.context-notification :refer [context-notification*]] - [app.main.ui.ds.product.loader :refer [loader*]] - [app.main.ui.icons :as deprecated-icon] - [app.main.worker :as mw] + [app.main.ui.jobs.progress :as jp] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] @@ -50,6 +50,7 @@ (let [entries (->> entries (mapv (fn [file] {:name (.-name file) + :size (.-size file) :uri (wapi/create-uri file)})) (not-empty))] (when entries @@ -108,45 +109,21 @@ (if (= (:file-id entry) (:file-id message)) (let [status (case (:status message) :progress :import-progress - :finish :import-success - :error :import-error)] - (-> entry - (assoc :progress (:progress message)) - (assoc :status status) - (assoc :error (:error message)) - (d/without-nils))) + :started :import-progress + :queued :import-queued + :finish :import-success + :error :import-error)] + ;; a message only carries what it knows: anything it does + ;; not bring (a stale milestone, an old error) is dropped + ;; instead of lingering on the row + (cond-> (assoc entry :status status) + (contains? message :progress) (assoc :progress (:progress message)) + (not (contains? message :progress)) (dissoc :progress) + (contains? message :error) (assoc :error (:error message)) + (not (contains? message :error)) (dissoc :error))) entry)) entries)) -(defn- parse-progress-message - [message] - (case (:type message) - :upload-data - (tr "dashboard.import.progress.upload-data" (:current message) (:total message)) - - :upload-media - (tr "dashboard.import.progress.upload-media" (:file message)) - - :process-page - (tr "dashboard.import.progress.process-page" (:file message)) - - :process-colors - (tr "dashboard.import.progress.process-colors") - - :process-typographies - (tr "dashboard.import.progress.process-typographies") - - :process-media - (tr "dashboard.import.progress.process-media") - - :process-components - (tr "dashboard.import.progress.process-components") - - :process-deleted-components - (tr "dashboard.import.progress.process-components") - - "")) - (defn- has-status-analyze? [item] (= (:status item) :analyze)) @@ -178,69 +155,81 @@ 0 resolution)) +(defn- pending-library-links + "The library links the wizard must create for the files of a resolution: + one per pending entry the user chose a candidate for. The file id comes + from the entry and never from the key of the map: what a key holds depends + on the result schema of the job-def that wrote it, while the wizard indexes + its selection by the id of the entry." + [resolution selection] + (->> (vals resolution) + (mapcat (fn [{:keys [id pending]}] + (keep (fn [entry] + (when-let [library-id (get-in selection [id (:id entry)])] + {:file-id id :library-id library-id})) + pending))))) + (defn- analyze-entries - [state entries] - (let [features (get @st/state :features)] - (->> (mw/ask-many! - {:cmd :analyze-import - :files entries - :features features}) - (rx/mapcat #(rx/delay emit-delay (rx/of %))) - (rx/filter some?) - (rx/subs! - (fn [message] - (when (some? (:error message)) - (st/emit! (ev/event {::ev/name "import-files-error" - :error (:error message)}))) - (swap! state update-with-analyze-result message)))))) + [state entries on-analyze-end] + (->> (imp/analyze entries) + (rx/mapcat #(rx/delay emit-delay (rx/of %))) + (rx/filter some?) + (rx/subs! + (fn [message] + (when (some? (:error message)) + (st/emit! (ev/event {::ev/name "import-files-error" + :error (:error message)}))) + (swap! state update-with-analyze-result message)) + (constantly nil) + ;; the wizard cannot offer its import step until it knows every + ;; file of every container + on-analyze-end))) (defn- import-files - [state library-resolution-data* project-id entries] + [state library-resolution-data* project-id entries on-job] (st/emit! (ev/event {::ev/name "import-files" :num-files (count entries)})) - (let [features (get @st/state :features)] - (->> (mw/ask-many! - {:cmd :import-files - :project-id project-id - :files entries - :features features}) - (rx/filter some?) - (rx/subs! - (fn [message] - ;; Capture library-resolution data if present (same for all - ;; entries from the same zip, so first one wins) - (if-let [resolution (-> (:libraries-resolution message) - (not-empty))] - (reset! library-resolution-data* resolution) - (swap! state update-entry-status message))))))) + (->> (imp/import-files {:project-id project-id + :entries entries + :on-job on-job}) + (rx/filter some?) + (rx/subs! + (fn [message] + ;; the resolution of the libraries of every package arrives once, + ;; when all of them are over + (if-let [resolution (-> (:libraries-resolution message) (not-empty))] + (reset! library-resolution-data* resolution) + (swap! state update-entry-status message)))))) (mf/defc import-entry* {::mf/memo true ::mf/private true} - [{:keys [entries entry edition can-be-deleted is-progress on-edit on-change on-delete]}] + [{:keys [entries entry edition on-edit on-change on-delete]}] (let [status (:status entry) ;; FIXME: rename to format format (:type entry) - loading? (or (= :analyze status) - (= :import-progress status) - (and is-progress (= :import-ready status))) analyze-error? (= :analyze-error status) - import-success? (= :import-success status) import-error? (= :import-error status) import-ready? (= :import-ready status) - level (cond - import-success? :success - import-ready? :success - import-error? :error - analyze-error? :error - loading? nil - :else :default) + progress (:progress entry) + upload? (= :upload (:stage progress)) + + ;; the container the file came in is fully sent, but its job does + ;; not exist yet: the wizard is still uploading the other files + ready? (and (= :import-progress status) + upload? + (imp/upload-done? entry)) + + ;; a file can be dropped while the wizard has not started yet: once + ;; the import runs, its entries are committed + can-be-deleted? (and import-ready? + (> (count entries) 1)) is-shared? (:shared entry) - progress (:progress entry) + subtitle (imp/format-file-size (:size entry)) file-id (:file-id entry) editing? (and (some? file-id) (= edition file-id)) @@ -280,44 +269,42 @@ [:div {:class (stl/css-case :file-entry true - :loading loading? - :success import-success? + :ready ready? + :success (= :import-success status) :error (or import-error? analyze-error?) :editable (and import-ready? (not editing?)))} - [:div {:class (stl/css :file-name)} - (when loading? [:> loader* {:width 26 :title (tr "labels.loading")}]) + [:div {:class (stl/css :file-main)} + [:div {:class (stl/css :file-icon) + :aria-hidden "true"} + [:> icon* {:icon-id i/folder :size "m"}]] - (if editing? - [:div {:class (stl/css :file-name-edit)} - [:input {:type "text" - :auto-focus true - :class (stl/css :file-name-input) - :aria-label (tr "dashboard.import.file-name-label") - :default-value (:name entry) - :on-key-press on-edit-key-press - :on-blur on-edit-blur}]] + [:div {:class (stl/css :file-meta)} + (if editing? + [:div {:class (stl/css :file-name-edit)} + [:input {:type "text" + :auto-focus true + :class (stl/css :file-name-input) + :aria-label (tr "dashboard.import.file-name-label") + :default-value (:name entry) + :on-key-press on-edit-key-press + :on-blur on-edit-blur}]] - [:div {:class (stl/css :file-name-label)} - (if loading? - [:> text* {:class (stl/css :file-name-label) - :as "span" - :typography t/body-medium} - (:name entry) - (when ^boolean is-shared? - [:> icon* {:icon-id i/library :class (stl/css :file-label-icon)}])] - [:> context-notification* - {:level level - :appearance :ghost - :class (stl/css :file-name-notification)} - [:> text* {:class (stl/css :file-name-label) + [:* + [:> text* {:class (stl/css :file-name-label) + :as "span" + :typography t/body-medium} + [:span {:class (stl/css :file-name-text)} + (:name entry)] + (when ^boolean is-shared? + [:> icon* {:icon-id i/library :class (stl/css :file-label-icon)}])] + (when (some? subtitle) + [:> text* {:class (stl/css :file-subtitle) :as "span" - :typography t/body-medium} - (:name entry) - (when ^boolean is-shared? - [:> icon* {:icon-id i/library :class (stl/css :file-label-icon)}])]])]) + :typography t/body-small} + subtitle])])] - (when ^boolean (or editable? can-be-deleted) + (when ^boolean (or editable? can-be-deleted?) [:div {:class (stl/css :edit-entry-buttons)} (when ^boolean editable? [:> icon-button* {:on-click on-edit' @@ -325,35 +312,36 @@ :icon-size "s" :aria-label (tr "labels.edit") :icon i/curve}]) - (when ^boolean can-be-deleted + (when ^boolean can-be-deleted? [:> icon-button* {:on-click on-delete' :variant "ghost" :icon-size "s" :aria-label (tr "labels.delete") - :icon i/delete}])])] - (cond - analyze-error? - [:> text* {:class (stl/css :error-message) - :as "span" - :typography t/body-small} - ;; backend-provided error key, dynamic by design - #_{:clj-kondo/ignore [:penpot/tr-dynamic]} - (if (some? (:error entry)) - (tr (:error entry)) - (tr "dashboard.import.analyze-error"))] + :icon i/delete}])]) - import-error? - [:> text* {:class (stl/css :error-message) - :as "span" - :typography t/body-small} - ;; backend-provided error key, dynamic by design - #_{:clj-kondo/ignore [:penpot/tr-dynamic]} - (if (some? (:error entry)) - (tr (:error entry)) - (tr "labels.error"))] + ;; a row carries its name and its size, and only the state that is not + ;; progress: the milestone in course and the files already imported are + ;; the business of the header, which writes the milestone once for + ;; every file of a container + (when ^boolean ready? + [:div {:class (stl/css :file-status)} + [:> icon* {:icon-id i/status-tick :size "s"}] + [:> text* {:as "span" + :typography t/body-medium} + (tr "dashboard.import.status.ready")]])] - (and (not import-success?) (some? progress)) - [:div {:class (stl/css :progress-message)} (parse-progress-message progress)]) + (when ^boolean (or analyze-error? import-error?) + [:div {:class (stl/css :file-error)} + [:> text* {:class (stl/css :error-message) + :as "span" + :typography t/body-small} + ;; the message is user-facing text already (a hint or a translated + ;; label), never a key + (if (some? (:error entry)) + (:error entry) + (if analyze-error? + (tr "dashboard.import.analyze-error") + (tr "labels.error")))]]) ;; This is legacy code, will be removed when legacy-zip format is removed [:div {:class (stl/css :linked-libraries)} @@ -368,7 +356,7 @@ [:span {:class (stl/css-case :linked-library-tag true :error error?)} - deprecated-icon/detach]])))]])) + [:> icon* {:icon-id i/detach}]]])))]])) (defn initialize-state [entries] @@ -573,13 +561,126 @@ ;; ── Stage components ──────────────────────────────────────────────── +(mf/defc import-steps* + {::mf/private true} + [{:keys [step]}] + (let [order [:check :upload :import] + label {:check (tr "dashboard.import.steps.check") + :upload (tr "dashboard.import.steps.upload") + :import (tr "dashboard.import.steps.import")} + current (count (take-while #(not= % step) order))] + [:ol {:class (stl/css :import-steps) + :aria-label (tr "dashboard.import.steps.label")} + (for [[idx id] (d/enumerate order)] + (let [done? (< idx current) + active? (= idx current)] + [:li {:key (name id) + :class (stl/css-case :import-step true + :done done? + :active active?) + :aria-current (when active? "step")} + [:span {:class (stl/css :step-marker) + :aria-hidden "true"} + (if done? + [:> icon* {:icon-id i/status-tick :size "s"}] + (str (inc idx)))] + [:> text* {:as "span" + :typography t/body-medium + :class (stl/css :step-label)} + (get label id)] + (when (< idx 2) + [:span {:class (stl/css :step-line) :aria-hidden "true"}])]))])) + +(defn- importing-title + "The title of the phase that rebuilds the files, with the files the + wizard is working on and the ones already done." + [total done] + (dm/str (tr "dashboard.import.importing.title" (i18n/c total)) + " · " + (tr "dashboard.import.importing.done" (i18n/c done)))) + +(mf/defc import-phase-header* + {::mf/private true} + [{:keys [status step entries template]}] + (let [total (+ (count entries) (if (some? template) 1 0)) + ;; the file the job is working on is reported by the job itself, so + ;; the counter moves while a container rebuilds its files one by one + done (imp/imported-count entries) + + phase (cond + (= :analyze status) :check + (= :import-ready status) :ready + (not= :import-progress status) nil + (= :upload step) :upload + (imp/waiting-slot? entries) :queued + :else :importing) + + ;; the bar measures the whole import, never a single file: the + ;; upload of every container put together while they are sent, and + ;; the animated one from there on, because the stages of a job are + ;; not comparable one with another + [title desc pct] + (case phase + :check [(tr "dashboard.import.check.title") + (tr "dashboard.import.check.message") + nil] + :ready [(tr "dashboard.import.ready.title") + (tr "dashboard.import.ready.message") + nil] + :upload [(tr "dashboard.import.upload.title") + (tr "dashboard.import.upload.message") + (imp/upload-pct entries)] + :queued [(tr "dashboard.import.queued.title") + (tr "dashboard.import.queued.message") + nil] + :importing [(importing-title total done) + (tr "dashboard.import.importing.message") + nil] + [nil nil nil]) + + ;; the milestone in course takes the place of the message: the files + ;; of a container share it, so writing it here keeps it once + desc + (or (jp/current-milestone-text entries) desc) + + ;; the bar paints what is running: indeterminate while the wizard + ;; works —checking, waiting for a slot, rebuilding— and determinate + ;; while it uploads, which is the only percentage it really knows + bar? (contains? #{:check :upload :queued :importing} phase)] + + (when (some? phase) + [:div {:class (stl/css :phase-header)} + [:> heading* {:level 3 + :typography t/headline-medium + :class (stl/css :phase-title)} + title] + [:> text* {:as "p" + :typography t/body-medium + :class (stl/css :phase-message)} + desc] + (when bar? + [:div {:class (stl/css-case :progress-bar true + :determinate (some? pct)) + :role "progressbar" + :aria-valuemin 0 + :aria-valuemax 100 + :aria-valuenow pct} + [:div {:class (stl/css :progress-fill) + :style (when (some? pct) + #js {:width (str pct "%")})}]])]))) + (mf/defc import-files-stage* {::mf/private true} - [{:keys [entries template status errors? import-success-total auto-linked-count + [{:keys [entries template status step errors? import-success-total auto-linked-count edition on-edit on-change on-delete - on-cancel on-continue on-accept pending-analysis?]}] + on-cancel on-cancel-import on-continue on-accept pending-analysis?]}] [:* + [:> import-steps* {:step step}] [:div {:class (stl/css :modal-content)} + [:> import-phase-header* {:status status + :step step + :entries entries + :template template}] (when (and (= :analyze status) errors?) [:> context-notification* {:level :warning @@ -602,7 +703,8 @@ :class (stl/css :context-notification-error)} (tr "dashboard.import.import-error.disclaimer")]) - (if (or (= :import-error status) (and (= :analyze status) errors?)) + (cond + (or (= :import-error status) (and (= :analyze status) errors?)) [:div {:class (stl/css :import-error-disclaimer)} [:div (tr "dashboard.import.import-error.message1")] [:ul {:class (stl/css :import-error-list)} @@ -626,26 +728,24 @@ (tr "dashboard.import.import-error.unknown-error"))])]))] [:div (tr "dashboard.import.import-error.message2")]] + (imp/awaiting-analysis? status errors?) + ;; while checking, the phase header carries the message: no rows, + ;; no spinners + nil + + :else (for [entry entries] [:> import-entry* {:edition edition :key (dm/str (:uri entry) "/" (:file-id entry)) :entry entry :entries entries - :is-progress (= :import-progress status) :on-edit on-edit :on-change on-change - :on-delete on-delete - :can-be-deleted (> (count entries) 1)}])) + :on-delete on-delete}])) - (when (some? template) - [:> import-entry* {:entry (assoc template :status status) - :can-be-deleted false}]) - - (when (= :import-progress status) - [:div {:class (stl/css :status-message) - :role "status" - :aria-live "polite"} - (tr "labels.uploading-file")])] + (when (and (some? template) + (not (imp/awaiting-analysis? status errors?))) + [:> import-entry* {:entry (assoc template :status status)}])] [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} @@ -664,11 +764,16 @@ (tr "labels.continue")] :import-progress - [:> button* {:class (stl/css :accept-btn) - :variant "primary" - :disabled true - :on-click on-accept} - (tr "labels.accept")] + [:* + [:> button* {:class (stl/css :cancel-button) + :variant "secondary" + :on-click on-cancel-import} + (tr "labels.cancel")] + [:> button* {:class (stl/css :accept-btn) + :variant "primary" + :disabled true + :on-click on-accept} + (tr "labels.accept")]] (:import-success :import-error) [:> button* {:class (stl/css :accept-btn) @@ -749,6 +854,20 @@ status* (mf/use-state :analyze) status (deref status*) + ;; the analysis of the dropped files reached its end: only then the + ;; wizard knows every file it is going to import + analysis-ended* + (mf/use-state false) + analysis-ended? + (deref analysis-ended*) + + ;; Jobs created while importing, to cancel them on demand, and + ;; the subscription to their messages, to stop listening. + jobs* (mf/use-state #{}) + jobs (deref jobs*) + sub* (mf/use-state nil) + sub (deref sub*) + edition* (mf/use-state nil) edition (deref edition*) @@ -795,8 +914,10 @@ (mf/deps entries) (fn [] (let [entries (filterv has-status-ready? entries)] + (reset! jobs* #{}) (reset! status* :import-progress) - (import-files state* resolution* project-id entries)))) + (reset! sub* (import-files state* resolution* project-id entries + #(swap! jobs* conj %)))))) continue-template (mf/use-fn @@ -841,13 +962,33 @@ (fn [file-id] (swap! state* remove-entry file-id))) + on-cancel-import + (mf/use-fn + (mf/deps jobs sub) + (fn [event] + (dom/prevent-default event) + ;; stop listening first, so no late message repaints the + ;; entries of a dialog that is going away + (when (some? sub) + (rx/dispose! sub)) + ;; a job may have just finished on its own: failures are + ;; ignored and an empty run cancels nothing + (run! dj/cancel-job jobs) + (reset! jobs* #{}) + (reset! sub* nil) + (st/emit! (modal/hide)))) + on-cancel (mf/use-fn - (mf/deps edition) + (mf/deps edition status on-cancel-import) (fn [event] (when (nil? edition) - (dom/prevent-default event) - (st/emit! (modal/hide))))) + (if (= :import-progress status) + ;; closing mid-import stops the jobs, like Cancel + (on-cancel-import event) + (do + (dom/prevent-default event) + (st/emit! (modal/hide))))))) on-continue (mf/use-fn @@ -864,22 +1005,17 @@ (mf/deps resolution selection on-finish-import) (fn [event] (dom/prevent-default event) - (let [slc selection] - ;; For each file with pending candidates, link it to the selected libraries - (->> (rx/from (seq resolution)) - (rx/merge-map - (fn [[file-id resolution-file]] - (->> (rx/from (:pending resolution-file)) - (rx/merge-map - (fn [{:keys [id]}] - (when-let [selected-lib (get-in slc [file-id id])] - (link-files-to-library! [file-id] selected-lib))))))) - (rx/subs! (constantly nil) - (constantly nil) - (fn [] - (st/emit! (modal/hide)) - (when (fn? on-finish-import) - (on-finish-import)))))))) + ;; For each file with pending candidates, link it to the selected libraries + (->> (rx/from (pending-library-links resolution selection)) + (rx/merge-map + (fn [{:keys [file-id library-id]}] + (link-files-to-library! [file-id] library-id))) + (rx/subs! (constantly nil) + (constantly nil) + (fn [] + (st/emit! (modal/hide)) + (when (fn? on-finish-import) + (on-finish-import))))))) on-wizard-next (mf/use-fn @@ -960,13 +1096,12 @@ (every? #(contains? visited (:id %)) unresolved-files)) (reset! status* :library-summary))) - (mf/with-effect [entries resolution] + (mf/with-effect [entries resolution analysis-ended?] (cond (some? template) (reset! status* :import-ready) - (and (seq entries) - (every? #(= :import-ready (:status %)) entries)) + (imp/ready-to-import? entries analysis-ended?) (reset! status* :import-ready) (and (seq entries) @@ -983,7 +1118,7 @@ ;; Run analyze operation on component mount (mf/with-effect [] - (let [sub (analyze-entries state* entries)] + (let [sub (analyze-entries state* entries #(reset! analysis-ended* true))] (partial rx/dispose! sub))) [:div {:class (stl/css :modal-overlay)} @@ -992,7 +1127,7 @@ [:> heading* {:level 2 :typography t/headline-large :class (stl/css :modal-title)} - (tr "dashboard.import")] + (tr "dashboard.import.title")] [:> icon-button* {:variant "ghost" :aria-label (tr "labels.close") :on-click on-cancel @@ -1005,6 +1140,7 @@ {:entries entries :template template :status status + :step (imp/import-step status entries) :errors? errors? :import-success-total import-success-total :auto-linked-count auto-linked-count @@ -1013,28 +1149,33 @@ :on-change on-entry-change :on-delete on-entry-delete :on-cancel on-cancel + :on-cancel-import on-cancel-import :on-continue on-continue :on-accept on-accept :pending-analysis? pending-analysis?}] :library-resolution - [:> import-library-resolution-stage* - {:current-unresolved-file current-unresolved-file - :selection selection - :on-select manage-on-select - :on-disconnect manage-on-disconnect - :visited visited - :last-file? last-unresolved-file? - :on-wizard-prev on-wizard-prev - :on-wizard-next on-wizard-next - :on-wizard-skip on-wizard-skip}] + [:* + [:> import-steps* {:step :import}] + [:> import-library-resolution-stage* + {:current-unresolved-file current-unresolved-file + :selection selection + :on-select manage-on-select + :on-disconnect manage-on-disconnect + :visited visited + :last-file? last-unresolved-file? + :on-wizard-prev on-wizard-prev + :on-wizard-next on-wizard-next + :on-wizard-skip on-wizard-skip}]] :library-summary - [:> import-library-summary-stage* - {:resolution resolution - :selection selection - :visited visited - :on-summary-back on-summary-back - :on-confirm-library-links on-confirm-library-links}] + [:* + [:> import-steps* {:step :import}] + [:> import-library-summary-stage* + {:resolution resolution + :selection selection + :visited visited + :on-summary-back on-summary-back + :on-confirm-library-links on-confirm-library-links}]] nil)]])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/dashboard/import.scss b/frontend/src/app/main/ui/dashboard/import.scss index f6765642b7..b67a2fe80a 100644 --- a/frontend/src/app/main/ui/dashboard/import.scss +++ b/frontend/src/app/main/ui/dashboard/import.scss @@ -34,7 +34,7 @@ background-color: var(--color-background-primary); border: $b-2 solid var(--color-background-quaternary); min-block-size: $sz-192; - inline-size: $sz-512; + inline-size: px2rem(720); max-block-size: px2rem(800); } @@ -56,6 +56,104 @@ .modal-title { color: var(--color-foreground-primary); + text-transform: uppercase; + letter-spacing: 0.02em; +} + +// ################################ +// WIZARD STEPS +// ################################ + +.import-steps { + display: flex; + align-items: center; + gap: 0; + list-style: none; + margin: 0; + padding: 0; +} + +.import-step { + --step-fg-color: var(--color-foreground-secondary); + --step-marker-border: var(--color-foreground-secondary); + + display: flex; + align-items: center; + gap: var(--sp-s); + flex-shrink: 0; + + &:not(:last-child) { + flex: 1 1 auto; + } + + &.active { + --step-fg-color: var(--color-foreground-primary); + --step-marker-border: var(--color-accent-primary); + } + + &.done { + --step-fg-color: var(--color-foreground-secondary); + --step-marker-border: var(--color-accent-primary); + --step-marker-bg-color: var(--color-accent-primary); + --step-marker-fg-color: var(--color-background-secondary); + } +} + +.step-marker { + display: flex; + justify-content: center; + align-items: center; + block-size: $sz-24; + inline-size: $sz-24; + flex-shrink: 0; + border-radius: 50%; + border: $b-1 solid var(--step-marker-border); + background-color: var(--step-marker-bg-color, transparent); + color: var(--step-marker-fg-color, var(--step-marker-border)); + font-size: px2rem(12); + font-weight: 500; + + svg { + block-size: $sz-16; + inline-size: $sz-16; + } +} + +.step-label { + color: var(--step-fg-color); + white-space: nowrap; +} + +.step-line { + flex: 1 1 auto; + block-size: $b-1; + min-inline-size: $sz-32; + margin-inline: var(--sp-m); + background-color: var(--color-background-quaternary); +} + +.import-step.active .step-line, +.import-step.done .step-line { + background-color: var(--color-background-quaternary); +} + +// ################################ +// PHASE HEADER +// ################################ + +.phase-header { + display: flex; + flex-direction: column; + gap: var(--sp-xs); +} + +.phase-title { + color: var(--color-foreground-primary); +} + +.phase-message { + color: var(--color-foreground-secondary); + margin: 0; } .modal-close-btn { @@ -64,13 +162,6 @@ inset-inline-end: px2rem(6); } -.status-message { - @include use-typography("body-small"); - - color: var(--color-foreground-primary); - font-style: italic; -} - .action-buttons { display: flex; justify-content: flex-end; @@ -93,8 +184,9 @@ flex-direction: column; gap: var(--sp-m); + &.ready, &.success { - --file-entry-fg-color: var(--color-accent-sucess); + --file-entry-fg-color: var(--color-accent-success); } &.error { @@ -106,16 +198,10 @@ } } -.error-message, -.progress-message { - display: flex; - align-items: center; - min-block-size: $sz-32; - color: var(--file-entry-fg-color); -} - .error-message { + display: flex; align-items: flex-start; + color: var(--file-entry-fg-color); white-space: pre-wrap; overflow-wrap: anywhere; } @@ -160,6 +246,86 @@ color: var(--file-entry-fg-color); } +.file-main { + display: flex; + align-items: center; + gap: var(--sp-m); +} + +.file-icon { + display: flex; + justify-content: center; + align-items: center; + block-size: px2rem(40); + inline-size: px2rem(40); + flex-shrink: 0; + border-radius: $br-8; + background-color: var(--color-background-tertiary); + color: var(--color-foreground-secondary); +} + +.file-meta { + display: flex; + flex-direction: column; + gap: var(--sp-xxs); + flex: 1 1 auto; + min-inline-size: 0; +} + +.file-subtitle { + color: var(--color-foreground-secondary); +} + +.file-status { + display: flex; + align-items: center; + gap: var(--sp-s); + flex-shrink: 0; + color: var(--file-entry-fg-color); + white-space: nowrap; +} + +.file-error { + padding-inline-start: calc(px2rem(40) + var(--sp-m)); +} + +.progress-bar { + block-size: px2rem(4); + border-radius: $br-8; + background-color: var(--color-background-tertiary); + overflow: hidden; +} + +.progress-fill { + block-size: 100%; + border-radius: $br-8; + background-color: var(--color-accent-primary); + + // the client cannot know a global percentage of the import work, so + // the fill slides through the bar while the jobs run; the upload is + // the exception, its chunk counters are real and the inline width + // style drives the fill then. The rule is written as physical `width` + // on purpose: the determinate fill gets its width from an inline + // style, and a logical `inline-size` here would map after the cascade + // and win over it. + width: 40%; + animation: progress-slide 1.2s ease-in-out infinite; + + .determinate & { + animation: none; + } +} + +@keyframes progress-slide { + from { + transform: translateX(-100%); + } + + to { + transform: translateX(250%); + } +} + .edit-entry-buttons { display: flex; align-items: center; @@ -182,7 +348,15 @@ display: flex; align-items: center; gap: var(--sp-m); - flex-grow: 1; + min-inline-size: 0; + color: var(--color-foreground-primary); +} + +// the status sits at the end of the same line: a long name gives way to it +.file-name-text { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .file-label-icon { @@ -221,10 +395,6 @@ --context-notification-bg-color: var(--color-background-primary); } -.file-name-notification { - flex-grow: 1; -} - .import-error-disclaimer { color: var(--color-foreground-secondary); } diff --git a/frontend/src/app/main/ui/dashboard/inline_edition.cljs b/frontend/src/app/main/ui/dashboard/inline_edition.cljs index 18f5930c60..c3686d3147 100644 --- a/frontend/src/app/main/ui/dashboard/inline_edition.cljs +++ b/frontend/src/app/main/ui/dashboard/inline_edition.cljs @@ -7,7 +7,6 @@ (ns app.main.ui.dashboard.inline-edition (:require-macros [app.main.style :as stl]) (:require - [app.main.ui.icons :as deprecated-icon] [app.util.dom :as dom] [app.util.keyboard :as kbd] [rumext.v2 :as mf])) @@ -68,7 +67,5 @@ :on-change on-input :on-key-down on-keyup :on-blur on-blur - :max-length max-length}] - [:span {:class (stl/css :close) - :on-click on-cancel} deprecated-icon/close]])) + :max-length max-length}]])) diff --git a/frontend/src/app/main/ui/dashboard/inline_edition.scss b/frontend/src/app/main/ui/dashboard/inline_edition.scss index 0414167696..1310bd2925 100644 --- a/frontend/src/app/main/ui/dashboard/inline_edition.scss +++ b/frontend/src/app/main/ui/dashboard/inline_edition.scss @@ -4,50 +4,31 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; .edit-wrapper { - border-radius: deprecated.$br-4; + border-radius: $br-4; display: flex; - padding-right: deprecated.$s-24; + padding-inline-end: var(--sp-xxl); position: relative; - margin-right: deprecated.$s-24; + margin-inline-end: var(--sp-xxl); } -input.element-title { - background-color: var(--input-background-color-active); - border-radius: deprecated.$br-8; +.element-title { + background-color: var(--color-background-primary); + border-radius: $br-8; color: var(--color-foreground-primary); - font-size: deprecated.$fs-16; - height: deprecated.$s-32; + font-size: px2rem(16); + block-size: $sz-32; margin: 0; border: none; - padding: deprecated.$s-6; - width: 100%; + padding: px2rem(6); + inline-size: 100%; &:focus-visible { - border: deprecated.$s-1 solid var(--color-accent-primary); + border: $b-1 solid var(--color-accent-primary); outline: none; } } - -.close { - cursor: pointer; - position: absolute; - top: deprecated.$s-1; - right: calc(-1 * deprecated.$s-8); - - svg { - fill: var(--color-foreground-secondary); - height: deprecated.$s-16; - transform: rotate(45deg) translateY(7px); - width: deprecated.$s-16; - margin: 0; - } - - &:hover { - svg { - fill: var(--element-foreground-warning); - } - } -} diff --git a/frontend/src/app/main/ui/dashboard/libraries.cljs b/frontend/src/app/main/ui/dashboard/libraries.cljs index 335d6ce621..e899521fca 100644 --- a/frontend/src/app/main/ui/dashboard/libraries.cljs +++ b/frontend/src/app/main/ui/dashboard/libraries.cljs @@ -13,6 +13,8 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [grid*]] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.hooks :as hooks] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -68,7 +70,10 @@ [:* [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-libraries-title {:class (stl/css :dashboard-title)} - [:h1 (tr "dashboard.libraries-title")]]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (tr "dashboard.libraries-title")]]] [:section {:class (stl/css :dashboard-container :no-bg :dashboard-shared) :ref rowref} diff --git a/frontend/src/app/main/ui/dashboard/libraries.scss b/frontend/src/app/main/ui/dashboard/libraries.scss index ae28f34484..e5555cfb9b 100644 --- a/frontend/src/app/main/ui/dashboard/libraries.scss +++ b/frontend/src/app/main/ui/dashboard/libraries.scss @@ -4,17 +4,13 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; -@use "common/refactor/common-dashboard"; +@use "ds/_borders.scss" as *; +@use "dashboard/common/header"; .dashboard-container { flex: 1 0 0; - margin-right: deprecated.$s-16; + margin-inline-end: var(--sp-l); overflow-y: auto; - width: 100%; - border-top: deprecated.$s-1 solid var(--color-background-quaternary); - - &.dashboard-projects { - user-select: none; - } + inline-size: 100%; + border-block-start: $b-1 solid var(--color-background-quaternary); } diff --git a/frontend/src/app/main/ui/dashboard/organization_team_switch.scss b/frontend/src/app/main/ui/dashboard/organization_team_switch.scss index 3fee83b7a4..bba4da94e7 100644 --- a/frontend/src/app/main/ui/dashboard/organization_team_switch.scss +++ b/frontend/src/app/main/ui/dashboard/organization_team_switch.scss @@ -80,7 +80,6 @@ width: $sz-16; color: transparent; fill: none; - stroke-width: 1px; stroke: var(--icon-foreground); } @@ -127,7 +126,6 @@ .chevron-icon svg { color: transparent; fill: none; - stroke-width: 1px; stroke: var(--icon-foreground); } @@ -416,7 +414,6 @@ width: $sz-16; color: transparent; fill: none; - stroke-width: 1px; stroke: var(--sidebar-action-icon-color); } diff --git a/frontend/src/app/main/ui/dashboard/placeholder.cljs b/frontend/src/app/main/ui/dashboard/placeholder.cljs index 100f1ed4f3..815c9d04b2 100644 --- a/frontend/src/app/main/ui/dashboard/placeholder.cljs +++ b/frontend/src/app/main/ui/dashboard/placeholder.cljs @@ -10,9 +10,9 @@ [app.main.data.event :as ev] [app.main.store :as st] [app.main.ui.dashboard.import :as udi] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]] [app.main.ui.ds.product.loader :refer [loader*]] - [app.main.ui.icons :as deprecated-icon] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [okulary.core :as l] @@ -131,7 +131,7 @@ :on-mouse-leave on-mouse-leave} (if show-text? (tr "dashboard.empty-project.create") - deprecated-icon/add)]])))) + [:> icon* {:icon-id i/add :size "xl"}])]])))) (mf/defc loading-placeholder* [] diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index fef26f5b9b..2371fb89ef 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -16,7 +16,9 @@ [app.main.data.project :as dpj] [app.main.data.team :as dtm] [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] + [app.main.ui.components.mcp-menu :refer [dashboard-mcp-menu*]] [app.main.ui.dashboard.deleted :as deleted] [app.main.ui.dashboard.grid :refer [line-grid*]] [app.main.ui.dashboard.import :as udi] @@ -27,6 +29,8 @@ [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.ds.layout.menu :refer [context-menu*]] [app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]] [app.main.ui.hooks :as hooks] @@ -46,10 +50,14 @@ [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-projects-title {:class (stl/css :dashboard-title)} - [:h1 (if default-team? - (tr "dashboard.personal-projects") - (tr "dashboard.projects-title"))]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (if default-team? + (tr "dashboard.personal-projects") + (tr "dashboard.projects-title"))]] [:div {:class (stl/css :dashboard-header-actions)} + [:> dashboard-mcp-menu*] [:> layout-toggle* {:layout layout :on-change on-change}] (when can-edit @@ -125,8 +133,9 @@ on-nav (mf/use-fn (mf/deps project-id) - (fn [] - (st/emit! (dcm/go-to-dashboard-files :project-id project-id)))) + (fn [event] + (st/emit! (dcm/go-to-dashboard-files :project-id project-id + ::rt/new-window (kbd/mod? event))))) toggle-pin (mf/use-fn @@ -284,7 +293,7 @@ :tab-index "0" :on-key-down (fn [event] (when (kbd/enter? event) - (on-nav)))} + (on-nav event)))} (tr "dashboard.show-all-files") [:> icon* {:icon-id i/arrow-right}]])]] diff --git a/frontend/src/app/main/ui/dashboard/projects.scss b/frontend/src/app/main/ui/dashboard/projects.scss index 7eeccbcfb3..050377b6a5 100644 --- a/frontend/src/app/main/ui/dashboard/projects.scss +++ b/frontend/src/app/main/ui/dashboard/projects.scss @@ -4,7 +4,7 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "ds/typography.scss" as t; @use "ds/_borders.scss" as *; @use "ds/spacing.scss" as *; diff --git a/frontend/src/app/main/ui/dashboard/search.cljs b/frontend/src/app/main/ui/dashboard/search.cljs index bce8ccc0d6..06393173cc 100644 --- a/frontend/src/app/main/ui/dashboard/search.cljs +++ b/frontend/src/app/main/ui/dashboard/search.cljs @@ -12,8 +12,10 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [grid*]] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.hooks :as hooks] - [app.main.ui.icons :as deprecated-icon] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [okulary.core :as l] @@ -55,24 +57,36 @@ [:* [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-search-title {:class (stl/css :dashboard-title)} - [:h1 (tr "dashboard.title-search")]]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (tr "dashboard.title-search")]]] [:section {:class (stl/css :dashboard-container :search :no-bg) :ref rowref} (cond (empty? search-term) [:div {:class (stl/css :grid-empty-placeholder :search)} - [:div {:class (stl/css :icon)} deprecated-icon/search] + [:div {:class (stl/css :icon)} + [:> icon* {:icon-id i/search + :size "xl" + :class (stl/css :search-icon)}]] [:div {:class (stl/css :text)} (tr "dashboard.type-something")]] (nil? result) [:div {:class (stl/css :grid-empty-placeholder :search)} - [:div {:class (stl/css :icon)} deprecated-icon/search] + [:div {:class (stl/css :icon)} + [:> icon* {:icon-id i/search + :size "xl" + :class (stl/css :search-icon)}]] [:div {:class (stl/css :text)} (tr "dashboard.searching-for" search-term)]] (empty? result) [:div {:class (stl/css :grid-empty-placeholder :search)} - [:div {:class (stl/css :icon)} deprecated-icon/search] + [:div {:class (stl/css :icon)} + [:> icon* {:icon-id i/search + :size "xl" + :class (stl/css :search-icon)}]] [:div {:class (stl/css :text)} (tr "dashboard.no-matches-for" search-term)]] :else diff --git a/frontend/src/app/main/ui/dashboard/search.scss b/frontend/src/app/main/ui/dashboard/search.scss index 4848eeaa97..ad7569e52d 100644 --- a/frontend/src/app/main/ui/dashboard/search.scss +++ b/frontend/src/app/main/ui/dashboard/search.scss @@ -4,25 +4,17 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; -@use "common/refactor/common-dashboard"; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "dashboard/common/header"; @use "./placeholder"; .dashboard-container { flex: 1 0 0; - margin-right: deprecated.$s-16; + margin-inline-end: var(--sp-l); overflow-y: auto; - width: 100%; - border-top: deprecated.$s-1 solid var(--color-background-quaternary); - - &.dashboard-projects { - user-select: none; - } - - &.dashboard-shared { - width: calc(100vw - deprecated.$s-320); - margin-right: deprecated.$s-52; - } + inline-size: 100%; + border-block-start: $b-1 solid var(--color-background-quaternary); } .grid-empty-placeholder.search { @@ -30,18 +22,16 @@ display: flex; justify-content: center; flex-direction: column; - height: deprecated.$s-200; + block-size: $sz-200; background: transparent; - border: deprecated.$s-1 solid var(--color-background-quaternary); - border-radius: deprecated.$br-8; + border: $b-1 solid var(--color-background-quaternary); + border-radius: $br-8; .text { color: var(--color-foreground-primary); } - - .icon svg { - stroke: var(--color-foreground-secondary); - width: deprecated.$s-32; - height: deprecated.$s-32; - } +} + +.search-icon { + color: var(--color-foreground-secondary); } diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index 2f37187244..d9514fb927 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -37,7 +37,6 @@ [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.ds.layout.menu :refer [context-menu*]] [app.main.ui.hooks :refer [use-focus-timer-ref]] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.nitrate.nitrate-form] [app.util.dom :as dom] [app.util.dom.dnd :as dnd] @@ -48,18 +47,6 @@ [goog.functions :as f] [rumext.v2 :as mf])) -(def ^:private clear-search-icon - (deprecated-icon/icon-xref :delete-text (stl/css :clear-search-icon))) - -(def ^:private search-icon - (deprecated-icon/icon-xref :search (stl/css :search-icon))) - -(def ^:private pin-icon - (deprecated-icon/icon-xref :pin (stl/css :pin-icon))) - -(def ^:private exit-icon - (deprecated-icon/icon-xref :exit (stl/css :exit-icon))) - (defn schedule-focus-by-id! [ref element-id] (when-let [h (mf/ref-val ref)] @@ -89,8 +76,9 @@ on-click (mf/use-fn (mf/deps project-id) - (fn [] - (st/emit! (dcm/go-to-dashboard-files :project-id project-id)))) + (fn [event] + (st/emit! (dcm/go-to-dashboard-files :project-id project-id + ::rt/new-window (kbd/mod? event))))) on-key-down (mf/use-fn @@ -100,7 +88,6 @@ (schedule-focus-by-id! focus-timer-ref (str project-id)) (st/emit! (dcm/go-to-dashboard-files :project-id project-id))))) - on-edit-open (mf/use-fn #(swap! local* assoc :edition? true)) @@ -238,15 +225,19 @@ (if (or @focused? (seq search-term)) [:button {:class (stl/css :search-btn :clear-search-btn) :tab-index "0" - :aria-label "dashboard-clear-search" + :aria-label (tr "dashboard.clear-search") :on-click on-clear-click :on-key-down handle-clear-search} - clear-search-icon] + [:> icon* {:icon-id i/delete-text + :class (stl/css :search-icon) + :aria-hidden true}]] [:button {:class (stl/css :search-btn) - :aria-label "dashboard-search" + :aria-label (tr "labels.search") :on-click on-clear-click} - search-icon])])) + [:> icon* {:icon-id i/search + :class (stl/css :search-icon) + :aria-hidden true}]])])) (mf/defc sidebar-content* {::mf/private true} @@ -297,8 +288,10 @@ go-drafts (mf/use-fn (mf/deps team-id default-project-id) - (fn [] - (st/emit! (dcm/go-to-dashboard-files :team-id team-id :project-id default-project-id)))) + (fn [event] + (st/emit! (dcm/go-to-dashboard-files :team-id team-id + :project-id default-project-id + ::rt/new-window (kbd/mod? event))))) go-drafts-with-key (mf/use-fn @@ -361,7 +354,6 @@ :keyboard-action go-drafts-with-key} [:span {:class (stl/css :element-title)} (tr "labels.drafts")]]]]] - [:div {:class (stl/css :sidebar-content-section)} [:div {:class (stl/css :sidebar-section-title)} (tr "labels.sources")] @@ -381,7 +373,6 @@ :keyboard-action go-libs-with-key} [:span {:class (stl/css :element-title)} (tr "labels.shared-libraries")]]]]] - [:div {:class (stl/css :sidebar-content-section) :data-testid "pinned-projects"} [:div {:class (stl/css :sidebar-section-title)} @@ -396,7 +387,9 @@ :team-id (:id team) :is-selected (= (:id item) (:id project))}])] [:div {:class (stl/css :sidebar-empty-placeholder)} - pin-icon + [:> icon* {:icon-id i/pin + :size "s" + :class (stl/css :pin-icon)}] [:span {:class (stl/css :empty-text)} (tr "dashboard.no-projects-placeholder")]])]] [:div {:class (stl/css-case :separator true :overflow-separator overflow?)}]]])) @@ -670,7 +663,6 @@ [:> dashboard-cta* {:profile profile}] [:> subscription-sidebar* {:profile profile}]))) - (when (and team profile) [:& comments-section {:profile profile @@ -701,7 +693,6 @@ [:li {:class (stl/css :profile-separator)}] - [:> dropdown-menu-item* {:class (stl/css-case :profile-dropdown-item true) :on-click on-menu-click :on-key-down (fn [event] @@ -746,7 +737,8 @@ [:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item :item-with-icon) :on-click handle-logout-click :data-testid "logout-profile-opt"} - exit-icon + [:> icon* {:icon-id i/exit + :class (stl/css :exit-icon)}] (tr "labels.logout")]] (when (and team profile) diff --git a/frontend/src/app/main/ui/dashboard/sidebar.scss b/frontend/src/app/main/ui/dashboard/sidebar.scss index 4bbd6d243b..a00c120ed5 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.scss +++ b/frontend/src/app/main/ui/dashboard/sidebar.scss @@ -4,14 +4,17 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/colors.scss" as *; @use "ds/spacing.scss" as *; @use "ds/_borders.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_utils.scss" as *; @use "ds/z-index.scss" as *; -@use "common/refactor/common-dashboard"; +@use "ds/mixins.scss" as *; + +// refactor/ stays for the menu placeholders (%menu-dropdown, +// %menu-item-base) of the profile menu and its submenus. @use "common/refactor/common-refactor.scss" as deprecated; // SIDEBAR COMPONENT @@ -20,19 +23,19 @@ grid-row: 1 / span 2; grid-column: 1 / span 2; grid-template-rows: auto 1fr auto; - height: 100%; - width: 100%; + block-size: 100%; + inline-size: 100%; margin: 0 var(--sp-l) 0 0; - border-right: $b-1 solid var(--panel-border-color); - background-color: var(--panel-background-color); + border-inline-end: $b-1 solid var(--color-background-quaternary); + background-color: var(--color-background-primary); } // SIDEBAR CONTENT COMPONENT .sidebar-content-wrapper { display: flex; flex-direction: column; - min-height: 0; - height: 100%; + min-block-size: 0; + block-size: 100%; overflow: hidden; } @@ -41,7 +44,7 @@ grid-template-rows: auto auto auto auto 1fr; gap: var(--sp-xxl); flex: 1; - min-height: 0; + min-block-size: 0; padding: 0; overflow: hidden auto; } @@ -52,17 +55,17 @@ } .separator { - height: var(--sp-xxs); - width: 94%; - margin-left: 3%; + block-size: var(--sp-xxs); + inline-size: 94%; + margin-inline-start: 3%; } .overflow-separator { - border-bottom: $b-1 solid var(--color-background-quaternary); + border-block-end: $b-1 solid var(--color-background-quaternary); } .sidebar-section-title { - @include t.use-typography("headline-small"); + @include use-typography("headline-small"); padding: 0 var(--sp-s) var(--sp-s) var(--sp-xxl); color: var(--color-foreground-secondary); @@ -82,31 +85,33 @@ cursor: pointer; &:hover { - background-color: var(--sidebar-element-background-color-hover); + background-color: var(--color-background-secondary); + // The item labels have no class. span { - color: var(--sidebar-element-foreground-color-hover); + color: var(--color-accent-primary); } } &.current { - background-color: var(--sidebar-element-background-color-selected); + background-color: var(--color-background-quaternary); .element-title { - color: var(--sidebar-element-foreground-color-selected); + color: var(--color-accent-primary); } } } +// The icon has no class. .recent-projects svg { - stroke: var(--main-icon-foreground); + stroke: var(--color-foreground-primary); } .sidebar-link { display: block; padding: var(--sp-s) var(--sp-s) var(--sp-s) var(--sp-xxl); - font-weight: deprecated.$fw400; - width: 100%; + font-weight: 400; + inline-size: 100%; &:hover { text-decoration: none; @@ -118,31 +123,32 @@ } .element-title { - @include deprecated.text-ellipsis; + @include text-ellipsis; - width: deprecated.$s-256; + inline-size: px2rem(256); color: var(--color-foreground-primary); - font-size: deprecated.$fs-14; + font-size: px2rem(14); } // Pinned projects .sidebar-empty-placeholder { padding: var(--sp-m); - color: var(--empty-message-foreground-color); + color: var(--color-foreground-secondary); display: flex; align-items: center; } +// The DS small icon draws 12px inside a 16px box: 2px less margin on each +// side keeps the drawing and the text where they were. .pin-icon { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - margin: 0 var(--sp-m); + flex-shrink: 0; + color: var(--color-foreground-secondary); + margin: 0 px2rem(10); } .empty-text { - font-size: deprecated.$fs-12; + font-size: px2rem(12); } // Search @@ -155,55 +161,57 @@ border: $b-1 solid transparent; margin: 0 var(--sp-l); border-radius: $br-8; - background-color: var(--search-bar-input-background-color); + background-color: var(--color-background-tertiary); } .input-text { - @include t.use-typography("title-small"); + @include use-typography("title-small"); - height: $sz-40; - width: 100%; + block-size: $sz-40; + inline-size: 100%; padding: $sz-6 var(--sp-m); margin: 0; border: transparent; border-radius: $br-8; background: transparent; - color: var(--search-bar-foreground-color); + color: var(--color-foreground-primary); &:focus, &:focus-within, &:focus-visible { outline: none; - border: $b-1 solid var(--search-bar-input-border-color-focus); + border: $b-1 solid var(--color-accent-primary); } ::placeholder { - color: var(--search-bar-placeholder-foreground-color); + color: var(--color-foreground-secondary); } } .search-btn { - @include deprecated.button-style; - @include deprecated.flex-center; + border: none; + background: none; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + --sidebar-search-foreground-color: var(--color-foreground-secondary); + + color: var(--sidebar-search-foreground-color); position: absolute; - right: 0; - height: var(--sp-xxl); - width: var(--sp-xxxl); + inset-inline-end: 0; + block-size: var(--sp-xxl); + inline-size: var(--sp-xxxl); padding: 0 var(--sp-s); } -.search-icon, -.clear-search-btn { - @extend %button-icon; - - --sidebar-search-foreground-color: var(--search-bar-icon-foreground-color); - - stroke: var(--sidebar-search-foreground-color); +.search-icon { + flex-shrink: 0; } .clear-search-btn:hover { - --sidebar-search-foreground-color: var(--search-bar-icon-foreground-color-hover); + --sidebar-search-foreground-color: var(--color-accent-primary); } // Profile @@ -212,33 +220,32 @@ display: grid; grid-template-columns: 1fr auto; padding: var(--sp-m) var(--sp-l); - border-top: $b-1 solid var(--panel-border-color); - background-color: var(--profile-section-background-color); + border-block-start: $b-1 solid var(--color-background-quaternary); + background-color: var(--color-background-tertiary); cursor: pointer; } .profile { - @include deprecated.button-style; - + border: none; + background: none; display: grid; grid-template-columns: auto 1fr; gap: var(--sp-s); cursor: pointer; - text-align: left; + text-align: start; } .profile-fullname { - @include t.use-typography("title-small"); - @include deprecated.text-ellipsis; + @include use-typography("title-small"); + @include text-ellipsis; align-self: center; - max-width: var(--sp-l) 0; - color: var(--profile-foreground-color); + color: var(--color-foreground-primary); } .profile-img { - height: $sz-40; - width: $sz-40; + block-size: $sz-40; + inline-size: $sz-40; border-radius: $br-circle; } @@ -247,14 +254,14 @@ inset-inline-start: var(--sp-s); inset-block-end: px2rem(72); // 72 is the height of the profile button - min-width: calc(100% - var(--sp-s)); + min-inline-size: calc(100% - var(--sp-s)); // TODO ADD animation fadeInUp } .profile-dropdown-item { @extend %menu-item-base; - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); block-size: $sz-40; margin-block-end: var(--sp-xs); @@ -262,29 +269,25 @@ padding-inline-start: var(--sp-l); &:hover { - color: var(--menu-foreground-color-hover); + color: var(--color-foreground-primary); .open-arrow { - --icon-stroke-color: var(--menu-foreground-color-hover); + --icon-stroke-color: var(--color-foreground-primary); } } } .profile-dropdown-item .open-arrow { - @include deprecated.flex-center; -} - -.profile-dropdown-item .open-arrow svg { - @extend %button-icon; - - stroke: var(--icon-foreground); + display: flex; + justify-content: center; + align-items: center; } .sub-menu { @extend %menu-dropdown; - inset-inline-start: calc(deprecated.$s-292 + var(--sp-s)); - min-width: deprecated.$s-192; + inset-inline-start: calc(#{px2rem(292)} + var(--sp-s)); + min-inline-size: $sz-192; } // Each submenu is positioned via its bottom edge; the visual top lands @@ -293,11 +296,11 @@ // its top edge sits one row above Community — aligning with the // "Help & Learning" trigger row in the parent menu. .sub-menu.help-learning { - inset-block-end: deprecated.$s-120; + inset-block-end: $sz-120; } .sub-menu.community { - inset-block-end: deprecated.$s-120; + inset-block-end: $sz-120; } .sub-menu.about { @@ -306,7 +309,7 @@ .submenu-item { @extend %menu-item-base; - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); block-size: $sz-40; margin-block-end: var(--sp-xs); @@ -314,7 +317,7 @@ padding-inline: var(--sp-l); &:hover { - color: var(--menu-foreground-color-hover); + color: var(--color-foreground-primary); } &.checking { @@ -334,7 +337,7 @@ } .submenu-separator { - border-top: $b-1 solid var(--color-background-quaternary); + border-block-start: $b-1 solid var(--color-background-quaternary); margin: 0; } @@ -345,8 +348,8 @@ } .menu-version { - @include t.use-typography("code-font"); - @include deprecated.text-ellipsis; + @include use-typography("code-font"); + @include text-ellipsis; color: var(--color-foreground-secondary); margin-inline-start: var(--sp-s); @@ -354,8 +357,8 @@ } .profile-separator { - height: $sz-6; - border-top: $b-1 solid var(--color-background-quaternary); + block-size: $sz-6; + border-block-start: $b-1 solid var(--color-background-quaternary); } .item-with-icon { @@ -365,38 +368,5 @@ } .exit-icon { - @extend %button-icon; - - stroke: var(--icon-foreground); -} - -.upgrade-plan-section { - @include deprecated.button-style; - - display: flex; - justify-content: space-between; - border: $b-1 solid var(--color-background-quaternary); - border-radius: var(--sp-s); - padding: var(--sp-m); - margin: var(--sp-m); color: var(--color-foreground-secondary); - cursor: pointer; -} - -.penpot-free { - @include t.use-typography("body-medium"); - - display: flex; - flex-direction: column; - text-align: left; -} - -.no-limits { - @include t.use-typography("body-small"); -} - -.power-up { - @include t.use-typography("body-small"); - - color: var(--color-accent-tertiary); } diff --git a/frontend/src/app/main/ui/dashboard/subscription.cljs b/frontend/src/app/main/ui/dashboard/subscription.cljs index 5548a5159e..c93644d90c 100644 --- a/frontend/src/app/main/ui/dashboard/subscription.cljs +++ b/frontend/src/app/main/ui/dashboard/subscription.cljs @@ -252,12 +252,12 @@ subscription-type (if nitrate? (:type nitrate-license) (get-subscription-type subscription)) subscription-is-trial (= "trialing" (:status (if nitrate? nitrate-license subscription))) go-to-subscription (mf/use-fn #(st/emit! (rt/nav :settings-subscription)))] - [:div {:class (stl/css :nitrate-current-plan)} - [:div {:class (stl/css :nitrate-current-plan-label)} + [:button {:class (stl/css :nitrate-current-plan) + :type "button" + :on-click go-to-subscription} + [:span {:class (stl/css :nitrate-current-plan-label)} (tr "subscription.current-plan.title")] - [:button {:class (stl/css :nitrate-current-plan-text) - :type "button" - :on-click go-to-subscription} + [:span {:class (stl/css :nitrate-current-plan-text)} (case subscription-type "professional" (tr "subscription.current-plan.professional") "unlimited" (if subscription-is-trial diff --git a/frontend/src/app/main/ui/dashboard/subscription.scss b/frontend/src/app/main/ui/dashboard/subscription.scss index c4635b94f1..4699b44d92 100644 --- a/frontend/src/app/main/ui/dashboard/subscription.scss +++ b/frontend/src/app/main/ui/dashboard/subscription.scss @@ -4,10 +4,12 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the menu item placeholder (%menu-item-base) of the +// power up submenu. @use "refactor/common-refactor.scss" as deprecated; -@use "common/refactor/common-dashboard"; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/_borders.scss" as *; +@use "ds/_utils.scss" as *; @use "ds/spacing.scss" as *; .cta-power-up { @@ -26,8 +28,9 @@ } .cta-top-section { - @include deprecated.button-style; - + border: none; + background: none; + cursor: pointer; display: grid; color: var(--color-foreground-secondary); grid-template-columns: 1fr auto; @@ -44,17 +47,11 @@ } .icon-dropdown { - @include deprecated.flex-center; - - height: 100%; - width: var(--sp-l); -} - -.icon-dropdown svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - transform: rotate(90deg); + display: flex; + justify-content: center; + align-items: center; + block-size: 100%; + inline-size: var(--sp-l); } .cta-bottom-section { @@ -69,12 +66,14 @@ } .cta-bottom-section .content { - @include t.use-typography("body-medium"); - @include deprecated.button-style; + @include use-typography("body-medium"); + border: none; + background: none; + cursor: pointer; color: var(--color-foreground-secondary); display: inline-block; - text-align: left; + text-align: start; } .cta-bottom-button { @@ -87,72 +86,67 @@ .cta-text, .cta-title { - text-align: left; + text-align: start; } .cta-title { - @include t.use-typography("body-small"); + @include use-typography("body-small"); margin-block-end: var(--sp-xs); } .highlighted .cta-title { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); margin-block-end: 0; } .cta-text { - @include t.use-typography("title-small"); + @include use-typography("title-small"); } .highlighted .cta-text { - @include t.use-typography("body-large"); + @include use-typography("body-large"); color: var(--color-foreground-primary); } .cta-bottom-section .content a { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-accent-tertiary); margin-inline-start: var(--sp-xs); } -.cta-link { - @include deprecated.button-style; - - align-self: end; - margin-inline-start: var(--sp-xs); -} - .team { display: grid; grid-auto-rows: min-content; gap: var(--sp-s); - max-width: deprecated.$s-1000; - width: 100%; + max-inline-size: px2rem(1000); + inline-size: 100%; } .team-label { - @include t.use-typography("headline-small"); + @include use-typography("headline-small"); - color: var(--title-foreground-color); + color: var(--color-foreground-secondary); } .team-text { - @include t.use-typography("title-medium"); + @include use-typography("title-medium"); color: var(--color-foreground-primary); } .manage-subscription-link { - @include deprecated.button-style; - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); + border: none; + background: none; + cursor: pointer; color: var(--color-accent-tertiary); display: flex; - margin-block-start: -8px; + margin-block-start: calc(-1 * var(--sp-s)); padding: 0; } @@ -169,7 +163,6 @@ .subscription-icon { stroke: var(--color-foreground-secondary); - stroke-width: 2.25px; } .menu-item { @@ -178,21 +171,15 @@ cursor: pointer; &:hover { - color: var(--menu-foreground-color-hover); - - .open-arrow { - svg { - stroke: var(--menu-foreground-color-hover); - } - } + color: var(--color-foreground-primary); } } .members-cta { - height: fit-content; + block-size: fit-content; margin-block-start: var(--sp-s); - margin-inline-start: deprecated.$s-52; - max-width: deprecated.$s-220; + margin-inline-start: px2rem(52); + max-inline-size: px2rem(220); .cta-title { line-height: 1.2; @@ -200,7 +187,7 @@ } .dashboard-cta { - height: fit-content; + block-size: fit-content; margin: var(--sp-l); .cta-title { @@ -209,7 +196,7 @@ } .cta-message { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-secondary); line-height: 1; @@ -231,13 +218,13 @@ } .nitrate-title { - @include t.use-typography("body-large"); + @include use-typography("body-large"); color: var(--color-foreground-primary); } .nitrate-info { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-secondary); margin-block: var(--sp-s) var(--sp-xxl); @@ -249,33 +236,36 @@ } .nitrate-bottom-button { - width: fit-content; - height: fit-content; + inline-size: fit-content; + block-size: fit-content; padding: var(--sp-s) var(--sp-m); } .nitrate-current-plan { + display: flex; + flex-direction: column; + align-items: flex-start; + box-sizing: border-box; + inline-size: calc(100% - 2 * var(--sp-m)); border-radius: var(--sp-s); margin-block: var(--sp-s) var(--sp-m); margin-inline: var(--sp-m); background: var(--color-background-tertiary); border: $b-1 solid var(--color-background-quaternary); padding: var(--sp-m) var(--sp-l); + text-align: start; + cursor: pointer; } .nitrate-current-plan-label { - @include t.use-typography("body-small"); + @include use-typography("body-small"); padding-block-end: var(--sp-xs); color: var(--color-foreground-secondary); } .nitrate-current-plan-text { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); - background: none; - border: none; - padding: 0; - cursor: pointer; color: var(--color-foreground-primary); } diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index b84ed95f0b..5629337572 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -10,6 +10,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.schema :as sm] + [app.common.types.organization :as cto] [app.config :as cfg] [app.main.data.common :as dcm] [app.main.data.event :as ev] @@ -52,33 +53,6 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) -(def ^:private arrow-icon - (deprecated-icon/icon-xref :arrow (stl/css :arrow-icon))) - -(def ^:private menu-icon - (deprecated-icon/icon-xref :menu (stl/css :menu-icon))) - -(def ^:private organization-menu-icon - (deprecated-icon/icon-xref :menu (stl/css :organization-menu-icon))) - -(def ^:private warning-icon - (deprecated-icon/icon-xref :msg-warning (stl/css :warning-icon))) - -(def ^:private success-icon - (deprecated-icon/icon-xref :msg-success (stl/css :success-icon))) - -(def ^:private image-icon - (deprecated-icon/icon-xref :img (stl/css :image-icon))) - -(def ^:private user-icon - (deprecated-icon/icon-xref :user (stl/css :user-icon))) - -(def ^:private document-icon - (deprecated-icon/icon-xref :document (stl/css :document-icon))) - -(def ^:private group-icon - (deprecated-icon/icon-xref :group (stl/css :group-icon))) - (mf/defc header* {::mf/wrap [mf/memo]} [{:keys [section team profile]}] @@ -121,23 +95,34 @@ [:header {:class (stl/css :dashboard-header :team) :data-testid "dashboard-header"} [:div {:class (stl/css :dashboard-title)} - [:h1 (cond - members-section? (tr "labels.members") - settings-section? (tr "labels.settings") - invitations-section? (tr "labels.invitations") - webhooks-section? (tr "labels.webhooks") - :else nil)]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (cond + members-section? (tr "labels.members") + settings-section? (tr "labels.settings") + invitations-section? (tr "labels.invitations") + webhooks-section? (tr "labels.webhooks") + :else nil)]] [:nav {:class (stl/css :dashboard-header-menu)} [:ul {:class (stl/css :dashboard-header-options)} - [:li {:class (when members-section? (stl/css :active))} - [:a {:on-click on-nav-members} (tr "labels.members")]] - [:li {:class (when invitations-section? (stl/css :active))} - [:a {:on-click on-nav-invitations} (tr "labels.invitations")]] + [:li + [:a {:class (stl/css-case :dashboard-header-link true + :active members-section?) + :on-click on-nav-members} (tr "labels.members")]] + [:li + [:a {:class (stl/css-case :dashboard-header-link true + :active invitations-section?) + :on-click on-nav-invitations} (tr "labels.invitations")]] (when (contains? cfg/flags :webhooks) - [:li {:class (when webhooks-section? (stl/css :active))} - [:a {:on-click on-nav-webhooks} (tr "labels.webhooks")]]) - [:li {:class (when settings-section? (stl/css :active))} - [:a {:on-click on-nav-settings} (tr "labels.settings")]]]] + [:li + [:a {:class (stl/css-case :dashboard-header-link true + :active webhooks-section?) + :on-click on-nav-webhooks} (tr "labels.webhooks")]]) + [:li + [:a {:class (stl/css-case :dashboard-header-link true + :active settings-section?) + :on-click on-nav-settings} (tr "labels.settings")]]]] [:div {:class (stl/css :dashboard-buttons)} (when (and (or invitations-section? members-section?) (not-empty invitations)) (let [invite-button (mf/html @@ -323,8 +308,11 @@ [:div {:class (stl/css :modal-restricted-header)} [:h2 {:class (stl/css :modal-restricted-title)} (tr "modals.invite-restricted-members.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-restricted-content)} [:p (tr "modals.invite-restricted-members.description")] @@ -409,13 +397,14 @@ on-hide (mf/use-fn #(reset! show? false))] [:* (if (and can-change-rol not-superior (not (and is-you is-owner))) - [:div {:class (stl/css :rol-selector :has-priv) - :role "combobox" - :aria-labelledby "role-label-id" - :on-click on-show} - [:span {:class (stl/css :rol-label) - :id "role-label-id"} role] - arrow-icon] + [:button {:class (stl/css :rol-selector :has-priv) + :type "button" + :aria-haspopup "listbox" + :aria-expanded @show? + :on-click on-show} + [:span {:class (stl/css :rol-label)} role] + [:> icon* {:icon-id i/arrow-down + :class (stl/css :arrow-icon)}]] [:div {:class (stl/css :rol-selector)} [:span {:class (stl/css :rol-label)} role]]) @@ -451,9 +440,12 @@ (when (or is-you? (and can-delete? (not (and is-owner? (not owner?))))) [:* - [:button {:class (stl/css :menu-btn) - :on-click on-show} - menu-icon] + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.options") + :aria-haspopup "menu" + :aria-expanded @show? + :icon i/menu + :on-click on-show}] [:& dropdown {:show @show? :on-close on-hide :dropdown-id (str "member-actions-" (:id member))} [:ul {:class (stl/css :actions-dropdown)} @@ -691,10 +683,14 @@ [:* (if (and can-invite (= status :pending)) - [:div {:class (stl/css :rol-selector :has-priv) - :on-click on-show} + [:button {:class (stl/css :rol-selector :has-priv) + :type "button" + :aria-haspopup "listbox" + :aria-expanded @show? + :on-click on-show} [:span {:class (stl/css :rol-label)} label] - arrow-icon] + [:> icon* {:icon-id i/arrow-down + :class (stl/css :arrow-icon)}]] [:div {:class (stl/css :rol-selector)} [:span {:class (stl/css :rol-label)} label]]) @@ -872,8 +868,11 @@ (tr "dashboard.invitation-modal.title.delete-invitations") (tr "dashboard.invitation-modal.title.resend-invitations"))] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-invitation-content)} [:p @@ -1000,8 +999,11 @@ [:h2 {:class (stl/css :modal-select-organization-title)} title] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] (when text [:div {:class (stl/css :modal-content :modal-select-organization-text)} text]) @@ -1380,8 +1382,11 @@ (tr "modals.edit-webhook.title") (tr "modals.create-webhook.title"))] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-content)} [:div {:class (stl/css :fields-row)} @@ -1422,8 +1427,10 @@ (tr "labels.webhooks")] [:> i18n/tr-html* {:class (stl/css :hero-desc) :content (tr "dashboard.webhooks.description")}] - [:button {:class (stl/css :hero-btn) - :on-click #(st/emit! (modal/show :webhook {}))} + [:> button* {:variant "primary" + :type "button" + :class (stl/css :hero-btn) + :on-click #(st/emit! (modal/show :webhook {}))} (tr "dashboard.webhooks.create")]]) (mf/defc webhook-actions* @@ -1434,9 +1441,12 @@ on-hide (mf/use-fn #(reset! show? false))] (if can-edit [:* - [:button {:class (stl/css :menu-btn) - :on-click on-show} - menu-icon] + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.options") + :aria-haspopup "menu" + :aria-expanded @show? + :icon i/menu + :on-click on-show}] [:& dropdown {:show @show? :on-close on-hide :dropdown-id "webhook-actions"} [:ul {:class (stl/css :webhook-actions-dropdown)} [:li {:on-click on-edit @@ -1502,8 +1512,10 @@ [:div {:class (stl/css :table-field :last-delivery) :title last-delivery-text} (if (nil? error-code) - success-icon - warning-icon)] + [:> icon* {:icon-id i/msg-success + :class (stl/css :success-icon)}] + [:> icon* {:icon-id i/msg-warning + :class (stl/css :warning-icon)}])] [:div {:class (stl/css :table-field :uri)} [:div (dm/str (:uri webhook))]] [:div {:class (stl/css :table-field :active)} @@ -1584,10 +1596,9 @@ ;; Filter to organizations where user is allowed to create/add teams organizations (mf/with-memo [all-organizations profile-id] (->> all-organizations - (filter (fn [organization] - (let [perm (get-in organization [:permissions :create-teams]) - is-owner? (= profile-id (:owner-id organization))] - (or (= perm "any") is-owner?)))))) + (filter #(cto/allowed? :create-team + {:organization-perms % + :profile-id profile-id})))) ;; Keep parity with UX requirement: hide only when user belongs to one organization. can-change-organization? (mf/with-memo [all-organizations] @@ -1657,7 +1668,9 @@ (when can-edit [:button {:class (stl/css :update-overlay) :on-click on-image-click} - image-icon]) + [:> icon* {:icon-id i/img + :size "l" + :class (stl/css :image-icon)}]]) [:img {:class (stl/css :team-image) :src (cfg/resolve-team-photo-url team)}] (when can-edit @@ -1688,7 +1701,8 @@ :type "button" :class (stl/css-case :organization-options-btn (not show-organization-options-menu?) :organization-options-btn-open show-organization-options-menu?) :on-click on-show-options-click} - organization-menu-icon + [:> icon* {:icon-id i/menu + :class (stl/css :organization-menu-icon)}] [:& dropdown {:show show-organization-options-menu? :on-close close-organization-options-menu :dropdown-id "organization-options"} [:ul {:class (stl/css :organization-dropdown) @@ -1720,7 +1734,8 @@ (str (:name owner) " (" (tr "labels.owner") ")")]] [:div {:class (stl/css :block-content)} - user-icon + [:> icon* {:icon-id i/user + :class (stl/css :user-icon)}] [:span {:class (stl/css :block-text)} (tr "dashboard.num-of-members" (count members))]]] @@ -1729,12 +1744,14 @@ (tr "dashboard.team-projects")] [:div {:class (stl/css :block-content)} - group-icon + [:> icon* {:icon-id i/group + :class (stl/css :group-icon)}] [:span {:class (stl/css :block-text)} (tr "labels.num-of-projects" (i18n/c (dec (:projects stats))))]] [:div {:class (stl/css :block-content)} - document-icon + [:> icon* {:icon-id i/document + :class (stl/css :document-icon)}] [:span {:class (stl/css :block-text)} (tr "labels.num-of-files" (i18n/c (:files stats)))]]] diff --git a/frontend/src/app/main/ui/dashboard/team.scss b/frontend/src/app/main/ui/dashboard/team.scss index 2a8b754496..4966b8ab87 100644 --- a/frontend/src/app/main/ui/dashboard/team.scss +++ b/frontend/src/app/main/ui/dashboard/team.scss @@ -4,10 +4,12 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the component-family placeholders: %input-checkbox +// and %checkbox-icon (checkboxes, #12380) and %modal-* (modals). @use "refactor/common-refactor.scss" as deprecated; -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "ds/_utils.scss" as *; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_borders.scss" as *; @use "ds/mixins.scss" as *; @@ -44,7 +46,7 @@ } .block-label { - @include t.use-typography("headline-small"); + @include use-typography("headline-small"); color: var(--color-foreground-secondary); } @@ -75,11 +77,8 @@ align-items: center; block-size: $sz-16; inline-size: $sz-16; - color: transparent; - fill: none; - stroke-width: $b-1; margin: 0 auto; - stroke: var(--color-foreground-secondary); + color: var(--color-foreground-secondary); } .team-icon { @@ -98,8 +97,8 @@ .team-image { position: absolute; - top: 0; - left: 0; + inset-block-start: 0; + inset-inline-start: 0; border-radius: 50%; inline-size: $sz-120; block-size: $sz-120; @@ -114,8 +113,8 @@ justify-content: center; align-items: center; position: absolute; - top: 0; - left: 0; + inset-block-start: 0; + inset-inline-start: 0; block-size: 100%; inline-size: 100%; z-index: var(--z-index-set); @@ -124,17 +123,7 @@ } .image-icon { - display: flex; - justify-content: center; - align-items: center; - block-size: $sz-16; - inline-size: $sz-16; - color: transparent; - fill: none; - stroke-width: $b-1; - min-inline-size: $sz-24; - min-block-size: $sz-24; - stroke: var(--color-foreground-primary); + color: var(--color-foreground-primary); } // TEAM MEMBERS PAGE @@ -150,11 +139,6 @@ scrollbar-gutter: stable; } -.dashboard-team-members.dashboard-top-cta { - flex-direction: column; - justify-content: flex-start; -} - .team-members { display: grid; grid-template-rows: auto 1fr; @@ -164,7 +148,7 @@ } .table-header { - @include t.use-typography("headline-small"); + @include use-typography("headline-small"); display: grid; align-items: center; @@ -184,7 +168,7 @@ inline-size: 100%; block-size: 100%; max-inline-size: $sz-1000; - margin-top: var(--sp-l); + margin-block-start: var(--sp-l); color: var(--color-foreground-secondary); } @@ -250,23 +234,23 @@ .member-name, .member-email { @include text-ellipsis; - @include t.use-typography("body-large"); + @include use-typography("body-large"); } .member-email { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-secondary); } .you { color: var(--color-foreground-secondary); - margin-left: px2rem(6); + margin-inline-start: px2rem(6); } // ROL INFO .rol-selector { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); position: relative; display: grid; @@ -282,6 +266,9 @@ } .has-priv { + border: none; + color: inherit; + text-align: start; cursor: pointer; } @@ -302,13 +289,13 @@ background-color: var(--color-background-tertiary); border: $b-2 solid var(--color-background-quaternary); margin: 0; - bottom: calc(-1 * px2rem(76)); + inset-block-end: calc(-1 * px2rem(76)); inline-size: fit-content; min-inline-size: $sz-160; } .rol-dropdown-item { - @include t.use-typography("body-small"); + @include use-typography("body-small"); display: flex; align-items: center; @@ -325,24 +312,6 @@ } // MEMBER ACTIONS -.menu-icon { - display: flex; - justify-content: center; - align-items: center; - block-size: $sz-16; - inline-size: $sz-16; - color: transparent; - fill: none; - stroke-width: $b-1; - stroke: var(--color-foreground-primary); -} - -.menu-btn { - border: none; - background: none; - cursor: pointer; -} - .input-checkbox { // TODO: remove this extended class. @extend %input-checkbox; @@ -363,15 +332,17 @@ background-color: var(--color-background-tertiary); border: $b-2 solid var(--color-background-quaternary); margin: 0; - bottom: calc(-1 * var(--sp-xxxl)); - right: 0; - left: unset; + + // Opens below the button, whatever the menu and button heights. + inset-block-start: calc(100% + var(--sp-xs)); + inset-inline-end: 0; + inset-inline-start: unset; inline-size: fit-content; min-inline-size: $sz-160; } .action-dropdown-item { - @include t.use-typography("body-small"); + @include use-typography("body-small"); display: flex; align-items: center; @@ -409,21 +380,17 @@ } .invitations-actions { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); display: flex; justify-content: end; align-items: center; gap: var(--sp-l); - color: var(--title-foreground-color); + color: var(--color-foreground-secondary); block-size: $sz-40; margin-block-end: px2rem(36); } -.error-notification { - margin-block-end: var(--sp-xxl); -} - .table-row-invitations { grid-template-columns: 43% 1fr $sz-88 var(--sp-xxl); align-items: center; @@ -431,7 +398,7 @@ .empty-invitations { inline-size: 100%; - margin-top: var(--sp-l); + margin-block-start: var(--sp-l); border: $b-1 solid var(--color-background-quaternary); border-radius: $br-8; color: var(--color-foreground-secondary); @@ -439,11 +406,6 @@ text-align: center; } -.empty-invitations-buttons { - inline-size: fit-content; - margin: auto; -} - .no-permission-text { max-inline-size: $sz-512; margin: auto; @@ -468,18 +430,13 @@ .field-email { @include text-ellipsis; - @include t.use-typography("body-large"); + @include use-typography("body-large"); display: flex; gap: var(--sp-l); align-items: center; } -.invitations-dropdown { - bottom: calc(-1 * px2rem(112)); - right: calc(-1 * var(--sp-xl)); -} - // WEBHOOKS SECTION .dashboard-team-webhooks { display: grid; @@ -515,7 +472,7 @@ } .webhooks-hero { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); display: grid; grid-template-rows: auto 1fr auto; @@ -527,24 +484,20 @@ } .hero-title { - @include t.use-typography("title-large"); + @include use-typography("title-large"); color: var(--color-foreground-primary); } .hero-desc { - @include t.use-typography("body-large"); + @include use-typography("body-large"); color: var(--color-foreground-secondary); - margin-bottom: 0; + margin-block-end: 0; max-inline-size: $sz-512; } .hero-btn { - // TODO: Remove this extended class using a DS component - @extend %button-primary; - - block-size: $sz-32; max-inline-size: $sz-512; } @@ -584,14 +537,16 @@ background-color: var(--color-background-tertiary); border: $b-2 solid var(--color-background-quaternary); margin: 0; - right: calc(-1 * var(--sp-l)); - bottom: calc(-1 * $sz-40); + inset-inline-end: calc(-1 * var(--sp-l)); + + // Opens below the button, whatever the menu and button heights. + inset-block-start: calc(100% + var(--sp-xs)); inline-size: fit-content; min-inline-size: $sz-160; } .webhook-dropdown-item { - @include t.use-typography("body-small"); + @include use-typography("body-small"); display: flex; align-items: center; @@ -613,10 +568,7 @@ align-items: center; block-size: $sz-16; inline-size: $sz-16; - color: transparent; - fill: none; - stroke-width: $b-1; - stroke: var(--color-accent-success); + color: var(--color-accent-success); } .warning-icon { @@ -625,10 +577,7 @@ align-items: center; block-size: $sz-16; inline-size: $sz-16; - color: transparent; - fill: none; - stroke-width: $b-1; - stroke: var(--color-accent-warning); + color: var(--color-accent-warning); } // INVITE MEMBERS MODAL @@ -641,22 +590,22 @@ max-block-size: $sz-512; box-shadow: var(--el-shadow-dark); position: fixed; - top: px2rem(72); - right: var(--sp-m); - left: unset; + inset-block-start: px2rem(72); + inset-inline-end: var(--sp-m); + inset-inline-start: unset; inline-size: $sz-400; padding: var(--sp-xxxl); background-color: var(--color-background-primary); z-index: var(--z-index-set); &.hero { - top: px2rem(216); - right: var(--sp-xxxl); + inset-block-start: px2rem(216); + inset-inline-end: var(--sp-xxxl); } } .modal-team-container-workspace { - top: $sz-40; + inset-block-start: $sz-40; z-index: var(--z-index-set); } @@ -687,11 +636,7 @@ align-items: center; block-size: $sz-16; inline-size: $sz-16; - color: transparent; - fill: none; - stroke-width: $b-1; - stroke: var(--color-foreground-secondary); - transform: rotate(90deg); + color: var(--color-foreground-secondary); } .action-buttons { @@ -711,12 +656,12 @@ justify-content: center; align-items: center; position: fixed; - left: 0; - top: 0; + inset-inline-start: 0; + inset-block-start: 0; block-size: 100%; inline-size: 100%; z-index: var(--z-index-set); - background-color: var(--overlay-color); + background-color: var(--color-overlay-default); } .modal-container { @@ -732,27 +677,29 @@ } .modal-header { - margin-bottom: var(--sp-xxl); + margin-block-end: var(--sp-xxl); } .modal-title { - @include t.use-typography("title-small"); + @include use-typography("title-small"); color: var(--color-foreground-primary); } +// Tooltip wrapper of the modal close buttons: it places them in the corner. .modal-close-btn { - // TODO remove extended class creating a modal component - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { - @include t.use-typography("body-small"); + @include use-typography("body-small"); display: flex; flex-direction: column; gap: var(--sp-xxl); - margin-bottom: var(--sp-xxl); + margin-block-end: var(--sp-xxl); } .fields-row { @@ -762,7 +709,7 @@ } .select-title { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-primary); } @@ -790,7 +737,7 @@ display: flex; align-items: center; - @include t.use-typography("body-large"); + @include use-typography("body-large"); label { display: flex; @@ -815,7 +762,7 @@ span { @extend %checkbox-icon; - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-secondary); } @@ -823,7 +770,7 @@ input { margin: 0; - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-secondary); } @@ -842,13 +789,13 @@ } .modal-invitation-title { - @include t.use-typography("headline-large"); + @include use-typography("headline-large"); color: var(--color-foreground-primary); } .modal-invitation-content { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); overflow: auto; color: var(--color-foreground-secondary); @@ -877,13 +824,13 @@ } .modal-restricted-title { - @include t.use-typography("headline-large"); + @include use-typography("headline-large"); color: var(--color-foreground-primary); } .modal-restricted-content { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); margin-block-end: 0; } @@ -955,13 +902,13 @@ } .modal-select-organization-content { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-secondary); } .modal-select-organization-info { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); display: flex; flex-direction: column; @@ -971,13 +918,13 @@ } .modal-select-organization-title { - @include t.use-typography("headline-large"); + @include use-typography("headline-large"); color: var(--color-foreground-primary); } .modal-select-organization-text { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-secondary); } @@ -1023,10 +970,7 @@ align-items: center; block-size: $sz-16; inline-size: $sz-16; - color: transparent; - fill: none; - stroke-width: $b-1; - stroke: var(--stroke-color); + color: var(--stroke-color); } .organization-dropdown { @@ -1042,13 +986,13 @@ background-color: var(--color-background-tertiary); border: $b-2 solid var(--color-background-quaternary); margin: 0; - top: var(--sp-xxxl); + inset-block-start: var(--sp-xxxl); inline-size: fit-content; min-inline-size: $sz-160; } .organization-dropdown-item { - @include t.use-typography("body-small"); + @include use-typography("body-small"); display: flex; align-items: center; @@ -1071,5 +1015,5 @@ } a { - color: var(--modal-link-foreground-color); + color: var(--color-accent-primary); } diff --git a/frontend/src/app/main/ui/dashboard/team_form.cljs b/frontend/src/app/main/ui/dashboard/team_form.cljs index 81803fa4a1..08f6dcaad6 100644 --- a/frontend/src/app/main/ui/dashboard/team_form.cljs +++ b/frontend/src/app/main/ui/dashboard/team_form.cljs @@ -18,7 +18,8 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] @@ -118,8 +119,11 @@ [:h2 {:class (stl/css :modal-title)} (tr "labels.create-team")]) - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-content)} [:& fm/input {:type "text" @@ -162,7 +166,10 @@ [:div {:class (stl/css :modal-container)} [:div {:class (stl/css :modal-header)} [:h2 {:class (stl/css :modal-title)} title] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-content)} [:div message]]]])) diff --git a/frontend/src/app/main/ui/dashboard/team_form.scss b/frontend/src/app/main/ui/dashboard/team_form.scss index 946057fa95..1047c8f3d8 100644 --- a/frontend/src/app/main/ui/dashboard/team_form.scss +++ b/frontend/src/app/main/ui/dashboard/team_form.scss @@ -4,9 +4,11 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*, to move with the +// modals refactor) and the input placeholder (%input-element-label, with the inputs). @use "refactor/common-refactor.scss" as deprecated; @use "ds/_utils.scss" as *; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; .modal-overlay { @extend %modal-overlay-base; @@ -21,20 +23,23 @@ } .modal-title { - @include t.use-typography("headline-large"); + @include use-typography("headline-large"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } +// Tooltip wrapper of the close button: it places it in the corner. .modal-close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { margin-block-end: var(--sp-xxl); color: var(--color-foreground-secondary); - @include t.use-typography("body-large"); + @include use-typography("body-large"); } .team-form { @@ -52,24 +57,16 @@ flex-direction: column; gap: var(--sp-xs); align-items: flex-start; - width: 100%; + inline-size: 100%; border: none; background-color: transparent; - height: 100%; + block-size: 100%; } .action-buttons { @extend %modal-action-btns; } -.cancel-button { - @extend %modal-cancel-btn; -} - .accept-btn { @extend %modal-accept-btn; - - &.danger { - @extend %modal-danger-btn; - } } diff --git a/frontend/src/app/main/ui/dashboard/templates.cljs b/frontend/src/app/main/ui/dashboard/templates.cljs index cecb4db1aa..049d2f09e0 100644 --- a/frontend/src/app/main/ui/dashboard/templates.cljs +++ b/frontend/src/app/main/ui/dashboard/templates.cljs @@ -16,7 +16,7 @@ [app.main.data.modal :as modal] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.util.dom :as dom] [app.util.dom.normalize-wheel :as nw] [app.util.i18n :refer [tr]] @@ -25,12 +25,6 @@ [okulary.core :as l] [rumext.v2 :as mf])) -(def ^:private arrow-icon - (deprecated-icon/icon-xref :arrow (stl/css :arrow-icon))) - -(def ^:private download-icon - (deprecated-icon/icon-xref :add (stl/css :download-icon))) - (def builtin-templates (l/derived :builtin-templates st/state)) @@ -77,6 +71,7 @@ [:div {:class (stl/css :title)} [:button {:tab-index "0" :class (stl/css :title-btn) + :aria-expanded (not is-collapsed) :on-click on-click :on-key-down on-key-down} [:span {:class (stl/css :title-text)} @@ -85,11 +80,13 @@ [:span {:class (stl/css :title-icon-container)} [:span {:class (stl/css :title-icon-text)} (tr "labels.show")] [:span {:class (stl/css :title-icon :title-icon-collapsed)} - arrow-icon]] + [:> icon* {:icon-id i/arrow + :class (stl/css :arrow-icon)}]]] [:span {:class (stl/css :title-icon-container)} [:span {:class (stl/css :title-icon-text)} (tr "labels.hide")] [:span {:class (stl/css :title-icon)} - arrow-icon]])]])) + [:> icon* {:icon-id i/arrow + :class (stl/css :arrow-icon)}]]])]])) (mf/defc card-item* [{:keys [item index is-visible collapsed on-import]}] @@ -101,26 +98,22 @@ (mf/use-fn (mf/deps on-import) (fn [event] - (on-import item event))) - - on-key-down - (mf/use-fn - (mf/deps on-import) - (fn [event] - (when (kbd/enter? event) - (dom/stop-propagation event) - (on-import item event))))] + (on-import item event)))] [:div {:class (stl/css :card-container) - :tab-index (if (or (not is-visible) collapsed) "-1" "0") :id id :data-index index} - [:a {:class (stl/css :template-card) - :on-click on-click - :on-mouse-down dom/prevent-default - :on-mouse-enter #(reset! hover? true) - :on-mouse-leave #(reset! hover? false) - :on-key-down on-key-down} + ;; Importing the template is an action: a button, which already + ;; handles Enter and Space. + [:button {:type "button" + :class (stl/css :template-card) + :tab-index (if (or (not is-visible) collapsed) "-1" "0") + :on-click on-click + :on-mouse-down dom/prevent-default + :on-mouse-enter #(reset! hover? true) + :on-mouse-leave #(reset! hover? false) + :on-focus #(reset! hover? true) + :on-blur #(reset! hover? false)} [:div {:class (stl/css :img-container)} [:img {:src (dm/str href) :alt (:name item) @@ -131,7 +124,8 @@ (if @hover? (tr "dashboard.template.add-to-project") (:name item))] - download-icon]]])) + [:> icon* {:icon-id i/add + :class (stl/css :download-icon)}]]]])) (mf/defc card-item-link* [{:keys [total is-visible collapsed section]}] @@ -286,8 +280,10 @@ [:button {:class (stl/css :move-button :move-left) :tab-index (if ^boolean collapsed "-1" "0") :on-click on-move-left - :on-key-down on-move-left} - arrow-icon]) + :on-key-down on-move-left + :aria-label (tr "labels.previous")} + [:> icon* {:icon-id i/arrow + :class (stl/css :arrow-icon)}]]) (when (:right @can-move) [:button {:class (stl/css :move-button :move-right) @@ -295,4 +291,5 @@ :on-click on-move-right :on-key-down on-move-right :aria-label (tr "labels.next")} - arrow-icon])])) + [:> icon* {:icon-id i/arrow + :class (stl/css :arrow-icon)}]])])) diff --git a/frontend/src/app/main/ui/dashboard/templates.scss b/frontend/src/app/main/ui/dashboard/templates.scss index eabb3c8976..b87c243df1 100644 --- a/frontend/src/app/main/ui/dashboard/templates.scss +++ b/frontend/src/app/main/ui/dashboard/templates.scss @@ -105,10 +105,7 @@ align-items: center; height: $sz-16; width: $sz-16; - color: transparent; - fill: none; - stroke-width: $b-1; - stroke: var(--color-foreground-secondary); + color: var(--color-foreground-secondary); } .move-button { @@ -131,7 +128,7 @@ background-color: var(--color-accent-primary); .arrow-icon { - stroke: var(--color-background-tertiary); + color: var(--color-background-tertiary); } } } @@ -190,13 +187,15 @@ padding: 0 var(--sp-xs) var(--sp-s) var(--sp-xs); border-radius: $br-8; border: $b-2 solid transparent; + background-color: transparent; - &:hover { - text-decoration: none; + &:hover, + &:focus-visible { + outline: none; border-color: var(--color-accent-primary); .download-icon { - stroke: var(--color-accent-primary); + color: var(--color-accent-primary); } .card-text { @@ -243,10 +242,7 @@ align-items: center; height: $sz-16; width: $sz-16; - color: transparent; - fill: none; - stroke-width: $b-1; - stroke: var(--icon-foreground); + color: var(--color-foreground-secondary); } .template-link { diff --git a/frontend/src/app/main/ui/delete_shared.cljs b/frontend/src/app/main/ui/delete_shared.cljs index 6e88fd41ab..4acd9b066d 100644 --- a/frontend/src/app/main/ui/delete_shared.cljs +++ b/frontend/src/app/main/ui/delete_shared.cljs @@ -11,8 +11,10 @@ [app.main.data.modal :as modal] [app.main.repo :as rp] [app.main.store :as st] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.ds.notifications.context-notification :refer [context-notification*]] - [app.main.ui.icons :as deprecated-icon] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as k] @@ -97,8 +99,11 @@ [:div {:class (stl/css :modal-container)} [:div {:class (stl/css :modal-header)} [:h2 {:class (stl/css :modal-title)} title] - [:button {:class (stl/css :modal-close-btn) - :on-click cancel-fn} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click cancel-fn}]] [:div {:class (stl/css :modal-content)} (when (and (string? subtitle) (not= subtitle "")) @@ -124,14 +129,12 @@ [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} (when-not (= cancel-label :omit) - [:input {:class (stl/css :cancel-button) - :type "button" - :value cancel-label - :on-click cancel-fn}]) + [:> button* {:variant "secondary" + :type "button" + :on-click cancel-fn} + cancel-label]) - [:input {:class (stl/css-case :accept-btn true - :danger (= accept-style :danger) - :primary (= accept-style :primary)) - :type "button" - :value accept-label - :on-click accept-fn}]]]]])) + [:> button* {:variant (if (= accept-style :danger) "destructive" "primary") + :type "button" + :on-click accept-fn} + accept-label]]]]])) diff --git a/frontend/src/app/main/ui/delete_shared.scss b/frontend/src/app/main/ui/delete_shared.scss index ab77bb5fd1..5851074638 100644 --- a/frontend/src/app/main/ui/delete_shared.scss +++ b/frontend/src/app/main/ui/delete_shared.scss @@ -4,15 +4,13 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*), which move with +// the modals refactor. @use "refactor/basic-rules.scss" as *; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; .modal-overlay { @extend %modal-overlay-base; - - &.transparent { - background-color: transparent; - } } .modal-container { @@ -23,33 +21,30 @@ grid-template-rows: auto minmax(0, 1fr) auto; } -.list-wrapper { - display: grid; - grid-template-rows: auto 1fr auto; - max-height: 100%; -} - .modal-title { - @include t.use-typography("headline-medium"); + @include use-typography("headline-medium"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } +// Tooltip wrapper of the close button: it places it in the corner. .modal-close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { - @include t.use-typography("body-small"); + @include use-typography("body-small"); display: grid; gap: var(--sp-s); } .element-list { - @include t.use-typography("body-large"); + @include use-typography("body-large"); - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); overflow-y: auto; margin-block: 0; } @@ -58,18 +53,6 @@ @extend %modal-action-btns; } -.cancel-button { - @extend %modal-cancel-btn; -} - -.accept-btn { - @extend %modal-accept-btn; - - &.danger { - @extend %modal-danger-btn; - } -} - .modal-scd-msg { margin-block: 0; } @@ -77,8 +60,8 @@ .modal-scd-msg, .modal-subtitle, .modal-msg { - @include t.use-typography("body-large"); + @include use-typography("body-large"); - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); line-height: 1.5; } diff --git a/frontend/src/app/main/ui/ds/buttons/button.cljs b/frontend/src/app/main/ui/ds/buttons/button.cljs index c7ea4245ab..14985a6896 100644 --- a/frontend/src/app/main/ui/ds/buttons/button.cljs +++ b/frontend/src/app/main/ui/ds/buttons/button.cljs @@ -36,7 +36,8 @@ :button-destructive (= variant "destructive")) props (mf/spread-props props {:class [class internal-class] :href to - :type (d/nilv type "button") + ;; On an anchor, `type` is a MIME type hint. + :type (when-not to (d/nilv type "button")) :ref (fn [node] (when on-ref (on-ref node)))})] diff --git a/frontend/src/app/main/ui/ds/buttons/button.stories.jsx b/frontend/src/app/main/ui/ds/buttons/button.stories.jsx index f030431e12..fcdd86108d 100644 --- a/frontend/src/app/main/ui/ds/buttons/button.stories.jsx +++ b/frontend/src/app/main/ui/ds/buttons/button.stories.jsx @@ -6,6 +6,7 @@ import * as React from "react"; import Components from "@target/components"; +import { within, expect } from "storybook/test"; const { Button } = Components; const { icons } = Components.meta; @@ -67,3 +68,34 @@ export const Destructive = { variant: "destructive", }, }; + +// With `to`, the button renders an anchor: it keeps the button look but it is +// a real link (href, open in a new tab, announced as a link). +export const AsLink = { + args: { + variant: "secondary", + to: "https://penpot.app", + children: "Go to Penpot", + }, +}; + +export const TestAsLinkRendersAnchor = { + args: AsLink.args, + play: async ({ canvasElement }) => { + const link = within(canvasElement).getByRole("link", { + name: "Go to Penpot", + }); + await expect(link).toHaveAttribute("href", "https://penpot.app"); + // `type` on an anchor is a MIME type hint, not a button type. + await expect(link).not.toHaveAttribute("type"); + }, +}; + +export const TestButtonDefaultsToTypeButton = { + play: async ({ canvasElement }) => { + const button = within(canvasElement).getByRole("button", { + name: "Lorem ipsum", + }); + await expect(button).toHaveAttribute("type", "button"); + }, +}; diff --git a/frontend/src/app/main/ui/ds/buttons/buttons.mdx b/frontend/src/app/main/ui/ds/buttons/buttons.mdx index 5eaf8ca5b5..4bcbbd0156 100644 --- a/frontend/src/app/main/ui/ds/buttons/buttons.mdx +++ b/frontend/src/app/main/ui/ds/buttons/buttons.mdx @@ -71,8 +71,23 @@ Action buttons are a variant of `icon-button*` (`"action"`) but smaller. ### Semantics -In some cases, a button needs to be semantically an 'a' tag, but visually maintain the button style. -In that case, the button accepts a 'to' property with the expected 'href'. It will then be rendered as an anchor element. +When the control takes the user to another page or URL, it must be a link, even if it looks like a button. +Pass the destination in `to`: `button*` then renders an `` with the button style, so it can be +opened in a new tab and assistive technologies announce it as a link. It renders no `type` attribute, +which on an anchor would mean a MIME type. + +```clj +[:> button* {:variant "secondary" + :to (rt/resolve-uri router :auth-login {})} + (tr "profile.recovery.go-to-login")] +``` + +To keep the in-app navigation on a plain click, add an `on-click` that calls `prevent-default` (unless +Ctrl/Cmd is pressed) and emits the navigation event. Other anchor attributes, such as `target`, pass through. + + + +Use a plain `button*` (without `to`) for actions that do not change the URL, such as opening a modal. ### Accessibility diff --git a/frontend/src/app/main/ui/ds/buttons/icon_button.scss b/frontend/src/app/main/ui/ds/buttons/icon_button.scss index 291c352081..85cda9703e 100644 --- a/frontend/src/app/main/ui/ds/buttons/icon_button.scss +++ b/frontend/src/app/main/ui/ds/buttons/icon_button.scss @@ -41,6 +41,7 @@ --button-hover-bg-color: transparent; --button-hover-fg-color: var(--color-accent-primary); --button-active-bg-color: var(--color-background-quaternary); + --button-active-fg-color: var(--color-accent-primary); --button-disabled-bg-color: transparent; --button-disabled-fg-color: var(--color-accent-primary-muted); --button-focus-bg-color: transparent; @@ -49,6 +50,12 @@ --button-focus-outer-ring-color: var(--color-accent-primary); --button-width: #{$sz-24}; --button-height: #{$sz-24}; + + // A toggled-on action button shows only the accent icon; the background + // stays for the click feedback (:active). + &[aria-pressed="true"] { + --button-active-bg-color: transparent; + } } .flyout-indicator { diff --git a/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs b/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs index 991532c963..6d05480deb 100644 --- a/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs +++ b/frontend/src/app/main/ui/ds/foundations/assets/icon.cljs @@ -165,6 +165,9 @@ (def ^:icon-id group "group") (def ^:icon-id gutter-horizontal "gutter-horizontal") (def ^:icon-id gutter-vertical "gutter-vertical") +(def ^:icon-id handlers-equal "handlers-equal") +(def ^:icon-id handlers-independent "handlers-independent") +(def ^:icon-id handlers-mirror "handlers-mirror") (def ^:icon-id help "help") (def ^:icon-id hide "hide") (def ^:icon-id history "history") @@ -238,10 +241,11 @@ (def ^:icon-id search "search") (def ^:icon-id separate-nodes "separate-nodes") (def ^:icon-id settings "settings") +(def ^:icon-id share "share") (def ^:icon-id shown "shown") (def ^:icon-id size-horizontal "size-horizontal") (def ^:icon-id size-vertical "size-vertical") -(def ^:icon-id snap-nodes "snap-nodes") +(def ^:icon-id snap "snap") (def ^:icon-id status-alert "status-alert") (def ^:icon-id status-tick "status-tick") (def ^:icon-id status-update "status-update") diff --git a/frontend/src/app/main/ui/exports/assets.cljs b/frontend/src/app/main/ui/exports/assets.cljs index f1be8e32a2..6f7f2311aa 100644 --- a/frontend/src/app/main/ui/exports/assets.cljs +++ b/frontend/src/app/main/ui/exports/assets.cljs @@ -16,7 +16,9 @@ [app.main.data.modal :as modal] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.workspace.shapes :refer [shape-wrapper]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr c]] @@ -25,15 +27,6 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) -(def ^:private neutral-icon - (deprecated-icon/icon-xref :msg-neutral (stl/css :icon))) - -(def ^:private error-icon - (deprecated-icon/icon-xref :delete-text (stl/css :icon))) - -(def ^:private close-icon - (deprecated-icon/icon-xref :close (stl/css :close-icon))) - (mf/defc export-multiple-dialog* {::mf/private true} [{:keys [exports title cmd no-selection origin name]}] @@ -83,9 +76,11 @@ [:div {:class (stl/css :modal-header)} [:h2 {:class (stl/css :modal-title)} title] - [:button {:class (stl/css :modal-close-btn) - :on-click cancel-fn} - deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click cancel-fn}]] [:* [:div {:class (stl/css :modal-content)} @@ -98,12 +93,12 @@ (cond all-checked? [:span {:class (stl/css-case :checkobox-tick true :global/checked true)} - deprecated-icon/tick] + [:> icon* {:icon-id i/tick :size "s"}]] all-unchecked? [:span {:class (stl/css-case :checkobox-tick true :global/uncheked true)}] :else [:span {:class (stl/css-case :checkobox-tick true :global/intermediate true)} - deprecated-icon/remove-icon])]] + [:> icon* {:icon-id i/remove :size "s"}]])]] [:div {:class (stl/css :selection-title)} (tr "dashboard.export-multiple.selected" (c (count enabled-exports)) @@ -122,7 +117,7 @@ (if (:enabled export) [:span {:class (stl/css-case :checkobox-tick true :global/checked true)} - deprecated-icon/tick] + [:> icon* {:icon-id i/tick :size "s"}]] [:span {:class (stl/css-case :checkobox-tick true :global/uncheked true)}])] @@ -158,19 +153,16 @@ (when (> (count all-exports) 0) [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} - [:input {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click cancel-fn}] + [:> button* {:variant "secondary" + :on-click cancel-fn} + (tr "labels.cancel")] - [:input {:class (stl/css-case :accept-btn true - :btn-disabled (or in-progress? all-unchecked?)) - :disabled (or in-progress? all-unchecked?) - :type "button" - :value (if in-progress? - (tr "workspace.options.exporting-object") - (tr "labels.export")) - :on-click (when-not in-progress? accept-fn)}]]])]]])) + [:> button* {:variant "primary" + :disabled (or in-progress? all-unchecked?) + :on-click (when-not in-progress? accept-fn)} + (if in-progress? + (tr "workspace.options.exporting-object") + (tr "labels.export"))]]])]]])) (mf/defc shapes-no-selection [] [:div {:class (stl/css :no-selection)} @@ -215,8 +207,8 @@ [] (let [state (mf/deref refs/export) profile (mf/deref refs/profile) - theme (or (:theme profile) theme/default) - is-default-theme? (= theme/default theme) + theme (theme/use-theme profile) + dark-theme? (= "dark" theme) error? (:error state) ;; The exporter is at capacity: worth its own wording, so the user ;; knows retrying later is the thing to do. @@ -231,12 +223,13 @@ queued? (and (some? job-id) (= "queued" status)) cancelling? (and (some? job-id) (= "cancelling" status)) cancelled? (and (some? job-id) (= "cancelled" status)) - ;; Only the wasm backend can actually stop: a browser render holds its - ;; pool slot until playwright gives up. - cancellable? (and (some? job-id) - (= "wasm" (:backend state)) - (:in-progress state) - (not cancelling?)) + ;; Server wasm jobs and local client-side exports can stop; a non-wasm + ;; browser render holds its pool slot until playwright gives up. + cancellable? (and (:in-progress state) + (not cancelling?) + (or (= "local" (:backend state)) + (and (some? job-id) + (= "wasm" (:backend state))))) total (or (:total state) (count items)) complete? (= progress total) circ (* 2 Math/PI 12) @@ -252,13 +245,13 @@ error? clr/new-danger (or cancelling? cancelled?) clr/new-warning - healthy? (if is-default-theme? + healthy? (if dark-theme? clr/new-primary clr/new-primary-light) (not healthy?) clr/new-warning) background-clr - (if is-default-theme? + (if dark-theme? clr/background-quaternary clr/background-quaternary-light) @@ -290,8 +283,11 @@ [:* (when widget-visible? - [:div {:class (stl/css :export-progress-widget) - :on-click toggle-detail-visibility} + [:button {:type "button" + :class (stl/css :export-progress-widget) + :aria-label title + :aria-expanded (boolean detail-visible?) + :on-click toggle-detail-visibility} [:svg {:width "24" :height "24"} [:circle {:r "10" :cx "12" @@ -313,9 +309,8 @@ (when detail-visible? [:div {:class (stl/css-case :export-progress-modal true :has-error error?)} - (if error? - error-icon - neutral-icon) + [:> icon* {:icon-id (if error? i/delete-text i/msg-neutral) + :class (stl/css :icon)}] [:div {:class (stl/css :export-progress-title)} [:div {:class (stl/css :title-text)} title] @@ -341,9 +336,11 @@ [:span {:class (stl/css :progress)} (dm/str progress " / " total)])] - [:button {:class (stl/css :progress-close-button) - :on-click toggle-detail-visibility} - close-icon] + [:> icon-button* {:variant "action" + :tooltip-class (stl/css :progress-close-button) + :aria-label (tr "labels.close") + :icon i/close + :on-click toggle-detail-visibility}] (when-not error? [:svg {:class (stl/css :progress-bar) diff --git a/frontend/src/app/main/ui/exports/assets.scss b/frontend/src/app/main/ui/exports/assets.scss index 2780d53ea7..db3ec2d612 100644 --- a/frontend/src/app/main/ui/exports/assets.scss +++ b/frontend/src/app/main/ui/exports/assets.scss @@ -4,66 +4,75 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for the shared modal and checkbox placeholders, which move to the DS +// with the modals and the checkboxes. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/mixins.scss" as *; +@use "ds/typography.scss" as *; // PROGRESS WIDGET .export-progress-widget { - @include deprecated.flex-center; - - width: deprecated.$s-28; - height: deprecated.$s-28; + display: flex; + justify-content: center; + align-items: center; + inline-size: $sz-28; + block-size: $sz-28; + padding: 0; + border: none; + background: none; + cursor: pointer; } // PROGRESS MODAL .export-progress-modal { - --export-modal-bg-color: var(--alert-background-color-default); - --export-modal-fg-color: var(--alert-text-foreground-color-default); - --export-modal-icon-color: var(--alert-icon-foreground-color-default); - --export-modal-border-color: var(--alert-border-color-default); + --export-modal-bg-color: var(--color-background-primary); + --export-modal-fg-color: var(--color-foreground-primary); + --export-modal-icon-color: var(--color-foreground-primary); + --export-modal-border-color: var(--color-background-quaternary); position: absolute; - right: deprecated.$s-16; - top: deprecated.$s-48; + inset-inline-end: var(--sp-l); + inset-block-start: $sz-48; display: grid; - grid-template-columns: deprecated.$s-24 1fr deprecated.$s-24; + grid-template-columns: $sz-24 1fr $sz-24; grid-template-areas: "icon text close" "bar bar bar"; - gap: deprecated.$s-4 deprecated.$s-8; - padding-block-start: deprecated.$s-8; + gap: var(--sp-xs) var(--sp-s); + padding-block-start: var(--sp-s); background-color: var(--export-modal-bg-color); - border: deprecated.$s-1 solid var(--export-modal-border-color); - border-radius: deprecated.$br-8; - z-index: deprecated.$z-index-modal; + border: $b-1 solid var(--export-modal-border-color); + border-radius: $br-8; + z-index: var(--z-index-set); overflow: hidden; } .has-error { - --export-modal-bg-color: var(--alert-background-color-error); - --export-modal-fg-color: var(--alert-text-foreground-color-error); - --export-modal-icon-color: var(--alert-icon-foreground-color-error); - --export-modal-border-color: var(--alert-border-color-error); + --export-modal-bg-color: var(--color-background-error); + --export-modal-icon-color: var(--color-accent-error); + --export-modal-border-color: var(--color-accent-error); grid-template-areas: "icon text close"; - gap: deprecated.$s-8; - padding-block: deprecated.$s-8; + gap: var(--sp-s); + padding-block: var(--sp-s); } .icon { - @extend %button-icon; - grid-area: icon; align-self: center; - margin-inline-start: deprecated.$s-8; - stroke: var(--export-modal-icon-color); + margin-inline-start: var(--sp-s); + color: var(--export-modal-icon-color); } .export-progress-title { - @include deprecated.body-medium-typography; + @include use-typography("body-medium"); display: grid; grid-template-columns: auto 1fr; - gap: deprecated.$s-8; + gap: var(--sp-s); grid-area: text; align-self: center; padding: 0; @@ -72,130 +81,137 @@ } .progress { - @include deprecated.body-medium-typography; + @include use-typography("body-medium"); - padding-left: deprecated.$s-8; + padding-inline-start: var(--sp-s); margin: 0; align-self: center; - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); } +// Link-styled text button: stays a raw button. .retry-btn { - @include deprecated.button-style; - @include deprecated.body-small-typography; + @include use-typography("body-small"); display: inline; - text-align: left; - color: var(--modal-link-foreground-color); + text-align: start; + color: var(--color-accent-primary); margin: 0; padding: 0; + border: none; + background: none; + cursor: pointer; } .progress-close-button { - @include deprecated.button-style; - - padding: 0; - margin-inline-end: deprecated.$s-8; -} - -.close-icon { - @extend %button-icon; - - stroke: var(--export-modal-icon-color); + margin-inline-end: var(--sp-s); } .progress-bar { - margin-top: 0; + margin-block-start: 0; grid-area: bar; } // EXPORT MODAL .modal-overlay { @extend %modal-overlay-base; - - &.transparent { - background-color: transparent; - } } .modal-container { @extend %modal-container-base; - max-height: calc(10 * deprecated.$s-80); + max-block-size: px2rem(800); } .modal-header { - margin-bottom: deprecated.$s-24; + margin-block-end: var(--sp-xxl); } .modal-title { - @include deprecated.headline-medium-typography; + @include use-typography("headline-medium"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } .modal-close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content, .no-selection { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - margin-bottom: deprecated.$s-24; + margin-block-end: var(--sp-xxl); .modal-link { - @include deprecated.body-large-typography; + @include use-typography("body-large"); text-decoration: none; cursor: pointer; - color: var(--modal-link-foreground-color); + color: var(--color-accent-primary); } .selection-header { - @include deprecated.flex-row; - - height: deprecated.$s-32; - margin-bottom: deprecated.$s-4; + display: flex; + align-items: center; + gap: var(--sp-xs); + block-size: $sz-32; + margin-block-end: var(--sp-xs); .selection-btn { - @include deprecated.button-style; @extend %input-checkbox; - @include deprecated.flex-center; - height: deprecated.$s-24; - width: deprecated.$s-24; + display: flex; + justify-content: center; + align-items: center; + block-size: $sz-24; + inline-size: $sz-24; padding: 0; - margin-left: deprecated.$s-16; + margin-inline-start: var(--sp-l); + border: none; + background: none; + cursor: pointer; span { @extend %checkbox-icon; + + // The DS icon draws its 12px glyph centered in a 16px box: keep that + // box, unshrunk, instead of the 12px the placeholder gives a legacy + // icon. + &:global(.checked) svg, + &:global(.intermediate) svg { + flex-shrink: 0; + inline-size: $sz-16; + block-size: $sz-16; + } } } .selection-title { - @include deprecated.body-large-typography; + @include use-typography("body-large"); - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); } } .selection-wrapper { position: relative; - width: 100%; - height: fit-content; + inline-size: 100%; + block-size: fit-content; } .selection-shadow { - width: 100%; - height: 100%; + inline-size: 100%; + block-size: 100%; &::after { position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 50px; + inset-block-end: 0; + inset-inline-start: 0; + inline-size: 100%; + block-size: px2rem(50); background: linear-gradient(to top, rgb(24 24 26 / 1) 0%, rgb(24 24 26 / 0) 100%); content: ""; pointer-events: none; @@ -203,84 +219,99 @@ } .selection-list { - @include deprecated.flex-column; - - max-height: deprecated.$s-400; + display: flex; + flex-direction: column; + gap: var(--sp-xs); + max-block-size: $sz-400; overflow-y: auto; - padding-bottom: deprecated.$s-12; + padding-block-end: var(--sp-m); .selection-row { - @include deprecated.flex-row; - - background-color: var(--entry-background-color); - min-height: deprecated.$s-40; - border-radius: deprecated.$br-8; + display: flex; + align-items: center; + gap: var(--sp-xs); + background-color: var(--color-background-tertiary); + min-block-size: $sz-40; + border-radius: $br-8; + // A list row, not a DS button: the whole row toggles its export. .selection-btn { - @include deprecated.button-style; - display: grid; grid-template-columns: min-content auto 1fr auto auto; align-items: center; - width: 100%; - height: 10%; - gap: deprecated.$s-8; - padding: 0 deprecated.$s-16; + inline-size: 100%; + block-size: 10%; + gap: var(--sp-s); + padding: 0 var(--sp-l); + border: none; + background: none; + cursor: pointer; .checkbox-wrapper { @extend %input-checkbox; - @include deprecated.flex-center; - height: deprecated.$s-24; - width: deprecated.$s-24; + display: flex; + justify-content: center; + align-items: center; + block-size: $sz-24; + inline-size: $sz-24; padding: 0; .checkobox-tick { @extend %checkbox-icon; + + // The DS icon draws its 12px glyph centered in a 16px box: keep that + // box, unshrunk, instead of the 12px the placeholder gives a + // legacy icon. + &:global(.checked) svg, + &:global(.intermediate) svg { + flex-shrink: 0; + inline-size: $sz-16; + block-size: $sz-16; + } } } - .selection-name { - @include deprecated.body-large-typography; - @include deprecated.text-ellipsis; + .selection-name, + .selection-scale, + .selection-extension { + @include use-typography("body-large"); + @include text-ellipsis; + color: var(--color-foreground-secondary); + } + + .selection-name { flex-grow: 1; - color: var(--modal-text-foreground-color); text-align: start; } .selection-scale { - @include deprecated.body-large-typography; - @include deprecated.text-ellipsis; - - min-width: deprecated.$s-108; - padding: deprecated.$s-12; - color: var(--modal-text-foreground-color); + min-inline-size: px2rem(108); + padding: var(--sp-m); } .selection-extension { - @include deprecated.body-large-typography; - @include deprecated.text-ellipsis; - - min-width: deprecated.$s-72; - padding: deprecated.$s-12; - color: var(--modal-text-foreground-color); + min-inline-size: px2rem(72); + padding: var(--sp-m); } } .image-wrapper { - @include deprecated.flex-center; - - min-height: deprecated.$s-32; - min-width: deprecated.$s-32; - background-color: var(--app-white); - border-radius: deprecated.$br-6; + display: flex; + justify-content: center; + align-items: center; + min-block-size: $sz-32; + min-inline-size: $sz-32; + background-color: var(--color-static-white); + border-radius: $br-6; margin: auto 0; + // The thumbnail markup has no class of its own. img, svg { object-fit: contain; - max-height: deprecated.$s-40; + max-block-size: $sz-40; } } } @@ -288,101 +319,14 @@ } .action-buttons { - @extend %modal-action-btns; -} - -.cancel-button { - @extend %modal-cancel-btn; -} - -.accept-btn { - @extend %modal-accept-btn; - - &.danger { - @extend %modal-danger-btn; - } + display: flex; + justify-content: flex-end; + gap: var(--sp-l); } .modal-scd-msg, -.modal-subtitle, .modal-msg { - @include deprecated.body-large-typography; + @include use-typography("body-large"); - color: var(--modal-text-foreground-color); -} - -.export-option { - @extend %input-checkbox; - - width: 100%; - align-items: flex-start; - - label { - align-items: flex-start; - - .modal-subtitle { - @include deprecated.body-large-typography; - - color: var(--modal-title-foreground-color); - } - } - - span { - margin-top: deprecated.$s-8; - } -} - -.option-content { - @include deprecated.flex-column; - @include deprecated.body-large-typography; -} - -.file-entry { - .file-name { - @include deprecated.flex-row; - - .file-icon { - @include deprecated.flex-center; - - height: deprecated.$s-16; - width: deprecated.$s-16; - - svg { - @extend %button-icon-small; - - stroke: var(--input-foreground); - } - } - - .file-name-label { - @include deprecated.body-large-typography; - @include deprecated.text-ellipsis; - } - } - - &.loading { - .file-name { - color: var(--modal-text-foreground-color); - } - } - - &.error { - .file-name { - color: var(--modal-text-foreground-color); - - .file-icon svg { - stroke: var(--modal-text-foreground-color); - } - } - } - - &.success { - .file-name { - color: var(--modal-text-foreground-color); - - .file-icon svg { - stroke: var(--modal-text-foreground-color); - } - } - } + color: var(--color-foreground-secondary); } diff --git a/frontend/src/app/main/ui/exports/files.cljs b/frontend/src/app/main/ui/exports/files.cljs index f142207511..fba7916046 100644 --- a/frontend/src/app/main/ui/exports/files.cljs +++ b/frontend/src/app/main/ui/exports/files.cljs @@ -11,36 +11,117 @@ [app.common.data.macros :as dm] [app.config :as cf] [app.main.data.exports.files :as fexp] + [app.main.data.imports :as imp] + [app.main.data.jobs :as dj] [app.main.data.modal :as modal] [app.main.store :as st] [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] - [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.ds.foundations.typography :as t] [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.ds.foundations.typography.text :refer [text*]] [app.main.ui.ds.notifications.context-notification :refer [context-notification*]] - [app.main.ui.ds.product.loader :refer [loader*]] - [app.main.ui.notifications.context-notification :refer [context-notification]] + [app.main.ui.jobs.progress :as jp] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [beicon.v2.core :as rx] + [cuerdas.core :as str] [rumext.v2 :as mf])) +(defn- same-file? + "Whether an id from a message names a row of the dialog. The ids cross + serialization boundaries, so a message can carry the id as a string + while the row keeps the uuid (or the other way round): both sides are + canonicalized instead of assuming one type." + [a b] + (= (str a) (str b))) + +(defn- error-hint + "What a failed row reports: the message of the failure when it says + something useful, or nil. A string error already is the message; a map + carries it under `:hint`; anything else is unwrapped with `ex-data` + first, which itself can answer a string." + [error] + (let [data (cond + (string? error) error + (map? error) error + :else (ex-data error))] + (some-> (if (string? data) data (:hint data)) + (str/trim) + (not-empty)))) + (defn- mark-file-error + "The export of a file failed: a terminal state that replaces any + other, so a row never reports success and failure at once." + [files file-id error] + (mapv (fn [file] + (if (same-file? file-id (:id file)) + (-> file + (assoc :export-error true + :error error + :loading false) + (dissoc :export-success :size :progress :queued)) + file)) + files)) + +(defn- mark-file-cancelled + "The job of a file was cancelled elsewhere: a neutral terminal state, + neither success nor error, replacing any other." [files file-id] - (mapv #(cond-> % - (= file-id (:id %)) - (assoc :export-error? true - :loading false)) + (mapv (fn [file] + (if (same-file? file-id (:id file)) + (-> file + (assoc :export-cancelled true + :loading false) + (dissoc :export-success :size :export-error :error + :progress :queued)) + file)) + files)) + +(defn- mark-all-cancelled + "Every file still loading is over: its job was just cancelled, or it + was never created because the exports run one after another." + [files] + (mapv (fn [file] + (if (:loading file) + (-> file + (assoc :export-cancelled true + :loading false) + (dissoc :progress :queued)) + file)) files)) (defn- mark-file-success + "The export of a file produced its artifact: a terminal state that + replaces any other, so a row never reports success and failure at + once." + [files file-id size] + (mapv (fn [file] + (if (same-file? file-id (:id file)) + (-> file + (assoc :export-success true + :size size + :loading false) + (dissoc :export-error :error :progress :queued)) + file)) + files)) + +(defn- mark-file-progress + "The milestone the job of a file is in, to show it while it runs." + [files file-id progress] + (mapv #(cond-> % + (same-file? file-id (:id %)) + (assoc :progress progress + :queued false)) + files)) + +(defn- mark-file-queued + "The job of a file exists but no worker picked it up yet." [files file-id] (mapv #(cond-> % - (= file-id (:id %)) - (assoc :export-success? true - :loading false)) + (same-file? file-id (:id %)) + (assoc :queued true)) files)) (defn- initialize-state @@ -54,27 +135,115 @@ (mf/defc export-entry* {::mf/private true} [{:keys [file]}] - (let [level (cond - (:export-success? file) :success - (:export-error? file) :error - :else :info)] + (let [success? (:export-success file) + error? (:export-error file) + cancelled? (:export-cancelled file) + progress (:progress file) + running? (and (:loading file) (some? progress)) + ;; the job of the file exists but no worker picked it up yet: its + ;; queue only ends with a milestone, so until then the row waits + waiting? (and (:loading file) (nil? progress)) + size (imp/format-file-size (:size file)) + ;; the size of the artifact only exists once the job produced it: + ;; until then the subtitle only says what it will be + subtitle (if (some? size) (dm/str size " .penpot") ".penpot")] [:div {:class (stl/css-case :file-entry true - :loading (:loading file) - :success (:export-success? file) - :error (:export-error? file))} + :success success? + :error error?)} - (if (:loading file) - [:div {:class (stl/css :file-name)} - [:> loader* {:width 26 - :title (tr "labels.loading")}] - [:> text* {:class (stl/css :file-name-label) + [:div {:class (stl/css :file-main)} + [:div {:class (stl/css :file-icon) + :aria-hidden "true"} + [:> icon* {:icon-id (if error? i/close i/folder) :size "m"}]] + + [:div {:class (stl/css :file-meta)} + [:> text* {:class (stl/css :file-name-label) + :as "span" + :typography t/body-medium} + (:name file)] + [:> text* {:class (stl/css :file-subtitle) + :as "span" + :typography t/body-small} + subtitle]] + + ;; a row carries its name and what its artifact will weigh, and only + ;; the state that is its own: while the file runs, the milestone and + ;; the bar live under it + (cond + success? + [:div {:class (stl/css :file-status)} + [:> icon* {:icon-id i/status-tick :size "s"}] + [:> text* {:as "span" + :typography t/body-medium} + (tr "files-export-modal.status.exported")]] + + waiting? + [:div {:class (stl/css :file-status)} + [:> icon* {:icon-id i/clock :size "s"}] + [:> text* {:as "span" + :typography t/body-medium} + (tr "files-export-modal.status.waiting")]])] + + ;; the milestone of the job, with an animated bar: the stages of an + ;; export are not comparable one with another, so the client cannot + ;; know a percentage of the file, only that it moves + (when running? + [:div {:class (stl/css :file-progress)} + [:> text* {:class (stl/css :progress-message) :as "span" - :typography t/body-large} - (:name file)]] + :typography t/body-small + :role "status" + :aria-live "polite"} + (jp/milestone-text progress)] + [:div {:class (stl/css :progress-bar) + :role "progressbar"} + [:div {:class (stl/css :progress-fill)}]]]) - [:> context-notification {:level level - :content (:name file)}])])) + (when error? + [:div {:class (stl/css :file-error)} + [:> text* {:class (stl/css :error-message) + :as "span" + :typography t/body-small} + ;; the reason is user-facing text already (the hint of the job), + ;; never a key + (tr "files-export-modal.export-error" + (or (error-hint (:error file)) + (tr "labels.error")))]]) + + ;; a cancelled file is a neutral terminal state, but the label is + ;; what tells the user why no artifact came out + (when cancelled? + [:div {:class (stl/css :file-error)} + [:> text* {:class (stl/css :status-message) + :as "span" + :typography t/body-small + :role "status" + :aria-live "polite"} + (tr "jobs.export-cancelled")]])])) + +(mf/defc export-phase-header* + {::mf/private true} + [{:keys [files]}] + (let [total (count files) + done (count (filterv :export-success files)) + ;; every file is its own job, so the header only counts them: the + ;; progress of each one lives in its row, there is no global bar + running? (boolean (some :loading files))] + [:div {:class (stl/css :phase-header)} + [:> heading* {:level 3 + :typography t/headline-medium + :class (stl/css :phase-title)} + (if running? + (tr "files-export-modal.exporting.title" (i18n/c total)) + (tr "files-export-modal.exported.title" (i18n/c done)))] + (when (or running? (pos? done)) + [:> text* {:as "p" + :typography t/body-medium + :class (stl/css :phase-message)} + (if running? + (tr "files-export-modal.exporting.message") + (tr "files-export-modal.exported.message"))])])) (mf/defc export-dialog {::mf/register modal/components @@ -90,26 +259,97 @@ state (deref state*) selected (:selected state) status (:status state) + ;; files still in flight: the dialog status never leaves + ;; :exporting, so loading rows are the signal of a running + ;; export, not the status + in-progress? (boolean (->> state :files (some :loading))) + + ;; Jobs still running, as file-id -> job-id, to cancel them on + ;; demand; a file that reaches its outcome leaves the map, so + ;; closing a finished export cancels nothing. And the + ;; subscription to their messages, to stop listening. + jobs* (mf/use-state {}) + jobs (deref jobs*) + sub* (mf/use-state nil) + sub (deref sub*) start-export (mf/use-fn (mf/deps team-id selected files) (fn [] (swap! state* assoc :status :exporting) - (->> (fexp/export-files :files files :type selected) - (rx/subs! - (fn [{:keys [file-id error filename uri] :as result}] - (if error - (swap! state* update :files mark-file-error file-id) - (do - (swap! state* update :files mark-file-success file-id) - (dom/trigger-download-uri filename "application/penpot" uri)))))))) + (reset! jobs* {}) + (reset! sub* (->> (fexp/export-files :files files :type selected + :on-job #(swap! jobs* assoc (:file-id %) (:job-id %))) + (rx/subs! + (fn [{:keys [file-id error filename uri size progress queued + cancelled]}] + (cond + (some? progress) + (swap! state* update :files mark-file-progress file-id progress) + + (true? queued) + (swap! state* update :files mark-file-queued file-id) + + (some? error) + (swap! jobs* dissoc file-id) + (swap! state* update :files mark-file-error file-id error) + + (true? cancelled) + (swap! jobs* dissoc file-id) + (swap! state* update :files mark-file-cancelled file-id) + + ;; only a message carrying the artifact + ;; downloads: anything else is ignored + (some? uri) + (do + (swap! jobs* dissoc file-id) + (swap! state* update :files mark-file-success file-id size) + (dom/trigger-download-uri filename "application/penpot" uri))))))))) + + on-stop-jobs + (mf/use-fn + (mf/deps jobs sub) + (fn [] + ;; stop listening first, so no late message repaints the rows + (when (some? sub) + (rx/dispose! sub)) + ;; only the jobs still in flight are cancelled: finished ones + ;; already left the map, and failures are ignored because a job may have just finished on its own + (run! dj/cancel-job (vals jobs)) + (reset! jobs* {}) + (reset! sub* nil))) + + on-cancel-export + (mf/use-fn + (mf/deps on-stop-jobs state*) + (fn [event] + (dom/prevent-default event) + (on-stop-jobs) + ;; the dialog stays open with the terminal states: closing it + ;; is what dismisses it, cancelling whatever is still pending + (swap! state* update :files mark-all-cancelled))) + + on-close + (mf/use-fn + (mf/deps on-stop-jobs) + (fn [event] + (dom/prevent-default event) + ;; closing dismisses the dialog, cancelling whatever is still + ;; pending, so an export never outlives its dialog + (on-stop-jobs) + (st/emit! (modal/hide)))) on-cancel (mf/use-fn + (mf/deps status in-progress? on-cancel-export on-close) (fn [event] - (dom/prevent-default event) - (st/emit! (modal/hide)))) + ;; the X cancels like Cancel Export while files are still in + ;; flight, and dismisses like Done once everything reached a + ;; terminal state + (if (and (= :exporting status) in-progress?) + (on-cancel-export event) + (on-close event)))) on-accept (mf/use-fn @@ -124,6 +364,7 @@ (let [type (-> (dom/get-target event) (dom/get-data "type") (keyword))] + (swap! state* assoc :selected type))))] (mf/with-effect [has-libs?] @@ -137,7 +378,7 @@ [:> heading* {:level 2 :typography t/headline-large :class (stl/css :modal-title)} - (tr "files-download-modal.title")] + (tr "files-export-modal.title")] [:> icon-button* {:variant "ghost" :aria-label (tr "labels.close") :on-click on-cancel @@ -202,7 +443,7 @@ [:div {:class (stl/css :action-buttons)} [:> button* {:variant "secondary" :type "button" - :on-click on-cancel} + :on-click on-close} (tr "labels.cancel")] [:> button* {:variant "primary" @@ -211,22 +452,20 @@ (tr "labels.continue")]]]] (= status :exporting) - (let [in-progress? (->> state :files (some :loading))] - [:* - [:div {:class (stl/css :modal-content)} - (for [file (:files state)] - [:> export-entry* {:file file :key (dm/str (:id file))}]) + [:* + [:div {:class (stl/css :modal-content)} + [:> export-phase-header* {:files (:files state)}] + (for [file (:files state)] + [:> export-entry* {:file file :key (dm/str (:id file))}])] - (when in-progress? - [:> text* {:as "span" :typography t/body-large :class (stl/css :status-message) - :role "status" - :aria-live "polite"} - (tr "labels.downloading-file")])] - - [:div {:class (stl/css :modal-footer)} - [:div {:class (stl/css :action-buttons)} + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + (if in-progress? + [:> button* {:variant "secondary" + :type "button" + :on-click on-cancel-export} + (tr "files-export-modal.cancel-export")] [:> button* {:variant "primary" :type "button" - :disabled in-progress? - :on-click on-cancel} - (tr "labels.close")]]]]))]])) + :on-click on-close} + (tr "files-export-modal.done")])]]])]])) diff --git a/frontend/src/app/main/ui/exports/files.scss b/frontend/src/app/main/ui/exports/files.scss index b2a56e435e..6fa97eae35 100644 --- a/frontend/src/app/main/ui/exports/files.scss +++ b/frontend/src/app/main/ui/exports/files.scss @@ -48,6 +48,12 @@ flex-direction: column; gap: var(--sp-l); margin-block-end: var(--sp-xxl); + + // like the import wizard: the rows scroll and the header and footer + // stay put, instead of the content pushing the footer out of the box + flex: 1; + min-block-size: 0; + overflow: hidden auto; } .modal-content-extended { @@ -153,60 +159,153 @@ margin: 0; } -.file-entry { - --file-entry-color: var(--color-foreground-secondary); +.phase-header { + display: flex; + flex-direction: column; + gap: var(--sp-xs); +} - &.loading { - .file-name { - color: var(--color-foreground-secondary); - } +.phase-title { + color: var(--color-foreground-primary); +} + +.phase-message { + color: var(--color-foreground-secondary); + margin: 0; +} + +.file-entry { + --file-entry-fg-color: var(--color-foreground-secondary); + + display: flex; + flex-direction: column; + gap: var(--sp-m); + + &.success { + --file-entry-fg-color: var(--color-accent-success); } &.error { - .file-name { - color: var(--color-foreground-secondary); + --file-entry-fg-color: var(--color-accent-error); - .file-icon svg { - stroke: var(--color-foreground-secondary); - } - } - } - - &.success { - .file-name { - color: var(--color-foreground-secondary); - - .file-icon svg { - stroke: var(--color-foreground-secondary); - } - } + padding: var(--sp-m); + border-radius: $br-8; + background-color: var(--color-background-error); } } -.file-name { +.file-main { display: flex; align-items: center; gap: var(--sp-m); +} - .file-icon { - display: flex; - justify-content: center; - align-items: center; - block-size: px2rem(16); - inline-size: px2rem(16); - color: var(--color-foreground-secondary); - } +.file-icon { + display: flex; + justify-content: center; + align-items: center; + block-size: px2rem(40); + inline-size: px2rem(40); + flex-shrink: 0; + border-radius: $br-8; + background-color: var(--color-background-tertiary); + color: var(--color-foreground-secondary); - .file-name-label { - display: block; - max-inline-size: 99%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + .error & { + background-color: var(--color-accent-error); + color: var(--color-background-error); } } -.icon-status-tick { - fill: none; - stroke: var(--color-accent-primary); +.file-meta { + display: flex; + flex-direction: column; + gap: var(--sp-xxs); + flex: 1 1 auto; + min-inline-size: 0; + + .error & { + color: var(--color-foreground-primary); + } +} + +.file-name-label { + display: block; + max-inline-size: 99%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--color-foreground-primary); +} + +.file-subtitle { + color: var(--color-foreground-secondary); + + .error & { + color: var(--file-entry-fg-color); + } +} + +.file-status { + display: flex; + align-items: center; + gap: var(--sp-s); + flex-shrink: 0; + color: var(--file-entry-fg-color); + white-space: nowrap; +} + +.file-progress { + display: flex; + flex-direction: column; + gap: var(--sp-xs); +} + +.progress-message { + color: var(--color-foreground-secondary); +} + +.progress-bar { + block-size: px2rem(4); + border-radius: $br-8; + background-color: var(--color-background-tertiary); + overflow: hidden; +} + +.progress-fill { + block-size: 100%; + border-radius: $br-8; + background-color: var(--color-accent-primary); + + // the twin of the indeterminate bar of the import wizard: the stages + // of an export are not comparable one with another, so the fill slides + // through the bar while the job of the file runs + width: 40%; + animation: progress-slide 1.2s ease-in-out infinite; +} + +@keyframes progress-slide { + from { + transform: translateX(-100%); + } + + to { + transform: translateX(250%); + } +} + +.file-error { + padding-inline-start: calc(px2rem(40) + var(--sp-m)); + + .error & { + padding-inline-start: 0; + } +} + +.error-message { + display: flex; + align-items: flex-start; + color: var(--file-entry-fg-color); + white-space: pre-wrap; + overflow-wrap: anywhere; } diff --git a/frontend/src/app/main/ui/flex_controls/common.cljs b/frontend/src/app/main/ui/flex_controls/common.cljs index 72bed0cf0f..b4f1e7ab6f 100644 --- a/frontend/src/app/main/ui/flex_controls/common.cljs +++ b/frontend/src/app/main/ui/flex_controls/common.cljs @@ -1,7 +1,10 @@ (ns app.main.ui.flex-controls.common (:require [app.main.constants :as mconst] + [app.main.data.workspace.transforms :as dwt] + [app.main.store :as st] [app.main.streams :as ms] + [app.main.ui.context :as ctx] [app.main.ui.formats :as fmt] [rumext.v2 :as mf])) @@ -14,6 +17,19 @@ ;; `ms/clear-transform-preview!` for the rationale. (def clear-transform-preview! ms/clear-transform-preview!) +(defn use-read-only-cancel + "Answers whether the workspace is read-only, and cancels the resize in + progress (`resizing` holds a truthy value while dragging) when it turns + read-only, as when saves fail: the preview is dropped and the release + commits nothing." + [resizing] + (let [read-only? (mf/use-ctx ctx/workspace-read-only?)] + (mf/with-effect [read-only?] + (when (and read-only? @resizing) + (reset! resizing nil) + (st/emit! (dwt/cancel-transform)))) + read-only?)) + ;; ------------------------------------------------ ;; CONSTANTS ;; ------------------------------------------------ diff --git a/frontend/src/app/main/ui/flex_controls/gap.cljs b/frontend/src/app/main/ui/flex_controls/gap.cljs index c297807e2d..bb00f37586 100644 --- a/frontend/src/app/main/ui/flex_controls/gap.cljs +++ b/frontend/src/app/main/ui/flex_controls/gap.cljs @@ -38,15 +38,17 @@ last-pos (mf/use-var nil) negate? (:resize-negate? rect-data) axis (:resize-axis rect-data) + read-only? (fcc/use-read-only-cancel resizing) on-pointer-down (mf/use-fn - (mf/deps frame-id gap-type gap) + (mf/deps frame-id gap-type gap read-only?) (fn [event] - (dom/capture-pointer event) - (reset! resizing gap-type) - (reset! start (dom/get-client-position event)) - (reset! original-value (:initial-value rect-data)))) + (when-not read-only? + (dom/capture-pointer event) + (reset! resizing gap-type) + (reset! start (dom/get-client-position event)) + (reset! original-value (:initial-value rect-data))))) calc-modifiers (mf/use-fn @@ -76,11 +78,14 @@ (st/emit! (dwm/apply-wasm-modifiers modifiers) (dwt/finish-transform)))) + ;; Nothing to apply for a resize the lock cancelled + (when (and (not (features/active-feature? @st/state "render-wasm/v1")) + (= @resizing gap-type)) + (st/emit! (dwm/apply-modifiers))) + (reset! resizing nil) (reset! start nil) - (reset! original-value 0) - (when (not (features/active-feature? @st/state "render-wasm/v1")) - (st/emit! (dwm/apply-modifiers))))) + (reset! original-value 0))) on-pointer-move (mf/use-fn @@ -243,7 +248,8 @@ (+ (:x2 (:selrect shape)) (get-in shape [:layout-item-margin :m2]))) y (:y1 block) h (- (:y2 block) (:y1 block))] - {:x x + {:key (str frame-id "-" (:id shape)) + :x x :y y :height h :width (:column-gap gap) @@ -254,7 +260,7 @@ :gap-type (if is-col? :row-gap :column-gap)})) create-cgdd-block - (fn [block] + (fn [index block] (let [x (if flip-x (- (:x1 block) (:column-gap gap)) (:x2 block)) @@ -262,7 +268,8 @@ (+ y1 (:p3 padding)) (+ y1 (:p1 padding))) h (- height (+ (:p1 padding) (:p3 padding)))] - {:x x + {:key (str frame-id "-block-" index) + :x x :y y :width (:column-gap gap) :height h @@ -282,7 +289,8 @@ (:row-gap gap)) (+ (:y2 (:selrect shape)) (get-in shape [:layout-item-margin :m3]))) w (- (:x2 block) (:x1 block))] - {:x x + {:key (str frame-id "-" (:id shape)) + :x x :y y :width w :height (:row-gap gap) @@ -293,7 +301,7 @@ :gap-type (if is-col? :row-gap :column-gap)})) create-rgdd-block - (fn [block] + (fn [index block] (let [x (if flip-x (+ x1 (:p2 padding)) (+ x1 (:p4 padding))) @@ -301,7 +309,8 @@ (- (:y1 block) (:row-gap gap)) (:y2 block)) w (- width (+ (:p2 padding) (:p4 padding)))] - {:x x + {:key (str frame-id "-block-" index) + :x x :y y :width w :height (:row-gap gap) @@ -313,9 +322,9 @@ display-blocks (if is-col? (->> (drop-last wrap-blocks) - (map create-cgdd-block)) + (map-indexed create-cgdd-block)) (->> (drop-last wrap-blocks) - (map create-rgdd-block))) + (map-indexed create-rgdd-block))) display-children (if is-col? (->> children-to-display @@ -324,10 +333,10 @@ (map create-cgdd)))] [:g.gaps {:pointer-events "visible"} - (for [[index display-item] (d/enumerate (concat display-blocks display-children))] + (for [display-item (concat display-blocks display-children)] (let [gap-type (:gap-type display-item)] [:> gap-display* - {:key (str frame-id index) + {:key (:key display-item) :frame-id frame-id :zoom zoom :gap-type gap-type diff --git a/frontend/src/app/main/ui/flex_controls/margin.cljs b/frontend/src/app/main/ui/flex_controls/margin.cljs index 8638776132..5b3a4aba81 100644 --- a/frontend/src/app/main/ui/flex_controls/margin.cljs +++ b/frontend/src/app/main/ui/flex_controls/margin.cljs @@ -31,15 +31,17 @@ last-pos (mf/use-var nil) negate? (true? (:resize-negate? rect-data)) axis (:resize-axis rect-data) + read-only? (fcc/use-read-only-cancel resizing?) on-pointer-down (mf/use-fn - (mf/deps shape-id margin-num margin) + (mf/deps shape-id margin-num margin read-only?) (fn [event] - (dom/capture-pointer event) - (reset! resizing? true) - (reset! start (dom/get-client-position event)) - (reset! original-value (:initial-value rect-data)))) + (when-not read-only? + (dom/capture-pointer event) + (reset! resizing? true) + (reset! start (dom/get-client-position event)) + (reset! original-value (:initial-value rect-data))))) calc-modifiers (mf/use-fn @@ -76,17 +78,19 @@ (fn [event] (dom/release-pointer event) - (when (features/active-feature? @st/state "render-wasm/v1") - (let [[_ modifiers] (calc-modifiers @last-pos)] - (st/emit! (dwm/apply-wasm-modifiers modifiers) - (dwt/finish-transform)))) + ;; Nothing to apply for a resize the lock cancelled + (when @resizing? + (when (features/active-feature? @st/state "render-wasm/v1") + (let [[_ modifiers] (calc-modifiers @last-pos)] + (st/emit! (dwm/apply-wasm-modifiers modifiers) + (dwt/finish-transform)))) + + (when (not (features/active-feature? @st/state "render-wasm/v1")) + (st/emit! (dwm/apply-modifiers)))) (reset! resizing? false) (reset! start nil) - (reset! original-value 0) - - (when (not (features/active-feature? @st/state "render-wasm/v1")) - (st/emit! (dwm/apply-modifiers))))) + (reset! original-value 0))) on-pointer-move (mf/use-fn diff --git a/frontend/src/app/main/ui/flex_controls/padding.cljs b/frontend/src/app/main/ui/flex_controls/padding.cljs index eb4a9680ef..2f898f91b7 100644 --- a/frontend/src/app/main/ui/flex_controls/padding.cljs +++ b/frontend/src/app/main/ui/flex_controls/padding.cljs @@ -31,15 +31,17 @@ last-pos (mf/use-var nil) negate? (true? (:resize-negate? rect-data)) axis (:resize-axis rect-data) + read-only? (fcc/use-read-only-cancel resizing?) on-pointer-down (mf/use-fn - (mf/deps frame-id rect-data padding-num) + (mf/deps frame-id rect-data padding-num read-only?) (fn [event] - (dom/capture-pointer event) - (reset! resizing? true) - (reset! start (dom/get-client-position event)) - (reset! original-value (:initial-value rect-data)))) + (when-not read-only? + (dom/capture-pointer event) + (reset! resizing? true) + (reset! start (dom/get-client-position event)) + (reset! original-value (:initial-value rect-data))))) calc-modifiers (mf/use-fn @@ -76,17 +78,19 @@ (fn [event] (dom/release-pointer event) - (when (features/active-feature? @st/state "render-wasm/v1") - (let [[_ modifiers] (calc-modifiers @last-pos)] - (st/emit! (dwm/apply-wasm-modifiers modifiers) - (dwt/finish-transform)))) + ;; Nothing to apply for a resize the lock cancelled + (when @resizing? + (when (features/active-feature? @st/state "render-wasm/v1") + (let [[_ modifiers] (calc-modifiers @last-pos)] + (st/emit! (dwm/apply-wasm-modifiers modifiers) + (dwt/finish-transform)))) + + (when (not (features/active-feature? @st/state "render-wasm/v1")) + (st/emit! (dwm/apply-modifiers)))) (reset! resizing? false) (reset! start nil) - (reset! original-value 0) - - (when (not (features/active-feature? @st/state "render-wasm/v1")) - (st/emit! (dwm/apply-modifiers))))) + (reset! original-value 0))) on-pointer-move (mf/use-fn diff --git a/frontend/src/app/main/ui/hooks.cljs b/frontend/src/app/main/ui/hooks.cljs index 3225932153..ca3663ada9 100644 --- a/frontend/src/app/main/ui/hooks.cljs +++ b/frontend/src/app/main/ui/hooks.cljs @@ -16,6 +16,7 @@ [app.main.store :as st] [app.util.dom :as dom] [app.util.dom.dnd :as dnd] + [app.util.keyboard :as kbd] [app.util.storage :as storage] [app.util.timers :as ts] [app.util.webapi :as wapi] @@ -87,6 +88,122 @@ ;; Do not remove commented out lines, they are useful to debug events when ;; things go weird. +;; Native drag and drop swallows wheel events while a drag is in progress, so +;; scrolling a long list mid-drag is done here: when the pointer gets near the +;; top or bottom edge of the scrollable container, it is scrolled on every +;; animation frame, faster the closer the pointer is to the edge. + +(def ^:private auto-scroll-zone-size 56) +(def ^:private auto-scroll-max-speed 18) +(def ^:private auto-scroll-stale-ms 500) + +;; Speeds are given in pixels per frame at 60 Hz and scaled by the real time +;; between frames, so the scroll goes as fast on a 120 Hz screen. +(def ^:private auto-scroll-frame-ms (/ 1000 60)) +(def ^:private auto-scroll-max-frame-ms 100) + +;; State of the current drag: the scroll container, found once when the drag +;; starts, the scroll speed, the pending animation frame and when the pointer +;; was last seen over the list. +(defonce ^:private auto-scroll-state (atom nil)) + +(defn- scrollable-ancestor + [node] + (loop [node (some-> ^js node .-parentElement)] + (when (some? node) + (let [overflow (-> (dom/get-computed-styles node) + (dom/get-property-value "overflow-y"))] + (if (and (or (= overflow "auto") (= overflow "scroll")) + (> (.-scrollHeight ^js node) (.-clientHeight ^js node))) + node + (recur (.-parentElement ^js node))))))) + +(defn- auto-scroll-speed + "Pixels to scroll on each frame given the pointer position, negative + upwards. Zero when the pointer is outside the edge zones. The zones take + at most a quarter of the container each, so the middle half of a short + list never scrolls." + [{:keys [top bottom]} pointer-y] + (let [zone (mth/min auto-scroll-zone-size (/ (- bottom top) 4)) + top-distance (- pointer-y top) + bottom-distance (- bottom pointer-y) + ramp (fn [distance] + (-> (- zone distance) + (/ zone) + (* auto-scroll-max-speed) + (mth/ceil)))] + (cond + (or (neg? top-distance) (neg? bottom-distance)) 0 + (< top-distance zone) (- (ramp top-distance)) + (< bottom-distance zone) (ramp bottom-distance) + :else 0))) + +(defn- stop-auto-scroll! + "Stops the scroll loop, keeping the scroll container for the rest of the + drag." + [] + (some-> (:frame @auto-scroll-state) ts/cancel-af!) + (swap! auto-scroll-state dissoc :frame :last-frame-at :offset)) + +(defn- end-auto-scroll! + [] + (stop-auto-scroll!) + (reset! auto-scroll-state nil)) + +(defn- start-auto-scroll! + "Remembers the scroll container of the dragged `node`. A loop left over + from a previous drag whose end was lost is stopped first." + [node] + (stop-auto-scroll!) + (reset! auto-scroll-state {:element (scrollable-ancestor node)})) + +(defn- auto-scroll-tick + [timestamp] + (let [{:keys [element speed updated-at last-frame-at offset]} @auto-scroll-state] + ;; The stale check stops the loop if the drag ended without us being + ;; notified, for example when a dragend event is lost. + (if (or (nil? element) + (> (- timestamp updated-at) auto-scroll-stale-ms)) + (end-auto-scroll!) + (let [;; Capped so a late frame does not make the list jump. + elapsed (if (some? last-frame-at) + (mth/min (- timestamp last-frame-at) auto-scroll-max-frame-ms) + auto-scroll-frame-ms) + ;; Keep the fraction of a pixel for the next frame, otherwise + ;; slow speeds would be lost on fast screens. + offset (+ (or offset 0) (* speed (/ elapsed auto-scroll-frame-ms))) + step (js/Math.trunc offset)] + (when-not (zero? step) + (dom/scroll-by! element 0 step)) + (swap! auto-scroll-state assoc + :offset (- offset step) + :last-frame-at timestamp + :frame (ts/raf auto-scroll-tick)))))) + +(defn- update-auto-scroll! + "Starts, updates or stops the auto scroll of the container of `node` + depending on how close the pointer is to its edges." + [node pointer-y] + (let [element (:element @auto-scroll-state) + ;; The drag may have started outside this list, or the loop ended + ;; on the stale check: find the container of `node` again. + element (if (dom/child? node element) + element + (scrollable-ancestor node)) + speed (if (some? element) + (auto-scroll-speed (dom/get-bounding-rect element) pointer-y) + 0)] + (swap! auto-scroll-state assoc + :element element + :speed speed + :updated-at (js/performance.now)) + (cond + (zero? speed) + (stop-auto-scroll!) + + (nil? (:frame @auto-scroll-state)) + (swap! auto-scroll-state assoc :frame (ts/raf auto-scroll-tick))))) + (defn use-sortable [& {:keys [data-type data on-drop on-drag on-hold disabled detect-center? draggable?] :or {draggable? true} @@ -123,6 +240,7 @@ (dnd/set-data! event data-type data) (dnd/set-drag-image! event (dnd/invisible-image)) (dnd/set-allowed-effect! event "move") + (start-auto-scroll! (mf/ref-val ref)) (when (fn? on-drag) (on-drag data))))) @@ -148,7 +266,8 @@ (subscribe-to-drag-end) ;; (dnd/trace event data "drag-over") (let [side (dnd/drop-side event detect-center?)] - (swap! state assoc :over side))))) + (swap! state assoc :over side))) + (update-auto-scroll! (mf/ref-val ref) (.-clientY event)))) on-drag-leave (fn [event] @@ -162,6 +281,7 @@ ;; (dnd/trace event data "drop") (let [side (dnd/drop-side event detect-center?) drop-data (dnd/get-data event data-type)] + (end-auto-scroll!) (cleanup) (rx/push! global-drag-end nil) (when (fn? on-drop) @@ -171,6 +291,7 @@ (fn [event] (dom/stop-propagation event) ;; (dnd/trace event data "drag-end") + (end-auto-scroll!) (rx/push! global-drag-end nil) (cleanup)) @@ -477,3 +598,44 @@ (rx/dispose! sub)))) [rowref limit]))) + +(defn use-escape-to-close + "Returns an `on-key-down` handler that closes a panel with Escape. + + The panel only closes while the user is working in it: the latest + pointer press or focus change must have landed inside the element + that owns the handler, or that element must hold focus when it + mounts. Focus left behind in a panel after the user moved on to the + canvas does not count, so Escape keeps its usual meaning there. + + The event is never stopped: the global Escape shortcut still runs + and finishes any in-progress operation. + + `dismiss-nested`, when given, is called first; it returns true when + it consumed the Escape (an open menu, an inline rename), and then + the panel stays open." + ([on-close] + (use-escape-to-close on-close nil)) + ([on-close dismiss-nested] + (let [last-target-ref (mf/use-ref nil)] + (mf/with-effect [] + (let [track (fn [event] + (mf/set-ref-val! last-target-ref (dom/get-target event)))] + ;; A panel that autofocuses its input on open has already + ;; received its `focusin` before this effect runs. + (mf/set-ref-val! last-target-ref (dom/get-active)) + (.addEventListener js/document "pointerdown" track true) + (.addEventListener js/document "focusin" track true) + #(do (.removeEventListener js/document "pointerdown" track true) + (.removeEventListener js/document "focusin" track true)))) + + (mf/use-fn + (mf/deps on-close dismiss-nested) + (fn [event] + (let [panel (dom/get-current-target event) + last-target (mf/ref-val last-target-ref)] + (when (and (kbd/esc? event) + (dom/child? last-target panel) + (not (and (fn? dismiss-nested) (dismiss-nested)))) + (dom/blur! (dom/get-target event)) + (on-close)))))))) diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index 7dec276e5d..4dc9408889 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -13,7 +13,6 @@ ;; Keep the list of icons sorted (def ^:icon icon-verify (icon-xref :icon-verify)) -(def ^:icon loader (icon-xref :loader)) (def ^:icon logo (icon-xref :penpot-logo)) (def ^:icon logo-icon (icon-xref :penpot-logo-icon)) (def ^:icon logo-error-screen (icon-xref :logo-error-screen)) @@ -224,7 +223,6 @@ (def ^:icon size-horizontal (icon-xref :size-horizontal)) (def ^:icon size-vertical (icon-xref :size-vertical)) (def ^:icon snap (icon-xref :snap)) -(def ^:icon snap-nodes (icon-xref :snap-nodes)) (def ^:icon status-alert (icon-xref :status-alert)) (def ^:icon status-tick (icon-xref :status-tick)) (def ^:icon status-update (icon-xref :status-update)) diff --git a/frontend/src/app/main/ui/inspect/attributes/svg.cljs b/frontend/src/app/main/ui/inspect/attributes/svg.cljs index 8344208afc..11fb8f5dfa 100644 --- a/frontend/src/app/main/ui/inspect/attributes/svg.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/svg.cljs @@ -19,7 +19,7 @@ (map (fn [[attr-key attr-value]] (str (d/name attr-key) ":" attr-value))) (str/join "; "))) -(mf/defc svg-attr [{:keys [attr value]}] +(mf/defc svg-attr* [{:keys [attr value]}] (if (map? value) [:* [:div {:class (stl/css :attributes-subtitle)} @@ -27,7 +27,7 @@ [:> copy-button* {:data (map->css value)}]] (for [[attr-key attr-value] value] - [:& svg-attr {:attr attr-key :value attr-value :key (str/join "svg-key-" (d/name attr-key))}])] + [:> svg-attr* {:attr attr-key :value attr-value :key (str/join "svg-key-" (d/name attr-key))}])] (let [attr-name (as-> attr $ (d/name $) @@ -41,11 +41,11 @@ :class (stl/css :copy-btn-title)} [:div {:class (stl/css :button-children)} (str value)]]]]))) -(mf/defc svg-block +(mf/defc svg-block* [{:keys [shape]}] [:* (for [[attr-key attr-value] (:svg-attrs shape)] - [:& svg-attr {:attr attr-key :value attr-value :key (str/join "svg-block-key-" (d/name attr-key))}])]) + [:> svg-attr* {:attr attr-key :value attr-value :key (str/join "svg-block-key-" (d/name attr-key))}])]) (mf/defc svg-panel* [{:keys [shapes]}] @@ -56,4 +56,4 @@ {:title (tr "workspace.sidebar.options.svg-attrs.title") :class (stl/css :title-wrapper) :title-class (stl/css :svg-attr-title)}] - [:& svg-block {:shape shape}]]))) + [:> svg-block* {:shape shape}]]))) diff --git a/frontend/src/app/main/ui/inspect/attributes/text.cljs b/frontend/src/app/main/ui/inspect/attributes/text.cljs index 51cf288266..e6434bd722 100644 --- a/frontend/src/app/main/ui/inspect/attributes/text.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/text.cljs @@ -31,7 +31,7 @@ (map #(dm/str (d/name %) ": " (get style %) ";")) (str/join "\n"))) -(mf/defc typography-block +(mf/defc typography-block* [{:keys [text style]}] (let [color-format* (mf/use-state :hex) color-format (deref color-format*) @@ -138,17 +138,17 @@ (str/trim text)]]])) -(mf/defc text-block [{:keys [shape]}] +(mf/defc text-block* [{:keys [shape]}] (let [style-text-blocks (->> (:content shape) (txt/content->text+styles) (remove (fn [[_ text]] (str/empty? (str/trim text)))) (mapv (fn [[style text]] (vector (merge (types.text/get-default-text-attrs) style) text))))] (for [[idx [full-style text]] (map-indexed vector style-text-blocks)] - [:& typography-block {:key idx - :shape shape - :style full-style - :text text}]))) + [:> typography-block* {:key idx + :shape shape + :style full-style + :text text}]))) (mf/defc text-panel* [{:keys [shapes]}] @@ -160,5 +160,5 @@ :title-class (stl/css :text-atrr-title)}] (for [shape shapes] - [:& text-block {:shape shape - :key (dm/str "text-block" (:id shape))}])])) + [:> text-block* {:shape shape + :key (dm/str "text-block" (:id shape))}])])) diff --git a/frontend/src/app/main/ui/inspect/code.cljs b/frontend/src/app/main/ui/inspect/code.cljs index 78b748a064..d7a170a67e 100644 --- a/frontend/src/app/main/ui/inspect/code.cljs +++ b/frontend/src/app/main/ui/inspect/code.cljs @@ -14,20 +14,25 @@ [app.common.types.shape-tree :as ctst] [app.config :as cfg] [app.main.data.event :as ev] + [app.main.features :as features] [app.main.fonts :as fonts] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.code-block :refer [code-block*]] [app.main.ui.components.copy-button :refer [copy-button*]] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.hooks.resize :refer [use-resize-hook]] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.shapes.text.fontfaces :refer [shapes->fonts]] [app.util.clipboard :as clipboard] [app.util.code-beautify :as cb] [app.util.code-gen :as cg] + [app.util.code-gen.markup-svg-wasm :as svg.wasm] [app.util.dom :as dom] [app.util.http :as http] + [app.util.i18n :refer [tr]] [beicon.v2.core :as rx] [cuerdas.core :as str] [okulary.core :as l] @@ -113,6 +118,9 @@ collapsed-css? (contains? @collapsed* :css) collapsed-markup? (contains? @collapsed* :markup) + ;; With render-wasm active, never use classic SVG codegen for SVG markup. + wasm-svg-markup? (features/use-feature "render-wasm/v1") + objects (use-objects from) shapes @@ -148,9 +156,11 @@ markup-code (mf/use-memo - (mf/deps markup-type shapes images-data) + (mf/deps markup-type shapes images-data wasm-svg-markup?) (fn [] - (cg/generate-formatted-markup-code objects markup-type shapes))) + (if (and (= markup-type "svg") wasm-svg-markup?) + (svg.wasm/get-formatted-svg-markup (mapv :id shapes)) + (cg/generate-formatted-markup-code objects markup-type shapes)))) on-markup-copied (mf/use-fn @@ -278,8 +288,9 @@ [:div {:class (stl/css-case :element-options true :viewer-code-block (= :viewer from))} [:div {:class (stl/css :attributes-block)} - [:button {:class (stl/css :download-button) - :on-click handle-copy-all-code} + [:> button* {:variant "secondary" + :class (stl/css :download-button) + :on-click handle-copy-all-code} "Copy all code"]] #_[:div.attributes-block @@ -291,11 +302,12 @@ [:div {:class (stl/css :code-row-lang)} [:button {:class (stl/css :toggle-btn) :data-type "css" + :aria-label "CSS" + :aria-expanded (not collapsed-css?) :on-click handle-collapse} - [:span {:class (stl/css-case - :collapsabled-icon true - :rotated collapsed-css?)} - deprecated-icon/arrow]] + [:> icon* {:icon-id (if collapsed-css? i/arrow-right i/arrow-down) + :size "s" + :class (stl/css :collapsabled-icon)}]] [:div {:class (stl/css :code-lang-option)} "CSS"] @@ -306,9 +318,10 @@ ;; :options [{:label "CSS" :value "css"}]}] [:div {:class (stl/css :action-btns)} - [:button {:class (stl/css :expand-button) - :on-click on-expand} - deprecated-icon/code] + [:> icon-button* {:variant "ghost" + :aria-label (tr "inspect.code.expand-panel") + :icon i/code + :on-click on-expand}] [:> copy-button* {:data copy-css-fn :class (stl/css :css-copy-btn) @@ -330,11 +343,12 @@ [:div {:class (stl/css :code-row-lang)} [:button {:class (stl/css :toggle-btn) :data-type "markup" + :aria-label (str/upper markup-type) + :aria-expanded (not collapsed-markup?) :on-click handle-collapse} - [:span {:class (stl/css-case - :collapsabled-icon true - :rotated collapsed-markup?)} - deprecated-icon/arrow]] + [:> icon* {:icon-id (if collapsed-markup? i/arrow-right i/arrow-down) + :size "s" + :class (stl/css :collapsabled-icon)}]] [:& radio-buttons {:selected markup-type :on-change set-markup @@ -347,9 +361,10 @@ :id :svg}]] [:div {:class (stl/css :action-btns)} - [:button {:class (stl/css :expand-button) - :on-click on-expand} - deprecated-icon/code] + [:> icon-button* {:variant "ghost" + :aria-label (tr "inspect.code.expand-panel") + :icon i/code + :on-click on-expand}] [:> copy-button* {:data copy-html-fn :class (stl/css :html-copy-btn) diff --git a/frontend/src/app/main/ui/inspect/code.scss b/frontend/src/app/main/ui/inspect/code.scss index 58acf7b110..01367318d6 100644 --- a/frontend/src/app/main/ui/inspect/code.scss +++ b/frontend/src/app/main/ui/inspect/code.scss @@ -4,45 +4,55 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for the copy buttons, styled here until the shared copy button +// moves to the DS. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .element-options { display: flex; flex-direction: column; - height: calc(100vh - #{deprecated.$s-160}); // TODO: Fix this hardcoded value + block-size: calc(100vh - #{$sz-160}); // TODO: Fix this hardcoded value overflow: hidden; - padding-bottom: deprecated.$s-16; + padding-block-end: var(--sp-l); overflow-y: auto; padding-inline: var(--sp-m); } .viewer-code-block { - height: calc(100vh - #{deprecated.$s-108}); // TODO: Fix this hardcoded value + block-size: calc(100vh - #{px2rem(108)}); // TODO: Fix this hardcoded value } +// A full-width DS button does not center its label on its own. .download-button { - @extend %button-secondary; - @include deprecated.uppercase-title-typography; - - height: deprecated.$s-32; - width: 100%; - margin: deprecated.$s-8 0; + justify-content: center; + inline-size: 100%; + margin: var(--sp-s) 0; } .code-block { - @include deprecated.code-typography; - + // TODO: No exact DS typography match found: the DS code-font names + // "Roboto Mono", the app loads it as "robotomono". + // Closest match: code-font. + font-family: "robotomono", monospace; + font-size: px2rem(12); + font-weight: 400; + line-height: 1.2; display: flex; flex-direction: column; - height: 100%; - min-height: 0; - padding: 0 deprecated.$s-4 deprecated.$s-8 0; + block-size: 100%; + min-block-size: 0; + padding: 0 var(--sp-xs) var(--sp-s) 0; + // The highlighted code markup has no class of its own. pre { - border-radius: deprecated.$br-8; - padding: deprecated.$s-16; + border-radius: $br-8; + padding: var(--sp-l); overflow: auto; - height: 100%; + block-size: 100%; } // Overrides background setted in the theme @@ -51,19 +61,19 @@ } &.collapsed { - height: initial; + block-size: initial; } } .code-row-lang { display: grid; - grid-template-columns: deprecated.$s-12 1fr deprecated.$s-60; - gap: deprecated.$s-4; - width: 100%; + grid-template-columns: $sz-12 1fr auto; + gap: var(--sp-xs); + inline-size: 100%; } .code-lang { - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); display: flex; align-items: center; @@ -71,92 +81,70 @@ .action-btns { display: grid; - grid-template-columns: 1fr 1fr; - gap: deprecated.$s-4; + grid-template-columns: auto auto; + gap: var(--sp-xs); } -.expand-button, .css-copy-btn, .html-copy-btn { @extend %button-tertiary; - height: deprecated.$s-32; - width: deprecated.$s-28; + block-size: $sz-32; + inline-size: $sz-28; svg { @extend %button-icon; - stroke: var(--icon-foreground); + stroke: var(--color-foreground-secondary); } } .code-lang-options { - max-width: deprecated.$s-108; + max-inline-size: px2rem(108); } .code-lang-select { - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); - width: deprecated.$s-72; - border: deprecated.$s-1 solid transparent; + inline-size: px2rem(72); + border: $b-1 solid transparent; background-color: transparent; - color: var(--menu-foreground-color-disabled); + color: var(--color-foreground-secondary); } .code-lang-option { - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); - width: deprecated.$s-72; - height: deprecated.$s-32; - padding: deprecated.$s-8; - color: var(--menu-foreground-color-disabled); + inline-size: px2rem(72); + block-size: $sz-32; + padding: var(--sp-s); + color: var(--color-foreground-secondary); } .code-row-display { flex: 1; - min-height: 0; + min-block-size: 0; overflow: hidden; - padding-bottom: deprecated.$s-8; + padding-block-end: var(--sp-s); } +// A disclosure toggle in a 12px column, not a DS button. .toggle-btn { - @include deprecated.button-style; + --toggle-btn-foreground-color: var(--color-foreground-secondary); display: flex; align-items: center; padding: 0; - color: var(--title-foreground-color); - stroke: var(--title-foreground-color); - - .collapsabled-icon { - @include deprecated.flex-center; - - height: deprecated.$s-24; - border-radius: deprecated.$br-8; - - svg { - @extend %button-icon-small; - - transform: rotate(90deg); - stroke: var(--icon-foreground); - } - - &.rotated svg { - transform: rotate(0deg); - } - } + border: none; + background: none; + cursor: pointer; + color: var(--toggle-btn-foreground-color); &:hover { - color: var(--title-foreground-color-hover); - stroke: var(--title-foreground-color-hover); - - .title { - color: var(--title-foreground-color-hover); - stroke: var(--title-foreground-color-hover); - } - - .collapsabled-icon svg { - stroke: var(--title-foreground-color-hover); - } + --toggle-btn-foreground-color: var(--color-foreground-primary); } } + +.collapsabled-icon { + flex-shrink: 0; +} diff --git a/frontend/src/app/main/ui/inspect/left_sidebar.cljs b/frontend/src/app/main/ui/inspect/left_sidebar.cljs index 971a3d8062..768d20a83a 100644 --- a/frontend/src/app/main/ui/inspect/left_sidebar.cljs +++ b/frontend/src/app/main/ui/inspect/left_sidebar.cljs @@ -23,14 +23,14 @@ #(-> (l/in [:viewer-local :collapsed id]) (l/derived st/state))) -(mf/defc layer-item - [{:keys [item selected objects depth component-child? hide-toggle?] :as props}] +(mf/defc layer-item* + [{:keys [item selected objects depth is-component-child hide-toggle]}] (let [id (:id item) selected? (contains? selected id) item-ref (mf/use-ref nil) depth (+ depth 1) - component-tree? (or component-child? (ctk/instance-root? item) (ctk/instance-head? item)) + component-tree? (or is-component-child (ctk/instance-root? item) (ctk/instance-head? item)) collapsed-iref (mf/use-memo @@ -77,7 +77,7 @@ :is-component-tree component-tree? :is-filtered false :is-expanded expanded? - :hide-toggle hide-toggle? + :hide-toggle hide-toggle :on-select-shape select-shape :on-toggle-collapse toggle-collapse} @@ -88,16 +88,16 @@ :data-testid (dm/str "children-" id)} (for [[index id] (reverse (d/enumerate (:shapes item)))] (when-let [item (get objects id)] - [:& layer-item + [:> layer-item* {:item item :selected selected :index index :objects objects :key (dm/str id) :depth depth - :component-child? component-tree?}]))])])) + :is-component-child component-tree?}]))])])) -(mf/defc left-sidebar +(mf/defc left-sidebar* [{:keys [frame page local]}] (let [selected (:selected local) objects (:objects page)] @@ -105,12 +105,10 @@ [:aside {:class (stl/css :settings-bar-left)} [:div {:class (stl/css :settings-bar-inside)} [:div {:class (stl/css :element-list)} - [:& layer-item + [:> layer-item* {:item frame :selected selected :index 0 :objects objects - :sortable? false - :filtered? false :depth -2 - :hide-toggle? true}]]]])) + :hide-toggle true}]]]])) diff --git a/frontend/src/app/main/ui/inspect/render.cljs b/frontend/src/app/main/ui/inspect/render.cljs index 751c1f4237..e102aa606c 100644 --- a/frontend/src/app/main/ui/inspect/render.cljs +++ b/frontend/src/app/main/ui/inspect/render.cljs @@ -12,7 +12,7 @@ [app.common.geom.shapes :as gsh] [app.main.data.viewer :as dv] [app.main.store :as st] - [app.main.ui.inspect.selection-feedback :refer [selection-feedback]] + [app.main.ui.inspect.selection-feedback :refer [selection-feedback*]] [app.main.ui.shapes.bool :as bool] [app.main.ui.shapes.circle :as circle] [app.main.ui.shapes.frame :as frame] @@ -208,7 +208,7 @@ :fill "none"} [:& render {:shape frame :view-box (:vbox size)}] - [:& selection-feedback + [:> selection-feedback* {:frame frame :objects objects :local local diff --git a/frontend/src/app/main/ui/inspect/selection_feedback.cljs b/frontend/src/app/main/ui/inspect/selection_feedback.cljs index 8be253b3ca..cf1463c2b7 100644 --- a/frontend/src/app/main/ui/inspect/selection_feedback.cljs +++ b/frontend/src/app/main/ui/inspect/selection_feedback.cljs @@ -31,7 +31,7 @@ ;; COMPONENTS ;; ------------------------------------------------ -(mf/defc selection-rect [{:keys [selrect zoom]}] +(mf/defc selection-rect* [{:keys [selrect zoom]}] (let [{:keys [x y width height]} selrect selection-rect-width (/ mconst/selection-rect-width zoom)] [:g.selection-rect @@ -43,7 +43,7 @@ :stroke mconst/select-color :stroke-width selection-rect-width}}]])) -(mf/defc selection-feedback +(mf/defc selection-feedback* [{:keys [frame local objects size]}] (let [{:keys [hover selected zoom]} local @@ -55,7 +55,7 @@ (when (d/not-empty? selected-shapes) [:g.selection-feedback {:pointer-events "none"} [:g.selected-shapes - [:& selection-rect {:selrect selrect :zoom zoom}] + [:> selection-rect* {:selrect selrect :zoom zoom}] [:> size-display* {:selrect selrect :zoom zoom}]] [:> measurement* {:bounds (assoc size :x 0 :y 0) diff --git a/frontend/src/app/main/ui/jobs/progress.cljs b/frontend/src/app/main/ui/jobs/progress.cljs new file mode 100644 index 0000000000..c3f1404a66 --- /dev/null +++ b/frontend/src/app/main/ui/jobs/progress.cljs @@ -0,0 +1,79 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.main.ui.jobs.progress + "How the milestone of a job is written: the stage in course and its + counter. + + The stage is a stable key of the vocabulary of the worker, never a + display string, so it is resolved here with a `case` of literal `tr` + calls: a stage this client does not know falls back to a generic label + instead of breaking the row or showing the raw key." + (:require + [app.util.i18n :as i18n :refer [tr]])) + +(defn- stage-label + [stage] + (case stage + :files (tr "jobs.progress.stage.files") + :pages (tr "jobs.progress.stage.pages") + :media (tr "jobs.progress.stage.media") + :thumbnails (tr "jobs.progress.stage.thumbnails") + :components (tr "jobs.progress.stage.components") + :colors (tr "jobs.progress.stage.colors") + :typographies (tr "jobs.progress.stage.typographies") + :tokens (tr "jobs.progress.stage.tokens") + :storage-objects (tr "jobs.progress.stage.storage-objects") + :relations (tr "jobs.progress.stage.relations") + :metadata (tr "jobs.progress.stage.metadata") + :manifest (tr "jobs.progress.stage.manifest") + :upload (tr "jobs.progress.stage.upload") + (tr "jobs.progress.stage.unknown"))) + +(defn- counter-label + "The counter of one scope, `3/8` when the worker knows the total and the + bare current when it does not." + [{:keys [current total]}] + (when (some? current) + (if (some? total) + (str current "/" total) + (str current)))) + +(defn upload-pct + "The whole percentage of the file already uploaded. The chunked upload + knows how many chunks the file has and which one is in course, so + this is the only counter the client can trust as a percentage." + [{:keys [counters]}] + (let [{:keys [current total]} (get counters :upload)] + (when (and (some? current) (some? total) (pos? total)) + (js/Math.round (* 100 (/ current total)))))) + +(defn- upload-label + "The upload of a file counts chunks, not units of work, so it is written + as the percentage of the file already sent instead of a counter." + [milestone] + (when-some [pct (upload-pct milestone)] + (str "(" pct "%)"))) + +(defn milestone-text + "The text of a milestone: the stage in course and its counter. The + upload of a file counts chunks, not units of work, so it is written as + the percentage of the file already sent instead of a counter." + [milestone] + (when-some [stage (:stage milestone)] + (let [own (if (= :upload stage) + (upload-label milestone) + (counter-label (get (:counters milestone) stage)))] + (str (stage-label stage) + (when (some? own) + (str " " own)))))) + +(defn current-milestone-text + "The text of the milestone in course, for a caller that writes the + progress once for a whole group of files: every file of a container + reports the same milestone, so a row would repeat it." + [entries] + (some (comp milestone-text :progress) entries)) diff --git a/frontend/src/app/main/ui/mcp_authorize.cljs b/frontend/src/app/main/ui/mcp_authorize.cljs new file mode 100644 index 0000000000..5f47fd5df6 --- /dev/null +++ b/frontend/src/app/main/ui/mcp_authorize.cljs @@ -0,0 +1,162 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.main.ui.mcp-authorize + (:require-macros [app.main.style :as stl]) + (:require + [app.common.uri :as uri] + [app.config :as cf] + [app.main.broadcast :as mbc] + [app.main.data.profile :as profile] + [app.main.router :as rt] + [app.main.store :as st] + [app.main.ui.auth.login-modal :refer [login-register-modal*]] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.foundations.typography :as typography] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] + [app.main.ui.ds.foundations.typography.text :refer [text*]] + [app.main.ui.ds.product.loader :refer [loader*]] + [app.util.http :as http] + [app.util.i18n :refer [tr]] + [app.util.storage :as storage] + [beicon.v2.core :as rx] + [rumext.v2 :as mf])) + +(defn- endpoint + [path] + (str (uri/join cf/public-uri (str "api/mcp/oauth/" path)))) + +(defn- prepare-login! + [] + (let [href (rt/get-current-href)] + ;; Save the return URL even when only SSO buttons are enabled and the + ;; password form (which normally stores it) never mounts. + (binding [storage/*sync* true] + (swap! storage/session assoc :login-redirect href)) + {:callback-url href})) + +(defn- cancel-login! + [] + ;; Do not let a later, unrelated login resume this cancelled attempt. + (when (= (:login-redirect storage/session) (rt/get-current-href)) + (binding [storage/*sync* true] + (swap! storage/session dissoc :login-redirect)))) + +(defn- oauth-request + [method path params] + (->> (http/send! (cond-> {:method method + :uri (endpoint path) + :response-type :json + :omit-default-headers true} + (= method :get) (assoc :query params) + (= method :post) (assoc :headers {"content-type" "application/x-www-form-urlencoded"} + :body (uri/map->query-string params)))) + (rx/map #(update % :body js->clj :keywordize-keys true)))) + +(mf/defc client-logo* + {::mf/private true} + [{:keys [uri]}] + (let [failed (mf/use-state false)] + (when-not @failed + [:img {:class (stl/css :client-logo) + :src uri + :alt "" + :referrer-policy "no-referrer" + :on-error #(reset! failed true)}]))) + +(mf/defc mcp-authorize* + [{:keys [route profile]}] + (let [handle (get-in route [:query-params :request]) + details (mf/use-state nil) + failed? (mf/use-state false) + login-params (mf/use-state nil) + cancelled? (mf/use-state false) + busy? (mf/use-state false) + ;; The ref also blocks a second click before React renders disabled buttons. + submitting (mf/use-memo (fn [] (atom false))) + + decide! + (mf/use-fn + (mf/deps handle @details) + (fn [decision] + (when (compare-and-set! submitting false true) + (reset! busy? true) + (->> (oauth-request :post "decision" + {:request handle :csrf (:csrf @details) :decision decision}) + (rx/subs! + (fn [{:keys [status body]}] + (if (= status 200) + (do + (when (= decision "approve") + (st/emit! (profile/refresh-profile) + (mbc/event :mcp/enable {}))) + (st/emit! (rt/nav-raw :href (:redirect-uri body)))) + (reset! failed? true))) + (fn [_] (reset! failed? true)))))))] + + (mf/with-effect [handle] + (reset! details nil) + (reset! failed? false) + (reset! login-params nil) + (reset! cancelled? false) + (reset! busy? false) + (reset! submitting false) + (let [subscription + (->> (oauth-request :get "request" {:request handle}) + (rx/subs! + (fn [{:keys [status body]}] + (case status + 200 (reset! details body) + 401 (reset! login-params (prepare-login!)) + (reset! failed? true))) + (fn [_] (reset! failed? true))))] + #(rx/dispose! subscription))) + + [:main {:class (stl/css :page)} + (if @login-params + [:> login-register-modal* + {:params @login-params + :on-register #(st/emit! (rt/reload true)) + :on-close (fn [] + (cancel-login!) + (reset! login-params nil) + (reset! cancelled? true))}] + [:section {:class (stl/css :card) :aria-labelledby "mcp-approval-title"} + [:> heading* {:level 1 :typography typography/title-large :id "mcp-approval-title"} + (tr "mcp.oauth.title")] + (cond + @cancelled? + [:> text* {:typography typography/body-large :role "status"} + (tr "mcp.oauth.cancelled")] + + @failed? + [:> text* {:typography typography/body-large :role "alert"} + (tr "mcp.oauth.failed")] + + (nil? @details) + [:> loader* {:title (tr "labels.loading")}] + + :else + [:* + [:div {:class (stl/css :client)} + (when-let [logo-uri (:logo-uri @details)] + [:> client-logo* {:key logo-uri :uri logo-uri}]) + [:div {:class (stl/css :client-details)} + [:> text* {:typography typography/body-large :class (stl/css :client-text)} + (tr "mcp.oauth.request" (or (:client-name @details) (:client-id @details)))] + [:> text* {:typography typography/body-small :class (stl/css :client-text)} + (:client-id @details)]]] + [:> text* {:typography typography/body-medium} + (tr "mcp.oauth.account" (:email profile))] + [:> text* {:typography typography/body-large} + (tr "mcp.oauth.access")] + [:> text* {:typography typography/body-medium} + (tr "mcp.oauth.enable")] + [:div {:class (stl/css :actions)} + [:> button* {:variant "secondary" :disabled @busy? :on-click #(decide! "deny")} + (tr "labels.cancel")] + [:> button* {:variant "primary" :disabled @busy? :on-click #(decide! "approve")} + (tr "mcp.oauth.approve")]]])])])) diff --git a/frontend/src/app/main/ui/mcp_authorize.scss b/frontend/src/app/main/ui/mcp_authorize.scss new file mode 100644 index 0000000000..64eed408fa --- /dev/null +++ b/frontend/src/app/main/ui/mcp_authorize.scss @@ -0,0 +1,61 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS SUBSIDIARY SL + +@use "ds/_sizes.scss" as *; + +.page { + display: grid; + place-items: center; + min-block-size: 100%; + padding: var(--sp-xl); + background: var(--color-background-primary); + color: var(--color-foreground-primary); +} + +.card { + display: flex; + flex-direction: column; + gap: var(--sp-l); + max-inline-size: 36rem; + padding: var(--sp-xxl); + border: 1px solid var(--color-background-quaternary); + border-radius: var(--br-8); +} + +.client { + display: flex; + align-items: center; + gap: var(--sp-l); + margin-block-end: var(--sp-s); + overflow-wrap: anywhere; +} + +.client-logo { + inline-size: $sz-48; + block-size: $sz-48; + flex-shrink: 0; + object-fit: contain; + border-radius: var(--br-8); +} + +.client-details { + display: flex; + flex-direction: column; + gap: calc(var(--sp-s) - var(--sp-xxs)); + min-inline-size: 0; +} + +.client-text { + margin: 0; +} + +.actions { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: var(--sp-m); + margin-block-start: var(--sp-l); +} diff --git a/frontend/src/app/main/ui/notifications/context_notification.cljs b/frontend/src/app/main/ui/notifications/context_notification.cljs index 923a4958a3..9455f284c2 100644 --- a/frontend/src/app/main/ui/notifications/context_notification.cljs +++ b/frontend/src/app/main/ui/notifications/context_notification.cljs @@ -10,29 +10,16 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.main.ui.components.link-button :as lb] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [rumext.v2 :as mf])) -(def ^:private neutral-icon - (deprecated-icon/icon-xref :msg-neutral (stl/css :icon))) - -(def ^:private error-icon - (deprecated-icon/icon-xref :delete-text (stl/css :icon))) - -(def ^:private success-icon - (deprecated-icon/icon-xref :status-tick (stl/css :icon))) - -(def ^:private info-icon - (deprecated-icon/icon-xref :help (stl/css :icon))) - -(defn get-icon-by-level +(defn- level->icon-id [level] (case level - :warning neutral-icon - :error error-icon - :success success-icon - :info info-icon - neutral-icon)) + :error i/delete-text + :success i/status-tick + :info i/help + i/msg-neutral)) (mf/defc context-notification "They are persistent, informative and non-actionable. @@ -49,7 +36,8 @@ :success (= level :success) :info (= level :info)))} - (get-icon-by-level level) + [:> icon* {:icon-id (level->icon-id level) + :class (stl/css :icon)}] ;; The content can arrive in markdown format, in these cases ;; we will use the prop is-html to true to indicate it and diff --git a/frontend/src/app/main/ui/notifications/context_notification.scss b/frontend/src/app/main/ui/notifications/context_notification.scss index e4cb80b794..cd8de495c1 100644 --- a/frontend/src/app/main/ui/notifications/context_notification.scss +++ b/frontend/src/app/main/ui/notifications/context_notification.scss @@ -4,71 +4,65 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/typography.scss" as *; .context-notification { - --context-notification-bg-color: var(--alert-background-color-default); - --context-notification-fg-color: var(--alert-text-foreground-color-default); - --context-notification-icon-color: var(--alert-icon-foreground-color-default); - --context-notification-border-color: var(--alert-border-color-default); + --context-notification-bg-color: var(--color-background-primary); + --context-notification-fg-color: var(--color-foreground-primary); + --context-notification-icon-color: var(--color-foreground-primary); + --context-notification-border-color: var(--color-background-quaternary); box-sizing: border-box; display: grid; - grid-template-columns: deprecated.$s-16 1fr; - gap: deprecated.$s-8; - min-height: deprecated.$s-32; - height: fit-content; - width: 100%; - padding: deprecated.$s-8; - border: deprecated.$s-1 solid var(--context-notification-border-color); - border-radius: deprecated.$br-8; + grid-template-columns: $sz-16 1fr; + gap: var(--sp-s); + min-block-size: $sz-32; + block-size: fit-content; + inline-size: 100%; + padding: var(--sp-s); + border: $b-1 solid var(--context-notification-border-color); + border-radius: $br-8; background-color: var(--context-notification-bg-color); } .warning { - --context-notification-bg-color: var(--alert-background-color-warning); - --context-notification-fg-color: var(--alert-text-foreground-color-warning); - --context-notification-icon-color: var(--alert-icon-foreground-color-warning); - --context-notification-border-color: var(--alert-border-color-warning); + --context-notification-bg-color: var(--color-background-warning); + --context-notification-fg-color: var(--color-foreground-primary); + --context-notification-icon-color: var(--color-accent-warning); + --context-notification-border-color: var(--color-accent-warning); } .success { - --context-notification-bg-color: var(--alert-background-color-success); - --context-notification-fg-color: var(--alert-text-foreground-color-success); - --context-notification-icon-color: var(--alert-icon-foreground-color-success); - --context-notification-border-color: var(--alert-border-color-success); + --context-notification-bg-color: var(--color-background-success); + --context-notification-fg-color: var(--color-foreground-primary); + --context-notification-icon-color: var(--color-accent-success); + --context-notification-border-color: var(--color-accent-success); } .info { - --context-notification-bg-color: var(--alert-background-color-info); - --context-notification-fg-color: var(--alert-text-foreground-color-info); - --context-notification-icon-color: var(--alert-icon-foreground-color-info); - --context-notification-border-color: var(--alert-border-color-info); -} - -.default { - --context-notification-bg-color: var(--alert-background-color-default); - --context-notification-fg-color: var(--alert-text-foreground-color-default); - --context-notification-icon-color: var(--alert-icon-foreground-color-default); - --context-notification-border-color: var(--alert-border-color-default); + --context-notification-bg-color: var(--color-background-info); + --context-notification-fg-color: var(--color-foreground-primary); + --context-notification-icon-color: var(--color-accent-info); + --context-notification-border-color: var(--color-accent-info); } .error { - --context-notification-bg-color: var(--alert-background-color-error); - --context-notification-fg-color: var(--alert-text-foreground-color-error); - --context-notification-icon-color: var(--alert-icon-foreground-color-error); - --context-notification-border-color: var(--alert-border-color-error); + --context-notification-bg-color: var(--color-background-error); + --context-notification-fg-color: var(--color-foreground-primary); + --context-notification-icon-color: var(--color-accent-error); + --context-notification-border-color: var(--color-accent-error); } .icon { - @extend %button-icon; - + flex-shrink: 0; align-self: self-start; - stroke: var(--context-notification-icon-color); + color: var(--context-notification-icon-color); } .context-text { - @include deprecated.body-small-typography; + @include use-typography("body-small"); align-self: center; color: var(--context-notification-fg-color); @@ -81,12 +75,12 @@ .link, .contain-html .context-text a { - @include deprecated.body-small-typography; + @include use-typography("body-small"); align-self: center; display: inline; - text-align: left; - height: deprecated.$s-16; + text-align: start; + block-size: $sz-16; margin: 0; - color: var(--modal-link-foreground-color); + color: var(--color-accent-primary); } diff --git a/frontend/src/app/main/ui/onboarding/questions.cljs b/frontend/src/app/main/ui/onboarding/questions.cljs index a3c19b32fc..2b65c4a2ee 100644 --- a/frontend/src/app/main/ui/onboarding/questions.cljs +++ b/frontend/src/app/main/ui/onboarding/questions.cljs @@ -15,6 +15,7 @@ [app.main.data.profile :as du] [app.main.store :as st] [app.main.ui.components.forms :as fm] + [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.icons :as deprecated-icon] [app.util.i18n :as i18n :refer [tr]] [cuerdas.core :as str] @@ -43,8 +44,9 @@ [:div {:class (stl/css :action-buttons)} (when (some? on-prev) - [:button {:class (stl/css :prev-button) - :on-click on-prev} + [:> button* {:variant "secondary" + :type "button" + :on-click on-prev} (tr "labels.previous")]) [:> fm/submit-button* diff --git a/frontend/src/app/main/ui/onboarding/questions.scss b/frontend/src/app/main/ui/onboarding/questions.scss index 9a1adaccc6..77a8742e87 100644 --- a/frontend/src/app/main/ui/onboarding/questions.scss +++ b/frontend/src/app/main/ui/onboarding/questions.scss @@ -4,8 +4,10 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*), which move with +// the modals refactor. @use "refactor/common-refactor.scss" as deprecated; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/_borders.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_utils.scss" as *; @@ -22,19 +24,15 @@ @extend %modal-accept-btn; } -.prev-button { - @extend %modal-cancel-btn; -} - .modal-container { - max-width: $sz-964; - max-height: fit-content; + max-inline-size: $sz-964; + max-block-size: fit-content; inline-size: fit-content; padding-inline: $sz-96; padding-block: $sz-40 $sz-64; border-radius: $br-8; - border: $b-2 solid var(--modal-border-color); - background-color: var(--modal-background-color); + border: $b-2 solid var(--color-background-quaternary); + background-color: var(--color-background-primary); } .form-wrapper { @@ -44,15 +42,15 @@ } .paginator { - @include t.use-typography("title-small"); + @include use-typography("title-small"); - text-align: right; - color: var(--modal-text-foreground-color); + text-align: end; + color: var(--color-foreground-secondary); } .radio label, .select span { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); } .header-image { @@ -62,25 +60,25 @@ } .modal-title { - @include t.use-typography("title-large"); + @include use-typography("title-large"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); min-block-size: $sz-32; margin-block: auto; } .modal-subtitle { - @include t.use-typography("body-large"); + @include use-typography("body-large"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); margin: 0; padding: 0; } .modal-text { - @include t.use-typography("body-large"); + @include use-typography("body-large"); - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); margin: 0; } diff --git a/frontend/src/app/main/ui/routes.cljs b/frontend/src/app/main/ui/routes.cljs index 8096051d40..fa9926911b 100644 --- a/frontend/src/app/main/ui/routes.cljs +++ b/frontend/src/app/main/ui/routes.cljs @@ -33,7 +33,8 @@ "Enabled route names. Query-string routing: the `screen` query param carries the route name (`?screen=¶ms`); it is router-owned and reserved, every other param travels as a plain query param." - (into #{:auth-login + (into #{:mcp-authorize + :auth-login :auth-register :auth-register-validate :auth-register-success diff --git a/frontend/src/app/main/ui/settings.cljs b/frontend/src/app/main/ui/settings.cljs index 97043fae0a..6303b7a5de 100644 --- a/frontend/src/app/main/ui/settings.cljs +++ b/frontend/src/app/main/ui/settings.cljs @@ -11,6 +11,8 @@ [app.main.refs :as refs] [app.main.router :as rt] [app.main.store :as st] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.hooks :as hooks] [app.main.ui.modal :refer [modal-container*]] [app.main.ui.settings.change-email] @@ -33,7 +35,11 @@ [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div {:class (stl/css :dashboard-title)} - [:h1 {:data-testid "account-title"} (tr "dashboard.your-account-title")]]]) + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text) + :data-testid "account-title"} + (tr "dashboard.your-account-title")]]]) (mf/defc settings* [{:keys [route type error-report-id error-href]}] diff --git a/frontend/src/app/main/ui/settings.scss b/frontend/src/app/main/ui/settings.scss index 5898dbac3b..62d4e141f1 100644 --- a/frontend/src/app/main/ui/settings.scss +++ b/frontend/src/app/main/ui/settings.scss @@ -4,7 +4,7 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "ds/_utils.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_borders.scss" as *; diff --git a/frontend/src/app/main/ui/settings/change_email.cljs b/frontend/src/app/main/ui/settings/change_email.cljs index 98e15e2c58..613ae3a9d6 100644 --- a/frontend/src/app/main/ui/settings/change_email.cljs +++ b/frontend/src/app/main/ui/settings/change_email.cljs @@ -14,7 +14,8 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.notifications.context-notification :refer [context-notification]] [app.util.i18n :as i18n :refer [tr]] [beicon.v2.core :as rx] @@ -88,8 +89,11 @@ [:h2 {:class (stl/css :modal-title) :data-testid "change-email-title"} (tr "modals.change-email.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-content)} [:& context-notification @@ -114,7 +118,8 @@ [:div {:class (stl/css :action-buttons) :data-testid "change-email-submit"} [:> fm/submit-button* - {:label (tr "modals.change-email.submit")}]]]]]])) + {:label (tr "modals.change-email.submit") + :class (stl/css :submit-button)}]]]]]])) diff --git a/frontend/src/app/main/ui/settings/change_email.scss b/frontend/src/app/main/ui/settings/change_email.scss index 4795cf6ac6..afff303004 100644 --- a/frontend/src/app/main/ui/settings/change_email.scss +++ b/frontend/src/app/main/ui/settings/change_email.scss @@ -4,7 +4,11 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*), which move with +// the modals refactor. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .modal-overlay { @extend %modal-overlay-base; @@ -13,49 +17,45 @@ .modal-container { @extend %modal-container-base; - min-width: deprecated.$s-408; + min-inline-size: px2rem(408); } .modal-header { - margin-bottom: deprecated.$s-24; + margin-block-end: var(--sp-xxl); } .modal-title { - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } +// Tooltip wrapper of the close button: it places it in the corner. .modal-close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { - @include deprecated.flex-column; - @include deprecated.body-small-typography; + @include use-typography("body-small"); - gap: deprecated.$s-24; - margin-bottom: deprecated.$s-24; + display: flex; + flex-direction: column; + gap: var(--sp-xxl); + margin-block-end: var(--sp-xxl); } .fields-row { - @include deprecated.flex-column; -} - -.select-title { - @include deprecated.body-small-typography; - - color: var(--modal-title-foreground-color); + display: flex; + flex-direction: column; + gap: var(--sp-xs); } .action-buttons { @extend %modal-action-btns; - - button { - @extend %modal-accept-btn; - } } -.cancel-button { - @extend %modal-cancel-btn; +.submit-button { + @extend %modal-accept-btn; } diff --git a/frontend/src/app/main/ui/settings/delete_account.cljs b/frontend/src/app/main/ui/settings/delete_account.cljs index 678ff29f20..b916734729 100644 --- a/frontend/src/app/main/ui/settings/delete_account.cljs +++ b/frontend/src/app/main/ui/settings/delete_account.cljs @@ -14,8 +14,9 @@ [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.components.organization-avatar :refer [organization-avatar*]] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.notifications.context-notification :refer [context-notification]] [app.util.i18n :as i18n :refer [tr]] [beicon.v2.core :as rx] @@ -62,8 +63,11 @@ [:div {:class (stl/css :modal-header)} [:h2 {:class (stl/css :modal-title)} (tr "modals.delete-account.title")] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-content)} [:div {:class (stl/css :warning-notice)} @@ -100,11 +104,12 @@ [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} - [:button {:class (stl/css :cancel-button) - :on-click modal/hide!} + [:> button* {:variant "secondary" + :type "button" + :on-click modal/hide!} (tr "modals.delete-account.cancel")] - [:button {:class (stl/css-case :accept-button true - :danger true) - :on-click on-accept - :data-testid "delete-account-btn"} + [:> button* {:variant "destructive" + :type "button" + :on-click on-accept + :data-testid "delete-account-btn"} (tr "modals.delete-account.confirm")]]]]])) diff --git a/frontend/src/app/main/ui/settings/delete_account.scss b/frontend/src/app/main/ui/settings/delete_account.scss index 4d0c1efafc..f27a48c7a4 100644 --- a/frontend/src/app/main/ui/settings/delete_account.scss +++ b/frontend/src/app/main/ui/settings/delete_account.scss @@ -4,8 +4,10 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*), which move with +// the modals refactor. @use "refactor/common-refactor.scss" as deprecated; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/spacing.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_utils.scss" as *; @@ -19,54 +21,48 @@ display: flex; flex-direction: column; - min-width: deprecated.$s-408; + min-inline-size: px2rem(408); } .modal-header { - margin-bottom: deprecated.$s-24; + margin-block-end: var(--sp-xxl); } .modal-title { - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } +// Tooltip wrapper of the close button: it places it in the corner. .modal-close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { - @include deprecated.flex-column; - @include deprecated.body-small-typography; + @include use-typography("body-small"); + display: flex; + flex-direction: column; flex: 1 1 auto; - min-height: 0; - gap: deprecated.$s-24; - margin-bottom: deprecated.$s-24; + min-block-size: 0; + gap: var(--sp-xxl); + margin-block-end: var(--sp-xxl); } .warning-notice { flex-shrink: 0; } -.fields-row { - @include deprecated.flex-column; -} - -.select-title { - @include deprecated.body-small-typography; - - color: var(--modal-title-foreground-color); -} - .organizations-section { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); display: flex; flex-direction: column; flex: 1 1 auto; - min-height: 0; + min-block-size: 0; gap: var(--sp-l); } @@ -98,7 +94,7 @@ display: flex; flex-direction: column; flex: 1 1 auto; - min-height: 0; + min-block-size: 0; gap: var(--sp-l); margin: 0; padding: 0; @@ -119,28 +115,16 @@ } .organization-name { - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } .organization-counts { display: flex; flex-direction: row; gap: var(--sp-s); - color: var(--df-secondary); + color: var(--color-foreground-secondary); } .action-buttons { @extend %modal-action-btns; } - -.cancel-button { - @extend %modal-cancel-btn; -} - -.accept-button { - @extend %modal-accept-btn; - - &.danger { - @extend %modal-danger-btn; - } -} diff --git a/frontend/src/app/main/ui/settings/feedback.cljs b/frontend/src/app/main/ui/settings/feedback.cljs index b5a1a88373..70ed8999d8 100644 --- a/frontend/src/app/main/ui/settings/feedback.cljs +++ b/frontend/src/app/main/ui/settings/feedback.cljs @@ -131,7 +131,9 @@ :show-success? true}] (when report - [:a {:class (stl/css :link :download-button) :on-click on-download} + [:button {:type "button" + :class (stl/css :link :download-button) + :on-click on-download} (tr "labels.download" "report.txt")])] [:> fm/submit-button* diff --git a/frontend/src/app/main/ui/settings/feedback.scss b/frontend/src/app/main/ui/settings/feedback.scss index aee1490e20..d38c897861 100644 --- a/frontend/src/app/main/ui/settings/feedback.scss +++ b/frontend/src/app/main/ui/settings/feedback.scss @@ -81,5 +81,11 @@ display: inline-block; min-inline-size: $sz-160; text-align: center; + background: none; + cursor: pointer; + + &:hover { + text-decoration: underline; + } } } diff --git a/frontend/src/app/main/ui/settings/integrations.cljs b/frontend/src/app/main/ui/settings/integrations.cljs index 334ca32806..159dc1414b 100644 --- a/frontend/src/app/main/ui/settings/integrations.cljs +++ b/frontend/src/app/main/ui/settings/integrations.cljs @@ -486,11 +486,7 @@ [:> heading* {:level 2 :typography t/title-medium :class (stl/css :color-primary :mcp-server-title)} - (tr "integrations.mcp-server.title")] - [:> text* {:as "span" - :typography t/body-small - :class (stl/css :beta)} - (tr "integrations.mcp-server.title.beta")]] + (tr "integrations.mcp-server.title")]] [:> text* {:as "div" :typography t/body-medium diff --git a/frontend/src/app/main/ui/settings/integrations.scss b/frontend/src/app/main/ui/settings/integrations.scss index 174cf8a73d..c224fed5d8 100644 --- a/frontend/src/app/main/ui/settings/integrations.scss +++ b/frontend/src/app/main/ui/settings/integrations.scss @@ -27,14 +27,6 @@ inline-size: fit-content; } -.beta { - color: var(--color-accent-primary); - border: $b-1 solid var(--color-accent-primary); - inline-size: fit-content; - padding: var(--sp-xxs) var(--sp-s); - border-radius: $br-4; -} - .title { display: flex; flex-direction: row; diff --git a/frontend/src/app/main/ui/settings/options.cljs b/frontend/src/app/main/ui/settings/options.cljs index c4d711ba79..b841f3567d 100644 --- a/frontend/src/app/main/ui/settings/options.cljs +++ b/frontend/src/app/main/ui/settings/options.cljs @@ -49,7 +49,7 @@ (update :lang #(or % "")) (update :theme #(if (= % "default") "dark" - (or % "dark"))))) + (or % theme/default))))) form (fm/use-form :schema schema:options-form :initial initial)] @@ -58,7 +58,10 @@ :on-submit on-submit :form form} - [:h3 (tr "labels.language")] + [:> heading* {:level 3 + :typography t/headline-small + :class (stl/css :section-title)} + (tr "labels.language")] [:div {:class (stl/css :fields-row)} [:& fm/select {:options (into [{:label "Auto (browser)" :value ""}] @@ -68,7 +71,10 @@ :name :lang :data-testid "setting-lang"}]] - [:h3 (tr "dashboard.theme-change")] + [:> heading* {:level 3 + :typography t/headline-small + :class (stl/css :section-title)} + (tr "dashboard.theme-change")] [:div {:class (stl/css :fields-row)} [:& fm/select {:label (tr "dashboard.select-ui-theme") :name :theme @@ -128,7 +134,7 @@ [:div {:class (stl/css :form-container) :data-testid "settings-form"} [:> heading* {:level 2 :typography t/title-large - :class (stl/css :color-primary) + :class (stl/css :page-title) :id "options-section-title"} (tr "labels.settings")] [:> options-form*]] diff --git a/frontend/src/app/main/ui/settings/options.scss b/frontend/src/app/main/ui/settings/options.scss index cdae1e185b..91cf8dc3c4 100644 --- a/frontend/src/app/main/ui/settings/options.scss +++ b/frontend/src/app/main/ui/settings/options.scss @@ -13,6 +13,16 @@ display: grid; } +.options-form { + display: flex; + flex-direction: column; +} + +.section-title { + color: var(--color-foreground-primary); + margin-block: var(--sp-s); +} + .form-container { --menu-max-height-up: #{px2rem(180)}; diff --git a/frontend/src/app/main/ui/settings/profile.cljs b/frontend/src/app/main/ui/settings/profile.cljs index 1c1add422b..14cfb8097c 100644 --- a/frontend/src/app/main/ui/settings/profile.cljs +++ b/frontend/src/app/main/ui/settings/profile.cljs @@ -17,6 +17,8 @@ [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.forms :as fm] [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -71,7 +73,9 @@ [:div {:class (stl/css :options)} [:div.change-email - [:a {:on-click on-show-change-email} + [:button {:type "button" + :class (stl/css :link) + :on-click on-show-change-email} (tr "dashboard.change-email")]]]] [:> fm/submit-button* @@ -80,9 +84,11 @@ :class (stl/css :btn-primary)}] [:div {:class (stl/css :links)} - [:div {:class (stl/css :link-item)} - [:a {:on-click on-show-delete-account - :data-testid "remove-acount-btn"} + [:div + [:button {:type "button" + :class (stl/css :link) + :on-click on-show-delete-account + :data-testid "remove-acount-btn"} (tr "dashboard.remove-account")]]]])) ;; --- Profile Photo Form @@ -119,9 +125,12 @@ [:form {:class (stl/css :avatar-form)} [:div {:class (stl/css :image-change-field)} - [:span {:class (stl/css :update-overlay) - :on-click on-image-click} (tr "labels.update")] - [:img {:src photo}] + [:button {:type "button" + :class (stl/css :update-overlay) + :on-click on-image-click} + (tr "labels.update")] + [:img {:class (stl/css :avatar-image) + :src photo}] (when has-photo? [:button {:type "button" :class (stl/css :delete-overlay) @@ -146,6 +155,10 @@ [:section {:class (stl/css :dashboard-settings) :aria-labelledby "profile-section-title"} [:div {:class (stl/css :form-container)} - [:h2 {:id "profile-section-title"} (tr "labels.profile")] + [:> heading* {:level 2 + :typography t/title-large + :id "profile-section-title" + :class (stl/css :page-title)} + (tr "labels.profile")] [:> profile-photo-form*] [:> profile-form*]]]) diff --git a/frontend/src/app/main/ui/settings/profile.scss b/frontend/src/app/main/ui/settings/profile.scss index 1a6fca03c6..d98a0f5996 100644 --- a/frontend/src/app/main/ui/settings/profile.scss +++ b/frontend/src/app/main/ui/settings/profile.scss @@ -4,316 +4,93 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "common/refactor/common-refactor" as *; +// Only for %button-primary: the save button is the shared forms submit +// button, which moves to the DS with the forms component. +@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .dashboard-settings { display: flex; - width: 100%; + inline-size: 100%; justify-content: center; align-items: center; +} - a:not(.button-primary, .link) { - color: var(--color-foreground-secondary); +// "Change email" and "Want to remove your account?" open modals, so they +// are buttons that keep the look of a text link. +.link { + @include use-typography("body-medium"); + + color: var(--color-accent-primary); + background: none; + border: none; + padding: 0; + cursor: pointer; + + &:hover { + text-decoration: underline; } } .form-container { display: flex; flex-direction: column; - max-width: $s-500; - width: $s-580; - margin: $s-80 auto $s-120 auto; + max-inline-size: $sz-500; + inline-size: px2rem(580); + margin-block: px2rem(80) px2rem(120); + margin-inline: auto; justify-content: center; - - form { - display: flex; - flex-direction: column; - - .btn-secondary { - width: 100%; - font-size: $fs-11; - text-transform: uppercase; - background-color: var(--color-background-tertiary); - color: var(--color-foreground-primary); - - &:hover { - color: var(--color-accent-primary); - background-color: var(--color-background-quaternary); - } - } - - hr { - display: none; - } - } - - .fields-row { - --input-height: #{$s-40}; - - margin-bottom: $s-20; - flex-direction: column; - - .options { - display: flex; - justify-content: flex-end; - font-size: $fs-14; - margin-top: $s-12; - } - } - - .field { - margin-bottom: $s-20; - } - - .field-title { - color: var(--color-foreground-primary); - - &:not(:first-child) { - margin-top: $s-64; - } - } - - .field-text { - color: var(--color-foreground-secondary); - } - - .custom-input, - .custom-select { - flex-direction: column-reverse; - - label { - position: relative; - text-transform: uppercase; - color: var(--color-foreground-primary); - font-size: $fs-11; - margin-bottom: $s-12; - margin-left: calc(-1 * $s-4); - } - - input, - select { - background-color: var(--color-background-tertiary); - border-radius: $br-8; - border-color: transparent; - color: var(--color-foreground-primary); - padding: 0 $s-16; - - &:focus { - outline: $s-1 solid var(--color-accent-primary); - } - - ::placeholder { - color: var(--color-foreground-secondary); - } - } - - .help-icon { - bottom: $s-12; - top: auto; - - svg { - fill: var(--color-foreground-secondary); - } - } - - &.disabled { - input { - background-color: var(--input-background-color-disabled); - border-color: var(--color-background-quaternary); - color: var(--color-foreground-secondary); - } - } - - .input-container { - background-color: var(--color-background-tertiary); - border-radius: $br-8; - border-color: transparent; - margin-top: $s-24; - - .main-content { - label { - position: absolute; - top: calc(-1 * $s-24); - } - - span { - color: var(--color-foreground-primary); - } - } - - &:focus { - border: $s-1 solid var(--color-accent-primary); - } - } - - textarea { - border-radius: $br-8; - padding: $s-12 $s-16; - background-color: var(--color-background-tertiary); - color: var(--color-foreground-primary); - border: none; - - &:focus { - outline: $s-1 solid var(--color-accent-primary); - } - } - } - - &.two-columns { - max-width: $s-520; - justify-content: space-between; - flex-direction: row; - } - - h1 { - font-size: $fs-36; - color: var(--color-background-tertiary); - margin-bottom: $s-20; - } - - .subtitle { - font-size: $fs-24; - color: var(--color-background-tertiary); - margin-bottom: $s-20; - } - - .notification-icon { - justify-content: center; - display: flex; - margin-bottom: $s-48; - - svg { - fill: var(--color-background-primary); - height: 40%; - width: 40%; - } - } - - .notification-text { - font-size: $fs-16; - color: var(--color-background-primary); - margin-bottom: $s-20; - } - - .notification-text-email { - background: var(--color-foreground-primary); - border-radius: $br-4; - color: var(--color-background-primary); - font-size: $fs-16; - font-weight: $fw500; - margin: $s-24 0 $s-40 0; - padding: $s-16; - text-align: center; - } - - h2 { - font-size: $fs-24; - font-weight: $fw400; - color: var(--color-foreground-primary); - display: flex; - align-items: center; - margin: $s-16 0; - } - - h3 { - font-size: $fs-12; - font-weight: $fw400; - color: var(--color-foreground-primary); - display: flex; - align-items: center; - margin: $s-8 0; - text-transform: uppercase; - } - - a:not(.button-primary) { - &:hover { - text-decoration: underline; - } - } - - p { - color: var(--color-background-primary); - } - - hr { - border-color: var(--color-foreground-secondary); - } - - .links { - margin-top: $s-12; - } } -form.avatar-form { +.page-title { + color: var(--color-foreground-primary); + display: flex; + align-items: center; + margin-block: var(--sp-l); + margin-inline: 0; +} + +.fields-row { + --input-height: #{$sz-40}; + + margin-block-end: var(--sp-xl); + flex-direction: column; +} + +.options { + display: flex; + justify-content: flex-end; + margin-block-start: var(--sp-m); +} + +.links { + margin-block-start: var(--sp-m); +} + +.avatar-form { display: flex; flex-direction: column; - width: $s-148; - height: $s-148; - margin: $s-16 0; + inline-size: px2rem(148); + block-size: px2rem(148); + margin-block: var(--sp-l); + margin-inline: 0; +} - img { - border-radius: 50%; - flex-shrink: 0; - height: 100%; - margin-right: $s-16; - width: 100%; - } +.avatar-image { + border-radius: $br-circle; + flex-shrink: 0; + block-size: 100%; + margin-inline-end: var(--sp-l); + inline-size: 100%; } .image-change-field { position: relative; - width: 100%; - height: 100%; - - .update-overlay { - opacity: 0; - cursor: pointer; - position: absolute; - width: 100%; - height: 100%; - border-radius: 50%; - font-size: $fs-24; - color: var(--color-foreground-primary); - line-height: 6; - text-align: center; - background: var(--color-accent-tertiary); - z-index: $z-index-modal; - } - - .delete-overlay { - position: absolute; - top: $s-4; - inset-inline-end: $s-4; - display: flex; - align-items: center; - justify-content: center; - width: $s-32; - height: $s-32; - padding: 0; - border: none; - border-radius: 50%; - background: var(--color-background-primary); - color: var(--color-foreground-primary); - cursor: pointer; - opacity: 0; - transition: opacity 0.15s ease-in-out; - z-index: calc(#{$z-index-modal} + 1); - - &:hover { - background: var(--color-background-quaternary); - color: var(--color-accent-primary); - } - } - - input[type="file"] { - width: 100%; - height: 100%; - position: absolute; - opacity: 0; - cursor: pointer; - top: 0; - z-index: $z-index-modal; - } + inline-size: 100%; + block-size: 100%; &:hover { .update-overlay { @@ -326,52 +103,70 @@ form.avatar-form { } } +.update-overlay { + @include use-typography("title-large"); + + opacity: 0; + cursor: pointer; + position: absolute; + inline-size: 100%; + block-size: 100%; + border: none; + border-radius: $br-circle; + color: var(--color-foreground-primary); + + // Draws the label in the middle of the circle. + line-height: 6; + text-align: center; + background: var(--color-accent-tertiary); + + // The button centers its 144px line in the 148px circle; the bottom + // padding keeps the label where the old span drew it. + padding: 0 0 var(--sp-xs); + z-index: var(--z-index-set); + + &:focus-visible { + opacity: 0.8; + } +} + +.delete-overlay { + --delete-overlay-background-color: var(--color-background-primary); + --delete-overlay-foreground-color: var(--color-foreground-primary); + + position: absolute; + inset-block-start: var(--sp-xs); + inset-inline-end: var(--sp-xs); + display: flex; + align-items: center; + justify-content: center; + inline-size: $sz-32; + block-size: $sz-32; + padding: 0; + border: none; + border-radius: $br-circle; + background: var(--delete-overlay-background-color); + color: var(--delete-overlay-foreground-color); + cursor: pointer; + opacity: 0; + transition: opacity 0.15s ease-in-out; + z-index: calc(var(--z-index-set) + 1); + + &:hover { + --delete-overlay-background-color: var(--color-background-quaternary); + --delete-overlay-foreground-color: var(--color-accent-primary); + } +} + .profile-form { display: flex; flex-direction: column; - max-width: $s-368; - width: 100%; -} - -.newsletter-subs { - border-bottom: $s-1 solid var(--color-foreground-secondary); - border-top: $s-1 solid var(--color-foreground-secondary); - padding: $s-32 0; - margin-bottom: $s-32; - - .newsletter-title { - font-family: "worksans", "vazirmatn", sans-serif; - color: var(--color-foreground-secondary); - font-size: $fs-14; - } - - label { - font-family: "worksans", "vazirmatn", sans-serif; - color: var(--color-background-primary); - font-size: $fs-12; - margin-right: calc(-1 * $s-16); - margin-bottom: $s-12; - } - - .info { - color: var(--color-foreground-secondary); - font-size: $fs-12; - margin-bottom: $s-8; - } - - .input-checkbox label { - align-items: flex-start; - } -} - -.btn-secondary { - @extend %button-secondary; - - height: $s-32; + max-inline-size: px2rem(368); + inline-size: 100%; } .btn-primary { @extend %button-primary; - height: $s-32; + block-size: $sz-32; } diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index 652842ff26..3bd4545bf6 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -15,16 +15,11 @@ [app.main.router :as rt] [app.main.store :as st] [app.main.ui.dashboard.sidebar :refer [profile-section*]] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] [rumext.v2 :as mf])) -(def ^:private arrow-icon - (deprecated-icon/icon-xref :arrow (stl/css :arrow-icon))) - -(def ^:private feedback-icon - (deprecated-icon/icon-xref :feedback (stl/css :feedback-icon))) ;; FIXME: move to common (def ^:private go-settings-profile @@ -82,7 +77,8 @@ [:div {:class (stl/css :sidebar-content-section)} [:button {:class (stl/css :back-to-dashboard) :on-click go-dashboard} - arrow-icon + [:> icon* {:icon-id i/arrow-left + :class (stl/css :arrow-icon)}] [:span {:class (stl/css :back-text)} (tr "labels.dashboard")]]] [:hr {:class (stl/css :sidebar-separator)}] @@ -143,7 +139,9 @@ [:li {:class (stl/css-case :current feedback? :settings-item true) :on-click go-settings-feedback} - feedback-icon + [:> icon* {:icon-id i/feedback + :size "s" + :class (stl/css :feedback-icon)}] [:span {:class (stl/css :element-title)} (tr "labels.contact-us")]])]]])) (mf/defc sidebar* diff --git a/frontend/src/app/main/ui/settings/sidebar.scss b/frontend/src/app/main/ui/settings/sidebar.scss index e63882a113..dc905a9911 100644 --- a/frontend/src/app/main/ui/settings/sidebar.scss +++ b/frontend/src/app/main/ui/settings/sidebar.scss @@ -4,8 +4,11 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_utils.scss" as *; @use "ds/mixins.scss" as *; +@use "ds/typography.scss" as *; +@use "ds/z-index.scss" as *; .dashboard-sidebar { @include custom-scrollbar; @@ -14,24 +17,24 @@ grid-row: 1 / span 2; display: grid; grid-template-rows: 1fr auto; - height: 100%; - padding-block-start: deprecated.$s-16; - border-right: deprecated.$s-1 solid var(--panel-border-color); - z-index: deprecated.$z-index-1; - background-color: var(--panel-background-color); + block-size: 100%; + padding-block-start: var(--sp-l); + border-inline-end: $b-1 solid var(--color-background-quaternary); + z-index: var(--z-index-panels); + background-color: var(--color-background-primary); } .sidebar-content { display: grid; grid-template-rows: auto auto 1fr; - height: 100%; + block-size: 100%; padding: 0; overflow-y: auto; } .sidebar-separator { border-color: transparent; - margin: deprecated.$s-12 deprecated.$s-16; + margin: var(--sp-m) var(--sp-l); } .sidebar-nav-settings { @@ -43,46 +46,48 @@ } .settings-item { - --settings-foreground-color: var(--menu-foreground-color-rest); + --settings-foreground-color: var(--color-foreground-secondary); --settings-background-color: transparent; display: flex; align-items: center; - padding: deprecated.$s-8 deprecated.$s-8 deprecated.$s-8 deprecated.$s-24; + padding: var(--sp-s) var(--sp-s) var(--sp-s) var(--sp-xxl); color: var(--settings-foreground-color); background-color: var(--settings-background-color); cursor: pointer; &:hover { - --settings-foreground-color: var(--sidebar-element-foreground-color-hover); - --settings-background-color: var(--sidebar-element-background-color-hover); + --settings-foreground-color: var(--color-accent-primary); + --settings-background-color: var(--color-background-secondary); } &.current { - --settings-foreground-color: var(--sidebar-element-foreground-color-selected); - --settings-background-color: var(--sidebar-element-background-color-selected); + --settings-foreground-color: var(--color-accent-primary); + --settings-background-color: var(--color-background-quaternary); } } +// The DS small icon draws 12px inside a 16px box: 4px less margin keeps +// the label where it was (the drawing sits 2px to the right). .feedback-icon { - @extend %button-icon-small; - - stroke: var(--settings-foreground-color); - margin-right: deprecated.$s-8; + flex-shrink: 0; + color: var(--settings-foreground-color); + margin-inline-end: var(--sp-xs); } .element-title { - @include deprecated.text-ellipsis; - @include deprecated.body-medium-typography; + @include text-ellipsis; + @include use-typography("body-medium"); } .back-to-dashboard { - @include deprecated.button-style; - + border: none; + background: none; + cursor: pointer; display: flex; align-items: center; - padding: deprecated.$s-12 deprecated.$s-16; - font-size: deprecated.$fs-14; + padding: var(--sp-m) var(--sp-l); + font-size: px2rem(14); } .back-text { @@ -90,9 +95,7 @@ } .arrow-icon { - @extend %button-icon; - - stroke: var(--icon-foreground); - transform: rotate(180deg); - margin-right: deprecated.$s-12; + flex-shrink: 0; + color: var(--color-foreground-secondary); + margin-inline-end: var(--sp-m); } diff --git a/frontend/src/app/main/ui/settings/subscription.cljs b/frontend/src/app/main/ui/settings/subscription.cljs index d1db4cab5f..3b879563a9 100644 --- a/frontend/src/app/main/ui/settings/subscription.cljs +++ b/frontend/src/app/main/ui/settings/subscription.cljs @@ -16,6 +16,7 @@ [app.main.ui.components.forms :as fm] [app.main.ui.dashboard.subscription :refer [get-subscription-type]] [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.ds.foundations.assets.raw-svg :refer [raw-svg*]] [app.main.ui.nitrate.nitrate-activation-success-modal] @@ -93,6 +94,7 @@ [:button {:class (stl/css :cta-button :more-info) :on-click cta-link-with-icon} cta-text-with-icon [:> icon* {:icon-id "open-link" + :class (stl/css :cta-icon) :size "s"}]]) (when has-cta-link [:button {:class (stl/css-case :cta-button true @@ -137,7 +139,7 @@ [:button {:class (stl/css :cta-button :show-editors-button) :on-click handle-click} (tr "subscription.settings.management.dialog.editors") [:> icon* {:icon-id (if show-editors-list i/arrow-up i/arrow-down) - :class (stl/css :icon-dropdown) + :class (stl/css :cta-icon) :size "s"}]] (when show-editors-list [:* @@ -262,9 +264,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-title :subscription-title)} (if (= unlimited-modal-step 2) (tr "subscription.settings.management-dialog.step-2-title") @@ -382,12 +386,15 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog :subscription-success)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-success-content)} [:div {:class (stl/css :modal-start)} - [:> raw-svg* {:id (if (= "light" (:theme profile)) "logo-subscription-light" "logo-subscription")}]] + [:> raw-svg* {:id (if (= "light" (:theme profile)) "logo-subscription-light" "logo-subscription") + :class (stl/css :modal-start-logo)}]] [:div {:class (stl/css :modal-end)} [:div {:class (stl/css :modal-title)} @@ -835,9 +842,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-title :subscription-title :nitrate-subscription)} (tr "nitrate.form.title")] @@ -889,9 +898,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-title :subscription-title)} (tr "nitrate.contact-sales.title" subscription-type)] [:div {:class (stl/css :modal-content)} @@ -942,9 +953,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-title :subscription-title)} (tr "nitrate.subscription.settings.manual-cancel")] diff --git a/frontend/src/app/main/ui/settings/subscription.scss b/frontend/src/app/main/ui/settings/subscription.scss index ff7885506d..b1fd03d2b6 100644 --- a/frontend/src/app/main/ui/settings/subscription.scss +++ b/frontend/src/app/main/ui/settings/subscription.scss @@ -4,8 +4,10 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*), which move with +// the modals refactor. @use "refactor/common-refactor.scss" as deprecated; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/_borders.scss" as *; @use "ds/spacing.scss" as *; @use "ds/_sizes.scss" as *; @@ -39,34 +41,29 @@ } .membership-date { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-secondary); margin-inline-start: var(--sp-s); } -.subscription-member, .penpot-member { - @extend %button-icon; -} - -.penpot-member { - stroke: var(--icon-foreground); + color: var(--color-foreground-secondary); } .subscription-member { - stroke: var(--color-badge-premium); + color: var(--color-badge-premium); } .title-section { - @include t.use-typography("title-large"); + @include use-typography("title-large"); color: var(--color-foreground-primary); margin-block-end: var(--sp-xxl); } .plan-section-title { - @include t.use-typography("headline-small"); + @include use-typography("headline-small"); color: var(--color-foreground-primary); } @@ -100,29 +97,26 @@ } .plan-title-icon { - @extend %button-icon; - + display: flex; + justify-content: center; + align-items: center; + color: var(--color-foreground-primary); block-size: var(--sp-xl); inline-size: var(--sp-xl); - border-radius: 6px; - border: 1.75px solid var(--color-foreground-primary); -} - -.plan-title-icon svg { - stroke: var(--color-foreground-primary); - stroke-width: 2.25px; + border-radius: $br-6; + border: $b-2 solid var(--color-foreground-primary); } .plan-card-title, .plan-price-value, .price-value { - @include t.use-typography("title-medium"); + @include use-typography("title-medium"); color: var(--color-foreground-primary); } .plan-editors { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); align-self: end; color: var(--color-foreground-primary); @@ -130,7 +124,7 @@ } .plan-price-period { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-primary); } @@ -144,7 +138,7 @@ } .plan-cancel-date { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-primary); } @@ -158,7 +152,7 @@ .benefits-title, .benefit { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-secondary); } @@ -172,29 +166,24 @@ } .cta-button { - @include t.use-typography("body-medium"); - @include deprecated.button-style; + @include use-typography("body-medium"); + border: none; + background: none; + cursor: pointer; align-items: center; color: var(--color-accent-primary); display: flex; margin-block-start: var(--sp-m); - text-align: left; + text-align: start; } -.cta-button svg { - @extend %button-icon; - +.cta-icon { block-size: var(--sp-l); inline-size: var(--sp-l); - stroke: var(--color-accent-primary); margin-inline-start: var(--sp-xs); } -.icon-dropdown svg { - transform: rotate(90deg); -} - .bottom-link { margin-block-start: var(--sp-xs); } @@ -204,7 +193,7 @@ } .inline-error { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-error); margin-block: var(--sp-s) 0; @@ -225,15 +214,18 @@ min-inline-size: px2rem(648); } +// Tooltip wrapper of the close buttons: it places them in the corner. .close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-title { - @include t.use-typography("title-large"); + @include use-typography("title-large"); margin-block-end: var(--sp-xxxl); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); display: flex; gap: var(--sp-m); } @@ -243,7 +235,7 @@ } .modal-text-medium { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); } .modal-text-cap { @@ -257,7 +249,7 @@ .modal-text-medium strong, .text-strikethrough strong, .modal-text-cap strong { - font-weight: deprecated.$fw700; + font-weight: 700; } .modal-content, @@ -309,13 +301,13 @@ } } -.modal-start svg { +.modal-start-logo { inline-size: 100%; block-size: auto; } .editors-text { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); margin: 0; } @@ -326,7 +318,7 @@ } .editors-list { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); list-style-position: inside; list-style-type: none; @@ -342,7 +334,7 @@ } .error-message { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-error); margin-block-start: var(--sp-s); @@ -361,7 +353,7 @@ } .unlimited-capped-warning { - @include t.use-typography("body-small"); + @include use-typography("body-small"); background-color: var(--color-background-tertiary); border-radius: var(--sp-s); @@ -374,28 +366,12 @@ padding-inline: 0; } -.radio-btns { - display: flex; - flex-direction: column; - padding-block-end: var(--sp-xl); - gap: var(--sp-s); -} - -.radio-btns label { - @include t.use-typography("body-large"); - - padding: 0; - display: flex; - align-items: center; - color: var(--color-foreground-secondary); -} - .modal-contact-content { gap: var(--sp-xl); } .downgrade-warning { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); background-color: var(--color-background-tertiary); border-radius: var(--sp-s); @@ -412,13 +388,13 @@ } .downgrade-item { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); margin-block-end: var(--sp-l); } .cta-link { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); align-items: center; color: var(--color-accent-primary); diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 20defd27f0..b03d37bc99 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -168,6 +168,10 @@ (dm/str render-id "-" id) id))) (dissoc :id) + ;; Stored keys are kebab-case; React needs camelCase props. + ;; Stored data is whitelist-clean by construction, so this + ;; conversion drops nothing. + (csvg/attrs->props) (json/->js :key-fn name))))) (defn get-fill-style diff --git a/frontend/src/app/main/ui/shapes/svg_defs.cljs b/frontend/src/app/main/ui/shapes/svg_defs.cljs index 32d6a0b6c5..a824f56e6b 100644 --- a/frontend/src/app/main/ui/shapes/svg_defs.cljs +++ b/frontend/src/app/main/ui/shapes/svg_defs.cljs @@ -33,20 +33,20 @@ (let [{:keys [tag attrs content]} node transform-gradient? (and (contains? csvg/gradient-tags tag) - (= "userSpaceOnUse" (get attrs :gradientUnits "objectBoundingBox"))) + (= "userSpaceOnUse" (get attrs :gradient-units "objectBoundingBox"))) transform-pattern? (and (= :pattern tag) - (= "userSpaceOnUse" (get attrs :patternContentUnits "userSpaceOnUse")) - (= "userSpaceOnUse" (get attrs :patternUnits "userSpaceOnUse"))) + (= "userSpaceOnUse" (get attrs :pattern-content-units "userSpaceOnUse")) + (= "userSpaceOnUse" (get attrs :pattern-units "userSpaceOnUse"))) transform-clippath? (and (= :clipPath tag) - (= "userSpaceOnUse" (get attrs :clipPathUnits "userSpaceOnUse"))) + (= "userSpaceOnUse" (get attrs :clip-path-units "userSpaceOnUse"))) transform-filter? (and (contains? csvg/filter-tags tag) - (= "userSpaceOnUse" (get attrs :filterUnits "objectBoundingBox"))) + (= "userSpaceOnUse" (get attrs :filter-units "objectBoundingBox"))) transform-mask? (and (= :mask tag) - (= "userSpaceOnUse" (get attrs :maskUnits "objectBoundingBox"))) + (= "userSpaceOnUse" (get attrs :mask-units "objectBoundingBox"))) attrs (-> attrs diff --git a/frontend/src/app/main/ui/static.cljs b/frontend/src/app/main/ui/static.cljs index 9376ec6ef1..588dd0f970 100644 --- a/frontend/src/app/main/ui/static.cljs +++ b/frontend/src/app/main/ui/static.cljs @@ -177,8 +177,10 @@ [:div {:class (stl/css :change-section)} (tr "auth.register") " " - [:a {:data-section "register" - :on-click set-section} + [:button {:type "button" + :class (stl/css :link) + :data-section "register" + :on-click set-section} (tr "auth.register-submit")]]] :register @@ -191,8 +193,11 @@ [:div {:class (stl/css :change-section)} (tr "auth.already-have-account") " " - [:a {:data-section "login" - :on-click set-section} (tr "auth.login-here")]] + [:button {:type "button" + :class (stl/css :link) + :data-section "login" + :on-click set-section} + (tr "auth.login-here")]] [:div {:class (stl/css :links)} [:hr {:class (stl/css :separator)}] [:> register/terms-service-privacy-policy*]]] @@ -425,7 +430,10 @@ [:p {:class (stl/css :desc-text)} (tr "labels.internal-error.desc-message-second")]] (when (some? report) - [:a {:class (stl/css :download-link) :on-click on-download} (tr "labels.download" "report.txt")]) + [:button {:type "button" + :class (stl/css :download-link) + :on-click on-download} + (tr "labels.download" "report.txt")]) [:div {:class (stl/css :buttons-container)} [:> button* {:variant "secondary" diff --git a/frontend/src/app/main/ui/static.scss b/frontend/src/app/main/ui/static.scss index 77e7961895..d53ae93f87 100644 --- a/frontend/src/app/main/ui/static.scss +++ b/frontend/src/app/main/ui/static.scss @@ -208,6 +208,14 @@ color: var(--color-foreground-primary); text-transform: lowercase; + background: none; + border: none; + padding: 0; + cursor: pointer; + + &:hover { + text-decoration: underline; + } } .sign-info { @@ -373,9 +381,22 @@ inline-size: 100%; text-align: center; - // Native inside change-section — no class available - a { + .link { + font: inherit; + font-weight: 500; + + // A button is a block in its line: the normal line height keeps the box of + // the inline link it replaces. + line-height: normal; color: var(--color-accent-primary); + background: none; + border: none; + padding: 0; + cursor: pointer; + + &:hover { + text-decoration: underline; + } } } diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index f3cb52db0b..2422d347c2 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -22,9 +22,10 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.context :as ctx] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.ds.product.loader :refer [loader*]] [app.main.ui.hooks :as hooks] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.modal :refer [modal-container*]] [app.main.ui.viewer.comments :refer [comments-layer comments-sidebar*]] [app.main.ui.viewer.header :as header] @@ -99,23 +100,33 @@ go-first-frame (mf/use-fn #(st/emit! dv/select-first-frame))] [:* (when (pos? index) - [:button {:class (stl/css-case :viewer-go-prev true - :left-bar left-bar) - :on-click go-prev-frame - :aria-label (tr "labels.previous")} - deprecated-icon/arrow]) + [:> icon-button* {:variant "secondary" + :class (stl/css :viewer-go-button) + :tooltip-class (stl/css-case :viewer-go-prev true + :left-bar left-bar) + :tooltip-placement "right" + :aria-label (tr "labels.previous") + :icon i/arrow-left + :on-click go-prev-frame}]) (when (< (+ index 1) num-frames) - [:button {:class (stl/css-case :viewer-go-next true - :comment-sidebar comment-sidebar - :right-bar right-bar) - :on-click go-next-frame - :aria-label (tr "labels.next")} - deprecated-icon/arrow]) + [:> icon-button* {:variant "secondary" + :class (stl/css :viewer-go-button) + :tooltip-class (stl/css-case :viewer-go-next true + :comment-sidebar comment-sidebar + :right-bar right-bar) + :tooltip-placement "left" + :aria-label (tr "labels.next") + :icon i/arrow-right + :on-click go-next-frame}]) [:div {:class (stl/css-case :viewer-bottom true :left-bar left-bar)} - [:button {:on-click go-first-frame - :class (stl/css :reset-button)} - deprecated-icon/reload] + [:> icon-button* {:variant "secondary" + :class (stl/css :reset-button) + :tooltip-class (stl/css :reset-button-wrapper) + :tooltip-placement "top-right" + :aria-label (tr "viewer.go-to-first-board") + :icon i/reload + :on-click go-first-frame}] [:span {:class (stl/css :counter)} (str/join " / " [(+ index 1) num-frames])] [:span]]])) diff --git a/frontend/src/app/main/ui/viewer.scss b/frontend/src/app/main/ui/viewer.scss index d727764672..bef25833d1 100644 --- a/frontend/src/app/main/ui/viewer.scss +++ b/frontend/src/app/main/ui/viewer.scss @@ -4,12 +4,16 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/mixins.scss" as *; +@use "ds/typography.scss" as *; .viewer-layout { - height: 100vh; + block-size: 100vh; display: grid; - grid-template-rows: deprecated.$s-48 auto; + grid-template-rows: $sz-48 auto; grid-template-columns: 1fr; user-select: none; } @@ -18,41 +22,41 @@ overflow: hidden; grid-row: 2 / span 1; display: grid; - grid-template-rows: deprecated.$s-252 auto; + grid-template-rows: $sz-252 auto; grid-template-columns: 1fr; - background-color: var(--viewer-background-color); + background-color: var(--color-background-secondary); } .empty-state { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--empty-message-foreground-color); + color: var(--color-foreground-secondary); display: grid; place-items: center; - height: 100%; - width: 100%; -} - -.viewer-header { - grid-row: 1 / span 1; + block-size: 100%; + inline-size: 100%; } .inspect-layout { display: grid; - grid-template-rows: deprecated.$s-48 auto; + grid-template-rows: $sz-48 auto; grid-template-columns: 1fr; - height: 100vh; - margin-top: 0; + block-size: 100vh; + margin-block-start: 0; user-select: none; } .thumbnails-close { - @include deprecated.button-style; - + border: none; + background: none; + cursor: pointer; grid-row: 1 / span 2; grid-column: 1 / span 1; - z-index: deprecated.$z-index-10; - background-color: var(--overlay-color); + + // TODO: no DS z-index token for 10; it keeps the backdrop above the + // viewer content (z-index 2), level with the thumbnails panel. + z-index: 10; + background-color: var(--color-overlay-default); } .thumbnails-close.invisible { @@ -60,98 +64,104 @@ } .viewer-section { - @extend %new-scrollbar; + @include custom-scrollbar($include-selection: false, $include-placeholder: false); grid-row: 1 / span 2; grid-column: 1 / span 1; display: flex; align-items: center; flex-wrap: wrap; - height: calc(100vh - deprecated.$s-48); + block-size: calc(100vh - #{$sz-48}); overflow: auto; + + // The comments layer renders inside the section: its text selection and + // placeholders keep the styles they had. + ::selection { + background: var(--color-accent-background-select); + color: var(--color-static-white); + } + + ::placeholder { + @include use-typography("body-small"); + + color: var(--color-foreground-secondary); + } } .inspect-layout .viewer-section { flex-wrap: nowrap; - margin-top: 0; - height: 100%; + margin-block-start: 0; + block-size: 100%; overflow: hidden; } +// Tooltip wrappers of the previous and next buttons. .viewer-go-prev, .viewer-go-next { - @extend %button-secondary; - @include deprecated.flex-center; - position: absolute; - right: deprecated.$s-8; - height: deprecated.$s-64; - width: deprecated.$s-32; - top: calc(50vh - deprecated.$s-32); - z-index: deprecated.$z-index-2; - background-color: var(--viewer-controls-background-color); + inset-inline-end: var(--sp-s); + inset-block-start: calc(50vh - #{$sz-32}); + + // TODO: no DS z-index token for 2; it keeps the controls above the + // viewer section. + z-index: 2; transition: transform 400ms ease 300ms; +} - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } +.viewer-go-button { + --button-height: #{$sz-64}; + --button-bg-color: var(--color-background-primary); + --button-border-color: var(--color-background-tertiary); } .viewer-go-next.comment-sidebar { - right: deprecated.$s-280; + inset-inline-end: #{px2rem(280)}; } .viewer-go-prev { - left: deprecated.$s-8; - right: unset; - - svg { - transform: rotate(180deg); - } + inset-inline-start: var(--sp-s); + inset-inline-end: unset; } .viewer-bottom { position: fixed; - bottom: 0; + inset-block-end: 0; display: flex; align-items: flex-start; justify-content: space-between; - width: 100%; - height: deprecated.$s-40; - padding-right: 0 deprecated.$s-8 deprecated.$s-40 deprecated.$s-8; + inline-size: 100%; + block-size: $sz-40; transition: transform 400ms ease 300ms; - z-index: deprecated.$z-index-2; + + // TODO: no DS z-index token for 2; it keeps the controls above the + // viewer section. + z-index: 2; pointer-events: none; } +.reset-button-wrapper { + margin-inline-start: var(--sp-s); +} + .reset-button { - @extend %button-secondary; - @include deprecated.flex-center; + --button-width: #{$sz-28}; + --button-bg-color: var(--color-background-primary); + --button-border-color: var(--color-background-tertiary); - height: deprecated.$s-32; - width: deprecated.$s-28; - margin-left: deprecated.$s-8; - background-color: var(--viewer-controls-background-color); pointer-events: all; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } } .counter { - @include deprecated.flex-center; - @include deprecated.body-small-typography; + @include use-typography("body-small"); - border-radius: deprecated.$br-8; - width: deprecated.$s-64; - height: deprecated.$s-32; - color: var(--viewer-thumbnails-control-foreground-color); - background-color: var(--viewer-controls-background-color); + display: flex; + justify-content: center; + align-items: center; + border-radius: $br-8; + inline-size: $sz-64; + block-size: $sz-32; + color: var(--color-foreground-secondary); + background-color: var(--color-background-primary); } .viewer-wrapper { @@ -169,8 +179,8 @@ .viewer-overlay-background { position: absolute; - top: 0; - left: 0; + inset-block-start: 0; + inset-inline-start: 0; &.visible { background-color: rgb(0 0 0 / 0.2); @@ -185,24 +195,11 @@ clip-path: inset(0 0 0 0); grid-column: 1 / 1; grid-row: 1 / 1; - - .not-fixed { - position: absolute; - } - - .fixed { - position: fixed; - pointer-events: none; - - .frame-children g { - pointer-events: auto; - } - } } /** FULLSCREEN */ [data-fullscreen="true"] .viewer-bottom { - transform: translateY(deprecated.$s-40); + transform: translateY($sz-40); } [data-force-visible="true"] .viewer-bottom { @@ -210,20 +207,20 @@ } [data-fullscreen="true"] .viewer-go-next { - transform: translateX(deprecated.$s-40); + transform: translateX($sz-40); } [data-fullscreen="true"] .viewer-go-prev { - transform: translateX(-#{deprecated.$s-40}); + transform: translateX(-#{$sz-40}); } [data-fullscreen="true"] .viewer-content { - transform: translateY(-#{deprecated.$s-48}); - height: 100vh; + transform: translateY(-#{$sz-48}); + block-size: 100vh; } [data-fullscreen="true"] .viewer-section { - height: 100vh; + block-size: 100vh; } [data-force-visible="true"] .viewer-go-next { diff --git a/frontend/src/app/main/ui/viewer/comments.cljs b/frontend/src/app/main/ui/viewer/comments.cljs index aac174d880..9e56e7f4e8 100644 --- a/frontend/src/app/main/ui/viewer/comments.cljs +++ b/frontend/src/app/main/ui/viewer/comments.cljs @@ -20,7 +20,7 @@ [app.main.store :as st] [app.main.ui.comments :as cmt] [app.main.ui.components.dropdown :refer [dropdown]] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.workspace.comments :as wc] [app.main.ui.workspace.viewport.utils :as utils] [app.util.dom :as dom] @@ -66,11 +66,17 @@ (parse-boolean))] (st/emit! (dcm/update-options {:show-sidebar? (not mode)})))))] - [:div {:class (stl/css :view-options) - :data-testid "viewer-comments-dropdown" - :on-click toggle-dropdown} - [:span {:class (stl/css :dropdown-title)} (tr "labels.comments")] - [:span {:class (stl/css :icon-dropdown)} deprecated-icon/arrow] + [:div {:class (stl/css :view-options-wrapper)} + [:button {:type "button" + :class (stl/css :view-options) + :data-testid "viewer-comments-dropdown" + :aria-expanded (deref show-dropdown?) + :aria-haspopup "menu" + :on-click toggle-dropdown} + [:span {:class (stl/css :dropdown-title)} (tr "labels.comments")] + [:> icon* {:icon-id i/arrow-down + :size "s" + :class (stl/css :icon-dropdown)}]] [:& dropdown {:show @show-dropdown? :on-close hide-dropdown} @@ -83,7 +89,7 @@ :on-click update-mode} [:span {:class (stl/css :label)} (tr "labels.show-all-comments")] (when (or (= :all cmode) (nil? cmode)) - [:span {:class (stl/css :icon)} deprecated-icon/tick])] + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon)}])] [:li {:class (stl/css-case :dropdown-element true @@ -92,8 +98,7 @@ :on-click update-mode} [:span {:class (stl/css :label)} (tr "labels.show-your-comments")] (when (= :yours cmode) - [:span {:class (stl/css :icon)} - deprecated-icon/tick])] + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon)}])] [:li {:class (stl/css-case :dropdown-element true @@ -102,8 +107,7 @@ :on-click update-mode} [:span {:class (stl/css :label)} (tr "labels.show-mentions")] (when (= :mentions cmode) - [:span {:class (stl/css :icon)} - deprecated-icon/tick])] + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon)}])] [:li {:class (stl/css :separator)}] @@ -114,8 +118,7 @@ :on-click update-show} [:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")] (when (= :pending cshow) - [:span {:class (stl/css :icon)} - deprecated-icon/tick])] + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon)}])] [:li {:class (stl/css :separator)}] @@ -126,7 +129,7 @@ :on-click update-options} [:span {:class (stl/css :label)} (tr "labels.show-comments-list")] (when show-sidebar? - [:span {:class (stl/css :icon)} deprecated-icon/tick])]]]])) + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon)}])]]]])) (defn- update-thread-position diff --git a/frontend/src/app/main/ui/viewer/comments.scss b/frontend/src/app/main/ui/viewer/comments.scss index c254f0b9ba..09ed1d98f7 100644 --- a/frontend/src/app/main/ui/viewer/comments.scss +++ b/frontend/src/app/main/ui/viewer/comments.scss @@ -4,91 +4,71 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "ds/_borders.scss" as *; +// Only for the shared menu placeholders, which move to the DS with the +// menus and dropdowns. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; // COMMENT DROPDOWN ON HEADER +.view-options-wrapper { + position: relative; +} + .view-options { - @include deprecated.body-small-typography; + @include use-typography("body-small"); display: flex; align-items: center; - position: relative; - gap: deprecated.$s-4; - height: deprecated.$s-32; - padding: deprecated.$s-8; - border-radius: deprecated.$br-8; - background-color: var(--input-background-color); + border: none; + gap: var(--sp-xs); + block-size: $sz-32; + padding: var(--sp-s); + border-radius: $br-8; + background-color: var(--color-background-tertiary); cursor: pointer; } .dropdown { @extend %menu-dropdown; - right: deprecated.$s-2; - top: calc(deprecated.$s-2 + deprecated.$s-48); - width: deprecated.$s-272; - padding: deprecated.$s-6; + inset-inline-end: px2rem(2); + inset-block-start: px2rem(50); + inline-size: px2rem(272); + padding: px2rem(6); } .dropdown-title { - @include deprecated.body-small-typography; + @include use-typography("body-small"); flex-grow: 1; - color: var(--input-foreground-color-active); + color: var(--color-foreground-primary); } .label { flex-grow: 1; - color: var(--input-foreground-color); + color: var(--color-foreground-secondary); } .icon, .icon-dropdown { - @include deprecated.flex-center; - - height: 100%; - width: deprecated.$s-16; - - svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - } -} - -.icon-dropdown svg { - transform: rotate(90deg); + flex-shrink: 0; + color: var(--color-foreground-secondary); } .dropdown-element { @extend %dropdown-element-base; - .icon { - @include deprecated.flex-center; - - height: 100%; - width: deprecated.$s-16; - - svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - } - } - &:hover .label { - color: var(--input-foreground-color-active); + color: var(--color-foreground-primary); } } .dropdown-element.selected { .label { - color: var(--input-foreground-color-active); - } - - .icon svg { - stroke: var(--input-foreground-color); + color: var(--color-foreground-primary); } } @@ -96,36 +76,41 @@ position: relative; block-size: var(--sp-xs); inline-size: calc(100% + var(--sp-s)); - border-top: $b-1 solid var(--color-background-quaternary); - left: calc(-1 * var(--sp-xs)); - margin-top: var(--sp-s); + border-block-start: $b-1 solid var(--color-background-quaternary); + inset-inline-start: calc(-1 * var(--sp-xs)); + margin-block-start: var(--sp-s); } // FLOATING COMMENT .viewer-comments-container { position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: deprecated.$z-index-1; + inset-block-start: 0; + inset-inline-start: 0; + inline-size: 100%; + block-size: 100%; + + // TODO: no DS z-index token for 1 (floating elements). + z-index: 1; } .threads { position: absolute; - top: 0; - left: 0; + inset-block-start: 0; + inset-inline-start: 0; } // COMMENT SIDEBAR .comments-sidebar { position: absolute; - right: 0; - top: deprecated.$s-44; - width: var(--right-sidebar-width); - height: calc(100vh - deprecated.$s-48); - z-index: deprecated.$z-index-10; - background-color: var(--panel-background-color); + inset-inline-end: 0; + inset-block-start: px2rem(44); + inline-size: var(--right-sidebar-width); + block-size: calc(100vh - #{$sz-48}); + background-color: var(--color-background-primary); + + // TODO: no DS z-index token for 10; it keeps the list above the viewer + // content. + z-index: 10; } .settings-bar-inside { @@ -133,5 +118,5 @@ } .comments-section { - background-color: var(--panel-background-color); + background-color: var(--color-background-primary); } diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index 3d1404373a..a433cc5c7f 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -12,15 +12,21 @@ [app.main.data.shortcuts :as scd] [app.main.data.viewer :as dv] [app.main.data.viewer.shortcuts :as sc] + [app.main.refs :as refs] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] + [app.main.ui.ds.foundations.assets.raw-svg :as raw-svg :refer [raw-svg*]] [app.main.ui.exports.assets :refer [progress-widget]] [app.main.ui.formats :as fmt] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.viewer.comments :refer [comments-menu]] [app.main.ui.viewer.interactions :refer [flows-menu* interactions-menu*]] [app.util.dom :as dom] [app.util.i18n :refer [tr]] + [app.util.keyboard :as kbd] [okulary.core :as l] [rumext.v2 :as mf])) @@ -47,11 +53,11 @@ (let [open* (mf/use-state false) open? (deref open*) - open-dropdown + toggle-dropdown (mf/use-fn (fn [event] (dom/stop-propagation event) - (reset! open* true))) + (swap! open* not))) close-dropdown (mf/use-fn @@ -73,28 +79,31 @@ (dom/stop-propagation event) (on-decrease)))] - [:div {:class (stl/css-case :zoom-widget true - :selected open?) - :on-click open-dropdown - :title (tr "workspace.header.zoom")} - [:span {:class (stl/css :label)} (fmt/format-percent zoom)] + [:div {:class (stl/css :zoom-widget-wrapper)} + [:button {:type "button" + :class (stl/css-case :zoom-widget true + :selected open?) + :on-click toggle-dropdown + :title (tr "workspace.header.zoom") + :aria-expanded open?} + [:span {:class (stl/css :label)} (fmt/format-percent zoom)]] [:& dropdown {:show open? :on-close close-dropdown} [:ul {:class (stl/css :dropdown)} [:li {:class (stl/css :basic-zoom-bar)} [:span {:class (stl/css :zoom-btns)} - [:button {:class (stl/css :zoom-btn) - :on-click on-decrease} - [:span {:class (stl/css :zoom-icon)} - deprecated-icon/remove-icon]] + [:> icon-button* {:variant "ghost" + :aria-label (tr "shortcuts.decrease-zoom") + :on-click on-decrease + :icon i/remove}] [:p {:class (stl/css :zoom-text)} (fmt/format-percent zoom)] - [:button {:class (stl/css :zoom-btn) - :on-click on-increase} - [:span {:class (stl/css :zoom-icon)} - deprecated-icon/add]]] - [:button {:class (stl/css :reset-btn) - :on-click on-zoom-reset} + [:> icon-button* {:variant "ghost" + :aria-label (tr "shortcuts.increase-zoom") + :on-click on-increase + :icon i/add}]] + [:> button* {:variant "ghost" + :on-click on-zoom-reset} (tr "workspace.header.reset-zoom")]] [:li {:class (stl/css :zoom-option) @@ -188,27 +197,30 @@ :on-fullscreen toggle-fullscreen}] (when (:in-team permissions) - [:span {:on-click go-to-workspace - :class (stl/css :edit-btn)} - deprecated-icon/curve]) + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.edit") + :icon i/curve + :on-click go-to-workspace}]) - [:span {:title (tr "viewer.header.fullscreen") - :class (stl/css-case :fullscreen-btn true - :selected fullscreen?) - :on-click toggle-fullscreen} - deprecated-icon/expand] + [:> icon-button* {:variant "ghost" + :aria-label (tr "viewer.header.fullscreen") + :aria-pressed (boolean fullscreen?) + :icon i/expand + :on-click toggle-fullscreen}] (when (:in-team permissions) - [:button {:on-click open-share-dialog - :class (stl/css :share-btn)} + [:> button* {:variant "primary" + :class (stl/css :share-btn) + :on-click open-share-dialog} (tr "labels.share")]) (when-not (:is-logged permissions) - [:span {:on-click open-login-dialog - :class (stl/css :go-log-btn)} (tr "labels.log-or-sign")])])) + [:> button* {:variant "ghost" + :on-click open-login-dialog} + (tr "labels.log-or-sign")])])) (mf/defc header-sitemap - [{:keys [project file page frame toggle-thumbnails] :as props}] + [{:keys [project file page frame toggle-thumbnails shown-thumbnails] :as props}] (let [project-name (:name project) file-name (:name file) page-name (:name page) @@ -236,11 +248,15 @@ :title (tr "viewer.header.sitemap")} [:span {:class (stl/css :project-name)} project-name] [:div {:class (stl/css :sitemap-text)} - [:div {:class (stl/css :breadcrumb) - :on-click open-dropdown} - [:span {:class (stl/css :breadcrumb-text)} - (dm/str file-name " / " page-name)] - [:span {:class (stl/css :icon)} deprecated-icon/arrow] + [:div {:class (stl/css :breadcrumb)} + [:button {:type "button" + :class (stl/css :breadcrumb-btn) + :aria-expanded (deref show-dropdown?) + :aria-haspopup "menu" + :on-click open-dropdown} + [:span {:class (stl/css :breadcrumb-text)} + (dm/str file-name " / " page-name)] + [:> icon* {:icon-id i/arrow-down :size "s" :class (stl/css :icon)}]] [:span "/"] [:& dropdown {:show @show-dropdown? :on-close close-dropdown} @@ -254,22 +270,29 @@ [:span {:class (stl/css :label)} (get-in file [:data :pages-index id :name])] (when (= page-id id) - [:span {:class (stl/css :icon-check)} deprecated-icon/tick])])]]] - [:div {:class (stl/css :current-frame) - :id "current-frame" - :on-click toggle-thumbnails} + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon-check)}])])]]] + [:button {:type "button" + :class (stl/css :current-frame) + :id "current-frame" + :aria-expanded (boolean shown-thumbnails) + :on-click toggle-thumbnails} [:span {:class (stl/css :frame-name)} frame-name] - [:span {:class (stl/css :icon)} deprecated-icon/arrow]]]])) - -(def ^:private penpot-logo-icon - (deprecated-icon/icon-xref :penpot-logo-icon (stl/css :logo-icon))) - + [:> icon* {:icon-id i/arrow-down :size "s" :class (stl/css :icon)}]]]])) (mf/defc header [{:keys [project file page frame zoom section permissions index interactions-mode shown-thumbnails share]}] - (let [go-to-dashboard + (let [in-team? (:in-team permissions) + router (mf/deref refs/router) + dashboard-href (when in-team? + (rt/resolve-uri router :dashboard-recent {:team-id (:team-id project)})) + + go-to-dashboard (mf/use-fn - #(st/emit! (dv/go-to-dashboard))) + (fn [event] + ;; Ctrl/Cmd-click opens the href in a new tab, as any link. + (when-not (kbd/mod? event) + (dom/prevent-default event) + (st/emit! (dv/go-to-dashboard))))) go-to-inspect (mf/use-fn @@ -309,46 +332,49 @@ :fullscreen (mf/deref fullscreen-ref)) :on-click close-thumbnails} [:div {:class (stl/css :nav-zone)} - ;; If the user doesn't have permission we disable the link + ;; Without team permission the logo is not a link: no href, no click. [:a {:class (stl/css :home-link) - :on-click go-to-dashboard + :href dashboard-href + :on-click (when in-team? go-to-dashboard) :data-testid "penpot-logo-link" - :style {:cursor (when-not (:in-team permissions) "auto") - :pointer-events (when-not (:in-team permissions) "none")}} - penpot-logo-icon] + :style {:cursor (when-not in-team? "auto") + :pointer-events (when-not in-team? "none")}} + [:> raw-svg* {:id raw-svg/penpot-logo-icon + :class (stl/css :logo-icon)}]] [:& header-sitemap {:project project :file file :page page :frame frame :toggle-thumbnails toggle-thumbnails + :shown-thumbnails shown-thumbnails :index index}]] [:div {:class (stl/css :mode-zone)} - [:button {:on-click navigate - :data-value "interactions" - :class (stl/css-case :mode-zone-btn true - :selected (= section :interactions)) - :title (tr "viewer.header.interactions-section" (sc/get-tooltip :open-interactions))} - deprecated-icon/play] + [:> icon-button* {:variant "ghost" + :on-click navigate + :data-value "interactions" + :aria-pressed (= section :interactions) + :aria-label (tr "viewer.header.interactions-section" (sc/get-tooltip :open-interactions)) + :icon i/play}] (when (or (:in-team permissions) (= (:who-comment permissions) "all")) - [:button {:on-click navigate - :data-value "comments" - :class (stl/css-case :mode-zone-btn true - :selected (= section :comments)) - :title (tr "viewer.header.comments-section" (sc/get-tooltip :open-comments))} - deprecated-icon/comments]) + [:> icon-button* {:variant "ghost" + :on-click navigate + :data-value "comments" + :aria-pressed (= section :comments) + :aria-label (tr "viewer.header.comments-section" (sc/get-tooltip :open-comments)) + :icon i/comments}]) (when (or (:in-team permissions) (and (= (:type permissions) :share-link) (= (:who-inspect permissions) "all"))) - [:button {:on-click go-to-inspect - :class (stl/css-case :mode-zone-btn true - :selected (= section :inspect)) - :title (tr "viewer.header.inspect-section" (sc/get-tooltip :open-inspect))} - deprecated-icon/code])] + [:> icon-button* {:variant "ghost" + :on-click go-to-inspect + :aria-pressed (= section :inspect) + :aria-label (tr "viewer.header.inspect-section" (sc/get-tooltip :open-inspect)) + :icon i/code}])] [:& header-options {:section section :permissions permissions diff --git a/frontend/src/app/main/ui/viewer/header.scss b/frontend/src/app/main/ui/viewer/header.scss index ffe476cbda..aee1220748 100644 --- a/frontend/src/app/main/ui/viewer/header.scss +++ b/frontend/src/app/main/ui/viewer/header.scss @@ -4,22 +4,29 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for the shared menu placeholders, which move to the DS with the +// menus and dropdowns. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/mixins.scss" as *; +@use "ds/typography.scss" as *; .viewer-header { position: absolute; - top: 0; + inset-block-start: 0; grid-column: 1 / span 1; grid-row: 1 / span 1; display: grid; - grid-template-columns: 1fr deprecated.$s-92 1fr; + grid-template-columns: 1fr auto 1fr; justify-content: space-between; align-items: center; - height: deprecated.$s-48; - width: 100vw; - padding: deprecated.$s-8 deprecated.$s-12; + block-size: $sz-48; + inline-size: 100vw; + padding: var(--sp-s) var(--sp-m); transition: transform 400ms ease 300ms; - background-color: var(--panel-background-color); + background-color: var(--color-background-primary); } // FILE NAVIGATION @@ -28,8 +35,8 @@ display: flex; justify-content: flex-start; flex-basis: min-content; - width: 100%; - gap: deprecated.$s-12; + inline-size: 100%; + gap: var(--sp-m); } .home-link { @@ -39,143 +46,137 @@ } .logo-icon { - width: deprecated.$s-28; - height: deprecated.$s-28; - fill: var(--icon-foreground-hover); + inline-size: $sz-28; + block-size: $sz-28; + fill: var(--color-foreground-primary); } .sitemap-zone { - @include deprecated.flex-column; - + display: flex; + flex-direction: column; + gap: var(--sp-xs); position: relative; - width: 100%; + inline-size: 100%; } .project-name { - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); - color: var(--title-foreground-color); + color: var(--color-foreground-secondary); } .sitemap-text { - @include deprecated.flex-row; + display: flex; + align-items: center; + gap: var(--sp-xs); } .breadcrumb { - @include deprecated.body-small-typography; - @include deprecated.flex-row; + @include use-typography("body-small"); - color: var(--title-foreground-color); + display: flex; + align-items: center; + gap: var(--sp-xs); + color: var(--color-foreground-secondary); +} + +// Text and arrow that open the page menu: not a DS button. +.breadcrumb-btn { + @include use-typography("body-small"); + + display: flex; + align-items: center; + gap: var(--sp-xs); + padding: 0; + border: none; + background: none; + color: inherit; cursor: pointer; } .breadcrumb-text { - @include deprecated.text-ellipsis; + @include text-ellipsis; - max-width: 12vw; // This is a fallback - max-width: 12cqw; // This is a unit refered to container + max-inline-size: 12vw; // This is a fallback + max-inline-size: 12cqw; // This is a unit refered to container } -.icon { - @include deprecated.flex-center; - - height: deprecated.$s-16; - width: deprecated.$s-16; - - svg { - @extend %button-icon-small; - - transform: rotate(90deg); - stroke: var(--icon-foreground); - } -} - -.dropdown { - position: absolute; +.icon, +.icon-check { + flex-shrink: 0; + color: var(--color-foreground-secondary); } .dropdown-sitemap { @extend %menu-dropdown; - left: 0; - top: calc(deprecated.$s-2 + deprecated.$s-48); - width: deprecated.$s-272; - padding: deprecated.$s-6; + inset-inline-start: 0; + inset-block-start: px2rem(50); + inline-size: px2rem(272); + padding: px2rem(6); } .dropdown-element { @extend %dropdown-element-base; - .icon-check { - @include deprecated.flex-center; - - height: 100%; - width: deprecated.$s-16; - - svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - } + &:hover .label { + color: var(--color-foreground-primary); } - &:hover .label { - color: var(--input-foreground-color-active); + // The current page. + &.selected { + outline: none; + border-color: var(--color-background-quaternary); + background-color: var(--color-background-quaternary); + color: var(--color-accent-primary); } } +// Board name that toggles the thumbnails: not a DS button. .current-frame { - @include deprecated.body-small-typography; - @include deprecated.flex-row; + @include use-typography("body-small"); + display: flex; + align-items: center; + gap: var(--sp-xs); flex-grow: 1; - color: var(--title-foreground-color-hover); + padding: 0; + border: none; + background: none; + color: var(--color-foreground-primary); cursor: pointer; - .icon svg { - stroke: var(--title-foreground-color-hover); + .icon { + color: var(--color-foreground-primary); } } .frame-name { - @include deprecated.text-ellipsis; + @include text-ellipsis; - max-width: 17vw; // This is a fallback - max-width: 17cqw; // This is a unit refered to container + max-inline-size: 17vw; // This is a fallback + max-inline-size: 17cqw; // This is a unit refered to container } // SECTION BUTTONS .mode-zone { - @include deprecated.flex-row; - - height: 100%; -} - -.mode-zone-btn { - @extend %button-tertiary; - @include deprecated.flex-center; - - height: deprecated.$s-32; - width: deprecated.$s-28; - padding: 0; - - svg { - @extend %button-icon; - } -} - -.selected { - @extend %button-icon-selected; + display: flex; + align-items: center; + gap: var(--sp-xs); + block-size: 100%; } // OPTION AREA .options-zone { - @include deprecated.flex-row; - + display: flex; + align-items: center; position: relative; justify-content: flex-end; - gap: deprecated.$s-8; - z-index: deprecated.$z-index-10; + gap: var(--sp-s); + + // TODO: no DS z-index token for 10; it keeps the menus above the + // viewer content. + z-index: 10; } .view-options { @@ -185,92 +186,54 @@ cursor: pointer; } -.fullscreen-btn { - @extend %button-tertiary; - @include deprecated.flex-center; - - height: deprecated.$s-32; - width: deprecated.$s-28; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } -} - .share-btn { - @extend %button-primary; - - height: deprecated.$s-32; - min-width: deprecated.$s-72; - margin-left: deprecated.$s-4; -} - -.edit-btn { - @extend %button-tertiary; - @include deprecated.flex-center; - - height: deprecated.$s-32; - width: deprecated.$s-28; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } -} - -.go-log-btn { - @extend %button-tertiary; - @include deprecated.body-small-typography; - - height: deprecated.$s-32; - padding: 0 deprecated.$s-8; - border-radius: deprecated.$br-8; - color: var(--button-tertiary-foreground-color-rest); + min-inline-size: px2rem(72); + margin-inline-start: var(--sp-xs); } // ZOOM WIDGET -.zoom-widget { - @include deprecated.button-style; - @include deprecated.flex-center; +.zoom-widget-wrapper { + display: flex; +} - height: deprecated.$s-28; - min-width: deprecated.$s-64; - border-radius: deprecated.$br-8; +.zoom-widget { + --zoom-widget-foreground-color: var(--color-foreground-secondary); + + display: flex; + align-items: center; + justify-content: center; + block-size: $sz-28; + min-inline-size: $sz-64; + padding: 0; + border: none; + border-radius: $br-8; + background: none; + cursor: pointer; .label { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--button-tertiary-foreground-color-rest); - } - - &:hover { - .label { - color: var(--button-tertiary-foreground-color-focus); - } + color: var(--zoom-widget-foreground-color); } + &:hover, &.selected { - .label { - color: var(--button-tertiary-foreground-color-focus); - } + --zoom-widget-foreground-color: var(--color-foreground-primary); } } .dropdown { @extend %menu-dropdown; - right: deprecated.$s-2; - top: calc(deprecated.$s-2 + deprecated.$s-48); - width: deprecated.$s-272; + inset-inline-end: px2rem(2); + inset-block-start: px2rem(50); + inline-size: px2rem(272); } .basic-zoom-bar { display: flex; justify-content: space-between; - padding: deprecated.$s-6; + padding: px2rem(6); cursor: auto; } @@ -278,49 +241,15 @@ display: flex; } -.zoom-btn { - @extend %button-tertiary; - - height: deprecated.$s-28; - width: deprecated.$s-28; - border-radius: deprecated.$br-8; - - .zoom-icon { - @include deprecated.flex-center; - - width: deprecated.$s-24; - height: deprecated.$s-32; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } - } - - &:hover { - .zoom-icon svg { - stroke: var(--button-tertiary-foreground-color-hover); - } - } -} - .zoom-text { - @include deprecated.flex-center; - - height: 100%; - min-width: deprecated.$s-64; + display: flex; + justify-content: center; + align-items: center; + block-size: 100%; + min-inline-size: $sz-64; padding: 0; - margin: 0 deprecated.$s-2; - color: var(--modal-title-foreground-color); -} - -.reset-btn { - @extend %button-tertiary; - - color: var(--button-tertiary-foreground-color-hover); - height: deprecated.$s-28; - border-radius: deprecated.$br-8; + margin: 0 px2rem(2); + color: var(--color-foreground-primary); } .zoom-option { @@ -335,11 +264,11 @@ } &:hover { - color: var(--menu-foreground-color-hover); + color: var(--color-foreground-primary); .shortcuts { .shortcut-key { - color: var(--menu-foreground-color-hover); + color: var(--color-foreground-primary); } } } @@ -349,14 +278,14 @@ [data-fullscreen="true"] .viewer-header::after { content: " "; position: absolute; - width: 100%; - height: deprecated.$s-48; - left: 0; - top: deprecated.$s-48; + inline-size: 100%; + block-size: $sz-48; + inset-inline-start: 0; + inset-block-start: $sz-48; } [data-fullscreen="true"] .viewer-header { - transform: translateY(-#{deprecated.$s-48}); + transform: translateY(-#{$sz-48}); } [data-force-visible="true"] .viewer-header, diff --git a/frontend/src/app/main/ui/viewer/inspect.cljs b/frontend/src/app/main/ui/viewer/inspect.cljs index 3ffed2dc99..239c121080 100644 --- a/frontend/src/app/main/ui/viewer/inspect.cljs +++ b/frontend/src/app/main/ui/viewer/inspect.cljs @@ -13,7 +13,7 @@ [app.main.data.viewer :as dv] [app.main.store :as st] [app.main.ui.hooks.resize :refer [use-resize-hook]] - [app.main.ui.inspect.left-sidebar :refer [left-sidebar]] + [app.main.ui.inspect.left-sidebar :refer [left-sidebar*]] [app.main.ui.inspect.render :refer [render-frame-svg]] [app.main.ui.inspect.right-sidebar :refer [right-sidebar*]] [app.util.dom :as dom] @@ -92,9 +92,9 @@ (st/emit! (dv/select-shape (:id frame))))) [:* - [:& left-sidebar {:frame frame - :local local - :page page}] + [:> left-sidebar* {:frame frame + :local local + :page page}] [:div#inspect-svg-wrapper {:class (stl/css :inspect-svg-wrapper) :data-value (pr-str (:id frame)) :on-click handle-select-frame} diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs index bd96266d37..0aff863b5d 100644 --- a/frontend/src/app/main/ui/viewer/interactions.cljs +++ b/frontend/src/app/main/ui/viewer/interactions.cljs @@ -18,8 +18,8 @@ [app.main.features :as features] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.hooks :as h] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.viewer.shapes :as shapes] [app.main.ui.viewer.viewport-common :as vpc] [app.main.ui.viewer.viewport-wasm :as viewport.wasm] @@ -218,11 +218,17 @@ (st/emit! (dv/go-to-frame (:starting-frame flow))))))] (when flows - [:div {:on-click toggle-dropdown - :class (stl/css :view-options)} - [:span {:class (stl/css :icon)} deprecated-icon/play] - [:span {:class (stl/css :dropdown-title)} (:name current-flow)] - [:span {:class (stl/css :icon-dropdown)} deprecated-icon/arrow] + [:div {:class (stl/css :view-options-wrapper)} + [:button {:type "button" + :class (stl/css :view-options) + :aria-expanded show-dropdown? + :aria-haspopup "menu" + :on-click toggle-dropdown} + [:> icon* {:icon-id i/play :size "s" :class (stl/css :icon)}] + [:span {:class (stl/css :dropdown-title)} (:name current-flow)] + [:> icon* {:icon-id i/arrow-down + :size "s" + :class (stl/css :icon-dropdown)}]] [:& dropdown {:show show-dropdown? :on-close hide-dropdown} [:ul {:class (stl/css :dropdown)} @@ -236,7 +242,7 @@ :on-click select-flow} [:span {:class (stl/css :label)} (:name flow)] (when (= flow-id (:id current-flow)) - [:span {:class (stl/css :icon)} deprecated-icon/tick])])]]]))) + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon)}])])]]]))) (mf/defc interactions-menu* [{:keys [interactions-mode]}] @@ -253,10 +259,16 @@ (dom/stop-propagation event) (st/emit! (dv/set-interactions-mode mode)))))] - [:div {:on-click toggle-dropdown - :class (stl/css :view-options)} - [:span {:class (stl/css :dropdown-title)} (tr "viewer.header.interactions")] - [:span {:class (stl/css :icon-dropdown)} deprecated-icon/arrow] + [:div {:class (stl/css :view-options-wrapper)} + [:button {:type "button" + :class (stl/css :view-options) + :aria-expanded (deref show-dropdown?) + :aria-haspopup "menu" + :on-click toggle-dropdown} + [:span {:class (stl/css :dropdown-title)} (tr "viewer.header.interactions")] + [:> icon* {:icon-id i/arrow-down + :size "s" + :class (stl/css :icon-dropdown)}]] [:& dropdown {:show @show-dropdown? :on-close hide-dropdown} [:ul {:class (stl/css :dropdown)} @@ -267,7 +279,7 @@ [:span {:class (stl/css :label)} (tr "viewer.header.dont-show-interactions")] (when (= interactions-mode :hide) - [:span {:class (stl/css :icon)} deprecated-icon/tick])] + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon)}])] [:li {:class (stl/css-case :dropdown-element true :selected (= interactions-mode :show)) @@ -275,7 +287,7 @@ :data-mode "show"} [:span {:class (stl/css :label)} (tr "viewer.header.show-interactions")] (when (= interactions-mode :show) - [:span {:class (stl/css :icon)} deprecated-icon/tick])] + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon)}])] @@ -286,7 +298,7 @@ [:span {:class (stl/css :label)} (tr "viewer.header.show-interactions-on-click")] (when (= interactions-mode :show-on-click) - [:span {:class (stl/css :icon)} deprecated-icon/tick])]]]])) + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :icon)}])]]]])) (defn animate-go-to-frame [animation current-viewport orig-viewport current-size orig-size wrapper-size] diff --git a/frontend/src/app/main/ui/viewer/interactions.scss b/frontend/src/app/main/ui/viewer/interactions.scss index b17ac08b84..669e02940f 100644 --- a/frontend/src/app/main/ui/viewer/interactions.scss +++ b/frontend/src/app/main/ui/viewer/interactions.scss @@ -4,94 +4,75 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for the shared menu placeholders, which move to the DS with the +// menus and dropdowns. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; + +.view-options-wrapper { + position: relative; +} .view-options { - @include deprecated.body-small-typography; + @include use-typography("body-small"); display: flex; align-items: center; - position: relative; - gap: deprecated.$s-4; - height: deprecated.$s-32; - border-radius: deprecated.$br-8; - background-color: var(--input-background-color); - padding: deprecated.$s-8; + border: none; + gap: var(--sp-xs); + block-size: $sz-32; + border-radius: $br-8; + background-color: var(--color-background-tertiary); + padding: var(--sp-s); cursor: pointer; } .dropdown-title { - @include deprecated.body-small-typography; + @include use-typography("body-small"); flex-grow: 1; - color: var(--input-foreground-color-active); + color: var(--color-foreground-primary); } .label { flex-grow: 1; - color: var(--input-foreground-color); + color: var(--color-foreground-secondary); } .dropdown { @extend %menu-dropdown; - right: deprecated.$s-2; - top: calc(deprecated.$s-2 + deprecated.$s-48); - width: deprecated.$s-272; - padding: deprecated.$s-6; - max-height: calc(100vh - 3 * (deprecated.$s-2 + deprecated.$s-48)); + inset-inline-end: px2rem(2); + inset-block-start: px2rem(50); + inline-size: px2rem(272); + padding: px2rem(6); + max-block-size: calc(100vh - #{px2rem(150)}); overflow: auto; } .dropdown-element { @extend %dropdown-element-base; - min-height: deprecated.$s-32; - - .icon { - @include deprecated.flex-center; - - height: 100%; - width: deprecated.$s-16; - - svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - } - } + min-block-size: $sz-32; &:hover .label { - color: var(--input-foreground-color-active); + color: var(--color-foreground-primary); } } .dropdown-element.selected { .label { - color: var(--input-foreground-color-active); - } - - .icon svg { - stroke: var(--input-foreground-color); + color: var(--color-foreground-primary); } } .icon, .icon-dropdown { - @include deprecated.flex-center; - - height: 100%; - width: deprecated.$s-16; - - svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - } -} - -.icon-dropdown svg { - transform: rotate(90deg); + flex-shrink: 0; + color: var(--color-foreground-secondary); } // breakpoint 1013px diff --git a/frontend/src/app/main/ui/viewer/login.cljs b/frontend/src/app/main/ui/viewer/login.cljs index 6d643f2e46..fca78a1aa4 100644 --- a/frontend/src/app/main/ui/viewer/login.cljs +++ b/frontend/src/app/main/ui/viewer/login.cljs @@ -5,141 +5,13 @@ ;; Copyright (c) KALEIDOS SUBSIDIARY SL (ns app.main.ui.viewer.login - (:require-macros [app.main.style :as stl]) (:require - [app.common.logging :as log] - [app.config :as cf] - [app.main.data.auth :as da] [app.main.data.modal :as modal] - [app.main.store :as st] - [app.main.ui.auth.login :refer [login-dialog*]] - [app.main.ui.auth.recovery-request :refer [recovery-request-page*]] - [app.main.ui.auth.register :refer [register-methods* register-success-page* - register-validate-form* terms-service-privacy-policy*]] - [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] - [app.main.ui.ds.foundations.assets.icon :as i] - [app.util.dom :as dom] - [app.util.i18n :as i18n :refer [tr]] + [app.main.ui.auth.login-modal :refer [login-register-modal*]] [rumext.v2 :as mf])) -(log/set-level! :warn) - (mf/defc login-register-modal {::mf/register modal/components ::mf/register-as :login-register} [_] - (let [user-email (mf/use-state "") - register-token (mf/use-state "") - - current-section* (mf/use-state :login) - current-section (deref current-section*) - - set-current-section - (mf/use-fn - #(reset! current-section* %)) - - set-section - (mf/use-fn - (fn [event] - (let [section (-> (dom/get-current-target event) - (dom/get-data "value") - (keyword))] - (set-current-section section)))) - - go-back-to-login - (mf/use-fn - #(set-current-section :login)) - - main-section (or - (= current-section :login) - (= current-section :register) - (= current-section :register-validate)) - close - (fn [event] - (dom/prevent-default event) - (st/emit! (modal/hide))) - - success-email-sent - (fn [email] - (reset! user-email email) - (set-current-section :email-sent)) - - success-login - (fn [] - (.reload js/window.location true)) - - success-register - (fn [data] - (cond - (:invitation-token data) - (st/emit! (da/login-from-register - {:invitation-token (:invitation-token data)})) - - (:is-active data) - (st/emit! (modal/hide)) - - :else - (do - (reset! user-email (:email data)) - (set-current-section :email-sent))))] - - [:div {:class (stl/css :modal-overlay)} - [:div {:class (stl/css :modal-container)} - [:div {:class (stl/css :modal-header)} - [:h2 {:class (stl/css :modal-header-title)} (tr "labels.continue-with-penpot")] - [:> icon-button* {:variant "ghost" - :class (stl/css :modal-close) - :aria-label (tr "labels.close") - :on-click close - :icon i/close}]] - - [:div {:class (stl/css :modal-content)} - (case current-section - :login - [:div {:class (stl/css :login-form)} - [:> login-dialog* {:on-success-callback success-login - :origin :viewer}] - [:div {:class (stl/css :login-links)} - [:div - [:a {:on-click set-section - :data-value "recovery-request"} - (tr "auth.forgot-password")]] - (when (contains? cf/flags :registration) - [:div - [:span - (tr "auth.register") " "] - [:a {:on-click set-section - :data-value "register"} - (tr "auth.register-submit")]])]] - - :register - [:div {:class (stl/css :login-form)} - [:> register-methods* {:on-success-callback success-register}] - [:div {:class (stl/css :login-links)} - [:div - [:span (tr "auth.already-have-account") " "] - [:a {:on-click set-section - :data-value "login"} - (tr "auth.login-here")]]]] - - :register-validate - [:div {:class (stl/css :login-form)} - [:> register-validate-form* {:params {:token @register-token} - :on-success-callback success-email-sent}] - [:div {:class (stl/css :login-links)} - [:div - [:a {:on-click set-section - :data-value "register"} - (tr "labels.go-back")]]]] - - :recovery-request - [:> recovery-request-page* {:go-back-callback go-back-to-login - :on-success-callback success-email-sent}] - - :email-sent - [:div {:class (stl/css :login-form)} - [:> register-success-page* {:params {:email @user-email}}]]) - - (when main-section - [:div {:class (stl/css :login-links)} - [:> terms-service-privacy-policy*]])]]])) + [:> login-register-modal* {:on-close modal/hide!}]) diff --git a/frontend/src/app/main/ui/viewer/share_link.cljs b/frontend/src/app/main/ui/viewer/share_link.cljs index f0a4c3de22..6d43e1c60b 100644 --- a/frontend/src/app/main/ui/viewer/share_link.cljs +++ b/frontend/src/app/main/ui/viewer/share_link.cljs @@ -19,6 +19,9 @@ [app.main.router :as rt] [app.main.store :as st] [app.main.ui.components.select :refer [select]] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.icons :as deprecated-icon] [app.util.clipboard :as clipboard] [app.util.dom :as dom] @@ -169,10 +172,11 @@ [:div {:class (stl/css :share-link-header)} [:h2 {:class (stl/css :share-link-title)} (tr "common.share-link.title")] - [:button {:class (stl/css :modal-close-button) - :on-click on-close - :title (tr "labels.close")} - deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-button) + :aria-label (tr "labels.close") + :icon i/close + :on-click on-close}]] [:div {:class (stl/css :modal-content)} [:div {:class (stl/css :share-link-section)} (when (and (not confirm?) (some? current-link)) @@ -183,10 +187,10 @@ :placeholder (tr "common.share-link.placeholder") :read-only true}] - [:button {:class (stl/css :copy-button) - :title (tr "viewer.header.share.copy-link") - :on-click copy-link} - deprecated-icon/clipboard]]) + [:> icon-button* {:variant "secondary" + :aria-label (tr "viewer.header.share.copy-link") + :icon i/clipboard + :on-click copy-link}]]) [:div {:class (stl/css :hint-wrapper)} (when (not ^boolean confirm?) @@ -197,37 +201,38 @@ [:div {:class (stl/css :description)} (tr "common.share-link.confirm-deletion-link-description")] [:div {:class (stl/css :actions)} - [:input {:type "button" - :class (stl/css :button-cancel) - :on-click #(reset! confirm* false) - :value (tr "labels.cancel")}] - [:input {:type "button" - :class (stl/css :button-danger) - :on-click delete-link - :value (tr "common.share-link.destroy-link")}]]] + [:> button* {:variant "secondary" + :type "button" + :on-click #(reset! confirm* false)} + (tr "labels.cancel")] + [:> button* {:variant "destructive" + :type "button" + :on-click delete-link} + (tr "common.share-link.destroy-link")]]] (some? current-link) - [:input - {:type "button" - :class (stl/css :button-danger) - :on-click try-delete-link - :value (tr "common.share-link.destroy-link")}] + [:> button* {:variant "destructive" + :type "button" + :on-click try-delete-link} + (tr "common.share-link.destroy-link")] :else - [:input - {:type "button" - :class (stl/css :button-active) - :on-click create-link - :value (tr "common.share-link.get-link")}])]] + [:> button* {:variant "primary" + :type "button" + :on-click create-link} + (tr "common.share-link.get-link")])]] (when (not ^boolean confirm?) [:div {:class (stl/css :permissions-section)} [:button {:class (stl/css :manage-permissions) + :type "button" + :aria-expanded perms-visible? :on-click toggle-perms-visibility} [:span {:class (stl/css-case :icon true :rotated perms-visible?)} - deprecated-icon/arrow] + [:> icon* {:icon-id i/arrow + :class (stl/css :arrow-icon)}]] (tr "common.share-link.manage-ops")] (when ^boolean perms-visible? @@ -278,7 +283,7 @@ [:ul {:class (stl/css :pages-selection)} (for [{:keys [id name]} pages] - [:li {:class (stl/css :checkbox-wrapper) + [:li {:class (stl/css :checkbox-wrapper :page-row) :key (dm/str id)} [:label {:for (dm/str "page-" id)} [:span {:class (stl/css-case :global/checked (contains? selected id))} diff --git a/frontend/src/app/main/ui/viewer/share_link.scss b/frontend/src/app/main/ui/viewer/share_link.scss index 43e2ba8910..7cb4e94245 100644 --- a/frontend/src/app/main/ui/viewer/share_link.scss +++ b/frontend/src/app/main/ui/viewer/share_link.scss @@ -4,149 +4,139 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; + +// refactor/ stays for the modal placeholder (%modal-container-base), the +// link input (%input-element) and the page checkboxes (%input-checkbox, +// %button-icon-small for their tick), which move with their own groups. @use "refactor/common-refactor.scss" as deprecated; .share-modal { display: block; position: absolute; - top: deprecated.$s-52; - right: deprecated.$s-12; - left: calc(100vw - deprecated.$s-512); - z-index: deprecated.$z-index-modal; + inset-block-start: px2rem(52); + inset-inline: calc(100vw - #{$sz-512}) var(--sp-m); + z-index: var(--z-index-set); } .share-link-dialog { @extend %modal-container-base; - min-height: unset; + min-block-size: unset; } .share-link-header { - margin-bottom: deprecated.$s-24; + margin-block-end: var(--sp-xxl); } .share-link-title { - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } +// Tooltip wrapper of the close button: it places it in the corner. .modal-close-button { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { - @include deprecated.body-small-typography; - @include deprecated.flex-column; + @include use-typography("body-small"); - gap: deprecated.$s-24; + display: flex; + flex-direction: column; + gap: var(--sp-xxl); } .share-link-section { - @include deprecated.flex-column; - - gap: deprecated.$s-8; + display: flex; + flex-direction: column; + gap: var(--sp-s); } .hint-wrapper { - @include deprecated.flex-row; + display: flex; + align-items: center; + gap: var(--sp-xs); } .hint { flex-grow: 1; - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); } .custon-input-wrapper { - @include deprecated.flex-row; - - border-radius: deprecated.$br-8; - height: deprecated.$s-32; - width: 100%; - background-color: var(--input-background-color); + display: flex; + align-items: center; + gap: var(--sp-xs); + border-radius: $br-8; + block-size: $sz-32; + inline-size: 100%; + background-color: var(--color-background-tertiary); } .input-text { @extend %input-element; - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--input-foreground-color-active); - padding-left: deprecated.$s-8; + color: var(--color-foreground-primary); + padding-inline-start: var(--sp-s); margin: 0; flex-grow: 1; &:focus { outline: none; - border: deprecated.$s-1 solid var(--input-border-color-active); - } -} - -.copy-button { - @extend %button-secondary; - @include deprecated.flex-row; - - gap: deprecated.$s-8; - height: deprecated.$s-32; - width: deprecated.$s-28; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground-hover); + border: $b-1 solid var(--color-accent-primary); } } .description { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--modal-text-foreground-color); - margin-bottom: deprecated.$s-24; + color: var(--color-foreground-secondary); + margin-block-end: var(--sp-xxl); } .actions { - @include deprecated.flex-row; - + display: flex; + align-items: center; + gap: var(--sp-xs); justify-content: flex-end; } -.button-active { - @extend %modal-accept-btn; -} - -.button-cancel { - @extend %modal-cancel-btn; -} - -.button-danger { - @extend %modal-danger-btn; -} - .permissions-section { - @include deprecated.flex-column; - - gap: deprecated.$s-8; + display: flex; + flex-direction: column; + gap: var(--sp-s); } .manage-permissions { - @include deprecated.button-style; - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); - color: var(--menu-foreground-color-rest); - height: deprecated.$s-32; + border: none; + background: none; + cursor: pointer; + color: var(--color-foreground-secondary); + block-size: $sz-32; display: flex; align-items: center; padding: 0; } .icon { - @include deprecated.flex-center; + display: flex; + justify-content: center; + align-items: center; + margin-inline-end: px2rem(6); - margin-right: deprecated.$s-6; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); + .arrow-icon { + flex-shrink: 0; + color: var(--color-foreground-secondary); } &.rotated { @@ -158,74 +148,76 @@ .access-mode, .inspect-mode { display: flex; - width: 100%; + inline-size: 100%; } .view-mode { - max-height: deprecated.$s-216; + max-block-size: px2rem(216); overflow: hidden auto; scrollbar-gutter: stable; } .subtitle { - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); display: flex; align-items: center; justify-content: flex-start; - width: deprecated.$s-136; - height: deprecated.$s-32; + inline-size: px2rem(136); + block-size: $sz-32; } .items { flex-grow: 1; - color: var(--input-foreground-color-active); + color: var(--color-foreground-primary); } .select-all-row { - @include deprecated.flex-row; - + display: flex; + align-items: center; + gap: var(--sp-xs); justify-content: space-between; - height: deprecated.$s-32; - border-bottom: deprecated.$s-1 solid var(--input-border-color-disabled); + block-size: $sz-32; + border-block-end: $b-1 solid var(--color-background-quaternary); } .select-all-label { - color: var(--input-foreground-color-active); + color: var(--color-foreground-primary); } .pages-selection { margin: 0; +} - li { - border-bottom: deprecated.$s-1 solid var(--input-border-color-disabled); - } +.page-row { + border-block-end: $b-1 solid var(--color-background-quaternary); - li:last-child { - border-bottom: none; + &:last-child { + border-block-end: none; } } .count-pages, .current-tag { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--input-foreground-color); + color: var(--color-foreground-secondary); } .checkbox-wrapper { @extend %input-checkbox; - height: deprecated.$s-32; + block-size: $sz-32; padding: 0; span.checked { - background-color: var(--input-checkbox-background-color-active); - border: deprecated.$s-1 solid var(--input-checkbox-background-color-active); + background-color: var(--color-accent-primary); + border: $b-1 solid var(--color-accent-primary); + // The legacy tick icon has no class; it moves with the checkboxes. svg { @extend %button-icon-small; - stroke: var(--input-checkbox-foreground-color-active); + stroke: var(--color-background-primary); } } } diff --git a/frontend/src/app/main/ui/viewer/thumbnails.cljs b/frontend/src/app/main/ui/viewer/thumbnails.cljs index d7af5b5d41..d23c5009b8 100644 --- a/frontend/src/app/main/ui/viewer/thumbnails.cljs +++ b/frontend/src/app/main/ui/viewer/thumbnails.cljs @@ -14,7 +14,8 @@ [app.main.data.viewer :as dv] [app.main.render :as render] [app.main.store :as st] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.object :as obj] @@ -63,10 +64,18 @@ [:div {:class (stl/css :thumbnails-list-expanded)} children]] [:div {:class (stl/css :thumbnails-content)} - [:button {:class (stl/css :left-scroll-handler) - :on-click on-left-arrow-click} deprecated-icon/arrow] - [:button {:class (stl/css :right-scroll-handler) - :on-click on-right-arrow-click} deprecated-icon/arrow] + [:> icon-button* {:variant "ghost" + :class (stl/css :scroll-handler) + :tooltip-class (stl/css :left-scroll-handler) + :aria-label (tr "viewer.thumbnails.scroll-left") + :icon i/arrow-left + :on-click on-left-arrow-click}] + [:> icon-button* {:variant "ghost" + :class (stl/css :scroll-handler) + :tooltip-class (stl/css :right-scroll-handler) + :aria-label (tr "viewer.thumbnails.scroll-right") + :icon i/arrow-right + :on-click on-right-arrow-click}] [:div {:class (stl/css :thumbnails-list) :ref container @@ -76,15 +85,22 @@ children]]]))) (mf/defc thumbnails-summary* - [{:keys [on-toggle-expand on-close total]}] + [{:keys [on-toggle-expand on-close total is-expanded]}] [:div {:class (stl/css :thumbnails-summary)} [:span {:class (stl/css :counter)} (tr "labels.num-of-frames" (i18n/c total))] [:span {:class (stl/css :actions)} - [:button {:class (stl/css :expand-btn) - :on-click on-toggle-expand} deprecated-icon/arrow] - [:button {:class (stl/css :close-btn) - :on-click on-close} deprecated-icon/close]]]) + [:> icon-button* {:variant "ghost" + :aria-label (if is-expanded + (tr "labels.collapse") + (tr "labels.expand")) + :aria-expanded (boolean is-expanded) + :icon (if is-expanded i/arrow-up i/arrow-down) + :on-click on-toggle-expand}] + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.close") + :icon i/close + :on-click on-close}]]]) (mf/defc thumbnail-item* {::mf/wrap [mf/memo @@ -137,6 +153,7 @@ [:> thumbnails-summary* {:on-toggle-expand toggle-expand :on-close on-close + :is-expanded expanded? :total (count frames)}] [:> thumbnails-content* {:is-expanded expanded? :total (count frames)} diff --git a/frontend/src/app/main/ui/viewer/thumbnails.scss b/frontend/src/app/main/ui/viewer/thumbnails.scss index 63dc586a67..fdea0a3443 100644 --- a/frontend/src/app/main/ui/viewer/thumbnails.scss +++ b/frontend/src/app/main/ui/viewer/thumbnails.scss @@ -4,65 +4,52 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/mixins.scss" as *; +@use "ds/typography.scss" as *; .viewer-thumbnails { - background-color: var(--viewer-background-color); + background-color: var(--color-background-secondary); grid-row: 1 / span 1; grid-column: 1 / span 1; overflow: hidden; display: flex; flex-direction: column; - z-index: deprecated.$z-index-10; + + // TODO: no DS z-index token for 10; it keeps the panel above the + // viewer content (z-index 2). + z-index: 10; } .expanded { grid-row: 1 / span 2; - - .expand-btn svg { - transform: rotate(-90deg); - } } .thumbnails-summary { display: flex; justify-content: space-between; align-items: center; - height: deprecated.$s-32; - margin: deprecated.$s-24 deprecated.$s-24 0 deprecated.$s-24; + block-size: $sz-32; + margin: var(--sp-xxl) var(--sp-xxl) 0 var(--sp-xxl); } .counter { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--viewer-thumbnails-control-foreground-color); + color: var(--color-foreground-secondary); } .actions { - @include deprecated.flex-row; - - width: deprecated.$s-60; -} - -.expand-btn, -.close-btn { - @extend %button-tertiary; - - height: deprecated.$s-32; - width: deprecated.$s-28; - - svg { - @extend %button-icon; - } -} - -.expand-btn svg { - transform: rotate(90deg); + display: flex; + align-items: center; + gap: var(--sp-xs); } .thumbnails-content { display: grid; - grid-template-columns: deprecated.$s-40 auto deprecated.$s-40; + grid-template-columns: $sz-40 auto $sz-40; grid-template-rows: auto; } @@ -74,36 +61,28 @@ overflow: hidden; } +// Tooltip wrappers of the scroll buttons: they take the grid cell. .right-scroll-handler, .left-scroll-handler { - @extend %button-tertiary; - @include deprecated.flex-center; - grid-column: 3 / span 1; grid-row: 1 / span 1; - width: deprecated.$s-32; - height: deprecated.$s-60; margin: auto 0; - z-index: deprecated.$z-index-10; - opacity: 0; - - &:hover { - opacity: 1; - } - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } + z-index: 10; } .left-scroll-handler { grid-column: 1 / span 1; - grid-row: 1 / span 1; +} - svg { - transform: rotate(180deg); +// The scroll buttons are as tall as a thumbnail row and only show on hover. +.thumbnails-content .scroll-handler { + --button-height: #{px2rem(60)}; + + opacity: 0; + + &:hover, + &:focus-visible { + opacity: 1; } } @@ -120,45 +99,49 @@ position: relative; } +// A list item, not a DS button. .thumbnail-item { - @include deprecated.button-style; - display: flex; flex-direction: column; - padding: deprecated.$s-16; + padding: var(--sp-l); + border: none; + background: none; + cursor: pointer; } .thumbnail-preview { - @include deprecated.flex-center; - - width: deprecated.$s-132; - min-height: deprecated.$s-132; - height: deprecated.$s-132; - padding: deprecated.$s-4; + display: flex; + justify-content: center; + align-items: center; + inline-size: px2rem(132); + min-block-size: px2rem(132); + block-size: px2rem(132); + padding: var(--sp-xs); + // The board preview markup has no class of its own. svg { - width: 100%; - height: 100%; + inline-size: 100%; + block-size: 100%; } &.selected { - background-color: var(--viewer-thumbnail-background-color-selected); - border-radius: deprecated.$br-8; + background-color: var(--color-accent-primary-muted); + border-radius: $br-8; } &:hover { - border: deprecated.$s-1 solid var(--viewer-thumbnail-border-color); - border-radius: deprecated.$br-8; + border: $b-1 solid var(--color-accent-primary); + border-radius: $br-8; } } .thumbnail-info { - @include deprecated.body-small-typography; - @include deprecated.text-ellipsis; + @include use-typography("body-small"); + @include text-ellipsis; text-align: center; - color: var(--viewer-thumbnails-control-foreground-color); - padding: deprecated.$s-8 0; - width: 100%; - max-width: deprecated.$s-132; + color: var(--color-foreground-secondary); + padding: var(--sp-s) 0; + inline-size: 100%; + max-inline-size: px2rem(132); } diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index c626b36a29..4b37d284a9 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -27,6 +27,7 @@ [app.main.ui.workspace.components-debugger :refer [components-debugger*]] [app.main.ui.workspace.context-menu :refer [context-menu*]] [app.main.ui.workspace.coordinates :as coordinates] + [app.main.ui.workspace.file-restored-modal] [app.main.ui.workspace.libraries] [app.main.ui.workspace.nudge] [app.main.ui.workspace.palette :refer [palette*]] diff --git a/frontend/src/app/main/ui/workspace/color_palette.cljs b/frontend/src/app/main/ui/workspace/color_palette.cljs index 1c95587fc4..8b97d27034 100644 --- a/frontend/src/app/main/ui/workspace/color_palette.cljs +++ b/frontend/src/app/main/ui/workspace/color_palette.cljs @@ -18,9 +18,8 @@ [app.main.ui.components.search-bar :refer [search-bar*]] [app.main.ui.context :as ctx] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] - [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.ds.utilities.swatch :refer [swatch*]] - [app.main.ui.icons :as deprecated-icon] [app.util.color :as uc] [app.util.dom :as dom] [app.util.i18n :refer [tr]] @@ -186,10 +185,12 @@ :on-click on-toggle-search :aria-label (tr "workspace.assets.search")}]]) + ;; Arrows for scrolling the color palette, those buttons no not form part of the DS (when show-arrows? [:button {:class (stl/css :left-arrow) :disabled (= offset 0) - :on-click on-left-arrow-click} deprecated-icon/arrow]) + :on-click on-left-arrow-click} + [:> icon* {:icon-id i/arrow :class (stl/css :arrow-icon)}]]) [:div {:class (stl/css :color-palette-content) :ref container :on-wheel on-scroll} @@ -212,7 +213,8 @@ (when show-arrows? [:button {:class (stl/css :right-arrow) :disabled (= offset max-offset) - :on-click on-right-arrow-click} deprecated-icon/arrow])])) + :on-click on-right-arrow-click} + [:> icon* {:icon-id i/arrow :class (stl/css :arrow-icon)}]])])) (mf/defc recent-colors-palette* {::mf/private true} diff --git a/frontend/src/app/main/ui/workspace/color_palette.scss b/frontend/src/app/main/ui/workspace/color_palette.scss index fb0af2c527..5ac73b02a6 100644 --- a/frontend/src/app/main/ui/workspace/color_palette.scss +++ b/frontend/src/app/main/ui/workspace/color_palette.scss @@ -4,10 +4,12 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .color-palette { - height: 100%; + block-size: 100%; display: flex; } @@ -15,63 +17,60 @@ .palette-search-collapsed { display: flex; align-items: center; - padding-inline: deprecated.$s-4; + padding-inline: var(--sp-xs); } .palette-search { - gap: deprecated.$s-4; - width: deprecated.$s-192; - min-width: deprecated.$s-192; + gap: var(--sp-xs); + inline-size: $sz-192; + min-inline-size: $sz-192; } .palette-search-collapsed { - width: deprecated.$s-32; - min-width: deprecated.$s-32; + inline-size: $sz-32; + min-inline-size: $sz-32; } +// Raw buttons on purpose: full-height scroll arrows with a fade, not DS +// button sizes. .left-arrow, .right-arrow { - @include deprecated.button-style; - @include deprecated.flex-center; + --arrow-foreground-color: var(--color-foreground-secondary); + display: flex; + justify-content: center; + align-items: center; position: relative; - height: 100%; - width: deprecated.$s-24; + block-size: 100%; + inline-size: $sz-24; padding: 0; - z-index: deprecated.$z-index-5; + border: none; + background: none; + cursor: pointer; + color: var(--arrow-foreground-color); - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } + // Above the color list, which is positioned and comes after the left + // arrow, so the fade covers the colors that scroll under it. + z-index: 5; &::after { content: ""; position: absolute; - z-index: deprecated.$z-index-1; - bottom: 0; - left: calc(-1 * deprecated.$s-80); - height: 100%; - width: deprecated.$s-80; - background-image: linear-gradient( - to left, - var(--palette-button-shadow-initial) 0%, - var(--palette-button-shadow-final) 100% - ); + z-index: 1; + inset-block-end: 0; + inset-inline-start: calc(-1 * px2rem(80)); + block-size: 100%; + inline-size: px2rem(80); + background-image: linear-gradient(to left, var(--color-background-primary) 0%, transparent 100%); pointer-events: none; } &:hover { - svg { - stroke: var(--button-foreground-hover); - } + --arrow-foreground-color: var(--color-accent-primary); } &:disabled { - svg { - stroke: var(--button-foreground-color-disabled); - } + --arrow-foreground-color: var(--color-foreground-disabled); &::after { background-image: none; @@ -81,19 +80,15 @@ .left-arrow { &::after { - left: deprecated.$s-24; - background-image: linear-gradient( - to right, - var(--palette-button-shadow-initial) 0%, - var(--palette-button-shadow-final) 100% - ); + inset-inline-start: $sz-24; + background-image: linear-gradient(to right, var(--color-background-primary) 0%, transparent 100%); } &:disabled::after { background-image: none; } - svg { + .arrow-icon { transform: rotate(180deg); } } @@ -108,7 +103,7 @@ display: grid; grid-auto-flow: column; grid-auto-columns: var(--color-cell-width); - gap: deprecated.$s-8; + gap: var(--sp-s); } .color-cell { @@ -121,17 +116,18 @@ grid-template-columns: 100%; grid-template-rows: auto 1fr; justify-items: center; - height: 100%; + block-size: 100%; &.no-text { - @include deprecated.flex-center; - - width: deprecated.$s-32; + display: flex; + justify-content: center; + align-items: center; + inline-size: $sz-32; } } .color-palette-empty { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--palette-text-color); + color: var(--color-foreground-secondary); } diff --git a/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.cljs b/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.cljs index 4a67f0cdb1..5085b9af12 100644 --- a/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.cljs +++ b/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.cljs @@ -12,7 +12,7 @@ [app.main.ui.components.color-bullet :as cb] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.context :as ctx] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.util.i18n :refer [tr]] [rumext.v2 :as mf])) @@ -75,7 +75,7 @@ (dm/str "(" (:total-colors library) ")")]] (when (= selected id) [:span {:class (stl/css :icon-wrapper)} - deprecated-icon/tick])] + [:> icon* {:icon-id i/tick :size "s"}]])] [:div {:class (stl/css :color-sample) :style {:--bullet-size "20px"}} (for [color colors] @@ -100,7 +100,7 @@ (when (= selected :file) [:span {:class (stl/css :icon-wrapper)} - deprecated-icon/tick])] + [:> icon* {:icon-id i/tick :size "s"}]])] [:div {:class (stl/css :color-sample) :style {:--bullet-size "20px"}} (for [color local-colors] @@ -123,7 +123,7 @@ (when (= selected :recent) [:span {:class (stl/css :icon-wrapper)} - deprecated-icon/tick])] + [:> icon* {:icon-id i/tick :size "s"}]])] [:div {:class (stl/css :color-sample) :style {:--bullet-size "20px"}} diff --git a/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.scss b/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.scss index 48fc511406..bce2b5f6d0 100644 --- a/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.scss +++ b/frontend/src/app/main/ui/workspace/color_palette_ctx_menu.scss @@ -4,101 +4,91 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .palette-menu { position: absolute; - left: auto; - bottom: var(--height); - max-width: deprecated.$s-480; - padding: deprecated.$s-4; - margin: 0 0 deprecated.$s-4 0; - z-index: deprecated.$z-index-4; - border-radius: deprecated.$br-10; - background-color: var(--context-menu-background-color); + inset-inline-start: auto; + inset-block-end: var(--height); + max-inline-size: $sz-480; + padding: var(--sp-xs); + margin: 0 0 var(--sp-xs) 0; + + // Context menu level of the legacy scale, inside the palette. + z-index: 4; + border-radius: px2rem(10); + background-color: var(--color-background-tertiary); .palette-library, .recent-colors, .file-library { + --library-name-color: var(--color-foreground-secondary); + position: relative; display: flex; align-items: flex-start; - width: 100%; - padding: deprecated.$s-8; - border-radius: deprecated.$br-8; - margin-bottom: deprecated.$s-4; + inline-size: 100%; + padding: var(--sp-s); + border-radius: $br-8; + margin-block-end: var(--sp-xs); &:last-child { - margin-bottom: 0; + margin-block-end: 0; } .option-wrapper { - width: 100%; + inline-size: 100%; .library-name { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--context-menu-foreground-color); + color: var(--library-name-color); display: grid; - grid-template-columns: 1fr deprecated.$s-24; + grid-template-columns: 1fr $sz-24; .lib-name-wrapper { display: flex; - max-width: deprecated.$s-400; + max-inline-size: $sz-400; .lib-name { - @include deprecated.text-ellipsis; - - max-width: deprecated.$s-380; + display: block; + max-inline-size: px2rem(380); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .lib-num { - margin-left: deprecated.$s-4; + margin-inline-start: var(--sp-xs); } } .icon-wrapper { - margin-left: deprecated.$s-4; - - @include deprecated.flex-center; - - svg { - @extend %button-icon-small; - @include deprecated.flex-center; - - stroke: var(--icon-foreground); - } + display: flex; + justify-content: center; + align-items: center; + margin-inline-start: var(--sp-xs); } } .color-sample { display: flex; flex-direction: row; - gap: deprecated.$s-8; - margin-top: deprecated.$s-4; + gap: var(--sp-s); + margin-block-start: var(--sp-xs); } } &.selected, &:hover { - .option-wrapper .library-name { - color: var(--context-menu-foreground-color-selected); - - .icon-wrapper { - @include deprecated.flex-center; - - svg { - @include deprecated.flex-center; - @extend %button-icon-small; - - stroke: var(--context-menu-foreground-color-selected); - } - } - } + --library-name-color: var(--color-foreground-primary); } &:hover { - background-color: var(--context-menu-background-color-selected); + background-color: var(--color-background-quaternary); } } } diff --git a/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs b/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs index e08eda1d10..7aaf3ab797 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs @@ -310,6 +310,7 @@ :variant "comfortable" :class (stl/css :search-input) :default-value filter-term + :auto-focus true :on-change on-filter-tokens}] (if (seq sorted-tokens) [:div {:class (stl/css :color-tokens-inputs)} diff --git a/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs b/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs index 8338544f73..e1817621c6 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs @@ -15,8 +15,8 @@ [app.main.features :as features] [app.main.ui.components.numeric-input :as deprecated-input] [app.main.ui.components.reorder-handler :refer [reorder-handler*]] - [app.main.ui.components.select :refer [select]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.controls.select :refer [select*]] [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.formats :as fmt] [app.main.ui.hooks :as h] @@ -167,6 +167,10 @@ :on-focus handle-focus-stop-color :on-blur handle-blur-stop-color}]])) +(def ^:private gradient-type-options + [{:id "linear-gradient" :label "Linear"} + {:id "radial-gradient" :label "Radial"}]) + (mf/defc gradients* [{:keys [type stops @@ -200,9 +204,9 @@ handle-change-type (mf/use-callback (mf/deps on-change-type) - (fn [event] + (fn [type-id] (when on-change-type - (on-change-type event)))) + (on-change-type (keyword type-id))))) handle-add-stop (mf/use-callback @@ -336,12 +340,10 @@ :style {"--position" (dm/str (* offset 100) "%")}}]])]] [:div {:class (stl/css :gradient-options)} - [:& select - {:default-value type - :options [{:value :linear-gradient :label "Linear"} - {:value :radial-gradient :label "Radial"}] - :on-change handle-change-type - :class (stl/css :gradient-options-select)}] + [:> select* {:default-selected (d/name (d/nilv type :linear-gradient)) + :options gradient-type-options + :on-change handle-change-type + :wrapper-class (stl/css :gradient-options-select)}] [:div {:class (stl/css :gradient-options-buttons)} [:> icon-button* {:variant "ghost" diff --git a/frontend/src/app/main/ui/workspace/colorpicker/gradients.scss b/frontend/src/app/main/ui/workspace/colorpicker/gradients.scss index 0ce4019fd4..038e326ea4 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/gradients.scss +++ b/frontend/src/app/main/ui/workspace/colorpicker/gradients.scss @@ -4,18 +4,24 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for %input-element: the stop offset input moves to the DS with the +// numeric inputs group. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .gradient-panel { - margin-top: deprecated.$s-12; + margin-block-start: var(--sp-m); display: grid; - gap: deprecated.$s-4; - grid-template-rows: deprecated.$s-56 deprecated.$s-32 1fr; + gap: var(--sp-xs); + grid-template-rows: px2rem(56) $sz-32 1fr; } .gradient-preview { - width: 100%; - height: 100%; + inline-size: 100%; + block-size: 100%; display: flex; align-items: center; position: relative; @@ -23,19 +29,19 @@ } .gradient-background { - width: 100%; - height: deprecated.$s-20; - border-radius: deprecated.$br-4; + inline-size: 100%; + block-size: px2rem(20); + border-radius: $br-4; position: relative; cursor: pointer; } .gradient-preview-stop-wrapper { position: absolute; - width: calc(100% - deprecated.$s-24 - deprecated.$s-4); - height: 100%; - left: deprecated.$s-2; - top: calc(-1 * deprecated.$s-4); + inline-size: calc(100% - #{px2rem(28)}); + block-size: 100%; + inset-inline-start: px2rem(2); + inset-block-start: calc(-1 * var(--sp-xs)); pointer-events: none; } @@ -43,47 +49,47 @@ background-color: var(--color-foreground-primary); background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII="); background-position: left center; - background-size: deprecated.$s-8; - border-radius: deprecated.$br-6; - border: deprecated.$s-2 solid var(--color-foreground-primary); - box-shadow: 0 0 deprecated.$s-4 0 var(--menu-shadow-color); - height: calc(deprecated.$s-24 - deprecated.$s-2); - left: var(--position); + background-size: px2rem(8); + border-radius: $br-6; + border: $b-2 solid var(--color-foreground-primary); + box-shadow: 0 0 px2rem(4) 0 var(--color-shadow-dark); + block-size: px2rem(22); + inset-inline-start: var(--position); overflow: hidden; pointer-events: initial; position: absolute; - width: calc(deprecated.$s-24 - deprecated.$s-2); + inline-size: px2rem(22); &.is-selected, &:hover { - outline: deprecated.$s-2 solid var(--color-accent-primary); + outline: $b-2 solid var(--color-accent-primary); } } .gradient-preview-stop-decoration { background: var(--color-foreground-primary); border-radius: 100%; - bottom: deprecated.$s-32; - box-shadow: 0 0 deprecated.$s-4 0 var(--menu-shadow-color); - height: deprecated.$s-4; - left: calc(var(--position) + deprecated.$s-8); + inset-block-end: var(--sp-xxxl); + box-shadow: 0 0 px2rem(4) 0 var(--color-shadow-dark); + block-size: px2rem(4); + inset-inline-start: calc(var(--position) + var(--sp-s)); position: absolute; - width: deprecated.$s-4; + inline-size: px2rem(4); } .gradient-preview-stop-color { position: absolute; - left: 0; - width: 50%; - height: 100%; + inset-inline-start: 0; + inline-size: 50%; + block-size: 100%; background: var(--color-solid); } .gradient-preview-stop-alpha { position: absolute; - left: 50%; - width: 50%; - height: 100%; + inset-inline-start: 50%; + inline-size: 50%; + block-size: 100%; background: var(--color-alpha); } @@ -98,7 +104,7 @@ } .gradient-options-select { - width: deprecated.$s-140; + inline-size: px2rem(140); } .rotate-icon { @@ -108,18 +114,18 @@ .gradient-stops-list { display: flex; flex-direction: column; - gap: deprecated.$s-4; - max-height: deprecated.$s-180; + gap: var(--sp-xs); + max-block-size: px2rem(180); overflow: hidden auto; padding: 0 0 var(--sp-s) var(--sp-m); } .gradient-stops-entry { display: flex; - gap: deprecated.$s-4; - padding: deprecated.$s-2; - border-radius: deprecated.$br-12; - border: deprecated.$s-1 solid transparent; + gap: var(--sp-xs); + padding: var(--sp-xxs); + border-radius: $br-12; + border: $b-1 solid transparent; position: relative; &.is-selected { @@ -141,26 +147,26 @@ .offset-input-wrapper { @extend %input-element; - @include deprecated.body-small-typography; + @include use-typography("body-small"); - width: deprecated.$s-92; + inline-size: px2rem(92); } .gradient-separator { border-color: var(--color-background-quaternary); - border-width: deprecated.$s-3; - margin-left: calc(var(--sp-m) / 2); + border-width: px2rem(3); + margin-inline-start: calc(var(--sp-m) / 2); position: relative; - width: calc(100% - var(--sp-m)); + inline-size: calc(100% - var(--sp-m)); } .gradient-preview-stop-preview { background: var(--color-foreground-primary); border-radius: 50%; - height: deprecated.$s-4; - left: calc(var(--preview-position, 0%) - deprecated.$s-2); + block-size: px2rem(4); + inset-inline-start: calc(var(--preview-position, 0%) - #{px2rem(2)}); pointer-events: none; position: absolute; - top: calc(50% - deprecated.$s-2); - width: deprecated.$s-4; + inset-block-start: calc(50% - #{px2rem(2)}); + inline-size: px2rem(4); } diff --git a/frontend/src/app/main/ui/workspace/comments.cljs b/frontend/src/app/main/ui/workspace/comments.cljs index a6596bf3b6..7ab6ca7dda 100644 --- a/frontend/src/app/main/ui/workspace/comments.cljs +++ b/frontend/src/app/main/ui/workspace/comments.cljs @@ -17,10 +17,9 @@ [app.main.ui.comments :as cmt] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.context :as ctx] - [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.ds.product.empty-state :refer [empty-state*]] [app.main.ui.ds.product.panel-title :refer [panel-title*]] - [app.main.ui.icons :as deprecated-icon] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -56,25 +55,29 @@ :on-click update-mode} [:span {:class (stl/css :label)} (tr "labels.show-all-comments")] - [:span {:class (stl/css :icon)} deprecated-icon/tick]] + [:span {:class (stl/css :icon)} + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :tick-icon)}]]] [:li {:class (stl/css-case :dropdown-item true :selected (= :yours cmode)) :data-value "yours" :on-click update-mode} [:span {:class (stl/css :label)} (tr "labels.show-your-comments")] - [:span {:class (stl/css :icon)} deprecated-icon/tick]] + [:span {:class (stl/css :icon)} + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :tick-icon)}]]] [:li {:class (stl/css-case :dropdown-item true :selected (= :mentions cmode)) :data-value "mentions" :on-click update-mode} [:span {:class (stl/css :label)} (tr "labels.show-mentions")] - [:span {:class (stl/css :icon)} deprecated-icon/tick]] + [:span {:class (stl/css :icon)} + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :tick-icon)}]]] [:li {:class (stl/css :separator)}] [:li {:class (stl/css-case :dropdown-item true :selected (= :pending cshow)) :on-click update-show} [:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")] - [:span {:class (stl/css :icon)} deprecated-icon/tick]]])) + [:span {:class (stl/css :icon)} + [:> icon* {:icon-id i/tick :size "s" :class (stl/css :tick-icon)}]]]])) (mf/defc comments-sidebar* [{:keys [profiles threads page-id from-viewer]}] @@ -131,6 +134,9 @@ :on-close close-section}] [:button {:class (stl/css :mode-dropdown-wrapper) + :type "button" + :aria-haspopup "menu" + :aria-expanded options? :on-click toggle-mode-selector} [:span {:class (stl/css :mode-label)} @@ -138,7 +144,8 @@ (nil :all) (tr "labels.show-all-comments") :yours (tr "labels.show-your-comments") :mentions (tr "labels.show-mentions"))] - [:div {:class (stl/css :arrow-icon)} deprecated-icon/arrow]] + [:div {:class (stl/css :arrow-icon)} + [:> icon* {:icon-id i/arrow-down :size "s" :class (stl/css :arrow-svg)}]]] [:& dropdown {:show options? :on-close #(reset! state* false)} diff --git a/frontend/src/app/main/ui/workspace/comments.scss b/frontend/src/app/main/ui/workspace/comments.scss index b94930789d..ea18d51a42 100644 --- a/frontend/src/app/main/ui/workspace/comments.scss +++ b/frontend/src/app/main/ui/workspace/comments.scss @@ -6,17 +6,22 @@ @use "ds/_sizes.scss" as *; @use "ds/_borders.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; + +// refactor/ stays for the dropdown placeholders (%dropdown-wrapper, +// %dropdown-element-base), which move with the menus group. @use "refactor/common-refactor.scss" as deprecated; .comments-section { position: relative; - background-color: var(--panel-background-color); + background-color: var(--color-background-primary); display: grid; - grid-template-rows: deprecated.$s-40 deprecated.$s-48 1fr; + grid-template-rows: $sz-40 $sz-48 1fr; } .from-viewer { - padding: 0 deprecated.$s-8; + padding: 0 var(--sp-s); } .comments-title { @@ -24,53 +29,60 @@ } .mode-dropdown-wrapper { - @include deprecated.button-style; - @extend %asset-element; + @include use-typography("body-small"); - background-color: var(--color-background-tertiary); + --mode-dropdown-bg-color: var(--color-background-tertiary); + + border: none; + align-items: center; + color: var(--color-foreground-primary); + background-color: var(--mode-dropdown-bg-color); display: flex; - width: 100%; - max-width: deprecated.$s-256; - height: deprecated.$s-32; - padding: deprecated.$s-8; - border-radius: deprecated.$br-8; - margin: deprecated.$s-16 auto 0 auto; + inline-size: 100%; + max-inline-size: px2rem(256); + block-size: $sz-32; + padding: var(--sp-s); + border-radius: $br-8; + margin: var(--sp-l) auto 0 auto; cursor: pointer; position: relative; + + &:hover { + --mode-dropdown-bg-color: var(--color-background-quaternary); + } } .mode-label { - padding-right: 8px; + padding-inline-end: var(--sp-s); flex-grow: 1; display: flex; justify-content: flex-start; } .arrow-icon { - @include deprecated.flex-center; + display: flex; + justify-content: center; + align-items: center; + block-size: $sz-24; + inline-size: $sz-24; - height: deprecated.$s-24; - width: deprecated.$s-24; - - svg { - @extend %button-icon-small; - - transform: rotate(90deg); - stroke: var(--icon-foreground); + .arrow-svg { + flex-shrink: 0; + color: var(--color-foreground-secondary); } } .comment-mode-dropdown { @extend %dropdown-wrapper; - top: deprecated.$s-92; - left: deprecated.$s-12; - max-width: deprecated.$s-256; - width: 100%; + inset-block-start: px2rem(92); + inset-inline-start: var(--sp-m); + max-inline-size: px2rem(256); + inline-size: 100%; } .viewer-dropdown { - left: deprecated.$s-8; + inset-inline-start: var(--sp-s); } .dropdown-item { @@ -79,35 +91,40 @@ justify-content: space-between; .icon { - @include deprecated.flex-center; + display: flex; + justify-content: center; + align-items: center; + block-size: $sz-24; + inline-size: $sz-24; - height: deprecated.$s-24; - width: deprecated.$s-24; - - svg { - @extend %button-icon-small; - - stroke: transparent; + // %dropdown-element-base styles any svg inside a span (12px box and a + // stroke color); the DS icon needs its 16px box and currentColor. + .tick-icon { + flex-shrink: 0; + inline-size: $sz-16; + block-size: $sz-16; + stroke: currentcolor; + color: transparent; } } .label { - @include deprecated.body-small-typography; + @include use-typography("body-small"); } &:hover { - .icon svg { - stroke: transparent; + .tick-icon { + color: transparent; } } &.selected { .label { - color: var(--menu-foreground-color); + color: var(--color-foreground-primary); } - .icon svg { - stroke: var(--icon-foreground-hover); + .tick-icon { + color: var(--color-foreground-primary); } } } @@ -116,13 +133,13 @@ position: relative; block-size: var(--sp-xs); inline-size: calc(100% + var(--sp-s)); - border-top: $b-1 solid var(--color-background-quaternary); - left: calc(-1 * var(--sp-xs)); - margin-top: var(--sp-s); + border-block-start: $b-1 solid var(--color-background-quaternary); + inset-inline-start: calc(-1 * var(--sp-xs)); + margin-block-start: var(--sp-s); } .comments-section-content { - height: 100%; + block-size: 100%; overflow-y: auto; } diff --git a/frontend/src/app/main/ui/workspace/context_menu.scss b/frontend/src/app/main/ui/workspace/context_menu.scss index 3061e59a4b..e46fae5ace 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.scss +++ b/frontend/src/app/main/ui/workspace/context_menu.scss @@ -127,7 +127,6 @@ align-items: center; inline-size: $sz-16; color: var(--color-foreground-primary); - stroke-width: 1.33px; } .separator { diff --git a/frontend/src/app/main/ui/workspace/file_restored_modal.cljs b/frontend/src/app/main/ui/workspace/file_restored_modal.cljs new file mode 100644 index 0000000000..badadb4957 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/file_restored_modal.cljs @@ -0,0 +1,59 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.main.ui.workspace.file-restored-modal + (:require-macros [app.main.style :as stl]) + (:require + [app.common.time :as ct] + [app.main.data.modal :as modal] + [app.main.store :as st] + [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] + [app.main.ui.ds.foundations.typography.text :refer [text*]] + [app.util.i18n :refer [tr]] + [rumext.v2 :as mf])) + +(defn- restored-message + "Who restored which version: the version by its `label`, else by its + date, else as an earlier version." + [fullname label created-at] + (let [date (some-> created-at (ct/format-inst "PPp"))] + (cond + (and fullname label) (tr "workspace.file-restored.message-label" fullname label) + label (tr "workspace.file-restored.message-label-anonymous" label) + (and fullname date) (tr "workspace.file-restored.message-date" fullname date) + date (tr "workspace.file-restored.message-date-anonymous" date) + fullname (tr "workspace.file-restored.message" fullname) + :else (tr "workspace.file-restored.message-anonymous")))) + +#_{:clojure-lsp/ignore [:clojure-lsp/unused-public-var]} +(mf/defc file-restored-modal* + "Tells a peer that another user restored a version of the open file, + which was reloaded, and whether their edits not saved yet were lost." + {::mf/register modal/components + ::mf/register-as :file-restored} + [{:keys [fullname label created-at unsaved]}] + (let [on-close (mf/use-fn #(st/emit! (modal/hide)))] + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-dialog) + :role "dialog" + :aria-modal true + :aria-labelledby "file-restored-title"} + [:> heading* {:id "file-restored-title" + :level 2 + :typography t/title-medium + :class (stl/css :modal-title)} + (tr "workspace.file-restored.title")] + [:> text* {:as "p" :typography t/body-large} + (restored-message fullname label created-at)] + (when unsaved + [:> text* {:as "p" :typography t/body-large} + (tr "workspace.file-restored.unsaved-changes")]) + [:footer {:class (stl/css :modal-footer)} + [:> button* {:on-click on-close + :variant "primary"} + (tr "workspace.file-restored.accept")]]]])) diff --git a/frontend/src/app/main/ui/workspace/file_restored_modal.scss b/frontend/src/app/main/ui/workspace/file_restored_modal.scss new file mode 100644 index 0000000000..568841a417 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/file_restored_modal.scss @@ -0,0 +1,34 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS SUBSIDIARY SL + +@use "ds/_utils.scss" as *; +@use "refactor/common-refactor.scss" as deprecated; + +.modal-overlay { + @extend %modal-overlay-base; +} + +.modal-dialog { + @extend %modal-container-base; + + color: var(--color-foreground-secondary); + display: grid; + gap: var(--sp-s); + max-width: px2rem(482); // FIXME: This should be a token + + & > * { + margin: 0; // FIXME: This should be in reset styles + } +} + +.modal-title { + color: var(--color-foreground-primary); +} + +.modal-footer { + margin-block-start: var(--sp-m); + justify-self: end; +} diff --git a/frontend/src/app/main/ui/workspace/left_header.cljs b/frontend/src/app/main/ui/workspace/left_header.cljs index 37e6247b87..d16a6993fe 100644 --- a/frontend/src/app/main/ui/workspace/left_header.cljs +++ b/frontend/src/app/main/ui/workspace/left_header.cljs @@ -15,7 +15,8 @@ [app.main.refs :as refs] [app.main.router :as rt] [app.main.store :as st] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] + [app.main.ui.ds.foundations.assets.raw-svg :as raw-svg :refer [raw-svg*]] [app.main.ui.workspace.main-menu :as main-menu] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -69,33 +70,40 @@ #(st/emit! (dc/stop-picker) (modal/hide))) + router (mf/deref refs/router) + recent-href (rt/resolve-uri router :dashboard-recent {:team-id (:team-id project)}) + project-href (rt/resolve-uri router :dashboard-files {:team-id (:team-id project) + :project-id project-id}) + go-back (mf/use-fn - (fn [] - (close-modals) - ;; FIXME: move set-mode to uri? - (st/emit! :interrupt - (dw/set-options-mode :design) - (dcm/go-to-dashboard-recent)))) - - nav-to-project - (mf/use-fn - (mf/deps project-id) - #(st/emit! :interrupt - (dcm/go-to-dashboard-files ::rt/new-window true :project-id project-id)))] + (fn [event] + ;; Ctrl/Cmd-click opens the href in a new tab, as any link. + (when-not (kbd/mod? event) + (dom/prevent-default event) + (close-modals) + ;; FIXME: move set-mode to uri? + (st/emit! :interrupt + (dw/set-options-mode :design) + (dcm/go-to-dashboard-recent)))))] (mf/with-effect [editing?] (when ^boolean editing? (dom/select-text! (mf/ref-val input-ref)))) [:header {:class (dm/str class " " (stl/css :workspace-header-left))} - [:a {:on-click go-back - :class (stl/css :main-icon)} deprecated-icon/logo-icon] + [:a {:href recent-href + :on-click go-back + :class (stl/css :main-icon)} + [:> raw-svg* {:id raw-svg/penpot-logo-icon + :class (stl/css :logo)}]] [:div {:alt (tr "workspace.sitemap") :class (stl/css :project-tree)} - [:div - {:class (stl/css :project-name) - :on-click nav-to-project} + ;; Opens the project files in a new tab, as it always did. + [:a {:class (stl/css :project-name) + :href project-href + :target "_blank" + :rel "noopener"} (:name project)] (if ^boolean editing? [:input @@ -124,17 +132,11 @@ :saved (tr "workspace.header.saved") :retrying (tr "workspace.header.retrying") :error (tr "workspace.header.save-error") - nil)} - (case persistence-status - :pending deprecated-icon/status-alert - :saving deprecated-icon/status-alert - :saved deprecated-icon/status-tick - :retrying deprecated-icon/status-alert - :error deprecated-icon/status-wrong - nil)] + nil)}] [:div {:class (stl/css :file-name-label)} file-name]])] (when ^boolean shared? - [:span {:class (stl/css :shared-badge)} deprecated-icon/library]) + [:span {:class (stl/css :shared-badge)} + [:> icon* {:icon-id i/library :class (stl/css :shared-badge-icon)}]]) [:div {:class (stl/css :menu-section)} [:> main-menu/menu* {:layout layout :file file}]]])) diff --git a/frontend/src/app/main/ui/workspace/left_header.scss b/frontend/src/app/main/ui/workspace/left_header.scss index 663c7465b2..9fa600489d 100644 --- a/frontend/src/app/main/ui/workspace/left_header.scss +++ b/frontend/src/app/main/ui/workspace/left_header.scss @@ -4,77 +4,92 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .workspace-header-left { display: flex; align-items: center; - padding: deprecated.$s-12 deprecated.$s-12 deprecated.$s-8 deprecated.$s-12; - min-height: deprecated.$s-52; + padding: var(--sp-m) var(--sp-m) var(--sp-s) var(--sp-m); + min-block-size: px2rem(52); } .main-icon { - @include deprecated.flex-center; + display: flex; + justify-content: center; + align-items: center; + inline-size: $sz-32; + block-size: $sz-32; + min-block-size: $sz-32; + margin-inline-end: var(--sp-xs); +} - width: deprecated.$s-32; - height: deprecated.$s-32; - min-height: deprecated.$s-32; - margin-right: deprecated.$s-4; - - svg { - min-height: deprecated.$s-32; - width: deprecated.$s-32; - fill: var(--icon-foreground-hover); - } +.logo { + inline-size: $sz-32; + block-size: $sz-32; + fill: var(--color-foreground-primary); } .project-tree { position: relative; flex-grow: 1; flex-shrink: 1; - min-width: 0; - height: deprecated.$s-32; - min-height: deprecated.$s-32; + min-inline-size: 0; + block-size: $sz-32; + min-block-size: $sz-32; } .project-name, .file-name { - @include deprecated.uppercase-title-typography; - @include deprecated.text-ellipsis; - - height: deprecated.$s-16; - width: 100%; - padding-bottom: deprecated.$s-2; - color: var(--title-foreground-color); + display: block; + max-inline-size: 99%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + block-size: $sz-16; + inline-size: 100%; + padding-block-end: var(--sp-xxs); cursor: pointer; } -.file-name { - @include deprecated.small-title-typography; +.project-name { + @include use-typography("headline-small"); - text-transform: none; - color: var(--title-foreground-color-hover); + color: var(--color-foreground-secondary); +} + +.file-name { + @include use-typography("title-small"); + + color: var(--color-foreground-primary); align-items: center; display: flex; flex-direction: row; } .file-name-label { - @include deprecated.text-ellipsis; + display: block; + max-inline-size: 99%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .file-name-input { - @include deprecated.flex-center; + @include use-typography("title-small"); - width: 100%; + display: flex; + justify-content: center; + align-items: center; + inline-size: 100%; margin: 0; border: 0; padding: 0; - border-radius: deprecated.$br-4; - background-color: var(--input-background-color); - font-size: deprecated.$fs-14; - color: var(--input-foreground-color); - z-index: deprecated.$z-index-20; + border-radius: $br-4; + background-color: var(--color-background-tertiary); + color: var(--color-foreground-secondary); white-space: break-spaces; &:focus { @@ -83,46 +98,42 @@ } .shared-badge { - @include deprecated.flex-center; + display: flex; + justify-content: center; + align-items: center; + inline-size: $sz-16; + block-size: $sz-32; + margin-inline-end: var(--sp-xs); +} - width: deprecated.$s-16; - height: deprecated.$s-32; - margin-right: deprecated.$s-4; - - svg { - stroke: var(--button-secondary-foreground-color-rest); - fill: none; - height: deprecated.$s-16; - width: deprecated.$s-16; - } +.shared-badge-icon { + color: var(--color-foreground-secondary); } .status-notification { - width: deprecated.$s-6; - height: deprecated.$s-6; - border-radius: 50%; - margin-right: deprecated.$s-4; + --status-notification-background-color: transparent; + + inline-size: $sz-6; + block-size: $sz-6; + border-radius: $br-circle; + margin-inline-end: var(--sp-xs); flex-shrink: 0; + background-color: var(--status-notification-background-color); - &.pending-status { - background-color: var(--status-widget-background-color-warning); - } - - &.saving-status { - background-color: var(--status-widget-background-color-pending); + &.pending-status, + &.saving-status, + &.retrying-status { + --status-notification-background-color: var(--color-accent-warning); } &.saved-status { - background-color: var(--status-widget-background-color-success); + --status-notification-background-color: var(--color-accent-success); + animation: jump 0.3s ease-out; } - &.retrying-status { - background-color: var(--status-widget-background-color-warning); - } - &.error-status { - background-color: var(--status-widget-background-color-error); + --status-notification-background-color: var(--color-foreground-error); } } diff --git a/frontend/src/app/main/ui/workspace/libraries.cljs b/frontend/src/app/main/ui/workspace/libraries.cljs index cfb41dfb3d..49f4758437 100644 --- a/frontend/src/app/main/ui/workspace/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/libraries.cljs @@ -1087,7 +1087,8 @@ [:div {:class (stl/css :libraries-updates-item) :key (dm/str (:id typography))} [:* - [:div {:style {:font-family (:font-family typography) + [:div {:class (stl/css :typography-sample) + :style {:font-family (:font-family typography) :font-weight (:font-weight typography) :font-style (:font-style typography)}} (tr "workspace.assets.typography.sample")] diff --git a/frontend/src/app/main/ui/workspace/libraries.scss b/frontend/src/app/main/ui/workspace/libraries.scss index 87ad47c9a1..6dc6dfcec1 100644 --- a/frontend/src/app/main/ui/workspace/libraries.scss +++ b/frontend/src/app/main/ui/workspace/libraries.scss @@ -242,6 +242,10 @@ min-width: $sz-24; } +.typography-sample { + white-space: nowrap; +} + .name-block { color: var(--library-content-foreground-color); width: px2rem(168); diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs index 5a64cf76c9..79d2b80a20 100644 --- a/frontend/src/app/main/ui/workspace/main_menu.cljs +++ b/frontend/src/app/main/ui/workspace/main_menu.cljs @@ -23,6 +23,7 @@ [app.main.data.shortcuts :as scd] [app.main.data.workspace :as dw] [app.main.data.workspace.comments :as dwcm] + [app.main.data.workspace.history :as dwh] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.mcp :as mcp] [app.main.data.workspace.shortcuts :as sc] @@ -617,9 +618,9 @@ on-show-version-history (mf/use-fn - (mf/deps file-id) (fn [_] - (st/emit! (dw/toggle-layout-flag :document-history)))) + (st/emit! (-> (dwh/initialize-history) + (vary-meta assoc ::ev/origin "workspace:menu"))))) on-show-version-history-key-down (mf/use-fn diff --git a/frontend/src/app/main/ui/workspace/nudge.cljs b/frontend/src/app/main/ui/workspace/nudge.cljs index 7c2ee4f01c..fb44f4435d 100644 --- a/frontend/src/app/main/ui/workspace/nudge.cljs +++ b/frontend/src/app/main/ui/workspace/nudge.cljs @@ -12,7 +12,8 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.numeric-input :as deprecated-input] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as k] @@ -44,8 +45,11 @@ [:div {:class (stl/css :modal-container)} [:div {:class (stl/css :modal-header)} [:h2 {:class (stl/css :modal-title)} (tr "modals.nudge-title")] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-content)} [:div {:class (stl/css :input-wrapper)} [:label {:class (stl/css :modal-msg) diff --git a/frontend/src/app/main/ui/workspace/nudge.scss b/frontend/src/app/main/ui/workspace/nudge.scss index 892de2576d..2fe8b7739a 100644 --- a/frontend/src/app/main/ui/workspace/nudge.scss +++ b/frontend/src/app/main/ui/workspace/nudge.scss @@ -4,7 +4,11 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for the shared modal and labeled input placeholders, which move to +// the DS with the modals and the inputs. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .modal-overlay { @extend %modal-overlay-base; @@ -13,45 +17,47 @@ .modal-container { @extend %modal-container-base; - min-width: deprecated.$s-408; + min-inline-size: px2rem(408); } .modal-header { - margin-bottom: deprecated.$s-24; + margin-block-end: var(--sp-xxl); } .modal-title { - @include deprecated.headline-medium-typography; + @include use-typography("headline-medium"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } .modal-close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { - @include deprecated.flex-column; + @include use-typography("body-large"); - gap: deprecated.$s-24; - - @include deprecated.body-large-typography; - - margin-bottom: deprecated.$s-24; + display: flex; + flex-direction: column; + gap: var(--sp-xxl); + margin-block-end: var(--sp-xxl); } .input-wrapper { @extend %input-with-label; - @include deprecated.body-small-typography; + @include use-typography("body-small"); - label { + // Nested so it wins over the label rule of %input-with-label. + .modal-msg { text-transform: none; } } .modal-msg { - @include deprecated.body-large-typography; + @include use-typography("body-large"); - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); line-height: 1.5; } diff --git a/frontend/src/app/main/ui/workspace/palette.cljs b/frontend/src/app/main/ui/workspace/palette.cljs index 7887add48d..2f27304d97 100644 --- a/frontend/src/app/main/ui/workspace/palette.cljs +++ b/frontend/src/app/main/ui/workspace/palette.cljs @@ -18,9 +18,10 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.context :as ctx] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.hooks :as h] [app.main.ui.hooks.resize :as r] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.workspace.color-palette :refer [color-palette*]] [app.main.ui.workspace.color-palette-ctx-menu :refer [color-palette-ctx-menu*]] [app.main.ui.workspace.text-palette :refer [text-palette*]] @@ -95,6 +96,11 @@ (fn [_] (swap! state* assoc :show-menu false))) + on-toggle-menu + (mf/use-fn + (fn [_] + (swap! state* update :show-menu not))) + on-select-palette (mf/use-fn (mf/deps on-select) @@ -182,27 +188,31 @@ [:ul {:class (dm/str size-classname " " (stl/css-case :palette-btn-list true :hidden-bts hide-palettes?))} [:li {:class (stl/css :palette-item)} - [:button {:title (tr "workspace.toolbar.color-palette" (get-tt :toggle-colorpalette)) - :aria-label (tr "workspace.toolbar.color-palette" (get-tt :toggle-colorpalette)) - :class (stl/css-case :palette-btn true - :selected color-palette?) - :on-click on-select-color-palette} - deprecated-icon/drop-icon]] + [:> icon-button* {:variant "ghost" + :aria-pressed (boolean color-palette?) + :icon-size "s" + :aria-label (tr "workspace.toolbar.color-palette" (get-tt :toggle-colorpalette)) + :icon i/drop + :on-click on-select-color-palette}]] [:li {:class (stl/css :palette-item)} - [:button {:title (tr "workspace.toolbar.text-palette" (get-tt :toggle-textpalette)) - :aria-label (tr "workspace.toolbar.text-palette" (get-tt :toggle-textpalette)) - :class (stl/css-case :palette-btn true - :selected text-palette?) - :on-click on-select-text-palette} - deprecated-icon/text-palette]]] + [:> icon-button* {:variant "ghost" + :aria-pressed (boolean text-palette?) + :icon-size "s" + :aria-label (tr "workspace.toolbar.text-palette" (get-tt :toggle-textpalette)) + :icon i/text-palette + :on-click on-select-text-palette}]]] (if any-palette? [:* - [:button {:class (stl/css :palette-actions) - :on-click #(swap! state* update :show-menu not)} - deprecated-icon/menu] + [:> icon-button* {:variant "ghost" + :class (stl/css :palette-actions) + :aria-label (tr "workspace.toolbar.palette-options") + :aria-expanded (boolean show-menu?) + :aria-haspopup "menu" + :icon i/menu + :on-click on-toggle-menu}] [:div {:class (stl/css :palette) :ref container} (when text-palette? diff --git a/frontend/src/app/main/ui/workspace/palette.scss b/frontend/src/app/main/ui/workspace/palette.scss index b61814d2ba..d743596d59 100644 --- a/frontend/src/app/main/ui/workspace/palette.scss +++ b/frontend/src/app/main/ui/workspace/palette.scss @@ -4,17 +4,16 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "ds/spacing.scss" as *; -@use "ds/z-index.scss" as *; +@use "ds/_borders.scss" as *; @use "ds/_sizes.scss" as *; -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_utils.scss" as *; .palette-wrapper { position: absolute; - width: 100vw; - left: 0; - bottom: 0; - padding-bottom: deprecated.$s-4; + inline-size: 100vw; + inset-inline-start: 0; + inset-block-end: 0; + padding-block-end: var(--sp-xs); /** Aligns AI Chat button **/ display: flex; @@ -23,20 +22,26 @@ gap: var(--sp-s); } +// `right` and `width` stay physical: they are the transitioned properties. .palettes { - z-index: deprecated.$z-index-2; + // Above .viewport-overlays (z-index 1), which covers the whole viewport. + z-index: 2; position: relative; right: 0; grid-area: color-palette; display: grid; - grid-template: "resize resize resize" deprecated.$s-8 "buttons actions palette" 1fr / deprecated.$s-32 auto 1fr; - max-height: deprecated.$s-80; - height: var(--height); + grid-template-areas: + "resize resize resize" + "buttons actions palette"; + grid-template-rows: px2rem(8) 1fr; + grid-template-columns: $sz-32 auto 1fr; + max-block-size: px2rem(80); + block-size: var(--height); width: fit-content; - padding: deprecated.$s-0 deprecated.$s-0 deprecated.$s-8 deprecated.$s-8; - border-radius: deprecated.$br-8; - background-color: var(--palette-background-color); - border: deprecated.$s-2 solid var(--panel-border-color); + padding: 0 0 var(--sp-s) var(--sp-s); + border-radius: $br-8; + background-color: var(--color-background-primary); + border: $b-2 solid var(--color-background-quaternary); transition: right 0.3s, opacity 0.2s, @@ -48,23 +53,21 @@ .resize-area { grid-area: resize; - height: deprecated.$s-8; - z-index: deprecated.$z-index-4; - width: calc(100% - deprecated.$s-8); - border-radius: deprecated.$br-circle; + block-size: px2rem(8); + inline-size: calc(100% - px2rem(8)); + border-radius: $br-circle; cursor: ns-resize; - background-color: var(--palette-background-color); + background-color: var(--color-background-primary); } .palette-btn-list { grid-area: buttons; - background-color: var(--palette-background-color); - height: calc(var(--height) - deprecated.$s-16); - width: deprecated.$s-32; - margin: deprecated.$s-0; + background-color: var(--color-background-primary); + block-size: calc(var(--height) - px2rem(16)); + inline-size: $sz-32; + margin: 0; list-style: none; - z-index: deprecated.$z-index-2; - gap: deprecated.$s-2; + gap: var(--sp-xxs); &.mid-palette, &.small-palette { @@ -72,151 +75,171 @@ } .palette-item { - @include deprecated.flex-center; - - border-radius: deprecated.$br-8; - opacity: deprecated.$op-10; + display: flex; + justify-content: center; + align-items: center; + border-radius: $br-8; + opacity: 1; transition: opacity 1s ease; - - .palette-btn { - @extend %button-tertiary; - - height: deprecated.$s-32; - width: deprecated.$s-32; - border-radius: deprecated.$br-8; - background-clip: padding-box; - padding: 0; - - svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - } - - &.selected { - @extend %button-icon-selected; - } - } } } + // DS icon button stretched to the palette height. .palette-actions { - @extend %button-tertiary; + --button-height: calc(var(--height) - #{px2rem(16)}); grid-area: actions; - height: calc(var(--height) - deprecated.$s-16); - width: deprecated.$s-32; - padding: 0; - margin-left: deprecated.$s-4; - border-radius: deprecated.$br-8; - background-color: var(--palette-background-color); - z-index: deprecated.$z-index-2; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } + margin-inline-start: var(--sp-xs); } .palette { grid-area: palette; - width: 100%; - min-width: 0; + inline-size: 100%; + min-inline-size: 0; } } .handler { - @include deprecated.button-style; - @include deprecated.flex-center; - - width: deprecated.$s-12; - height: 100%; + display: flex; + justify-content: center; + align-items: center; + border: none; + background: none; + cursor: pointer; + inline-size: $sz-12; + block-size: 100%; .handler-btn { - width: deprecated.$s-4; - height: 100%; - max-height: deprecated.$s-40; - margin: deprecated.$s-8 deprecated.$s-4; + inline-size: px2rem(4); + block-size: 100%; + max-block-size: $sz-40; + margin: var(--sp-s) var(--sp-xs); padding: 0; - border-radius: deprecated.$s-4; - background-color: var(--palette-handler-background-color); + border-radius: $br-4; + background-color: var(--color-background-quaternary); } } .mid-palette, .small-palette { - grid-template-columns: deprecated.$s-64 auto 1fr; + grid-template-columns: px2rem(64) auto 1fr; } .hidden-bts { - right: deprecated.$s-2; - z-index: deprecated.$z-index-1; - width: 22px; - grid-template-columns: deprecated.$s-8 auto 1fr; + right: px2rem(2); + + // Back to the .viewport-overlays level (z-index 1) while collapsed. + z-index: 1; + width: px2rem(22); + grid-template-columns: px2rem(8) auto 1fr; padding: 0; border-inline-start: 0; border-start-start-radius: 0; border-end-start-radius: 0; .palette-btn-list { - opacity: deprecated.$op-0; + opacity: 0; visibility: hidden; - width: 0; + inline-size: 0; .palette-item { - opacity: deprecated.$op-0; + opacity: 0; visibility: hidden; - z-index: 0; } } .resize-area { visibility: hidden; - z-index: 0; - width: 0; + inline-size: 0; } .palette-actions { visibility: hidden; - z-index: 0; } .palette { visibility: hidden; - z-index: 0; } .handler { - padding-bottom: deprecated.$s-8; + padding-block-end: var(--sp-s); } } /** AI Chat button styles **/ +// Not used in palette.cljs: the external chat widget (Inkeep, see #6509) +// injected into #palette-wrapper may use these class names, so they stay. .help-btn { + --help-btn-foreground-color: var(--color-foreground-secondary); + --help-btn-background-color: var(--color-background-tertiary); + z-index: var(--z-index-panels); flex-shrink: 0; - - @extend %button-secondary; - + display: flex; + justify-content: center; + align-items: center; inline-size: $sz-40; block-size: $sz-40; - border-radius: deprecated.$br-circle; border: none; + border-radius: $br-circle; + background-color: var(--help-btn-background-color); + color: var(--help-btn-foreground-color); + cursor: pointer; - &.selected { - @extend %button-icon-selected; + // The widget markup is external, so its icon has no known class. + svg { + stroke: var(--help-btn-foreground-color); } &:hover { - border: none; + --help-btn-foreground-color: var(--color-accent-primary); + --help-btn-background-color: var(--color-background-quaternary); + + text-decoration: none; + } + + &:active { + --help-btn-foreground-color: var(--color-accent-primary); + --help-btn-background-color: var(--color-background-secondary); + + outline: none; + border: $b-1 solid var(--color-background-quaternary); + } + + &:focus-visible { + --help-btn-foreground-color: var(--color-foreground-secondary); + --help-btn-background-color: var(--color-background-tertiary); + + outline: none; + border: $b-1 solid var(--color-accent-primary); + } + + &:global(.disabled), + &[disabled], + &:disabled { + --help-btn-foreground-color: var(--color-foreground-disabled); + --help-btn-background-color: var(--color-background-quaternary); + + border: $b-1 solid var(--color-background-quaternary); + cursor: unset; + } + + &.selected { + --help-btn-foreground-color: var(--color-accent-primary); + --help-btn-background-color: var(--color-background-quaternary); + + outline: none; } } .icon-help { - @extend %button-icon; - - stroke: var(--icon-foreground); + display: flex; + justify-content: center; + align-items: center; inline-size: var(--sp-xxl); block-size: var(--sp-xxl); + color: transparent; + fill: none; + stroke-width: 1px; + stroke: var(--color-foreground-secondary); } diff --git a/frontend/src/app/main/ui/workspace/plugins.cljs b/frontend/src/app/main/ui/workspace/plugins.cljs index a6ff1b34f0..cd73c45664 100644 --- a/frontend/src/app/main/ui/workspace/plugins.cljs +++ b/frontend/src/app/main/ui/workspace/plugins.cljs @@ -20,7 +20,7 @@ [app.main.ui.components.title-bar :refer [title-bar*]] [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] - [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.icons :as deprecated-icon] [app.plugins.register :as preg] [app.util.avatars :as avatars] @@ -31,9 +31,6 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) -(def ^:private close-icon - (deprecated-icon/icon-xref :close (stl/css :close-icon))) - (defn- normalize-plugin-url "Automatically appens manifest.json if the plugin-uri comes without it." [plugin-url] @@ -211,7 +208,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog :plugin-management)} - [:button {:class (stl/css :close-btn) :on-click modal/hide!} close-icon] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}] [:div {:class (stl/css :modal-title)} (tr "workspace.plugins.title")] [:div {:class (stl/css :modal-content)} @@ -222,9 +223,9 @@ :placeholder (tr "workspace.plugins.search-placeholder") :class (stl/css-case :input-error error?)}] - [:button {:class (stl/css :primary-button) - :disabled @fetching-manifest? - :on-click on-install} + [:> button* {:variant "primary" + :disabled @fetching-manifest? + :on-click on-install} (tr "workspace.plugins.install")]] (when error-url? @@ -245,13 +246,17 @@ (if (empty? plugins-state) [:div {:class (stl/css :plugins-empty)} - [:div {:class (stl/css :plugins-empty-logo)} deprecated-icon/puzzle] + [:div {:class (stl/css :plugins-empty-logo)} + [:> icon* {:icon-id i/puzzle :size "m"}]] [:div {:class (stl/css :plugins-empty-text)} (tr "workspace.plugins.empty-plugins")] [:a {:class (stl/css :plugins-link) :href cfg/plugins-list-uri :target "_blank" :on-click #(st/emit! (ev/event {::ev/name "open-plugins-list"}))} - (tr "workspace.plugins.plugin-list-link") deprecated-icon/external-link]] + (tr "workspace.plugins.plugin-list-link") + [:> icon* {:icon-id i/external-link + :size "s" + :class (stl/css :plugins-link-icon)}]]] [:* [:> title-bar* {:collapsable false @@ -331,6 +336,34 @@ [:p {:class (stl/css :permissions-list-text)} (tr "workspace.plugins.permissions.allow-localstorage")]]) + (cond + (contains? permissions "allow:global") + [:div {:class (stl/css :permissions-list-entry)} + deprecated-icon/oauth-1 + [:p {:class (stl/css :permissions-list-text)} + (tr "workspace.plugins.permissions.allow-global")]]) + + (cond + (contains? permissions "manage:projects") + [:div {:class (stl/css :permissions-list-entry)} + deprecated-icon/oauth-1 + [:p {:class (stl/css :permissions-list-text)} + (tr "workspace.plugins.permissions.manage-projects")]]) + + (cond + (contains? permissions "manage:teams") + [:div {:class (stl/css :permissions-list-entry)} + deprecated-icon/oauth-1 + [:p {:class (stl/css :permissions-list-text)} + (tr "workspace.plugins.permissions.manage-teams")]]) + + (cond + (contains? permissions "manage:delete") + [:div {:class (stl/css :permissions-list-entry)} + deprecated-icon/oauth-1 + [:p {:class (stl/css :permissions-list-text)} + (tr "workspace.plugins.permissions.manage-delete")]]) + (cond (contains? permissions "clipboard:write") [:div {:class (stl/css :permissions-list-entry)} @@ -383,7 +416,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog :plugin-permissions)} - [:button {:class (stl/css :close-btn) :on-click on-close-dialog} close-icon] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click on-close-dialog}] [:div {:class (stl/css :modal-title)} (tr "workspace.plugins.permissions.title" (str/upper (:name plugin)))] [:div {:class (stl/css :modal-content)} @@ -395,17 +432,15 @@ [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} - [:input - {:class (stl/css :cancel-button :button-expand) - :type "button" - :value (tr "ds.confirm-cancel") - :on-click on-close-dialog}] + [:> button* {:variant "secondary" + :class (stl/css :button-expand) + :on-click on-close-dialog} + (tr "ds.confirm-cancel")] - [:input - {:class (stl/css :primary-button :button-expand) - :type "button" - :value (tr "ds.confirm-allow") - :on-click on-accept-dialog}]]]]])) + [:> button* {:variant "primary" + :class (stl/css :button-expand) + :on-click on-accept-dialog} + (tr "ds.confirm-allow")]]]]])) (mf/defc plugins-permissions-updated-dialog @@ -447,7 +482,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog :plugin-permissions)} - [:button {:class (stl/css :close-btn) :on-click on-close-dialog} close-icon] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click on-close-dialog}] [:div {:class (stl/css :modal-title)} (tr "workspace.plugins.permissions-update.title" (str/upper (:name plugin)))] @@ -458,17 +497,15 @@ [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} - [:input - {:class (stl/css :cancel-button :button-expand) - :type "button" - :value (tr "ds.confirm-cancel") - :on-click on-close-dialog}] + [:> button* {:variant "secondary" + :class (stl/css :button-expand) + :on-click on-close-dialog} + (tr "ds.confirm-cancel")] - [:input - {:class (stl/css :primary-button :button-expand) - :type "button" - :value (tr "ds.confirm-allow") - :on-click on-accept-dialog}]]]]])) + [:> button* {:variant "primary" + :class (stl/css :button-expand) + :on-click on-accept-dialog} + (tr "ds.confirm-allow")]]]]])) (mf/defc plugins-try-out-dialog @@ -502,7 +539,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog :plugin-try-out)} - [:button {:class (stl/css :close-btn) :on-click on-close-dialog} close-icon] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click on-close-dialog}] [:div {:class (stl/css :modal-title)} [:div {:class (stl/css :plugin-icon)} [:img {:src (if (some? icon) @@ -516,14 +557,12 @@ [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} - [:input - {:class (stl/css :cancel-button :button-expand) - :type "button" - :value (tr "workspace.plugins.try-out.cancel") - :on-click on-close-dialog}] + [:> button* {:variant "secondary" + :class (stl/css :button-expand) + :on-click on-close-dialog} + (tr "workspace.plugins.try-out.cancel")] - [:input - {:class (stl/css :primary-button :button-expand) - :type "button" - :value (tr "workspace.plugins.try-out.try") - :on-click on-accept-dialog}]]]]])) + [:> button* {:variant "primary" + :class (stl/css :button-expand) + :on-click on-accept-dialog} + (tr "workspace.plugins.try-out.try")]]]]])) diff --git a/frontend/src/app/main/ui/workspace/plugins.scss b/frontend/src/app/main/ui/workspace/plugins.scss index c67d5ea498..40f770c57c 100644 --- a/frontend/src/app/main/ui/workspace/plugins.scss +++ b/frontend/src/app/main/ui/workspace/plugins.scss @@ -4,8 +4,15 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; @use "ds/mixins.scss" as *; +@use "ds/typography.scss" as *; + +// Only for the modal placeholders (%modal-overlay-base, +// %modal-container-base), until the modals move to the DS modal. +@use "refactor/common-refactor.scss" as deprecated; .modal-overlay { @extend %modal-overlay-base; @@ -17,49 +24,47 @@ display: grid; grid-template-rows: auto 1fr auto; grid-template-columns: minmax(0, 1fr); - max-height: initial; + max-block-size: initial; &.plugin-permissions { - width: deprecated.$s-412; - max-width: deprecated.$s-412; + inline-size: px2rem(412); + max-inline-size: px2rem(412); } &.plugin-management { - width: deprecated.$s-472; - max-width: deprecated.$s-472; + inline-size: px2rem(472); + max-inline-size: px2rem(472); } &.plugin-try-out { - width: deprecated.$s-452; - max-width: deprecated.$s-452; + inline-size: px2rem(452); + max-inline-size: px2rem(452); } + // The separator of the plugins manager has no class. hr { border-color: var(--color-background-tertiary); } } .modal-footer { - margin-top: 2rem; + margin-block-start: var(--sp-xxxl); } +// Goes on the tooltip wrapper of the close icon button. .close-btn { - @extend %modal-close-btn-base; -} - -.close-icon { - @extend %button-icon; - - stroke: var(--icon-foreground); + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-title { - @include deprecated.headline-medium-typography; + @include use-typography("headline-medium"); - margin-block-end: deprecated.$s-32; - color: var(--modal-title-foreground-color); + margin-block-end: var(--sp-xxxl); + color: var(--color-foreground-primary); display: flex; - gap: deprecated.$s-12; + gap: var(--sp-m); } .modal-content { @@ -67,113 +72,92 @@ flex-direction: column; .plugin-permissions & { - gap: deprecated.$s-20; + gap: var(--sp-xl); } .plugin-management & { - height: deprecated.$s-380; - max-height: deprecated.$s-380; + block-size: px2rem(380); + max-block-size: px2rem(380); } } .modal-message { - font-size: deprecated.$fs-14; + // TODO: No exact DS typography match found. + // Closest match: body-medium. + font-size: px2rem(14); color: var(--color-foreground-secondary); } .modal-paragraph { - font-size: deprecated.$fs-14; + // TODO: No exact DS typography match found. + // Closest match: body-medium. + font-size: px2rem(14); color: var(--color-foreground-primary); } -.primary-button { - @extend %button-primary; - @include deprecated.headline-small-typography; - - padding: deprecated.$s-0 deprecated.$s-16; -} - +// The DS button does not center its label when it grows. .button-expand { - width: 100%; + inline-size: 100%; margin: 0; -} - -.cancel-button { - @extend %button-secondary; - @include deprecated.headline-small-typography; - - padding: deprecated.$s-0 deprecated.$s-16; -} - -.search-icon { - @include deprecated.flex-center; - - width: deprecated.$s-20; - padding: 0 0 0 deprecated.$s-8; - - svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - } + justify-content: center; } .top-bar { display: flex; - gap: deprecated.$s-8; + gap: var(--sp-s); } .open-button { display: flex; justify-content: center; align-items: center; - width: deprecated.$s-68; - min-width: deprecated.$s-68; - height: deprecated.$s-32; + inline-size: px2rem(68); + min-inline-size: px2rem(68); + block-size: $sz-32; text-transform: uppercase; } .plugins-list { - padding-top: deprecated.$s-20; + padding-block-start: var(--sp-xl); overflow: hidden auto; flex: 1; display: flex; flex-direction: column; - gap: deprecated.$s-12; + gap: var(--sp-m); } .plugins-list-element { display: flex; - gap: deprecated.$s-12; + gap: var(--sp-m); } .plugin-icon { - min-width: deprecated.$s-32; - min-height: deprecated.$s-32; - width: deprecated.$s-32; - height: deprecated.$s-32; - background: var(--button-secondary-background-color-rest); - padding: deprecated.$s-2; - border-radius: deprecated.$s-4; + min-inline-size: $sz-32; + min-block-size: $sz-32; + inline-size: $sz-32; + block-size: $sz-32; + background: var(--color-background-tertiary); + padding: var(--sp-xxs); + border-radius: $br-4; } .plugin-description { display: flex; flex-direction: column; - gap: deprecated.$s-8; - width: 100%; - min-width: 0; + gap: var(--sp-s); + inline-size: 100%; + min-inline-size: 0; } .plugin-title { - @include deprecated.body-medium-typography; + @include use-typography("body-medium"); @include text-ellipsis; color: var(--color-foreground-primary); } .plugin-summary { - @include deprecated.body-small-typography; + @include use-typography("body-small"); @include two-line-text-ellipsis; overflow-wrap: anywhere; @@ -184,116 +168,111 @@ display: flex; flex-direction: column; align-items: center; - gap: deprecated.$s-20; - height: 100%; + gap: var(--sp-xl); + block-size: 100%; justify-content: center; - padding: deprecated.$s-36 0; + padding: px2rem(36) 0; } .plugins-empty-logo { - width: deprecated.$s-44; - height: deprecated.$s-44; - border-radius: 50%; + inline-size: px2rem(44); + block-size: px2rem(44); + border-radius: $br-circle; display: flex; justify-content: center; align-items: center; background: var(--color-background-tertiary); - - svg { - width: deprecated.$s-16; - height: deprecated.$s-16; - stroke: var(--color-foreground-secondary); - fill: none; - } + color: var(--color-foreground-secondary); } .plugins-empty-text { - @include deprecated.body-small-typography; + @include use-typography("body-small"); color: var(--color-foreground-primary); } div.input-error { - border: deprecated.$s-1 solid var(--input-border-color-error); + border: $b-1 solid var(--status-color-error-500); } .info { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - margin-top: deprecated.$s-4; + margin-block-start: var(--sp-xs); + color: var(--info-foreground-color); &.error { - color: var(--input-border-color-error); + --info-foreground-color: var(--status-color-error-500); } &.success { - color: var(--input-border-color-success); + --info-foreground-color: var(--color-accent-primary); } } .plugins-link { + // TODO: No exact DS typography match found. + // Closest match: body-small. + font-size: px2rem(12); color: var(--color-accent-primary); - font-size: deprecated.$fs-12; display: inline-flex; align-items: center; - gap: deprecated.$s-4; + gap: var(--sp-xs); +} - svg { - margin-top: calc(-1 * deprecated.$s-2); - width: deprecated.$s-12; - height: deprecated.$s-12; - stroke: var(--color-accent-primary); - fill: none; - } +.plugins-link-icon { + margin-block-start: calc(-1 * var(--sp-xxs)); } .permissions-list { display: flex; flex-direction: column; - gap: deprecated.$s-24; + gap: var(--sp-xxl); } .permissions-list-entry { display: grid; - grid-template-columns: 24px 1fr; - gap: deprecated.$s-16; + grid-template-columns: $sz-24 1fr; + gap: var(--sp-l); align-items: center; + // The permission icons are legacy brand art with no class. svg { - width: deprecated.$s-24; - height: deprecated.$s-24; + inline-size: $sz-24; + block-size: $sz-24; stroke: var(--color-accent-primary); fill: none; } } .permissions-list-text { - @include deprecated.body-small-typography; + @include use-typography("body-small"); margin: 0; color: var(--color-foreground-secondary); } .permissions-disclaimer { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - padding: deprecated.$s-16; + padding: var(--sp-l); background: var(--color-background-quaternary); color: var(--color-foreground-primary); - border-radius: deprecated.$br-4; + border-radius: $br-4; } .action-buttons { display: flex; - gap: deprecated.$s-12; + gap: var(--sp-m); } .discover { - @include deprecated.body-small-typography; + @include use-typography("body-small"); color: var(--color-foreground-secondary); - margin-top: deprecated.$s-24; + margin-block-start: var(--sp-xxl); + // The link comes from a translated HTML string, with no class. a { color: var(--color-accent-primary); } diff --git a/frontend/src/app/main/ui/workspace/right_header.cljs b/frontend/src/app/main/ui/workspace/right_header.cljs index 28233e3204..b76ac50fa9 100644 --- a/frontend/src/app/main/ui/workspace/right_header.cljs +++ b/frontend/src/app/main/ui/workspace/right_header.cljs @@ -20,11 +20,11 @@ [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.context :as ctx] [app.main.ui.dashboard.team] + [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.exports.assets :refer [progress-widget]] [app.main.ui.formats :as fmt] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.workspace.presence :refer [active-sessions*]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -44,11 +44,11 @@ custom-shortcuts (mf/deref refs/custom-shortcuts) get-tt #(sc/get-effective-tooltip % custom-shortcuts) - open-dropdown + toggle-dropdown (mf/use-fn (fn [event] (dom/stop-propagation event) - (reset! open* true))) + (swap! open* not))) close-dropdown (mf/use-fn @@ -73,10 +73,12 @@ zoom (fmt/format-percent zoom {:precision 0})] [:* - [:div {:on-click open-dropdown - :class (stl/css-case :zoom-widget true - :selected open?) - :title (tr "workspace.header.zoom")} + [:button {:type "button" + :on-click toggle-dropdown + :class (stl/css-case :zoom-widget true + :selected open?) + :title (tr "workspace.header.zoom") + :aria-expanded open?} [:span {:class (stl/css :label)} zoom]] [:& dropdown {:show open? :on-close close-dropdown} [:ul {:class (stl/css :dropdown)} @@ -91,8 +93,8 @@ :aria-label (tr "shortcuts.increase-zoom") :on-click on-increase :icon i/add}]] - [:button {:class (stl/css :reset-btn) - :on-click on-zoom-reset} + [:> button* {:variant "ghost" + :on-click on-zoom-reset} (tr "workspace.header.reset-zoom")]] [:li {:class (stl/css :zoom-option) :on-click on-zoom-fit} @@ -215,35 +217,36 @@ :on-zoom-selected on-zoom-selected}]] [:div {:class (stl/css :comments-section)} - [:button {:title (tr "workspace.toolbar.comments" (get-tt :add-comment)) - :aria-label (tr "workspace.toolbar.comments" (get-tt :add-comment)) - :class (stl/css-case :comments-btn true - :selected (= selected-drawtool :comments)) - :on-click toggle-comments - :data-tool "comments" - :style {:position "relative"}} - deprecated-icon/comments + [:> icon-button* {:variant "action" + :class (stl/css :comments-btn) + :tooltip-placement "bottom-left" + :aria-pressed (= selected-drawtool :comments) + :aria-label (tr "workspace.toolbar.comments" (get-tt :add-comment)) + :icon i/comments + :on-click toggle-comments + :data-tool "comments"} (when ^boolean has-unread-comments? [:div {:class (stl/css :unread)}])]] (when-not ^boolean read-only? [:div {:class (stl/css :history-section)} - [:button - {:title (tr "workspace.sidebar.history") - :aria-label (tr "workspace.sidebar.history") - :class (stl/css-case :selected (contains? layout :document-history) - :history-button true) - :on-click toggle-history} - deprecated-icon/history]]) + [:> icon-button* {:variant "action" + :tooltip-placement "bottom-left" + :aria-pressed (contains? layout :document-history) + :aria-label (tr "workspace.sidebar.history") + :icon i/history + :on-click toggle-history}]]) (when display-share-button? - [:a {:class (stl/css :viewer-btn) - :title (tr "workspace.header.share") - :on-click open-share-dialog} - deprecated-icon/share]) + [:> icon-button* {:variant "action" + :tooltip-placement "bottom-left" + :aria-label (tr "workspace.header.share") + :icon i/share + :on-click open-share-dialog}]) - [:a {:class (stl/css :viewer-btn) - :title (tr "workspace.header.viewer" (get-tt :open-viewer)) - :on-click nav-to-viewer} - deprecated-icon/play]])) + [:> icon-button* {:variant "action" + :tooltip-placement "bottom-left" + :aria-label (tr "workspace.header.viewer" (get-tt :open-viewer)) + :icon i/play + :on-click nav-to-viewer}]])) diff --git a/frontend/src/app/main/ui/workspace/right_header.scss b/frontend/src/app/main/ui/workspace/right_header.scss index 2b338659aa..a67b400537 100644 --- a/frontend/src/app/main/ui/workspace/right_header.scss +++ b/frontend/src/app/main/ui/workspace/right_header.scss @@ -4,23 +4,30 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; + +// Only for the shared menu placeholders (%menu-dropdown, %menu-item-base, +// %shortcut-*), which move with the menus group. @use "refactor/common-refactor.scss" as deprecated; .workspace-header-right { display: flex; justify-content: space-between; align-items: center; - min-width: deprecated.$s-256; - padding: deprecated.$s-8; - gap: deprecated.$s-8; - background-color: var(--panel-background-color); + min-inline-size: px2rem(256); + padding: var(--sp-s); + gap: var(--sp-m); + background-color: var(--color-background-primary); } .users-section { position: relative; - min-width: deprecated.$s-32; - max-width: deprecated.$s-72; - padding: deprecated.$s-4 deprecated.$s-6; + min-inline-size: $sz-32; + max-inline-size: px2rem(72); + padding: var(--sp-xs) px2rem(6); } .separator { @@ -28,49 +35,46 @@ } .zoom-widget { - @include deprecated.button-style; + --zoom-widget-foreground-color: var(--color-foreground-secondary); display: flex; align-items: center; justify-content: center; - height: deprecated.$s-28; - max-width: deprecated.$s-48; - width: deprecated.$s-48; - border-radius: deprecated.$br-8; + block-size: $sz-28; + max-inline-size: $sz-48; + inline-size: $sz-48; + padding: 0; + border: none; + border-radius: $br-8; + background: none; + cursor: pointer; .label { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - height: 100%; - padding: deprecated.$s-8 0; - color: var(--button-tertiary-foreground-color-rest); - } - - &:hover { - .label { - color: var(--button-tertiary-foreground-color-focus); - } + block-size: 100%; + padding: var(--sp-s) 0; + color: var(--zoom-widget-foreground-color); } + &:hover, &.selected { - .label { - color: var(--button-tertiary-foreground-color-focus); - } + --zoom-widget-foreground-color: var(--color-foreground-primary); } } .dropdown { @extend %menu-dropdown; - right: deprecated.$s-2; - top: calc(deprecated.$s-2 + deprecated.$s-48); - width: deprecated.$s-272; + inset-inline-end: px2rem(2); + inset-block-start: px2rem(50); + inline-size: px2rem(272); } .basic-zoom-bar { display: flex; justify-content: space-between; - padding: deprecated.$s-6; + padding: px2rem(6); cursor: auto; } @@ -79,21 +83,14 @@ } .zoom-text { - @include deprecated.flex-center; - - height: 100%; - min-width: deprecated.$s-48; + display: flex; + justify-content: center; + align-items: center; + block-size: 100%; + min-inline-size: $sz-48; padding: 0; - margin: 0 deprecated.$s-2; - color: var(--modal-title-foreground-color); -} - -.reset-btn { - @extend %button-tertiary; - - color: var(--button-tertiary-foreground-color-hover); - height: deprecated.$s-28; - border-radius: deprecated.$br-8; + margin: 0 px2rem(2); + color: var(--color-foreground-primary); } .zoom-option { @@ -108,152 +105,30 @@ } &:hover { - color: var(--menu-foreground-color-hover); + color: var(--color-foreground-primary); .shortcuts { .shortcut-key { - color: var(--menu-foreground-color-hover); + color: var(--color-foreground-primary); } } } } +// Holds the unread dot. .comments-btn { - @extend %button-tertiary; - - border-radius: deprecated.$br-8; - margin: 0; - height: deprecated.$s-28; - width: deprecated.$s-28; - border: none; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - height: deprecated.$s-16; - width: deprecated.$s-16; - } - - &:hover { - background-color: transparent; - border: none; - } - - &.selected { - background-color: var(--button-tertiary-background-color-selected); - - svg { - stroke: var(--button-tertiary-foreground-color-active); - } - } -} - -.history-button { - @extend %button-tertiary; - - border-radius: deprecated.$br-8; - margin: 0; - height: deprecated.$s-28; - width: deprecated.$s-28; - border: none; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - height: deprecated.$s-16; - width: deprecated.$s-16; - } - - &:hover { - background-color: transparent; - border: none; - } - - &.selected { - background-color: var(--button-tertiary-background-color-selected); - - svg { - stroke: var(--button-tertiary-foreground-color-active); - } - } -} - -.persistence-status-widget { - @include deprecated.flex-center; - - width: deprecated.$s-28; - height: deprecated.$s-28; -} - -.status-icon { - @include deprecated.flex-center; - - width: deprecated.$s-24; - height: deprecated.$s-24; - margin: 0; - border-radius: deprecated.$br-circle; - - svg { - @extend %button-icon; - - stroke: var(--status-widget-icon-foreground-color); - } -} - -.pending-status { - background-color: var(--status-widget-background-color-warning); -} - -.saving-status { - background-color: var(--status-widget-background-color-pending); - - svg { - animation: spin-animation 1s infinite; - animation-timing-function: linear; - } -} - -.saved-status { - background-color: var(--status-widget-background-color-success); -} - -.error-status { - background-color: var(--status-widget-background-color-error); -} - -.share-btn, -.viewer-btn { - @extend %button-tertiary; - - border-radius: deprecated.$br-8; - margin: 0; - width: deprecated.$s-28; - height: deprecated.$s-28; - border: none; - - svg { - @extend %button-icon; - - height: deprecated.$s-16; - width: deprecated.$s-16; - stroke: var(--icon-foreground); - } - - &:hover { - background-color: transparent; - border: none; - } + position: relative; } .unread { position: absolute; - width: 8px; - height: 8px; - border: 2px solid var(--color-background-tertiary); - border-radius: 50%; + inline-size: px2rem(8); + block-size: px2rem(8); + border: $b-2 solid var(--color-background-tertiary); + border-radius: $br-circle; background: var(--color-foreground-error); - top: 6px; - right: 6px; + + // Over the top right corner of the icon, 4px from the 24px button edge. + inset-block-start: var(--sp-xs); + inset-inline-end: var(--sp-xs); } diff --git a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs index 5a9f1f3a92..74e702c681 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs @@ -18,6 +18,8 @@ [app.common.types.text :as txt] [app.config :as cf] [app.main.data.helpers :as dsh] + [app.main.data.persistence :as-alias dps] + [app.main.data.persistence.lock :as plk] [app.main.data.workspace :as dw] [app.main.data.workspace.texts :as dwt] [app.main.features :as features] @@ -34,7 +36,9 @@ [app.util.text.content :as content] [app.util.text.content.styles :as styles] [app.util.timers :as ts] + [beicon.v2.core :as rx] [cuerdas.core :as str] + [potok.v2.core :as ptk] [rumext.v2 :as mf])) (defn get-contrast-color [background-color] @@ -182,7 +186,28 @@ on-clipboard-change (fn [event] (let [style (.-detail event)] - (st/emit! (dw/set-clipboard-style style))))] + (st/emit! (dw/set-clipboard-style style)))) + + ;; A rollback that reverts the text or its styles puts the reverted + ;; content in the editor, which would otherwise keep showing and + ;; saving the rejected one. Saves failing do the same with the last + ;; committed content, since the typing not yet committed is dropped. + ;; The WASM canvas draws the editor's layout, so it is laid out + ;; again from that content. + rolled-back-sub + (->> st/stream + (rx/filter #(or (ptk/type? ::dps/rolled-back %) + (plk/cancel-gestures? %))) + (rx/subs! (fn [_] + (let [content (-> (dsh/lookup-page-objects @st/state) + (get shape-id) + :content) + current (content/dom->cljs (dwt/get-editor-root instance))] + (when (and (some? content) + (not (dwt/same-styled-text? content current))) + (dwt/set-editor-root! instance (content/cljs->dom content)) + (when (features/active-feature? @st/state "render-wasm/v1") + (st/emit! (dwt/v2-sync-wasm-text-layout shape-id content))))))))] (.addEventListener ^js global/document "keyup" on-key-up) (.addEventListener ^js instance "focus" on-focus) @@ -210,6 +235,7 @@ (.removeEventListener ^js instance "stylechange" on-style-change) (.removeEventListener ^js instance "change" on-change) (.removeEventListener ^js instance "clipboardchange" on-clipboard-change) + (rx/dispose! rolled-back-sub) (dwt/dispose! instance) (st/emit! (dwt/update-editor nil))))) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs index 74f95eea13..403f6c3030 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs @@ -19,7 +19,6 @@ [app.main.ui.css-cursors :as cur] [app.render-wasm.api :as wasm.api] [app.render-wasm.text-editor :as text-editor] - [app.util.clipboard :as clipboard] [app.util.dom :as dom] [app.util.keyboard :as kbd] [app.util.timers :as ts] @@ -59,35 +58,19 @@ "Home" "End" "PageUp" "PageDown" "Enter" "Backspace" "Delete" "Escape" "Tab"}) -(defn- caret-position - "Collapsed caret as {:para :offset} from the WASM selection, or nil." - [] - (when-let [{:keys [focus-para focus-offset]} (text-editor/text-editor-get-selection)] - {:para focus-para :offset focus-offset})) - -(defn- typed-range - "Normalized range covering the text inserted between `before` and `after`, or nil." - [before after] - (when (and before after) - (if (or (< (:para before) (:para after)) - (and (= (:para before) (:para after)) - (<= (:offset before) (:offset after)))) - {:start-para (:para before) :start-offset (:offset before) - :end-para (:para after) :end-offset (:offset after)} - {:start-para (:para after) :start-offset (:offset after) - :end-para (:para before) :end-offset (:offset before)}))) - (defn- sync-with-pending-caret-styles! - "Commit an insertion that consumed a pending caret style: sync the new text, - then restyle the just-typed `range` into its own span. `before` is the + "Commit an insertion that consumed a pending caret style. `before` is the pre-insert caret." [shape-id before] - (let [range (typed-range before (caret-position))] - ;; Sync first so the cached content stays index-aligned with WASM. - (text-editor/text-editor-sync-content) - (if-let [{:keys [content]} (wasm.api/apply-pending-caret-styles! shape-id range)] - (st/emit! (dwt/v3-update-editor-content shape-id content)) - (sync-wasm-text-editor-content!)))) + (when-let [event (dwt/v3-apply-pending-caret-styles shape-id before)] + (st/emit! event))) + +(defn- write-clipboard-items + "Write `items` (mime type -> string) to the DataTransfer of a copy or cut event. + Windows apps prefer text/html, or they may paste the editor's empty `
`." + [^js data items] + (doseq [[mime value] items] + (.setData data mime value))) (defn- collapse-input-caret "Replaces the browser selection with a collapsed caret at the end of `node`." @@ -281,45 +264,29 @@ (mf/use-fn (fn [^js event] (dom/prevent-default event) - ;; Pasted text keeps the surrounding style; drop any pending caret style. - (text-editor/clear-pending-caret-styles!) - (let [clipboard-data (.-clipboardData event) - text (.getData clipboard-data "text/plain")] - (when (and text (seq text)) - (text-editor/text-editor-insert-text text) - (sync-wasm-text-editor-content!) - (wasm.api/request-render-preserving-target "text-paste")) - (reset-input-node (mf/ref-val contenteditable-ref))))) + (when-let [data (.-clipboardData event)] + (st/emit! (dwt/v3-paste (.getData data "text/html") + (.getData data "text/plain")))) + (reset-input-node (mf/ref-val contenteditable-ref)))) on-copy (mf/use-fn (fn [^js event] (when (text-editor/text-editor-has-focus?) (dom/prevent-default event) - (when (text-editor/text-editor-has-selection?) - (let [text (or (text-editor/text-editor-export-selection) "") - html (clipboard/plain-text->html text) - data (.-clipboardData event)] - ;; text/html matters on Windows: many apps prefer CF_HTML, and - ;; without it they can pick up the empty contenteditable `
`. - (.setData data "text/plain" text) - (.setData data "text/html" html)))))) + (when-let [items (dwt/editor-selection-clipboard-data @st/state)] + (write-clipboard-items (.-clipboardData event) items))))) on-cut (mf/use-fn (fn [^js event] (when (text-editor/text-editor-has-focus?) (dom/prevent-default event) - (when (text-editor/text-editor-has-selection?) - (let [text (or (text-editor/text-editor-export-selection) "") - html (clipboard/plain-text->html text) - data (.-clipboardData event)] - (.setData data "text/plain" text) - (.setData data "text/html" html) - (when (seq text) - (text-editor/text-editor-delete-backward) - (sync-wasm-text-editor-content!) - (wasm.api/request-render-preserving-target "text-cut")))) + (when-let [items (dwt/editor-selection-clipboard-data @st/state)] + (write-clipboard-items (.-clipboardData event) items) + (text-editor/text-editor-delete-backward) + (sync-wasm-text-editor-content!) + (wasm.api/request-render-preserving-target "text-cut")) (reset-input-node (mf/ref-val contenteditable-ref))))) on-key-down @@ -470,7 +437,7 @@ (let [shape-id (text-editor/text-editor-get-active-shape-id) ;; The inserted character adopts a pending caret style, if any. pending-styles? (some? (text-editor/get-pending-caret-styles shape-id)) - before (when pending-styles? (caret-position))] + before (when pending-styles? (text-editor/caret-position))] (text-editor/text-editor-insert-text data) (if pending-styles? (sync-with-pending-caret-styles! shape-id before) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 9650b235f3..ff5b91e7ef 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -18,8 +18,9 @@ [app.main.ui.components.context-menu-a11y :refer [context-menu*]] [app.main.ui.components.search-bar :refer [search-bar*]] [app.main.ui.context :as ctx] + [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.workspace.sidebar.assets.common :as cmm] [app.main.ui.workspace.sidebar.assets.file-library :refer [file-library*]] [app.main.ui.workspace.sidebar.scroll :as sc] @@ -201,14 +202,18 @@ [:div {:class (stl/css :assets-header)} (when-not ^boolean read-only? (if (and (= num-libs 1) (empty? components) (not shared?)) - [:button {:class (stl/css :add-library-button) - :on-click show-libraries-dialog - :data-testid "libraries"} + [:> button* {:variant "primary" + :type "button" + :class (stl/css :libraries-button) + :on-click show-libraries-dialog + :data-testid "libraries"} (tr "workspace.assets.add-library")] - [:button {:class (stl/css :libraries-button) - :on-click show-libraries-dialog - :data-testid "libraries"} + [:> button* {:variant "secondary" + :type "button" + :class (stl/css :libraries-button) + :on-click show-libraries-dialog + :data-testid "libraries"} (tr "workspace.assets.manage-library")])) @@ -221,7 +226,8 @@ :title (tr "workspace.assets.filter") :class (stl/css-case :section-button true :opened menu-open?)} - deprecated-icon/filter-icon]] + [:> icon* {:icon-id i/filter + :class (stl/css :section-icon)}]]] [:> context-menu* {:on-close on-menu-close diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.scss b/frontend/src/app/main/ui/workspace/sidebar/assets.scss index 716d0682b8..97fb0733ca 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.scss @@ -4,141 +4,97 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; .assets-bar { display: grid; grid-auto-rows: max-content; // TODO: ugly hack :( Fix this! we shouldn't be hardcoding this height - height: calc(100vh - deprecated.$s-92); + block-size: calc(100vh - #{px2rem(92)}); scrollbar-gutter: stable; overflow-y: auto; - padding-top: deprecated.$s-8; + padding-block-start: var(--sp-s); } +// A DS button keeps its label at the start when it grows. .libraries-button { - @extend %button-secondary; - @include deprecated.uppercase-title-typography; - - gap: deprecated.$s-2; - height: deprecated.$s-32; - width: 100%; - margin-bottom: deprecated.$s-4; - border-radius: deprecated.$s-8; - - &:hover { - background-color: var(--button-secondary-background-color-hover); - color: var(--button-secondary-foreground-color-hover); - border: deprecated.$s-1 solid var(--button-secondary-border-color-hover); - } - - &:focus { - background-color: var(--button-secondary-background-color-focus); - color: var(--button-secondary-foreground-color-focus); - border: deprecated.$s-1 solid var(--button-secondary-border-color-focus); - } -} - -.add-library-button { - @extend %button-primary; - @include deprecated.uppercase-title-typography; - - gap: deprecated.$s-2; - height: deprecated.$s-32; - width: 100%; - margin-bottom: deprecated.$s-4; - border-radius: deprecated.$s-8; + inline-size: 100%; + margin-block-end: var(--sp-xs); + justify-content: center; } .section-button { - @include deprecated.flex-center; - @include deprecated.button-style; - - height: deprecated.$s-32; - width: deprecated.$s-32; + display: flex; + justify-content: center; + align-items: center; + background: none; + cursor: pointer; + block-size: $sz-32; + inline-size: $sz-32; margin: 0; - border: deprecated.$s-1 solid var(--input-border-color-rest); - border-radius: deprecated.$br-8 deprecated.$br-2 deprecated.$br-2 deprecated.$br-8; - background-color: var(--input-background-color-rest); + border: $b-1 solid var(--color-background-tertiary); + border-radius: $br-8 px2rem(2) px2rem(2) $br-8; + background-color: var(--color-background-tertiary); - svg { - height: deprecated.$s-16; - width: deprecated.$s-16; - stroke: var(--icon-foreground); + .section-icon { + flex-shrink: 0; + color: var(--color-foreground-secondary); + } + + // Before :focus and :hover, as the legacy placeholder was: the focus + // border wins over the opened state. + &.opened { + outline: none; + border-color: var(--color-background-quaternary); + background-color: var(--color-background-quaternary); + color: var(--color-accent-primary); + + .section-icon { + color: var(--color-accent-primary); + } } &:focus { - border: deprecated.$s-1 solid var(--input-border-color-focus); + border: $b-1 solid var(--color-accent-primary); outline: 0; - background-color: var(--input-background-color-focus); - color: var(--input-foreground-color-focus); + background-color: var(--color-background-tertiary); + color: var(--color-foreground-primary); - svg { - background-color: var(--input-background-color-focus); + .section-icon { + background-color: var(--color-background-tertiary); } } &:hover { - border: deprecated.$s-1 solid var(--input-border-color-hover); - background-color: var(--input-background-color-hover); + border: $b-1 solid var(--color-background-quaternary); + background-color: var(--color-background-quaternary); - svg { - background-color: var(--input-background-color-hover); - stroke: var(--button-foreground-hover); + .section-icon { + background-color: var(--color-background-quaternary); + color: var(--color-accent-primary); } &:focus { - border: deprecated.$s-1 solid var(--input-border-color-focus); + border: $b-1 solid var(--color-accent-primary); outline: 0; - background-color: var(--input-background-color-focus); - color: var(--input-foreground-color-focus); + background-color: var(--color-background-tertiary); + color: var(--color-foreground-primary); - svg { - background-color: var(--input-background-color-focus); + .section-icon { + background-color: var(--color-background-tertiary); } } } - - &.opened { - @extend %button-icon-selected; - } -} - -.sections-container { - @include deprecated.menu-shadow; - @include deprecated.flex-column; - - position: absolute; - top: deprecated.$s-84; - left: deprecated.$s-12; - width: deprecated.$s-192; - padding: deprecated.$s-4; - border-radius: deprecated.$br-8; - background-color: var(--menu-background-color); - z-index: deprecated.$z-index-2; -} - -.section-item { - @include deprecated.body-small-typography; - - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - padding: deprecated.$s-6; - border-radius: deprecated.$br-8; -} - -.section-btn { - @include deprecated.button-style; } .assets-header { - padding: 0 0 deprecated.$s-24 deprecated.$s-12; + padding: 0 0 var(--sp-xxl) var(--sp-m); } .search-wrapper { display: flex; - gap: deprecated.$s-4; + gap: var(--sp-xs); } diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs index b072139f1e..745491c625 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.cljs @@ -14,9 +14,9 @@ [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.components.title-bar :refer [title-bar*]] + [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.workspace.sidebar.assets.common :as cmm] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -159,8 +159,11 @@ (if create? (tr "workspace.assets.create-group") (tr "workspace.assets.rename-group"))] - [:button {:class (stl/css :modal-close-btn) - :on-click modal/hide!} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click modal/hide!}]] [:div {:class (stl/css :modal-content)} [:& fm/form {:form form :on-submit on-accept} @@ -172,16 +175,13 @@ [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} - [:input - {:class (stl/css :cancel-button) - :type "button" - :value (tr "labels.cancel") - :on-click modal/hide!}] + [:> button* {:variant "secondary" + :type "button" + :on-click modal/hide!} + (tr "labels.cancel")] - [:input - {:type "button" - :class (stl/css-case :accept-btn true - :global/disabled (not (:valid @form))) - :disabled (not (:valid @form)) - :value (if create? (tr "labels.create") (tr "labels.rename")) - :on-click on-accept}]]]]])) + [:> button* {:variant "primary" + :type "button" + :disabled (not (:valid @form)) + :on-click on-accept} + (if create? (tr "labels.create") (tr "labels.rename"))]]]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss index 003db6e756..2e752f499d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/groups.scss @@ -4,10 +4,14 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*), which move with +// the modals refactor, and the input placeholder (%input-with-label). @use "refactor/common-refactor.scss" as deprecated; +@use "ds/mixins.scss" as *; +@use "ds/typography.scss" as *; .group-title-wrapper { - width: 100%; + inline-size: 100%; display: flex; justify-content: space-between; @@ -30,21 +34,21 @@ } .group-title { - padding-left: deprecated.$s-4; + padding-inline-start: var(--sp-xs); } .pre-path { - margin-left: deprecated.$s-2; + margin-inline-start: var(--sp-xxs); text-transform: initial; - color: var(--title-foreground-color); + color: var(--color-foreground-secondary); } .path { - @include deprecated.text-ellipsis; + @include text-ellipsis; - margin-left: deprecated.$s-2; + margin-inline-start: var(--sp-xxs); text-transform: initial; - color: var(--title-foreground-color-hover); + color: var(--color-foreground-primary); } .modal-overlay { @@ -56,44 +60,35 @@ } .modal-header { - margin-bottom: deprecated.$s-24; + margin-block-end: var(--sp-xxl); } .modal-title { - @include deprecated.uppercase-title-typography; + @include use-typography("headline-small"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } +// Tooltip wrapper of the close button: it places it in the corner. .modal-close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - margin-bottom: deprecated.$s-24; + margin-block-end: var(--sp-xxl); } .input-wrapper { @extend %input-with-label; - @include deprecated.body-small-typography; + @include use-typography("body-small"); - margin-bottom: deprecated.$s-8; + margin-block-end: var(--sp-s); } .action-buttons { @extend %modal-action-btns; } - -.cancel-button { - @extend %modal-cancel-btn; -} - -.accept-btn { - @extend %modal-accept-btn; - - &.danger { - @extend %modal-danger-btn; - } -} diff --git a/frontend/src/app/main/ui/workspace/sidebar/debug.scss b/frontend/src/app/main/ui/workspace/sidebar/debug.scss index da70295d97..ced6252661 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/debug.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/debug.scss @@ -4,26 +4,34 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +@use "ds/_sizes.scss" as *; + +// Only for the checkbox placeholders (%input-checkbox, %checkbox-icon), +// until the checkboxes move to the DS. @use "refactor/common-refactor.scss" as deprecated; .debug-panel { display: flex; flex-direction: column; - background-color: var(--panel-background-color); + background-color: var(--color-background-primary); } .debug-panel-title { margin: var(--sp-s) var(--sp-s) 0 var(--sp-s); } +// The options list scrolls under the title when it does not fit. .debug-panel-inner { - padding: deprecated.$s-16 deprecated.$s-8; + flex: 1; + min-block-size: 0; + overflow-y: auto; + padding: var(--sp-l) var(--sp-s); } .checkbox-wrapper { @extend %input-checkbox; - height: deprecated.$s-32; + block-size: $sz-32; padding: 0; } diff --git a/frontend/src/app/main/ui/workspace/sidebar/debug_shape_info.cljs b/frontend/src/app/main/ui/workspace/sidebar/debug_shape_info.cljs index 92c9ae2e2b..d2cc9888af 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/debug_shape_info.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/debug_shape_info.cljs @@ -13,6 +13,7 @@ [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.ds.buttons.button :refer [button*]] [debug :as dbg] [rumext.v2 :as mf])) @@ -130,8 +131,12 @@ [:div {:class (stl/css :attrs-container) :key (dm/str "shape" idx)} [:div {:class (stl/css :shape-title)} [:div {:class (stl/css :shape-name)} (:name current)] - [:button {:on-click #(debug/dump-object (dm/str (:id current)))} "object"] - [:button {:on-click #(debug/dump-subtree (dm/str (:id current)) true)} "tree"]] + [:> button* {:variant "secondary" + :on-click #(debug/dump-object (dm/str (:id current)))} + "object"] + [:> button* {:variant "secondary" + :on-click #(debug/dump-subtree (dm/str (:id current)) true)} + "tree"]] [:div {:class (stl/css :shape-attrs)} (let [attrs (get-attrs current)] diff --git a/frontend/src/app/main/ui/workspace/sidebar/debug_shape_info.scss b/frontend/src/app/main/ui/workspace/sidebar/debug_shape_info.scss index c849565bb5..27ef6f4b32 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/debug_shape_info.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/debug_shape_info.scss @@ -4,34 +4,36 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/typography.scss" as t; +@use "ds/_utils.scss" as *; .shape-info { display: flex; flex-direction: column; - background-color: var(--panel-background-color); + background-color: var(--color-background-primary); color: var(--color-foreground-primary); - font-size: deprecated.$fs-12; + + @include t.use-typography("body-small"); + user-select: text; } -.shape-info-title { - margin: var(--sp-s) var(--sp-s) 0 var(--sp-s); -} - .attrs-container { - padding: deprecated.$s-16 deprecated.$s-8; + padding: var(--sp-l) var(--sp-s); overflow: hidden; } .shape-title { - font-size: deprecated.$fs-14; + @include t.use-typography("body-medium"); + background: var(--color-background-quaternary); color: var(--color-foreground-primary); - padding: deprecated.$s-8; - border-radius: deprecated.$s-8; + padding: var(--sp-s); + border-radius: $br-8; display: flex; - gap: deprecated.$s-4; + align-items: center; + gap: var(--sp-xs); } .shape-name { @@ -41,7 +43,7 @@ .attrs-container-attr { display: grid; grid-template-columns: 25% auto; - padding: deprecated.$s-4 0; + padding: var(--sp-xs) 0; &.vertical-layout { grid-template-columns: auto; @@ -51,8 +53,8 @@ .shape-attrs { overflow: auto; - height: calc(100% - 8px); - padding-bottom: 8px; + block-size: calc(100% - var(--sp-s)); + padding-block-end: var(--sp-s); } .shape-link { @@ -63,15 +65,15 @@ .shape-list { display: flex; flex-direction: column; - gap: deprecated.$s-4; + gap: var(--sp-xs); } .point-list { display: flex; - gap: deprecated.$s-8; + gap: var(--sp-s); } .cell-container { display: grid; - grid-template-columns: 100px 1fr; + grid-template-columns: px2rem(100) 1fr; } diff --git a/frontend/src/app/main/ui/workspace/sidebar/history.cljs b/frontend/src/app/main/ui/workspace/sidebar/history.cljs index 09e8e68583..293a79ce1e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/history.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/history.cljs @@ -13,9 +13,9 @@ [app.main.data.workspace.undo :as dwu] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.ds.product.empty-state :refer [empty-state*]] - [app.main.ui.icons :as deprecated-icon] [app.util.dom :as dom] [app.util.i18n :refer [tr] :as i18n] [cuerdas.core :as str] @@ -155,22 +155,23 @@ :move (tr "workspace.undo.entry.move" value) (tr "workspace.undo.entry.unknown" value)))) -(defn entry->icon [{:keys [type]}] +(defn- entry->icon-id + [{:keys [type]}] (case type - :page deprecated-icon/document - :shape deprecated-icon/svg - :rect deprecated-icon/rectangle - :circle deprecated-icon/ellipse - :text deprecated-icon/text - :path deprecated-icon/path - :frame deprecated-icon/board - :group deprecated-icon/group - :color deprecated-icon/drop-icon - :typography deprecated-icon/text-palette - :component deprecated-icon/component - :media deprecated-icon/img - :image deprecated-icon/img - deprecated-icon/svg)) + :page i/document + :shape i/svg + :rect i/rectangle + :circle i/ellipse + :text i/text + :path i/path + :frame i/board + :group i/group + :color i/drop + :typography i/text-palette + :component i/component + :media i/img + :image i/img + i/svg)) (defn is-shape? [type] (contains? #{:shape :rect :circle :text :path :frame :group} type)) @@ -339,7 +340,7 @@ [:div {:class (stl/css :history-entry-summary)} [:div {:class (stl/css :history-entry-summary-icon)} - (entry->icon entry)] + [:> icon* {:icon-id (entry->icon-id entry) :size "s"}]] [:div {:class (stl/css :history-entry-summary-text)} [:div {:class (stl/css :history-entry-title)} (entry->message entry)] @@ -363,11 +364,17 @@ [:span {:class (stl/css :history-entry-author)} (tr "workspace.undo.entry.by" author)])])] (when (:detail entry) - [:div {:class (stl/css-case :history-entry-summary-button true - :button-opened @show-detail?) - :on-click toggle-show-detail - :data-has-entry (dm/str (not (nil? (:detail entry))))} - deprecated-icon/arrow])] + [:> icon-button* {:variant "action" + :class (stl/css-case :history-entry-summary-button true + :button-opened @show-detail?) + :tooltip-class (stl/css :history-entry-summary-button-wrapper) + :aria-label (tr "workspace.undo.entry.show-details") + :aria-expanded @show-detail? + :icon i/arrow + :icon-size "s" + :icon-class (stl/css :history-entry-summary-arrow) + :on-click toggle-show-detail + :data-has-entry (dm/str (not (nil? (:detail entry))))}])] (when @show-detail? [:> history-entry-details* {:entry entry}])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/history.scss b/frontend/src/app/main/ui/workspace/sidebar/history.scss index e7e8b1a2cd..59d56a64ad 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/history.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/history.scss @@ -4,13 +4,14 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +@use "ds/_borders.scss" as *; @use "ds/_sizes.scss" as *; -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_utils.scss" as *; .history-toolbox { display: flex; flex-direction: column; - background-color: var(--panel-background-color); + background-color: var(--color-background-primary); } .history-entry-empty { @@ -22,21 +23,29 @@ } .history-entries { - height: calc(100vh - deprecated.$s-100); - padding: deprecated.$s-12; + block-size: calc(100vh - #{px2rem(100)}); + padding: var(--sp-m); overflow: hidden auto; - font-size: deprecated.$fs-12; + font-size: px2rem(12); } .history-entry { + --entry-background-color: var(--color-background-tertiary); + --entry-foreground-color: var(--color-foreground-secondary); + --entry-border-color: transparent; + --entry-arrow-opacity: 0; + display: flex; justify-content: center; flex-direction: column; - min-height: deprecated.$s-32; - margin: deprecated.$s-4; - padding: deprecated.$s-4 deprecated.$s-8; - border: deprecated.$s-2 solid transparent; - border-radius: deprecated.$s-8; + min-block-size: $sz-32; + margin: var(--sp-xs); + + // The end padding is small because the details button leaves 6px + // around its 12px arrow. + padding: var(--sp-xs) var(--sp-xxs) var(--sp-xs) var(--sp-s); + border: $b-2 solid var(--entry-border-color); + border-radius: $br-8; background-color: var(--entry-background-color); cursor: pointer; transition: border 0.2s; @@ -46,18 +55,18 @@ align-items: center; .history-entry-summary-icon { - svg { - @extend %button-icon-small; - - stroke: var(--entry-foreground-color); - } + display: flex; + color: var(--entry-foreground-color); } .history-entry-summary-text { display: flex; flex-direction: column; - gap: deprecated.$s-2; - margin: 0 deprecated.$s-8; + gap: var(--sp-xxs); + + // The DS icon draws 12px inside a 16px box: a smaller start margin + // keeps the text where it was. + margin-inline: var(--sp-xs) var(--sp-s); color: var(--color-foreground-primary); min-inline-size: 0; } @@ -73,7 +82,7 @@ // template ("hash · time by Name") survive rendering. Issue #7660. .history-entry-meta { color: var(--color-foreground-secondary); - font-size: deprecated.$fs-10; + font-size: px2rem(10); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -99,28 +108,28 @@ white-space: nowrap; } - .history-entry-summary-button { - opacity: deprecated.$op-0; - margin-left: auto; + // Tooltip wrapper of the details button, pushed to the end of the row. + .history-entry-summary-button-wrapper { + margin-inline-start: auto; + } - &.button-opened { - svg { - transform: rotate(90deg); - } + .history-entry-summary-button { + opacity: var(--entry-arrow-opacity); + + &:focus-visible { + --entry-arrow-opacity: 1; } - svg { - @extend %button-icon-small; - - stroke: var(--entry-foreground-color); + &.button-opened .history-entry-summary-arrow { + transform: rotate(90deg); } } } .history-entry-detail { display: block; - padding-top: deprecated.$s-16; - color: var(--modal-text-foreground-color); + padding-block-start: var(--sp-l); + color: var(--color-foreground-secondary); .history-entry-details-list { margin: 0; @@ -128,31 +137,16 @@ } &.disabled { - border-color: var(--entry-border-color-disabled); - background-color: var(--entry-background-color-disabled); + --entry-background-color: var(--color-background-primary); + --entry-border-color: var(--color-background-quaternary); } &.hover, &:hover { - background-color: var(--entry-background-color-hover); - color: var(--entry-foreground-color-hover); + --entry-background-color: var(--color-background-quaternary); + --entry-foreground-color: var(--color-foreground-primary); + --entry-arrow-opacity: 1; - .history-entry-summary { - .history-entry-summary-icon { - svg { - stroke: var(--entry-foreground-color-hover); - } - } - - .history-entry-summary-button { - opacity: deprecated.$op-10; - - &.button-opened { - svg { - transform: rotate(90deg); - } - } - } - } + color: var(--color-foreground-primary); } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs index 5733b8c3ce..6438e63914 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs @@ -21,9 +21,8 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.context :as ctx] - [app.main.ui.ds.foundations.assets.icon :refer [icon*]] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.hooks :as hooks] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.workspace.sidebar.layer-name :refer [layer-name*]] [app.util.dom :as dom] [app.util.i18n :refer [tr]] @@ -138,7 +137,9 @@ :data-testid "toggle-content" :aria-expanded is-expanded :on-click on-toggle-collapse} - deprecated-icon/arrow]) + [:> icon* {:icon-id i/arrow + :size "s" + :class (stl/css :toggle-content-icon)}]]) [:div {:class (stl/css :icon-shape) :on-double-click on-zoom-to-selected} @@ -189,7 +190,9 @@ (tr "workspace.shape.menu.show") (tr "workspace.shape.menu.hide")) :on-click on-toggle-visibility} - (if ^boolean hidden? deprecated-icon/hide deprecated-icon/shown)] + [:> icon* {:icon-id (if ^boolean hidden? i/hide i/shown) + :size "s" + :class (stl/css :element-action-icon)}]] [:button {:class (stl/css-case :block-element true :selected blocked?) @@ -197,7 +200,9 @@ (tr "workspace.shape.menu.unlock") (tr "workspace.shape.menu.lock")) :on-click on-toggle-blocking} - (if ^boolean blocked? deprecated-icon/lock deprecated-icon/unlock)]])] + [:> icon* {:icon-id (if ^boolean blocked? i/lock i/unlock) + :size "s" + :class (stl/css :element-action-icon)}]]])] children])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss b/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss index 445b8e5bc9..18a1395d52 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss @@ -27,9 +27,9 @@ &:hover { --layer-background-color: var(--color-background-secondary); --shadow-color: var(--color-background-secondary); - --context-hover-color: var(--layer-row-foreground-color-hover); + --context-hover-color: var(--color-accent-primary); --context-hover-opacity: 1; - --layer-foreground-color: var(--layer-row-foreground-color-hover); + --layer-foreground-color: var(--color-accent-primary); &.hidden { opacity: 1; @@ -200,43 +200,38 @@ align-items: center; block-size: 100%; inline-size: $sz-24; - padding-inline-start: var(--sp-s); - svg { - display: flex; - justify-content: center; - align-items: center; - color: transparent; - fill: none; - block-size: $sz-12; - inline-size: $sz-12; - stroke-width: 1.33px; - stroke: var(--color-foreground-secondary); - - .layer-row.hidden & { - opacity: 0.7; - } - - .layer-row.selected & { - stroke: var(--color-accent-primary); - } - - .layer-row.type-comp & { - stroke: var(--color-accent-secondary); - } - - .layer-row.highlight &, - .layer-row:hover & { - opacity: 1; - stroke: var(--color-accent-primary); - } - } + // The DS icon draws 12px inside a 16px box: 2px less start padding + // keeps the arrow where the old 12px icon was. + padding-inline-start: px2rem(6); .layer-row.selected & { background-color: var(--color-background-quaternary); } +} - &.inverse svg { +.toggle-content-icon { + color: var(--color-foreground-secondary); + + .layer-row.hidden & { + opacity: 0.7; + } + + .layer-row.selected & { + color: var(--color-accent-primary); + } + + .layer-row.type-comp & { + color: var(--color-accent-secondary); + } + + .layer-row.highlight &, + .layer-row:hover & { + opacity: 1; + color: var(--color-accent-primary); + } + + .toggle-content.inverse & { transform: rotate(90deg); } } @@ -256,26 +251,6 @@ background: var(--layer-row-action-btn-background); padding-inline-end: px2rem(6); - svg { - display: flex; - justify-content: center; - align-items: center; - color: transparent; - fill: none; - block-size: $sz-12; - inline-size: $sz-12; - stroke-width: 1.33px; - stroke: var(--color-foreground-secondary); - - .layer-row.hidden & { - opacity: 0.7; - } - - .type-comp & { - stroke: var(--color-accent-secondary); - } - } - .element-actions.selected & { display: flex; opacity: 0; @@ -294,21 +269,37 @@ display: flex; --layer-row-action-btn-background: var(--color-background-secondary); - - svg { - opacity: 1; - stroke: var(--color-accent-primary); - } } .layer-row.selected & { display: flex; --layer-row-action-btn-background: var(--color-background-quaternary); + } +} - svg { - stroke: var(--color-accent-primary); - } +.element-action-icon { + // The DS icon box is 16px and the button leaves 12px: keep the box + // whole, centered, instead of letting it shrink and cut the icon. + flex-shrink: 0; + color: var(--color-foreground-secondary); + + .layer-row.hidden & { + opacity: 0.7; + } + + .type-comp & { + color: var(--color-accent-secondary); + } + + .layer-row.highlight &, + .layer-row:hover & { + opacity: 1; + color: var(--color-accent-primary); + } + + .layer-row.selected & { + color: var(--color-accent-primary); } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_name.scss b/frontend/src/app/main/ui/workspace/sidebar/layer_name.scss index 23a36b1d1b..6d6fc10fff 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_name.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_name.scss @@ -4,16 +4,22 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_utils.scss" as *; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/typography.scss" as *; .element-name { - --element-name-color: var(--context-hover-color, var(--layer-row-foreground-color)); - --element-name-comp-color: var(--context-hover-color, var(--layer-row-component-foreground-color)); - --element-name-opacity: var(--context-hover-opacity, deprecated.$op-7); + --element-name-color: var(--context-hover-color, var(--color-foreground-secondary)); + --element-name-opacity: var(--context-hover-opacity, 0.7); - @include deprecated.text-ellipsis; - @include deprecated.body-small-typography; + @include use-typography("body-small"); + display: block; + max-inline-size: 99%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; color: var(--element-name-color); flex-grow: 1; block-size: 100%; @@ -21,15 +27,11 @@ min-inline-size: var(--sp-xl); &.selected { - color: var(--layer-row-foreground-color-selected); + --element-name-color: var(--color-accent-primary); } &.type-comp { - color: var(--element-name-comp-color); - - &.hidden { - opacity: var(--element-name-opacity); - } + --element-name-color: var(--context-hover-color, var(--color-accent-secondary)); } &.hidden { @@ -37,30 +39,26 @@ } } +// The rename input also carries .element-name, so it takes its typography, +// ellipsis and color from there. .element-name-input { - --element-name-input-border-color: var(--input-border-color-focus); - --element-name-input-color: var(--layer-row-foreground-color); - - @include deprecated.text-ellipsis; - @include deprecated.body-small-typography; - @include deprecated.remove-input-style; + --element-name-color: var(--color-foreground-secondary); + background: none; + outline: none; flex-grow: 1; - height: deprecated.$s-28; + block-size: $sz-28; max-inline-size: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size))); margin: 0; - padding-inline-start: deprecated.$s-6; - border-radius: deprecated.$br-8; - border: deprecated.$s-1 solid var(--element-name-input-border-color); - color: var(--element-name-input-color); + padding-inline-start: px2rem(6); + border-radius: $br-8; + border: $b-1 solid var(--color-accent-primary); &.selected { - min-inline-size: deprecated.$s-100; + min-inline-size: px2rem(100); } } .element-name-touched { - --element-name-touched-color: var(--layer-row-component-foreground-color); - - color: var(--element-name-touched-color); + color: var(--color-accent-secondary); } diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 5525364ee3..f841fbee91 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -386,6 +386,21 @@ (st/emit! dw/close-layers-search) (st/emit! (dw/open-layers-search :find {:force? true})))))) + close-search + (mf/use-fn + #(st/emit! dw/close-layers-search)) + + dismiss-filters-menu + (mf/use-fn + (mf/deps show-menu? hide-menu) + (fn [] + (when ^boolean show-menu? + (hide-menu) + true))) + + on-search-key-down + (hooks/use-escape-to-close close-search dismiss-filters-menu) + remove-filter (mf/use-fn (fn [event] @@ -587,7 +602,8 @@ #(mf/html (if show-search? [:* - [:div {:class (stl/css :tool-window-bar)} + [:div {:class (stl/css :tool-window-bar) + :on-key-down on-search-key-down} [:> search-bar* {:input-ref search-input-ref :class (stl/css :search-item) :on-change update-search-text @@ -612,7 +628,8 @@ :on-click toggle-search :icon i/close}]] - [:div {:class (stl/css :replace-wrapper)} + [:div {:class (stl/css :replace-wrapper) + :on-key-down on-search-key-down} (when ^boolean find-replace-mode? [:div {:class (stl/css :replace-row)} [:> input* {:type "text" diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.scss b/frontend/src/app/main/ui/workspace/sidebar/layers.scss index 4ff85ab137..e702e9c9fa 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.scss @@ -4,6 +4,7 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for the shared %menu-dropdown placeholder of the filters menu. @use "refactor/common-refactor.scss" as deprecated; @use "ds/borders.scss" as *; @use "ds/mixins.scss" as *; @@ -63,7 +64,7 @@ justify-content: center; block-size: var(--sp-xxxl); padding-inline-start: var(--sp-s); - color: var(--icon-foreground); + color: var(--color-foreground-secondary); } .focus-name { @@ -71,7 +72,7 @@ @include text-ellipsis; padding-inline-end: var(--sp-xs); - color: var(--title-foreground-color); + color: var(--color-foreground-secondary); } .focus-mode-tag-wrapper { @@ -91,18 +92,36 @@ } .layer-filter { - @extend %button-tag; + --layer-filter-background-color: var(--color-background-tertiary); + --layer-filter-border: none; + display: flex; + justify-content: center; + align-items: center; gap: px2rem(6); block-size: var(--sp-xxl); margin: var(--sp-xxs) 0; + border: var(--layer-filter-border); border-radius: $br-6; - background-color: var(--pill-background-color); + background-color: var(--layer-filter-background-color); + cursor: pointer; + + &:focus-visible { + --layer-filter-background-color: var(--color-background-secondary); + --layer-filter-border: #{$b-1} solid var(--color-accent-primary); + + outline: none; + } + + &:active { + --layer-filter-background-color: transparent; + --layer-filter-border: none; + } } .layer-filter-icon, .layer-filter-close { - color: var(--pill-foreground-color); + color: var(--color-foreground-primary); } .layer-filter-name { @@ -111,7 +130,7 @@ display: flex; align-items: center; justify-content: center; - color: var(--pill-foreground-color); + color: var(--color-foreground-primary); } .layers { @@ -213,6 +232,8 @@ inline-size: $sz-192; .filter-menu-item { + --filter-menu-item-background-color: transparent; + @include t.use-typography("body-small"); display: flex; @@ -221,60 +242,30 @@ inline-size: 100%; padding: px2rem(6); border-radius: $br-8; - - .filter-menu-item-name-wrapper { - display: flex; - align-items: center; - gap: var(--sp-s); - - .filter-menu-item-icon { - color: var(--menu-foreground-color); - } - - .filter-menu-item-name { - padding-block-start: var(--sp-xxs); - color: var(--menu-foreground-color); - } - } - - .filter-menu-item-tick { - color: var(--menu-foreground-color); - } + background-color: var(--filter-menu-item-background-color); &.selected { - background-color: var(--menu-background-color-selected); - - .filter-menu-item-name-wrapper { - .filter-menu-item-icon { - color: var(--menu-foreground-color); - } - - .filter-menu-item-name { - color: var(--menu-foreground-color); - } - } - - .filter-menu-item-tick { - color: var(--menu-foreground-color); - } + --filter-menu-item-background-color: var(--color-background-tertiary); } &:hover { - background-color: var(--menu-background-color-hover); - - .filter-menu-item-name-wrapper { - .filter-menu-item-icon { - color: var(--menu-foreground-color-hover); - } - - .filter-menu-item-name { - color: var(--menu-foreground-color-hover); - } - } - - .filter-menu-item-tick { - color: var(--menu-foreground-color-hover); - } + --filter-menu-item-background-color: var(--color-background-quaternary); } } + + .filter-menu-item-name-wrapper { + display: flex; + align-items: center; + gap: var(--sp-s); + } + + .filter-menu-item-icon, + .filter-menu-item-name, + .filter-menu-item-tick { + color: var(--color-foreground-primary); + } + + .filter-menu-item-name { + padding-block-start: var(--sp-xxs); + } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index b753def734..fffd6aa97b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -262,6 +262,10 @@ (mf/use-ctx ctx/permissions) render-context-lost? (mf/deref refs/render-context-lost?) + ;; While saves fail the workspace is locked and nothing may be edited + save-locked? + (some? (mf/deref refs/persistence-lock)) + options-mode (mf/deref refs/options-mode-global) @@ -289,7 +293,7 @@ (on-option-tab-change "design"))))) [:div {:class (stl/css :tool-window)} - (if (and (:can-edit permissions) (not render-context-lost?)) + (if (and (:can-edit permissions) (not render-context-lost?) (not save-locked?)) [:> tab-switcher* {:tabs options-tabs :on-change on-option-tab-change :selected (name options-mode) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs index 3ae778553f..7897e4c81e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.cljs @@ -11,7 +11,8 @@ [app.main.data.workspace.path :as dwdp] [app.main.data.workspace.shortcuts :as sc] [app.main.store :as st] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) @@ -57,67 +58,59 @@ (when (or path-edit? (not (and disabled-align disabled-distribute))) [:div {:class (stl/css :align-options)} [:div {:class (stl/css :align-group-horizontal)} - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.hleft" (sc/get-tooltip :align-left)) - :data-value "hleft" - :on-click align-objects} - deprecated-icon/align-left] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.align.hleft" (sc/get-tooltip :align-left)) + :icon i/align-left + :disabled disabled-align + :data-value "hleft" + :on-click align-objects}] - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.hcenter" (sc/get-tooltip :align-hcenter)) - :data-value "hcenter" - :on-click align-objects} - deprecated-icon/align-horizontal-center] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.align.hcenter" (sc/get-tooltip :align-hcenter)) + :icon i/align-horizontal-center + :disabled disabled-align + :data-value "hcenter" + :on-click align-objects}] - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.hright" (sc/get-tooltip :align-right)) - :data-value "hright" - :on-click align-objects} - deprecated-icon/align-right] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.align.hright" (sc/get-tooltip :align-right)) + :icon i/align-right + :disabled disabled-align + :data-value "hright" + :on-click align-objects}] - [:button {:class (stl/css-case :align-button true - :disabled disabled-distribute) - :disabled disabled-distribute - :title (tr "workspace.align.hdistribute" (sc/get-tooltip :h-distribute)) - :data-value "horizontal" - :on-click distribute-objects} - deprecated-icon/distribute-horizontally]] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.align.hdistribute" (sc/get-tooltip :h-distribute)) + :icon i/distribute-horizontally + :disabled disabled-distribute + :data-value "horizontal" + :on-click distribute-objects}]] [:div {:class (stl/css :align-group-vertical)} - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.vtop" (sc/get-tooltip :align-top)) - :data-value "vtop" - :on-click align-objects} - deprecated-icon/align-top] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.align.vtop" (sc/get-tooltip :align-top)) + :icon i/align-top + :disabled disabled-align + :data-value "vtop" + :on-click align-objects}] - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.vcenter" (sc/get-tooltip :align-vcenter)) - :data-value "vcenter" - :on-click align-objects} - deprecated-icon/align-vertical-center] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.align.vcenter" (sc/get-tooltip :align-vcenter)) + :icon i/align-vertical-center + :disabled disabled-align + :data-value "vcenter" + :on-click align-objects}] - [:button {:class (stl/css-case :align-button true - :disabled disabled-align) - :disabled disabled-align - :title (tr "workspace.align.vbottom" (sc/get-tooltip :align-bottom)) - :data-value "vbottom" - :on-click align-objects} - deprecated-icon/align-bottom] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.align.vbottom" (sc/get-tooltip :align-bottom)) + :icon i/align-bottom + :disabled disabled-align + :data-value "vbottom" + :on-click align-objects}] - [:button {:title (tr "workspace.align.vdistribute" (sc/get-tooltip :v-distribute)) - :class (stl/css-case :align-button true - :disabled disabled-distribute) - :disabled disabled-distribute - :data-value "vertical" - :on-click distribute-objects} - deprecated-icon/distribute-vertical-spacing]]]))) + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.align.vdistribute" (sc/get-tooltip :v-distribute)) + :icon i/distribute-vertical-spacing + :disabled disabled-distribute + :data-value "vertical" + :on-click distribute-objects}]]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.scss index 135544aa70..4386af7d6b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/align.scss @@ -4,13 +4,13 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_sizes.scss" as *; @use "../../../sidebar/common/sidebar.scss" as sidebar; .align-options { @include sidebar.option-grid-structure; - height: deprecated.$s-32; + block-size: $sz-32; } .align-group-horizontal, @@ -27,34 +27,3 @@ .align-group-vertical { grid-column: 5 / span 4; } - -.align-button { - @extend %button-tertiary; - - height: deprecated.$s-32; - width: deprecated.$s-32; - padding: 0; - border-radius: deprecated.$br-8; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } - - &.disabled { - cursor: default; - - svg { - stroke: var(--button-foreground-color-disabled); - } - - &:hover { - background-color: var(--panel-background-color); - - svg { - stroke: var(--button-foreground-color-disabled); - } - } - } -} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs index 46801115ad..84345ef667 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.cljs @@ -16,14 +16,11 @@ [app.main.features :as features] [app.main.store :as st] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] - [app.main.ui.icons :as deprecated-icon] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) -(def ^:private flatten-icon - (deprecated-icon/icon-xref :boolean-flatten (stl/css :flatten-icon))) - (mf/defc bool-options* [{:keys [total-selected shapes shapes-with-children]}] (let [head (first shapes) @@ -100,11 +97,9 @@ :title (str (tr "workspace.shape.menu.exclude") " (" (sc/get-tooltip :bool-exclude) ")") :id "bool-opt-exclude"}]]] - [:button - {:title (tr "workspace.shape.menu.flatten") - :class (stl/css-case - :flatten-button true - :disabled disabled-flatten) - :disabled disabled-flatten - :on-click flatten-objects} - flatten-icon]]))) + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :flatten-button) + :aria-label (tr "workspace.shape.menu.flatten") + :icon i/boolean-flatten + :disabled disabled-flatten + :on-click flatten-objects}]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.scss index 50c8ae0625..afd0ca9dac 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/bool.scss @@ -4,13 +4,12 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; @use "../../../sidebar/common/sidebar.scss" as sidebar; .boolean-options { @include sidebar.option-grid-structure; - height: var(--sp-xxxl); + block-size: var(--sp-xxxl); } .bool-group { @@ -20,35 +19,13 @@ } .flatten-button { - @extend %button-tertiary; - - height: deprecated.$s-32; - width: deprecated.$s-32; - border-radius: deprecated.$br-8; grid-column: 5 / span 1; - - --flatten-icon-foreground-color: var(--icon-foreground); - - &.disabled { - cursor: default; - - --flatten-icon-foreground-color: var(--button-foreground-color-disabled); - - &:hover { - background-color: var(--panel-background-color); - - --flatten-icon-foreground-color: var(--button-foreground-color-disabled); - } - } -} - -.flatten-icon { - @extend %button-icon; - - stroke: var(--flatten-icon-foreground-color); } .boolean-radio-btn { + --radio-btn-border-color: transparent; + --radio-btn-border-color-selected: transparent; + background-color: transparent; gap: var(--sp-xs); } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs index cda718388c..d72f730775 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs @@ -8,15 +8,28 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] + [app.common.types.library :as ctl] [app.main.data.workspace.colors :as dwc] [app.main.data.workspace.selection :as dws] [app.main.data.workspace.tokens.application :as dwta] [app.main.store :as st] [app.main.ui.components.title-bar :refer [title-bar*]] + [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row*]] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) +(defn- cached-shape-colors + [prev-cache cache libraries-colors shape file-id libraries] + (let [inputs [(:type shape) (:fills shape) (:strokes shape) (:shadow shape) + (:applied-tokens shape) (:content shape) file-id libraries-colors] + [cached-inputs cached] (when prev-cache (.get prev-cache (:id shape))) + colors (if (and cached-inputs (every? true? (map identical? cached-inputs inputs))) + cached + (dwc/shape-colors shape file-id libraries))] + (.set cache (:id shape) [inputs colors]) + colors)) + (defn- prepare-colors "Prepares and groups extracted color information from shapes. Input: @@ -62,8 +75,11 @@ This structure allows fast lookups of all shapes using the same visual color, regardless of whether it comes from local fills, strokes or shadow-colors." - [shapes file-id libraries] - (let [data (into [] (remove nil?) (dwc/extract-all-colors shapes file-id libraries)) + [prev-cache cache libraries-colors shapes file-id libraries] + (let [data (into [] + (comp (mapcat #(cached-shape-colors prev-cache cache libraries-colors % file-id libraries)) + (remove nil?)) + shapes) groups (d/group-by :attrs #(dissoc % :attrs) data) ;; Unique color attribute maps @@ -98,9 +114,19 @@ (mf/defc color-selection-menu* {::mf/wrap [#(mf/memo' % (mf/check-props ["shapes"]))]} [{:keys [shapes file-id libraries]}] - (let [{:keys [groups library-colors colors token-colors]} + (let [cache* (mf/use-ref nil) + libraries-colors* (mf/use-ref nil) + + {:keys [groups library-colors colors token-colors]} (mf/with-memo [file-id shapes libraries] - (prepare-colors shapes file-id libraries)) + (let [current (mapv (fn [[id library]] [id (ctl/get-colors (:data library))]) libraries) + previous (mf/ref-val libraries-colors*) + libraries-colors (if (= previous current) previous current) + cache (js/Map.) + result (prepare-colors (mf/ref-val cache*) cache libraries-colors shapes file-id libraries)] + (mf/set-ref-val! libraries-colors* libraries-colors) + (mf/set-ref-val! cache* cache) + result)) open* (mf/use-state true) open? (deref open*) @@ -222,8 +248,9 @@ :origin :color-selection :on-close on-close}])) (when (and (false? @expand-lib-color) (< 3 (count library-colors))) - [:button {:class (stl/css :more-colors-btn) - :on-click #(reset! expand-lib-color true)} + [:> button* {:variant "secondary" + :class (stl/css :more-colors-btn) + :on-click #(reset! expand-lib-color true)} (tr "workspace.options.more-lib-colors")])] [:div {:class (stl/css :selected-color-group)} @@ -240,8 +267,9 @@ :on-close on-close}]) (when (and (false? @expand-color) (< 3 (count colors))) - [:button {:class (stl/css :more-colors-btn) - :on-click #(reset! expand-color true)} + [:> button* {:variant "secondary" + :class (stl/css :more-colors-btn) + :on-click #(reset! expand-color true)} (tr "workspace.options.more-colors")])] [:div {:class (stl/css :selected-color-group)} @@ -263,6 +291,7 @@ (when (and (false? @expand-token-color) (< 3 (count token-colors))) - [:button {:class (stl/css :more-colors-btn) - :on-click #(reset! expand-token-color true)} + [:> button* {:variant "secondary" + :class (stl/css :more-colors-btn) + :on-click #(reset! expand-token-color true)} (tr "workspace.options.more-token-colors")])]])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.scss index 15da32944b..f49e29e66f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.scss @@ -4,7 +4,6 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; @use "../../../sidebar/common/sidebar.scss" as sidebar; .element-set { @@ -16,36 +15,25 @@ } .title-spacing-selected-colors { - padding-left: deprecated.$s-2; + padding-inline-start: var(--sp-xxs); margin: 0; } -.add-fill { - @extend %button-tertiary; - - height: deprecated.$s-32; - width: deprecated.$s-28; - - svg { - @extend %button-icon; - } -} - .element-content { grid-column: span 8; - - @include deprecated.flex-column; - - margin-bottom: deprecated.$s-8; + display: flex; + flex-direction: column; + gap: var(--sp-xs); + margin-block-end: var(--sp-s); } .selected-color-group { - @include deprecated.flex-column; + display: flex; + flex-direction: column; + gap: var(--sp-xs); } +// The DS button is stretched by the column; center its label. .more-colors-btn { - @extend %button-secondary; - @include deprecated.uppercase-title-typography; - - height: deprecated.$s-32; + justify-content: center; } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs index 6600b370ab..f3c9764e15 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs @@ -175,9 +175,7 @@ :editing editing? :creating creating?)} [:div {:class (stl/css-case :annotation-title true - :expandeable (not (or editing? creating?)) - :expanded expanded?) - :on-click on-toggle-expand} + :expanded expanded?)} (if (or editing? creating?) [:span {:class (stl/css :annotation-title-name)} @@ -185,7 +183,12 @@ (tr "workspace.options.component.edit-annotation") (tr "workspace.options.component.create-annotation"))] - [:* + ;; Only the arrow and the name toggle: the actions next to them + ;; are buttons too, and a button cannot hold other buttons. + [:button {:type "button" + :class (stl/css :annotation-title-toggle) + :aria-expanded (boolean expanded?) + :on-click on-toggle-expand} [:> icon* {:icon-id (if expanded? i/arrow-down i/arrow-right) :class (stl/css :annotation-title-icon-arrow) :size "s"}] @@ -198,31 +201,35 @@ (if (or ^boolean editing? ^boolean creating?) [:* - [:div {:title (if ^boolean creating? - (tr "labels.create") - (tr "labels.save")) - :on-click on-save - :class (stl/css :annotation-title-icon-action)} - [:> icon* {:icon-id i/tick - :class (stl/css-case :annotation-title-icon-ok true - :disabled invalid-text?)}]] - [:div {:class (stl/css :annotation-title-icon-action) - :title (tr "labels.discard") - :on-click on-discard} - [:> icon* {:icon-id i/close - :class (stl/css :annotation-title-icon-nok)}]]] + [:> icon-button* {:variant "ghost" + :aria-label (if ^boolean creating? + (tr "labels.create") + (tr "labels.save")) + :on-click on-save + :disabled invalid-text? + :tooltip-class (stl/css :annotation-title-icon-action) + :tooltip-placement "top-left" + :icon i/tick}] + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.discard") + :on-click on-discard + :tooltip-class (stl/css :annotation-title-icon-action) + :tooltip-placement "top-left" + :icon i/close}]] [:* - [:div {:class (stl/css :annotation-title-icon-action) - :title (tr "labels.edit") - :on-click on-edit} - [:> icon* {:icon-id i/curve - :class (stl/css :annotation-title-icon-ok)}]] - [:div {:class (stl/css :annotation-title-icon-action) - :title (tr "labels.delete") - :on-click on-delete-annotation} - [:> icon* {:icon-id i/delete - :class (stl/css :annotation-title-icon-nok)}]]]))]] + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.edit") + :on-click on-edit + :tooltip-class (stl/css :annotation-title-icon-action) + :tooltip-placement "top-left" + :icon i/curve}] + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.delete") + :on-click on-delete-annotation + :tooltip-class (stl/css :annotation-title-icon-action) + :tooltip-placement "top-left" + :icon i/delete}]]))]] [:div {:class (stl/css-case :annotation-body-hidden (not expanded?))} [:div {:class (stl/css :annotation-body)} @@ -618,9 +625,10 @@ [{:keys [item on-enter-group]}] (let [group-name (:name item) on-group-click #(on-enter-group group-name)] - [:div {:class (stl/css :swap-group) - :on-click on-group-click - :title group-name} + [:button {:type "button" + :class (stl/css :swap-group) + :on-click on-group-click + :title group-name} [:span {:class (stl/css :swap-group-name)} (cpn/last-path group-name)] @@ -896,10 +904,14 @@ (when menu-entries? [:div {:class (stl/css :pill-actions)} - [:button {:class (stl/css-case :pill-actions-btn true - :selected menu-open?) - :on-click on-menu-click} - [:> icon* {:icon-id i/menu}]] + [:> icon-button* {:variant "secondary" + :class (stl/css :pill-actions-btn) + :tooltip-class (stl/css :pill-actions-tooltip) + :aria-label (tr "labels.options") + :aria-pressed menu-open? + :tooltip-placement "top-left" + :on-click on-menu-click + :icon i/menu}] [:& dropdown {:show menu-open? :on-close on-menu-close} @@ -910,7 +922,7 @@ [:li {:key title :class (stl/css :pill-actions-dropdown-item) :on-click (partial do-action action)} - [:span title]]))]]])])) + [:span {:class (stl/css :pill-actions-dropdown-item-label)} title]]))]]])])) (mf/defc component-menu* [{:keys [shapes is-swap-opened]}] @@ -1039,12 +1051,14 @@ [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.options.component.variants-help-modal.title") :on-click on-click-variant-title-help + :tooltip-placement "top-left" :icon i/help}]) (when main-instance? [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.shape.menu.add-variant") :on-click (if is-variant? create-variant transform-into-variant) + :tooltip-placement "top-left" :icon i/variant}])])] (when open? @@ -1067,6 +1081,7 @@ [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.shape.menu.add-variant-property") :on-click add-new-property + :tooltip-placement "top-left" :icon i/add}])] (when is-swap-opened @@ -1151,7 +1166,8 @@ (tr "workspace.shape.menu.remove-variant-property.last-property") (tr "workspace.shape.menu.remove-variant-property")) :on-click on-remove - :disabled is-remove-disabled}]]])) + :disabled is-remove-disabled + :tooltip-placement "top-left"}]]])) (mf/defc component-variant-main* [{:keys [shapes]}] @@ -1283,10 +1299,12 @@ [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.options.component.variants-help-modal.title") :on-click on-click-variant-title-help + :tooltip-placement "top-left" :icon i/help}] [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.shape.menu.add-variant") :on-click (partial create-variant "workspace:button-design-tab-component") + :tooltip-placement "top-left" :icon i/variant}]]] (when open? @@ -1301,6 +1319,7 @@ [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.shape.menu.add-variant-property") :on-click (partial add-new-property "workspace:button-design-tab-component") + :tooltip-placement "top-left" :icon i/add}]] (when-not multi? diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss index ca8bbd7aab..5fd8e97fc0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss @@ -10,7 +10,6 @@ @use "ds/spacing.scss" as *; @use "ds/typography.scss" as t; @use "../../../sidebar/common/sidebar.scss" as sidebar; -@use "refactor/common-refactor.scss" as deprecated; .annotation { @include t.use-typography("body-small"); @@ -23,7 +22,12 @@ border: $b-1 solid var(--color-accent-primary); .annotation-title { - border-bottom: $b-1 solid var(--entry-border-color-disabled); + border-bottom: $b-1 solid var(--color-background-quaternary); + } + + // Keep the hover background of the actions off the border. + .annotation-title-actions { + padding: var(--sp-xxs); } .annotation-title-icon-action { @@ -46,6 +50,10 @@ display: flex; } + .annotation-title-actions { + padding: var(--sp-xxs); + } + .annotation-title-name { margin-inline-start: var(--sp-m); } @@ -59,16 +67,12 @@ .annotation-title { display: flex; align-items: center; - block-size: $sz-32; + min-block-size: $sz-32; &.expanded { border-bottom: $b-1 solid var(--color-background-quaternary); } - &.expandeable { - cursor: pointer; - } - &:hover { .annotation-title-icon-action { display: flex; @@ -76,6 +80,22 @@ } } +// Takes the whole free width of the title, so the clickable area stays +// as it was when the whole title toggled. +.annotation-title-toggle { + display: flex; + flex-grow: 1; + align-items: center; + align-self: stretch; + padding: 0; + border: none; + background: none; + color: inherit; + font: inherit; + text-align: start; + cursor: pointer; +} + .annotation-title-name { flex-grow: 1; } @@ -90,29 +110,10 @@ line-height: 2.5; } -.annotation-title-icon-action { - cursor: pointer; +// Tooltip wrapper of each action: hidden until the title is hovered or the +// annotation is being edited or created. +.annotation-title-actions .annotation-title-icon-action { display: none; - justify-content: center; - align-items: center; - inline-size: $sz-28; - block-size: $sz-32; - border-radius: $br-8; - margin: 0; - padding: 0; -} - -.annotation-title-icon-ok:hover { - color: var(--color-accent-success); - - &.disabled { - cursor: default; - color: var(--color-foreground-secondary); - } -} - -.annotation-title-icon-nok:hover { - color: var(--color-accent-error); } .annotation-body-hidden { @@ -329,6 +330,11 @@ .swap-group { @include t.use-typography("body-small"); + inline-size: 100%; + padding: 0; + border: none; + background: none; + text-align: start; cursor: pointer; display: grid; grid-template-columns: 1fr var(--sp-m); @@ -489,11 +495,6 @@ color: var(--color-foreground-secondary); } -.component-title-actions { - display: flex; - gap: var(--sp-xs); -} - .component-content { grid-column: span 8; display: flex; @@ -588,25 +589,35 @@ position: relative; } -.pill-actions-btn { - @extend %button-secondary; +// The tooltip wrapper is the flex item: let it fill the pill segment. +.pill-actions-tooltip { + block-size: 100%; + inline-size: 100%; +} - cursor: unset; +// Right segment of the pill: full height and square corners on the left. +.pill-actions-btn { block-size: 100%; inline-size: 100%; border-radius: 0 $br-8 $br-8 0; - - &.selected { - @extend %button-icon-selected; - } } .pill-actions-dropdown { - @extend %dropdown-wrapper; - - inline-size: $sz-252; + position: absolute; + inset-block-start: $sz-32; inset-inline-end: 0; - inset-inline-start: unset; + inline-size: $sz-252; + max-block-size: var(--menu-max-height, px2rem(300)); + padding: var(--sp-xxs); + margin: 0; + margin-block-start: px2rem(1); + border: $b-2 solid var(--color-background-quaternary); + border-radius: $br-8; + box-shadow: 0 0 px2rem(12) 0 var(--color-shadow-dark); + z-index: var(--z-index-dropdown); + overflow: hidden auto; + background-color: var(--color-background-tertiary); + color: var(--color-foreground-primary); &.extended { inset-block-start: $sz-48; @@ -614,7 +625,33 @@ } .pill-actions-dropdown-item { - @extend %dropdown-element-base; + @include t.use-typography("body-small"); + + --pill-actions-dropdown-item-foreground-color: var(--color-foreground-secondary); + --pill-actions-dropdown-item-background-color: transparent; + + display: flex; + align-items: center; + gap: var(--sp-s); + block-size: $sz-32; + padding: 0 var(--sp-s); + border-radius: $br-6; + cursor: pointer; + color: var(--pill-actions-dropdown-item-foreground-color); + background-color: var(--pill-actions-dropdown-item-background-color); + + &:hover { + --pill-actions-dropdown-item-foreground-color: var(--color-foreground-primary); + --pill-actions-dropdown-item-background-color: var(--color-background-quaternary); + } +} + +.pill-actions-dropdown-item-label { + display: block; + max-inline-size: 99%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .variant-property-list { diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs index ea4857a40a..1304fd3c1d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs @@ -40,14 +40,13 @@ params. Shared by the square, column and row grid-type layouts, each of which places it at a different point in their own layout." [{:keys [show disabled on-toggle]}] - [:button {:class (stl/css-case :show-more-options true - :selected show) - :on-click on-toggle - :disabled disabled} - [:> icon* {:icon-id i/menu - :size "m" - :aria-hidden true - :class (stl/css :show-options-icon)}]]) + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.options.grid.params.guide-defaults") + :aria-pressed show + :on-click on-toggle + :disabled disabled + :tooltip-placement "top-left" + :icon i/menu}]) (mf/defc default-options-dropdown* "Dropdown menu with the reset/save-as-default options for a grid's params. @@ -230,10 +229,12 @@ [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.options.guides.toggle-guide") :on-click handle-toggle-visibility + :tooltip-placement "top-left" :icon (if display "shown" "hide")}] [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.options.guides.remove-guide") :on-click on-remove + :tooltip-placement "top-left" :icon i/remove}]]] (when (:display grid) @@ -365,6 +366,7 @@ [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.options.guides.add-guide") :on-click handle-create-grid + :tooltip-placement "top-left" :icon i/add}]]] (when (and open? (seq grids)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.scss index ba80378e2a..aaf09cc4ee 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/frame_grid.scss @@ -272,65 +272,6 @@ inline-size: px2rem(156); } -.show-more-options { - --show-more-options-background-color: transparent; - --show-more-options-border-color: transparent; - --show-more-options-color: var(--color-foreground-secondary); - - background: none; - cursor: pointer; - display: grid; - place-content: center; - block-size: $sz-32; - inline-size: $sz-32; - border-radius: $br-8; - background-color: var(--show-more-options-background-color); - border: $b-2 solid var(--show-more-options-border-color); - color: var(--show-more-options-color); - - &:focus-visible { - outline: none; - border: $b-1 solid var(--color-accent-primary); - - --show-more-options-background-color: var(--color-background-tertiary); - --show-more-options-color: var(--color-foreground-primary); - } - - &:hover { - --show-more-options-background-color: var(--color-background-quaternary); - --show-more-options-border-color: var(--color-background-quaternary); - --show-more-options-color: var(--color-accent-primary); - } - - &:active { - outline: none; - - --show-more-options-background-color: var(--color-background-secondary); - --show-more-options-border-color: transparent; - --show-more-options-color: var(--color-accent-primary); - } - - &[disabled], - &:disabled { - --show-more-options-color: var(--color-foreground-disabled); - - cursor: unset; - pointer-events: none; - } - - &.selected { - outline: none; - - --show-more-options-background-color: var(--color-background-quaternary); - --show-more-options-border-color: var(--color-background-quaternary); - --show-more-options-color: var(--color-accent-primary); - } -} - -.show-more-options svg { - stroke: var(--show-more-options-color); -} - .height, .gutter, .margin { diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs index 2434c66c07..b7ee6b4f6e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs @@ -19,6 +19,7 @@ [app.main.ui.components.numeric-input :as deprecated-input] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.components.title-bar :refer [title-bar*]] + [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as deprecated-icon] @@ -289,8 +290,7 @@ :set-alignment set-justify-self}]] [:div {:class (stl/css :row)} - [:button - {:class (stl/css :edit-grid-btn) - :alt (tr "workspace.layout-grid.editor.options.edit-grid") - :on-click toggle-edit-mode} + [:> button* {:variant "secondary" + :class (stl/css :edit-grid-btn) + :on-click toggle-edit-mode} (tr "workspace.layout-grid.editor.options.edit-grid")]]])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.scss index 8c245a491f..6b2092b133 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.scss @@ -4,14 +4,20 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for %input-element and %button-icon: the inputs and their icons move +// to the DS in their own plan. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; @use "../../../sidebar/common/sidebar.scss" as sidebar; .grid-cell-menu-container { - @include deprecated.flex-column; - - margin-top: deprecated.$s-8; - gap: deprecated.$s-16; + display: flex; + flex-direction: column; + gap: var(--sp-l); + grid-column: span 8; + margin-block-start: var(--sp-s); } .grid-cell-menu { @@ -20,55 +26,52 @@ .grid-cell-menu-title { grid-column: span 8; - font-size: deprecated.$fs-11; -} - -.grid-cell-menu-container { - grid-column: span 8; + font-size: px2rem(11); } .row { - @include deprecated.flex-row; + display: flex; + align-items: center; + gap: var(--sp-xs); } .cell-mode :global(label) { - padding: 0 deprecated.$s-12; + padding: 0 var(--sp-m); } .edit-grid-btn { - @extend %button-secondary; - @include deprecated.uppercase-title-typography; - - width: 100%; - padding: deprecated.$s-8; + justify-content: center; + inline-size: 100%; } .area-input { @extend %input-element; - @include deprecated.body-small-typography; + @include use-typography("body-small"); - width: 100%; - padding: deprecated.$s-8; + inline-size: 100%; + padding: var(--sp-s); } .grid-coord-group { - @include deprecated.flex-row; - - border-radius: deprecated.$br-8; - padding-left: deprecated.$s-4; - background-color: var(--input-background-color); + display: flex; + align-items: center; + gap: var(--sp-xs); + border-radius: $br-8; + padding-inline-start: var(--sp-xs); + background-color: var(--color-background-tertiary); } +// The legacy icon is a raw svg with no class of its own. .icon svg { @extend %button-icon; - stroke: var(--icon-foreground); + stroke: var(--color-foreground-secondary); } .coord-input { @extend %input-element; - @include deprecated.body-small-typography; + @include use-typography("body-small"); - border-radius: 0 deprecated.$br-8 deprecated.$br-8 0; - border-left: deprecated.$s-1 solid var(--panel-background-color); + border-radius: 0 $br-8 $br-8 0; + border-inline-start: $b-1 solid var(--color-background-primary); } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs index c74fbdca56..eb8f59542b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs @@ -9,6 +9,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.render-wasm.api.select :as wselect] [app.main.data.workspace :as dw] [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.tokens.application :as dwta] @@ -116,7 +117,7 @@ (when wasm-renderer-enabled? (doseq [id ids] - (wasm.api/use-shape id) + (wselect/use-shape id) (wasm.api/set-shape-blend-mode value) (wasm.api/request-render "preview-blend-mode"))) @@ -130,7 +131,7 @@ (swap! state* assoc :preview-complete? true) (when wasm-renderer-enabled? (doseq [id ids] - (wasm.api/use-shape id) + (wselect/use-shape id) (wasm.api/set-shape-blend-mode current-blend-mode) (wasm.api/request-render "preview-blend-mode"))) (st/emit! (dw/unset-preview-blend-mode ids)))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index 4fb0ebde64..430f46526f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -25,8 +25,9 @@ [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.components.select :refer [select]] [app.main.ui.components.title-bar :refer [title-bar*]] + [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] - [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.formats :as fmt] [app.main.ui.hooks :as h] [app.main.ui.icons :as deprecated-icon] @@ -192,13 +193,12 @@ (mf/defc wrap-row {::mf/props :obj} [{:keys [wrap-type on-click]}] - [:button {:class (stl/css-case :wrap-button true - :selected (= wrap-type :wrap)) - :title (if (= :wrap wrap-type) - "No wrap" - "Wrap") - :on-click on-click} - deprecated-icon/wrap]) + [:> icon-button* {:variant "ghost" + :tooltip-placement "top-left" + :aria-pressed (= wrap-type :wrap) + :aria-label "Wrap" + :icon i/wrap + :on-click on-click}]) (mf/defc align-row {::mf/props :obj} @@ -662,14 +662,13 @@ (= type :multiple) [:> multiple-padding-selection* props])] - [:button {:class (stl/css-case - :padding-toggle true - :selected (= type :multiple)) - :title (tr "workspace.layout-grid.editor.padding.expand") - :aria-label (tr "workspace.layout-grid.editor.padding.expand") - :data-type (d/name type) - :on-click on-type-change'} - deprecated-icon/padding-extended]])) + [:> icon-button* {:variant "ghost" + :tooltip-placement "top-left" + :aria-pressed (= type :multiple) + :aria-label (tr "workspace.layout-grid.editor.padding.expand") + :data-type (d/name type) + :icon i/padding-extended + :on-click on-type-change'}]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; GAP @@ -847,10 +846,9 @@ (if-not active? (st/emit! (udw/start-edition-mode id)) (st/emit! :interrupt))))] - [:button - {:class (stl/css :edit-mode-btn) - :alt "Grid edit mode" - :on-click toggle-edit-mode} + [:> button* {:variant "secondary" + :class (stl/css :edit-mode-btn) + :on-click toggle-edit-mode} (tr "workspace.layout-grid.editor.options.edit-grid")])) (mf/defc align-grid-row @@ -988,9 +986,16 @@ :on-pointer-leave pointer-leave} [:div {:class (stl/css :track-info-container)} - [:div {:class (stl/css :track-info-dir-icon) - :on-click handle-select-track} - (if is-column deprecated-icon/flex-vertical deprecated-icon/flex-horizontal)] + ;; Raw button on purpose: it is the start segment of the track + ;; value field, not a DS button. + [:button {:type "button" + :class (stl/css :track-info-dir-icon) + :aria-label (if is-column + (tr "workspace.layout-grid.editor.select-column") + (tr "workspace.layout-grid.editor.select-row")) + :on-click handle-select-track} + [:> icon* {:icon-id (if is-column i/flex-vertical i/flex-horizontal) + :class (stl/css :track-info-dir)}]] [:div {:class (stl/css :track-info-value)} [:> deprecated-input/numeric-input* {:no-validate true @@ -1038,11 +1043,20 @@ [:div {:class (stl/css :grid-tracks) :data-testid testid} [:div {:class (stl/css :grid-track-header)} - [:button {:class (stl/css :expand-icon) :on-click toggle} deprecated-icon/menu] + ;; The track title next to it toggles too, as a larger pointer + ;; target; this button is the one for the keyboard. + [:button {:class (stl/css :expand-icon) + :aria-label (if is-column + (tr "workspace.layout-grid.editor.columns") + (tr "workspace.layout-grid.editor.rows")) + :aria-expanded (boolean expanded?) + :on-click toggle} + [:> icon* {:icon-id i/menu :class (stl/css :expand-icon-svg)}]] [:div {:class (stl/css :track-title) :on-click toggle} [:div {:class (stl/css :track-name) :title track-name} track-name] [:div {:class (stl/css :track-detail) :title track-detail} track-detail]] - [:button {:class (stl/css :add-column) :on-click add-track} deprecated-icon/add]] + [:button {:class (stl/css :add-column) :on-click add-track} + [:> icon* {:icon-id i/add :size "s" :class (stl/css :add-column-svg)}]]] (when expanded? [:> h/sortable-container* {} @@ -1570,8 +1584,9 @@ :aria-label (tr "labels.help-center") :on-click open-grid-help :icon i/help}] - [:button {:class (stl/css :exit-btn) - :on-click #(st/emit! (udw/clear-edition-mode))} + [:> button* {:variant "secondary" + :class (stl/css :exit-btn) + :on-click #(st/emit! (udw/clear-edition-mode))} (tr "workspace.layout-grid.editor.options.exit")]] [:div {:class (stl/css :row :first-row)} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.scss index 7182dc5729..e3a1f34117 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.scss @@ -4,13 +4,15 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -// TODO: When button replace remove this @use @use "ds/_utils.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_borders.scss" as *; @use "ds/typography.scss" as t; @use "ds/spacing.scss" as *; @use "ds/mixins.scss" as *; + +// Only for %input-element and %disabled-input (numeric inputs) and the +// secondary and tertiary placeholders of the raw track header buttons. @use "refactor/common-refactor.scss" as deprecated; @use "../../../sidebar/common/sidebar.scss" as sidebar; @@ -61,25 +63,6 @@ grid-column: span 4; } -// TODO: Replace this buttons with DS buttons -.wrap-button { - @extend %button-tertiary; - - border-radius: $br-8; - block-size: $sz-32; - inline-size: $sz-32; - - svg { - @extend %button-icon; - - stroke: var(--color-foreground-secondary); - } - - &.selected { - @extend %button-icon-selected; - } -} - .second-row, .third-row { grid-column: 1 / -1; @@ -155,25 +138,6 @@ gap: var(--sp-xs); } -// TODO: Replace this buttons with DS buttons -.padding-toggle { - @extend %button-tertiary; - - block-size: $sz-32; - inline-size: $sz-32; - border-radius: $br-8; - - svg { - @extend %button-icon; - - stroke: var(--color-foreground-secondary); - } - - &.selected { - @extend %button-icon-selected; - } -} - .grid-layout-menu { @include sidebar.option-grid-structure; } @@ -214,22 +178,14 @@ grid-column: span 5; } -// TODO: Replace this buttons with DS buttons .edit-mode-btn { - @extend %button-secondary; - @include t.use-typography("headline-small"); - inline-size: 100%; - padding: var(--sp-s); + justify-content: center; grid-column: span 7; } -// TODO: Replace this buttons with DS buttons .exit-btn { - @extend %button-secondary; - @include t.use-typography("headline-small"); - - padding: var(--sp-s) var(--sp-xl); + justify-content: center; grid-column: span 2; } @@ -275,22 +231,18 @@ grid-column: 1 / span 7; } -// TODO: Replace this buttons with DS buttons .track-info-dir-icon { + display: flex; + align-items: center; cursor: pointer; + border: none; border-radius: $br-8 0 0 $br-8; background-color: var(--color-background-tertiary); padding: 0 var(--sp-s); + color: var(--color-foreground-secondary); - svg { - @extend %button-icon; - - stroke: var(--color-foreground-secondary); - block-size: 100%; - } - - &:hover svg { - stroke: var(--color-foreground-primary); + &:hover { + color: var(--color-foreground-primary); } } @@ -347,7 +299,8 @@ color: var(--color-foreground-secondary); } -// TODO: Replace this buttons with DS buttons +// Raw buttons on purpose: they are 52px segments of the track header, not +// DS buttons. The nested icon rules win over the placeholder svg colors. .expand-icon { @extend %button-secondary; @@ -355,38 +308,23 @@ border-radius: $br-8 0 0 $br-8; border-inline-end: $b-1 solid var(--color-background-primary); - svg { - @extend %button-icon; - + .expand-icon-svg { stroke: var(--color-foreground-secondary); - fill: var(--color-foreground-secondary); } - &:hover, - &:active { - svg { - stroke: var(--color-accent-primary); - fill: var(--color-accent-primary); - } + &:hover .expand-icon-svg, + &:active .expand-icon-svg { + stroke: var(--color-accent-primary); } } -// TODO: Replace this buttons with DS buttons .add-column { @extend %button-tertiary; block-size: px2rem(52); - svg { - display: flex; - justify-content: center; - align-items: center; - color: transparent; - stroke-width: px2rem(1); - block-size: $sz-12; - inline-size: $sz-12; + .add-column-svg { stroke: var(--color-foreground-secondary); - fill: var(--color-foreground-secondary); } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs index 6a2aed7599..b06f672e9c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -19,6 +19,7 @@ [app.main.ui.components.numeric-input :as deprecated-input] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.components.title-bar :refer [title-bar*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.icons :as deprecated-icon] [app.main.ui.workspace.sidebar.options.common :as soc] @@ -408,12 +409,13 @@ (= type :multiple) [:> margin-multiple* props])] - [:button {:class (stl/css-case - :margin-mode true - :selected (= type :multiple)) - :title "Margin - multiple" - :on-click on-type-change'} - deprecated-icon/margin]])) + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :margin-mode) + :tooltip-placement "top-left" + :aria-pressed (= type :multiple) + :aria-label "Margin - multiple" + :icon i/margin + :on-click on-type-change'}]])) (mf/defc element-behaviour-horizontal {::mf/props :obj diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.scss index 52b6a165e3..914bc85ffc 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.scss @@ -78,18 +78,7 @@ } .margin-mode { - @extend %button-tertiary; - grid-column: 3; - height: deprecated.$s-32; - - svg { - @extend %button-icon; - } - - &.selected { - @extend %button-icon-selected; - } } .margin-simple { diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs index ad07f27f6d..4c33772662 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs @@ -7,11 +7,12 @@ (ns app.main.ui.workspace.sidebar.options.menus.svg-attrs (:require-macros [app.main.style :as stl]) (:require - [app.common.data :as d] + [app.common.svg :as csvg] [app.main.data.workspace.shapes :as dwsh] [app.main.store :as st] [app.main.ui.components.title-bar :refer [title-bar*]] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.util.dom :as dom] [app.util.functions :as uf] [app.util.i18n :refer [tr]] @@ -40,7 +41,7 @@ (fn [] (on-delete attr))) - label (->> attr last d/name)] + label (->> attr last csvg/stored-attr-display-name)] [:* (if (string? value) [:div {:class (stl/css :attr-content)} @@ -48,13 +49,13 @@ [:div {:class (stl/css :attr-input)} [:input {:value @last-value :on-change handle-change}]] - [:div {:class (stl/css :attr-actions)} - [:button {:class (stl/css :attr-action-btn) - :on-click handle-delete} - deprecated-icon/remove-icon]]] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.sidebar.options.svg-attrs.remove") + :on-click handle-delete + :icon i/remove}]] [:div {:class (stl/css :attr-nested-content)} [:div {:class (stl/css :attr-title)} - (str (d/name (last attr)))] + (str (csvg/stored-attr-display-name (last attr)))] (for [[key value] value] [:div {:class (stl/css :attr-row) :key key} [:> attribute-value* {:key key diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss index ed73954e38..949590ec94 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss @@ -4,77 +4,67 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for %input-element: inputs move to the DS in their own plan. @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .element-set { margin: 0; } .title-spacing-svg-attrs { - padding-left: deprecated.$s-2; + padding-inline-start: var(--sp-xxs); margin: 0; } .element-set-content { - @include deprecated.flex-column; - - margin: deprecated.$s-4 0 0 0; + display: flex; + flex-direction: column; + gap: var(--sp-xs); + margin-block-start: var(--sp-xs); } .attr-content { display: flex; - gap: deprecated.$s-4; + gap: var(--sp-xs); } .attr-name { - @include deprecated.body-small-typography; - @include deprecated.two-line-text-ellipsis; + @include use-typography("body-small"); - width: deprecated.$s-88; - margin: auto deprecated.$s-4; - margin-right: 0; display: inline-block; - color: var(--title-foreground-color); + overflow: hidden; + text-overflow: ellipsis; + inline-size: px2rem(88); + max-inline-size: 99%; + margin-block: auto; + margin-inline: var(--sp-xs) 0; + color: var(--color-foreground-secondary); } .attr-input { @extend %input-element; - @include deprecated.body-small-typography; + @include use-typography("body-small"); - width: deprecated.$s-124; -} - -.attr-actions { - display: flex; - gap: deprecated.$s-4; -} - -.attr-action-btn { - @extend %button-tertiary; - - width: deprecated.$s-28; - height: deprecated.$s-32; - - svg { - @extend %button-icon; - } + inline-size: px2rem(124); } .attr-nested-content { display: grid; - row-gap: deprecated.$s-4; + row-gap: var(--sp-xs); } .attr-title { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - font-size: deprecated.$fs-10; + font-size: px2rem(10); text-transform: uppercase; - margin-inline-start: deprecated.$s-4; + margin-inline-start: var(--sp-xs); color: var(--color-foreground-primary); } .attr-row { display: flex; - gap: deprecated.$s-4; + gap: var(--sp-xs); } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss index ddc47c5b97..b9354a9f10 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss @@ -63,7 +63,8 @@ $font-preview-box-height: 28px; .typography-selection-wrapper { display: grid; - grid-template-columns: $sz-24 auto 1fr; + grid-template-columns: minmax($sz-24, auto) auto 1fr; + column-gap: var(--sp-xs); flex: 1; block-size: 100%; inline-size: 100%; @@ -74,6 +75,16 @@ $font-preview-box-height: 28px; } } +.typography-sample { + display: flex; + justify-content: center; + align-items: center; + min-inline-size: $sz-24; + block-size: $sz-32; + white-space: nowrap; + color: var(--color-foreground-secondary); +} + .typography-name { color: var(--color-foreground-primary); } @@ -87,6 +98,7 @@ $font-preview-box-height: 28px; display: flex; align-items: center; + gap: var(--sp-xs); block-size: $sz-32; inline-size: 100%; border-radius: $br-8; @@ -113,8 +125,10 @@ $font-preview-box-height: 28px; display: flex; justify-content: center; align-items: center; - inline-size: $sz-24; + flex-shrink: 0; + min-inline-size: $sz-24; block-size: 100%; + white-space: nowrap; overflow: hidden; color: var(--color-foreground-primary); } @@ -126,6 +140,7 @@ $font-preview-box-height: 28px; font-size: px2rem(12); color: var(--font-name-wrapper-foreground-color); flex-grow: 1; + min-inline-size: 0; padding-inline-start: px2rem(6); margin: 0; border: none; @@ -156,7 +171,8 @@ $font-preview-box-height: 28px; @include t.use-typography("body-small"); display: grid; - grid-template-columns: $sz-24 auto 1fr $sz-28; + grid-template-columns: minmax($sz-24, auto) auto 1fr $sz-28; + column-gap: var(--sp-xs); flex: 1; align-items: center; block-size: $sz-32; @@ -178,6 +194,7 @@ $font-preview-box-height: 28px; align-items: center; min-inline-size: $sz-24; block-size: $sz-32; + white-space: nowrap; padding: 0; overflow: hidden; color: var(--color-foreground-primary); diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/shadow_row.scss b/frontend/src/app/main/ui/workspace/sidebar/options/rows/shadow_row.scss index a376138e7f..4e0eff8a0b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/shadow_row.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/shadow_row.scss @@ -8,6 +8,7 @@ @use "ds/typography.scss" as t; @use "ds/_borders.scss" as *; @use "ds/_utils.scss" as *; +@use "ds/mixins.scss" as *; @use "refactor/common-refactor.scss" as deprecated; @use "../../../sidebar/common/sidebar.scss" as sidebar; @@ -84,8 +85,13 @@ @include t.use-typography("body-small"); .shadow-advanced-label { + @include text-ellipsis; + padding-inline-start: var(--sp-s); - inline-size: px2rem(60); + flex-shrink: 0; + inline-size: auto; + max-inline-size: calc(100% - #{px2rem(36)}); + line-height: $sz-32; } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index a5ed1ce76b..ce7f53496d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -262,6 +262,20 @@ (assoc :layout-padding (merge-layout-padding values shape-values)))] (promote-simple-layout-padding-type merged-values))) +(def ^:private content-attrs-cache (js/WeakMap.)) + +(defn- get-content-attrs + "Moving a text keeps its `:content`, so the attrs read from it are cached by content." + [{:keys [content] :as shape} attrs] + (if (some? content) + (let [[cached-attrs cached] (.get content-attrs-cache content)] + (if (identical? cached-attrs attrs) + cached + (let [result (attrs/get-text-attrs-multi shape txt/default-text-attrs attrs)] + (.set content-attrs-cache content [attrs result]) + result))) + (attrs/get-text-attrs-multi shape txt/default-text-attrs attrs))) + (defn get-attrs* "Given a group of attributes that we want to extract and the shapes to extract them from returns a list of tuples [id, values] with the extracted properties for the shapes that @@ -283,6 +297,11 @@ (into [] (comp (mapcat tt/shape-attr->token-attrs) (distinct)) (type->editable-attrs type)))) + ;; The `:text` read mode reads values from `attrs`, not from the + ;; editable attrs, so its token attrs must come from `attrs` too. + text-token-attrs + (into [] (comp (mapcat tt/shape-attr->token-attrs) (distinct)) attrs) + merge-attrs (fn [v1 v2] (cond @@ -311,8 +330,9 @@ merge-token-values (fn [acc token-attrs applied-tokens] - "Merges token values across all token attributes derived from the shape's - editable attributes." + "Merges token values across all `token-attrs`. Callers derive them from + the attributes the shape's read mode reads: the editable attributes for + `:shape`, the whole group attributes for `:text`." (let [no-tokens? (empty? applied-tokens) stable (deref stable-token-acc)] (if (and no-tokens? @@ -353,14 +373,14 @@ (let [shape-attrs (select-keys shape attrs) content-attrs - (attrs/get-text-attrs-multi shape txt/default-text-attrs attrs) + (get-content-attrs shape attrs) new-values (-> values (merge-attrs shape-attrs) (merge-attrs content-attrs)) - new-token-acc (merge-token-values token-acc (type->token-attrs type) applied-tokens)] + new-token-acc (merge-token-values token-acc text-token-attrs applied-tokens)] [(conj ids id) new-values new-token-acc]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs index 016b9b8ba5..4d262f12be 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs @@ -23,6 +23,7 @@ [app.main.ui.components.search-bar :refer [search-bar*]] [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.ds.product.panel-title :refer [panel-title*]] + [app.main.ui.hooks :as hooks] [app.main.ui.shortcuts :as ss] [app.util.dom :as dom] [app.util.i18n :refer [tr]] @@ -60,7 +61,9 @@ filter-term* (mf/use-state "") filter-term (deref filter-term*) - close-fn #(st/emit! (dw/toggle-layout-flag :shortcuts)) + close-fn (mf/use-fn #(st/emit! (dw/toggle-layout-flag :shortcuts))) + + on-key-down (hooks/use-escape-to-close close-fn) {:keys [all-shortcuts all-sc-names all-sub-names all-section-names]} (ss/build-all-shortcuts workspace-shortcuts dashboard-shortcuts viewer-shortcuts) @@ -127,7 +130,8 @@ (mf/use-fn #(st/emit! (rt/nav :settings-shortcuts {} {::rt/new-window true})))] - [:div {:class (dm/str class " " (stl/css :shortcuts))} + [:div {:class (dm/str class " " (stl/css :shortcuts)) + :on-key-down on-key-down} [:> panel-title* {:class (stl/css :shortcuts-title) :text (tr "shortcuts.title") :on-close close-fn}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/versions.cljs b/frontend/src/app/main/ui/workspace/sidebar/versions.cljs index 107c4d0885..2f5a4dcf5a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/versions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/versions.cljs @@ -65,6 +65,9 @@ (tr "workspace.versions.warning.subtext" support-email)))) (defn- group-snapshots + "History entries of `data`: each pinned version, and the system + snapshots grouped by day, the copies kept before a repair apart from + the autosaves." [data] (->> (concat (->> data @@ -72,12 +75,15 @@ (map #(assoc % :type :version))) (->> data (filter #(= "system" (:created-by %))) - (group-by #(ct/format-inst (:created-at %) :iso-date)) - (map (fn [[day entries]] + (group-by (juxt #(ct/format-inst (:created-at %) :iso-date) + #(if (dwv/repair-snapshot? %) :repair :autosave))) + (map (fn [[[day kind] entries]] {:type :snapshot + :kind kind :created-at (ct/inst day) :snapshots entries})))) - (sort-by :created-at) + ;; On the same day the copies before a repair come above the autosaves + (sort-by (juxt :created-at #(if (= :repair (:kind %)) 1 0))) (map-indexed (fn [index item] (assoc item :index index))) (reverse))) @@ -297,8 +303,10 @@ [:li {:ref entry-ref :class (stl/css :version-entry-wrap)} [:> milestone-group* - {:label (tr "workspace.versions.autosaved.version" - (ct/format-inst (:created-at entry) :localized-date)) + {:label (let [day (ct/format-inst (:created-at entry) :localized-date)] + (if (= :repair (:kind entry)) + (tr "workspace.versions.repair.version" day) + (tr "workspace.versions.autosaved.version" day))) :snapshots (mapv :created-at (:snapshots entry)) :on-menu-click on-open-snapshot-menu}] diff --git a/frontend/src/app/main/ui/workspace/text_palette.cljs b/frontend/src/app/main/ui/workspace/text_palette.cljs index b65d5842f7..e951b6120b 100644 --- a/frontend/src/app/main/ui/workspace/text_palette.cljs +++ b/frontend/src/app/main/ui/workspace/text_palette.cljs @@ -14,7 +14,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.context :as ctx] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.util.dom :as dom] [app.util.i18n :refer [tr]] [app.util.object :as obj] @@ -144,7 +144,8 @@ (when show-arrows? [:button {:class (stl/css :left-arrow) :disabled (= offset 0) - :on-click on-left-arrow-click} deprecated-icon/arrow]) + :on-click on-left-arrow-click} + [:> icon* {:icon-id i/arrow :class (stl/css :arrow-icon)}]]) [:div {:class (stl/css :text-palette-content) :ref container @@ -173,7 +174,8 @@ (when show-arrows? [:button {:class (stl/css :right-arrow) :disabled (= offset max-offset) - :on-click on-right-arrow-click} deprecated-icon/arrow])])) + :on-click on-right-arrow-click} + [:> icon* {:icon-id i/arrow :class (stl/css :arrow-icon)}]])])) (mf/defc text-palette* {::mf/wrap [mf/memo]} diff --git a/frontend/src/app/main/ui/workspace/text_palette.scss b/frontend/src/app/main/ui/workspace/text_palette.scss index 61cf22e34b..32dd942dc9 100644 --- a/frontend/src/app/main/ui/workspace/text_palette.scss +++ b/frontend/src/app/main/ui/workspace/text_palette.scss @@ -4,54 +4,56 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .text-palette { - height: 100%; + block-size: 100%; display: flex; } +// Raw buttons on purpose: full-height scroll arrows with a fade, not DS +// button sizes. .left-arrow, .right-arrow { - @include deprecated.button-style; - @include deprecated.flex-center; + --arrow-foreground-color: var(--color-foreground-secondary); + display: flex; + justify-content: center; + align-items: center; position: relative; - height: 100%; - width: deprecated.$s-24; + block-size: 100%; + inline-size: $sz-24; padding: 0; - z-index: deprecated.$z-index-2; + border: none; + background: none; + cursor: pointer; + color: var(--arrow-foreground-color); - svg { - @extend %button-icon; - } + // Above the typography list, which is positioned and comes after the + // left arrow, so the fade covers the items that scroll under it. + z-index: 2; &::after { content: ""; position: absolute; - bottom: 0; - left: calc(-1 * deprecated.$s-80); - height: 100%; - width: deprecated.$s-80; - z-index: deprecated.$z-index-1; - background-image: linear-gradient( - to left, - var(--palette-button-shadow-initial) 0%, - var(--palette-button-shadow-final) 100% - ); + inset-block-end: 0; + inset-inline-start: calc(-1 * px2rem(80)); + block-size: 100%; + inline-size: px2rem(80); + z-index: 1; + background-image: linear-gradient(to left, var(--color-background-primary) 0%, transparent 100%); pointer-events: none; } &:hover { - svg { - stroke: var(--button-foreground-hover); - } + --arrow-foreground-color: var(--color-accent-primary); } &:disabled { - svg { - stroke: var(--button-foreground-color-disabled); - } + --arrow-foreground-color: var(--color-foreground-disabled); &::after { background-image: none; @@ -61,19 +63,11 @@ .left-arrow { &::after { - left: deprecated.$s-24; - background-image: linear-gradient( - to right, - var(--palette-button-shadow-initial) 0%, - var(--palette-button-shadow-final) 100% - ); + inset-inline-start: $sz-24; + background-image: linear-gradient(to right, var(--color-background-primary) 0%, transparent 100%); } - &.disabled ::after { - background-image: none; - } - - svg { + .arrow-icon { transform: rotate(180deg); } } @@ -85,55 +79,46 @@ .text-palette-inside { display: flex; - gap: deprecated.$s-8; + gap: var(--sp-s); } .typography-item { - @include deprecated.body-small-typography; + --typography-item-background-color: var(--color-background-tertiary); + + @include use-typography("body-small"); display: flex; flex-direction: column; justify-content: center; align-items: flex-start; - height: 100%; - width: deprecated.$s-136; - padding: deprecated.$s-8; - border-radius: deprecated.$br-8; - background-color: var(--palette-text-background-color); + block-size: 100%; + inline-size: px2rem(136); + padding: var(--sp-s); + border-radius: $br-8; + background-color: var(--typography-item-background-color); &:first-child { - margin-left: deprecated.$s-8; + margin-inline-start: var(--sp-s); + } + + .typography-name, + .typography-font, + .typography-data { + display: block; + max-inline-size: 99%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + block-size: $sz-16; + inline-size: $sz-120; + color: var(--color-foreground-secondary); } .typography-name { - @include deprecated.text-ellipsis; - - height: deprecated.$s-16; - width: deprecated.$s-120; - color: var(--palette-text-color-selected); - } - - .typography-font { - @include deprecated.text-ellipsis; - - height: deprecated.$s-16; - width: deprecated.$s-120; - color: var(--palette-text-color); - } - - .typography-data { - @include deprecated.text-ellipsis; - - height: deprecated.$s-16; - width: deprecated.$s-120; - color: var(--palette-text-color); + color: var(--color-foreground-primary); } &.mid-item { - .typography-name { - height: deprecated.$s-16; - } - .typography-data { display: none; } @@ -147,12 +132,12 @@ } &:hover { - background-color: var(--palette-text-background-color-hover); + --typography-item-background-color: var(--color-background-quaternary); } } .text-palette-empty { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--palette-text-color); + color: var(--color-foreground-secondary); } diff --git a/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.cljs b/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.cljs index cdc13170f2..fd4ef95f3b 100644 --- a/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.cljs +++ b/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.cljs @@ -10,7 +10,7 @@ [app.common.data.macros :as dm] [app.main.refs :as refs] [app.main.ui.components.dropdown :refer [dropdown]] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.util.i18n :refer [tr]] [rumext.v2 :as mf])) @@ -37,7 +37,7 @@ (when (= selected (:id cur-library)) [:span {:class (stl/css :icon-wrapper)} - deprecated-icon/tick])])) + [:> icon* {:icon-id i/tick :size "s"}]])])) [:li {:class (stl/css-case :file-library true @@ -51,4 +51,4 @@ (dm/str "(" (count typographies) ")")]] (when (= selected :file) [:span {:class (stl/css :icon-wrapper)} - deprecated-icon/tick])]]])) + [:> icon* {:icon-id i/tick :size "s"}]])]]])) diff --git a/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.scss b/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.scss index 9cf70b7e93..bc1ca05c25 100644 --- a/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.scss +++ b/frontend/src/app/main/ui/workspace/text_palette_ctx_menu.scss @@ -4,86 +4,77 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; .text-context-menu { position: absolute; - left: auto; - bottom: var(--height); - max-width: deprecated.$s-480; - padding: deprecated.$s-4; - margin: 0 0 deprecated.$s-4 0; - z-index: deprecated.$z-index-4; - border-radius: deprecated.$br-10; - background-color: var(--context-menu-background-color); + inset-inline-start: auto; + inset-block-end: var(--height); + max-inline-size: $sz-480; + padding: var(--sp-xs); + margin: 0 0 var(--sp-xs) 0; + + // Context menu level of the legacy scale, inside the palette. + z-index: 4; + border-radius: px2rem(10); + background-color: var(--color-background-tertiary); .palette-library, .file-library { + --library-foreground-color: var(--color-foreground-secondary); + position: relative; display: flex; justify-content: space-between; - width: 100%; - gap: deprecated.$s-8; - padding: deprecated.$s-8; - margin-bottom: deprecated.$s-4; - border-radius: deprecated.$br-8; + inline-size: 100%; + gap: var(--sp-s); + padding: var(--sp-s); + margin-block-end: var(--sp-xs); + border-radius: $br-8; &:last-child { - margin-bottom: 0; + margin-block-end: 0; } .library-name { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--context-menu-foreground-color); + color: var(--library-foreground-color); display: grid; - grid-template-columns: 1fr deprecated.$s-24; - max-width: deprecated.$s-400; + grid-template-columns: 1fr $sz-24; + max-inline-size: $sz-400; .lib-name { - @include deprecated.text-ellipsis; - - max-width: deprecated.$s-380; + display: block; + max-inline-size: px2rem(380); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .lib-num { - margin-left: deprecated.$s-4; + margin-inline-start: var(--sp-xs); } } .icon-wrapper { - margin-left: deprecated.$s-4; - - @include deprecated.flex-center; - - svg { - @include deprecated.flex-center; - @extend %button-icon-small; - - stroke: var(--icon-foreground); - } + display: flex; + justify-content: center; + align-items: center; + margin-inline-start: var(--sp-xs); + color: var(--library-foreground-color); } &.selected, &:hover { - .icon-wrapper { - @include deprecated.flex-center; - - svg { - @include deprecated.flex-center; - @extend %button-icon-small; - - stroke: var(--context-menu-foreground-color-selected); - } - } - - .library-name { - color: var(--context-menu-foreground-color-selected); - } + --library-foreground-color: var(--color-foreground-primary); } &:hover { - background-color: var(--context-menu-background-color-selected); + background-color: var(--color-background-quaternary); } } } diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs index 1e8d9a6198..477d7327ea 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs @@ -81,10 +81,7 @@ (sd/resolve-tokens-interactive tokens)) (rx/mapcat (fn [resolved-tokens] - (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token)) - resolved-value (if (contains? cf/flags :tokenscript) - (ts/tokenscript-symbols->penpot-unit resolved-value) - resolved-value)] + (let [{:keys [errors resolved-value]} (get resolved-tokens (:name token))] (if resolved-value (rx/of {:value resolved-value}) (rx/of {:error (first errors)}))))))))) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs index b3dbec0c94..a98e51ce74 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs @@ -58,10 +58,7 @@ (sd/resolve-tokens-interactive tokens)) (rx/mapcat (fn [resolved-tokens] - (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token)) - resolved-value (if (contains? cf/flags :tokenscript) - (ts/tokenscript-symbols->penpot-unit resolved-value) - resolved-value)] + (let [{:keys [errors resolved-value]} (get resolved-tokens (:name token))] (if resolved-value (rx/of {:value resolved-value}) (rx/of {:error (if errors diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs index 88e8346580..7833bd95ea 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs @@ -76,10 +76,7 @@ (sd/resolve-tokens-interactive tokens)) (rx/mapcat (fn [resolved-tokens] - (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token)) - resolved-value (if (contains? cf/flags :tokenscript) - (ts/tokenscript-symbols->penpot-unit resolved-value) - resolved-value)] + (let [{:keys [errors resolved-value]} (get resolved-tokens (:name token))] (if resolved-value (rx/of {:value resolved-value}) (rx/of {:error (if errors diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs index 8264836980..b7dd512b7f 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs @@ -144,16 +144,25 @@ (defn- resolve-value-tokenscript - [tokens prev-token value] - (let [result (ts/update-token tokens (assoc prev-token :value value)) - token-result (.-resolved result)] + [tokens prev-token token-name value] + (let [valid-token-name? + (and (string? token-name) + (re-matches cto/token-name-validation-regex token-name)) + + token + (assoc prev-token + :value value + :name (if (or (not valid-token-name?) (str/blank? token-name)) + "__PENPOT__TOKEN__NAME__PLACEHOLDER__" + token-name)) + + {:keys [errors resolved-value]} + ;; Remove previous token when renaming a token + (ts/resolve-token (dissoc tokens (:name prev-token)) token)] (rx/of - (cond - (ts/processor-error? token-result) {:error (wte/error-with-value :error.style-dictionary/missing-reference (some->> (.-dependencyChain token-result) - (seq) - (rest)))} - (instance? js/Error token-result) {:error (wte/error-with-value :error.style-dictionary/invalid-token-value value)} - :else {:value token-result})))) + (if (seq errors) + {:error (first errors)} + {:value resolved-value})))) (defn- resolve-value [tokens prev-token token-name value] @@ -178,10 +187,7 @@ (sd/resolve-tokens-interactive) (rx/mapcat (fn [resolved-tokens] - (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens (:name token)) - resolved-value (if (contains? cf/flags :tokenscript) - (ts/tokenscript-symbols->penpot-unit resolved-value) - resolved-value)] + (let [{:keys [errors resolved-value]} (get resolved-tokens (:name token))] (if resolved-value (rx/of {:value resolved-value}) (rx/of {:error (if errors diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs index 65bd7787c6..75e2317814 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs @@ -125,3 +125,30 @@ [error token-name] (let [translated-string (tr "errors.tokens.name-collision" token-name)] (= error translated-string))) + +(defn- referenced-token-name + "Name of the token `reference` points to, when `reference` is exactly + one `{name}` reference." + [reference] + (when (string? reference) + (let [reference (str/trim reference) + names (cto/find-token-value-references reference) + name (first names)] + (when (and (= 1 (count names)) + (= reference (str "{" name "}"))) + name)))) + +(defn referenced-token-value + "Composite value of the `token-type` token that `reference` points to, + following chained references. Nil when the chain does not end in a + `token-type` token with a composite value." + [tokens reference token-type] + (loop [reference reference + visited #{}] + (when-let [name (referenced-token-name reference)] + (let [{:keys [type value]} (get tokens name)] + (when (and (= token-type type) + (not (contains? visited name))) + (if (string? value) + (recur value (conj visited name)) + value)))))) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs index 90fef836e7..2d3807b57a 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.cljs @@ -18,6 +18,7 @@ [app.main.ui.forms :as forms] [app.main.ui.hooks :as hooks] [app.main.ui.workspace.tokens.management.forms.controls :as token.controls] + [app.main.ui.workspace.tokens.management.forms.controls.utils :as csu] [app.main.ui.workspace.tokens.management.forms.generic-form :as generic] [app.main.ui.workspace.tokens.management.forms.validators :refer [check-self-reference default-validate-token]] [app.util.dom :as dom] @@ -75,6 +76,25 @@ :blur "4" :spread "0"}) +(defn- make-default-value + [value] + (cond + (string? value) + {:reference value + :shadow []} + + (vector? value) + {:reference nil + :shadow (mapv (fn [shadow] + (-> shadow + (update :blur #(if (str/blank? %) "0" %)) + (update :spread #(if (str/blank? %) "0" %)))) + value)} + + :else + {:reference nil + :shadow [default-token-shadow]})) + (defn get-subtoken [token index prop value-subfield] (let [value (get-in token [:value value-subfield index prop])] @@ -202,19 +222,66 @@ :show-button (> length 1)}]))) (mf/defc reference-form* - [{:keys [token tokens] :as props}] - [:div {:class (stl/css :input-row-reference)} - [:> token.controls/input-composite* - {:placeholder (tr "workspace.tokens.reference-composite-shadow") - :aria-label (tr "labels.reference") - :icon i/drop-shadow - :name :reference - :token token - :tokens tokens}]]) + [{:keys [token tokens on-detach] :as props}] + (let [form (mf/use-ctx forms/context) + reference (get-in @form [:data :value :reference]) + + detached-value + (mf/with-memo [tokens reference] + (csu/referenced-token-value tokens reference :shadow)) + + on-detach-click + (mf/use-fn + (mf/deps detached-value on-detach) + (fn [event] + (dom/prevent-default event) + (on-detach detached-value)))] + + [:div {:class (stl/css :input-row-reference :reference-row)} + [:> token.controls/input-composite* + {:placeholder (tr "workspace.tokens.reference-composite-shadow") + :aria-label (tr "labels.reference") + :icon i/drop-shadow + :name :reference + :token token + :tokens tokens + :slot-end (when (some? detached-value) + (mf/html + [:> icon-button* {:variant "action" + :type "button" + :class (stl/css :detach-button) + :aria-label (tr "workspace.tokens.detach-reference") + :on-click on-detach-click + :icon i/detach}]))}]])) (mf/defc tabs-wrapper* [{:keys [token tokens tab handle-toggle value-subfield] :rest props}] (let [form (mf/use-ctx forms/context) + + ;; Form value of the detached reference, used so the shadow + ;; inputs show the resolved value of the copied fields. + detached* (mf/use-state nil) + detached (deref detached*) + + composite-token + (mf/with-memo [token detached] + (if (some? detached) + (assoc token :value detached) + token)) + + on-detach + (mf/use-fn + (mf/deps form handle-toggle) + (fn [value] + (let [value (make-default-value value)] + (swap! form (fn [state] + (-> state + (assoc-in [:data :value] value) + (update :errors dissoc :value) + (update :extra-errors dissoc :value)))) + (reset! detached* value) + (handle-toggle "composite")))) + on-add-shadow-block (mf/use-fn (mf/deps value-subfield) @@ -250,13 +317,14 @@ :id "reference-opt"}]]] (if (= tab :composite) - [:> composite-form* {:token token + [:> composite-form* {:token composite-token :tokens tokens :remove-shadow-block remove-shadow-block :value-subfield value-subfield}] [:> reference-form* {:token token - :tokens tokens}])])) + :tokens tokens + :on-detach on-detach}])])) ;; TODO: use cfo/make-schema:token-value and extend it with shadow and reference fields (defn- make-schema @@ -329,25 +397,6 @@ (or ref-valid? valid-composite-shadow?)))]])) -(defn- make-default-value - [value] - (cond - (string? value) - {:reference value - :shadow []} - - (vector? value) - {:reference nil - :shadow (mapv (fn [shadow] - (-> shadow - (update :blur #(if (str/blank? %) "0" %)) - (update :spread #(if (str/blank? %) "0" %)))) - value)} - - :else - {:reference nil - :shadow [default-token-shadow]})) - (mf/defc form* [{:keys [token token-type diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.scss b/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.scss index ead3c104fe..f3a7047749 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.scss +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/shadow.scss @@ -32,6 +32,15 @@ padding-inline-start: var(--sp-m); } +.detach-button { + visibility: hidden; +} + +.reference-row:hover .detach-button, +.reference-row:focus-within .detach-button { + visibility: visible; +} + .title-bar { display: grid; grid-template-columns: 1fr auto auto; diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/typography.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/typography.cljs index d23a9a1543..a34dced97c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/typography.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/typography.cljs @@ -13,10 +13,14 @@ [app.common.types.token :as cto] [app.main.data.workspace.tokens.errors :as wte] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.forms :as forms] [app.main.ui.workspace.tokens.management.forms.controls :as token.controls] + [app.main.ui.workspace.tokens.management.forms.controls.utils :as csu] [app.main.ui.workspace.tokens.management.forms.generic-form :as generic] [app.main.ui.workspace.tokens.management.forms.validators :refer [check-coll-self-reference check-self-reference default-validate-token]] + [app.util.dom :as dom] [app.util.i18n :refer [tr]] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -60,6 +64,25 @@ check-typography-token-self-reference]) (default-validate-token)))) +;; HELPERS + +(def ^:private composite-keys + [:font-family + :font-size + :font-weight + :line-height + :letter-spacing + :text-case + :text-decoration]) + +(defn- composite-value->form-value + "Form data for the composite tab from a typography token map `value`." + [value] + (-> (cond-> value + (:font-family value) + (update :font-family cto/join-font-family)) + (select-keys composite-keys))) + ;; COMPONENTS (mf/defc composite-form* @@ -172,40 +195,88 @@ :tokens tokens}]]])) (mf/defc reference-form* - [{:keys [token tokens] :as props}] - [:div {:class (stl/css :input-row)} - [:> token.controls/input-composite* - {:placeholder (tr "workspace.tokens.reference-composite") - :aria-label (tr "labels.reference") - :icon i/text-typography - :name :reference - :token token - :tokens tokens}]]) + [{:keys [token tokens on-detach] :as props}] + (let [form (mf/use-ctx forms/context) + reference (get-in @form [:data :value :reference]) + + detached-value + (mf/with-memo [tokens reference] + (csu/referenced-token-value tokens reference :typography)) + + on-detach-click + (mf/use-fn + (mf/deps detached-value on-detach) + (fn [event] + (dom/prevent-default event) + (on-detach detached-value)))] + + [:div {:class (stl/css :input-row :reference-row)} + [:> token.controls/input-composite* + {:placeholder (tr "workspace.tokens.reference-composite") + :aria-label (tr "labels.reference") + :icon i/text-typography + :name :reference + :token token + :tokens tokens + :slot-end (when (some? detached-value) + (mf/html + [:> icon-button* {:variant "action" + :type "button" + :class (stl/css :detach-button) + :aria-label (tr "workspace.tokens.detach-reference") + :on-click on-detach-click + :icon i/detach}]))}]])) (mf/defc tabs-wrapper* [{:keys [token tokens tab handle-toggle] :rest props}] - [:* - [:div {:class (stl/css :title-bar)} - [:div {:class (stl/css :title)} (tr "labels.typography")] - [:& radio-buttons {:class (stl/css :listing-options) - :selected (d/name tab) - :on-change handle-toggle - :name "reference-composite-tab"} - [:& radio-button {:icon i/layers - :value "composite" - :title (tr "workspace.tokens.individual-tokens") - :id "composite-opt"}] - [:& radio-button {:icon i/tokens - :value "reference" - :title (tr "workspace.tokens.use-reference") - :id "reference-opt"}]]] - [:div {:class (stl/css :inputs-wrapper)} - (if (= tab :composite) - [:> composite-form* {:token token - :tokens tokens}] + (let [form (mf/use-ctx forms/context) - [:> reference-form* {:token token - :tokens tokens}])]]) + ;; Raw value of the detached reference, used so the composite + ;; inputs show the resolved value of the copied fields. + detached* (mf/use-state nil) + detached (deref detached*) + + composite-token + (mf/with-memo [token detached] + (if (some? detached) + (assoc token :value detached) + token)) + + on-detach + (mf/use-fn + (mf/deps form handle-toggle) + (fn [value] + (swap! form (fn [state] + (-> state + (assoc-in [:data :value] (composite-value->form-value value)) + (update :errors dissoc :value) + (update :extra-errors dissoc :value)))) + (reset! detached* value) + (handle-toggle "composite")))] + + [:* + [:div {:class (stl/css :title-bar)} + [:div {:class (stl/css :title)} (tr "labels.typography")] + [:& radio-buttons {:class (stl/css :listing-options) + :selected (d/name tab) + :on-change handle-toggle + :name "reference-composite-tab"} + [:& radio-button {:icon i/layers + :value "composite" + :title (tr "workspace.tokens.individual-tokens") + :id "composite-opt"}] + [:& radio-button {:icon i/tokens + :value "reference" + :title (tr "workspace.tokens.use-reference") + :id "reference-opt"}]]] + [:div {:class (stl/css :inputs-wrapper)} + (if (= tab :composite) + [:> composite-form* {:token composite-token + :tokens tokens}] + + [:> reference-form* {:token token + :tokens tokens + :on-detach on-detach}])]])) ;; SCHEMA @@ -274,17 +345,8 @@ {:reference value} (map? value) - (let [value (cond-> value - (:font-family value) - (update :font-family cto/join-font-family))] - (select-keys value - [:font-family - :font-size - :font-weight - :line-height - :letter-spacing - :text-case - :text-decoration])) + (composite-value->form-value value) + :else {})] diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/typography.scss b/frontend/src/app/main/ui/workspace/tokens/management/forms/typography.scss index 2aa3e9707c..6fc73253bc 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/typography.scss +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/typography.scss @@ -23,6 +23,15 @@ gap: var(--sp-xs); } +.detach-button { + visibility: hidden; +} + +.reference-row:hover .detach-button, +.reference-row:focus-within .detach-button { + visibility: visible; +} + .title-bar { display: grid; grid-template-columns: 1fr auto; diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/validators.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/validators.cljs index 4fb9be3e4f..717e37f831 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/validators.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/validators.cljs @@ -44,9 +44,7 @@ (sd/resolve-tokens-interactive tokens')) (rx/mapcat (fn [resolved-tokens] - (let [resolved-token (cond-> (get resolved-tokens (:name token)) - (contains? cf/flags :tokenscript) - (update :resolved-value ts/tokenscript-symbols->penpot-unit))] + (let [resolved-token (get resolved-tokens (:name token))] (cond (:resolved-value resolved-token) (rx/of resolved-token) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs b/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs index cdb8d89284..a54a932e42 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs @@ -109,61 +109,59 @@ (str/join ", " (map attribute-dictionary values)) "."))) grouped-values))) -(defn- generate-tooltip - "Generates a tooltip for a given token" - [is-viewer shape theme-token token half-applied no-valid-value ref-not-in-active-set is-name-collision errors] - (let [{:keys [name type resolved-value value]} token - resolved-value-theme (:resolved-value theme-token) - resolved-value (or resolved-value-theme resolved-value) - {:keys [title] :as token-props} (dwta/get-token-properties theme-token) +(defn- tooltip-info + "For an invalid token the tooltip is only its error message. Otherwise + it shows the token's name and values and, for viewers, the attributes + of `shape` the token is applied to." + [token resolved-token shape state is-viewer] + (let [{:keys [name type value]} token + {:keys [is-half-applied has-missing-reference has-invalid-value + error-message]} state + resolved-value (or (:resolved-value resolved-token) (:resolved-value token)) + {:keys [title] :as token-props} (dwta/get-token-properties resolved-token) applied-tokens (:applied-tokens shape) - app-token-vals (set (vals applied-tokens)) app-token-keys (keys applied-tokens) - is-applied? (contains? app-token-vals name) - - - applied-to (if half-applied - (partially-applied-attr app-token-keys is-applied? token-props) - (tr "labels.all")) - grouped-values (group-by dimensions-dictionary app-token-keys) - - name-line - (mf/html [:* (dm/str (tr "workspace.tokens.token-name") ": ") - [:span {:class (stl/css :token-pill-tooltip-name)} name]]) - - rest-of-title (dm/str (tr "workspace.tokens.original-value" (dwtf/format-token-value value)) "\n" - (tr "workspace.tokens.resolved-value" (dwtf/format-token-value resolved-value)) - (when (= (:type token) :number) - (dm/str "\n" (tr "workspace.tokens.more-options"))))] + is-applied (contains? (set (vals applied-tokens)) name)] (cond - ;; If there are errors, show the appropriate message - ref-not-in-active-set - (tr "workspace.tokens.ref-not-valid" name) + has-missing-reference + {:message (tr "workspace.tokens.ref-not-valid" name)} - is-name-collision - (wte/resolve-error-message (first errors)) + error-message + {:message error-message} - no-valid-value - (tr "workspace.tokens.value-not-valid") + has-invalid-value + {:message (tr "workspace.tokens.value-not-valid")} - ;; If the token is applied and the user is a is-viewer, show the details - (and is-applied? is-viewer) - (mf/html - [:* - name-line - [:div rest-of-title] - [:div (tr "workspace.tokens.applied-to")] - [:div (if (= :dimensions type) - (translate-and-format grouped-values) - (str "- " title ": " applied-to))]]) - - ;; Otherwise only show the base title :else + {:name name + :value (dwtf/format-token-value value) + :resolved-value (dwtf/format-token-value resolved-value) + :has-more-options (= type :number) + :applied-to + (when (and is-applied is-viewer) + (if (= :dimensions type) + (translate-and-format (group-by dimensions-dictionary app-token-keys)) + (str "- " title ": " + (if is-half-applied + (partially-applied-attr app-token-keys is-applied token-props) + (tr "labels.all")))))}))) + +(defn- render-tooltip + [{:keys [message name value resolved-value has-more-options applied-to]}] + (or message (mf/html [:* - name-line - [:div rest-of-title]])))) + (dm/str (tr "workspace.tokens.token-name") ": ") + [:span {:class (stl/css :token-pill-tooltip-name)} name] + [:div (dm/str (tr "workspace.tokens.original-value" value) "\n" + (tr "workspace.tokens.resolved-value" resolved-value) + (when has-more-options + (dm/str "\n" (tr "workspace.tokens.more-options"))))] + (when applied-to + [:* + [:div (tr "workspace.tokens.applied-to")] + [:div applied-to]])]))) ;; FIXME: the token thould already have precalculated references, so ;; we don't need to perform this regex operation on each rerender @@ -195,12 +193,69 @@ (def token-types-with-status-icon #{:color :border-radius :rotation :sizing :dimensions :opacity :spacing :stroke-width}) +(defn- pill-state + "Works out whether the token is applied to the selection, partially + applied, disabled or invalid. Without a selection it counts as fully + applied." + [token active-theme-tokens selected-shapes is-selected-inside-layout] + (let [{:keys [name value type]} token + errors (:errors (get active-theme-tokens name)) + has-selected (seq selected-shapes) + tokenscript (contains? cf/flags :tokenscript) + + attributes + (as-> (get dwta/token-properties type) $ + (d/nilv (:all-attributes $) (:attributes $))) + + full-applied + (or (not has-selected) + (applied-all-attributes? token selected-shapes attributes)) + + applied + (boolean (and has-selected + (cfo/shapes-token-applied? token selected-shapes attributes))) + + half-applied (and applied (not full-applied)) + + missing-reference + (boolean + (if tokenscript + (wte/has-error-code? :error.style-dictionary/missing-reference errors) + (and (cfo/is-reference? token) + (not (contains-reference-value? value active-theme-tokens))))) + + name-collision + (= :error.token/name-collision (:error/code (first errors))) + + invalid-value (boolean (seq errors))] + + {:is-applied applied + :is-half-applied half-applied + :is-disabled + (boolean + (and has-selected + (not applied) + (not (attributes-match-selection? + selected-shapes attributes + {:selected-inside-layout? is-selected-inside-layout})))) + :has-missing-reference missing-reference + :has-invalid-value invalid-value + ;; Tokenscript errors carry their own message, as in the edit form. + :error-message + (when (and invalid-value (or tokenscript name-collision)) + (wte/resolve-error-message (first errors))) + :has-errors (or missing-reference invalid-value) + :status-icon-id + (cond + half-applied "token-status-partial" + full-applied "token-status-full" + :else "token-status-non-applied")})) + (mf/defc token-pill* {::mf/wrap [mf/memo]} [{:keys [on-click token on-context-menu selected-shapes is-selected-inside-layout active-theme-tokens can-edit]}] - (let [{:keys [name value type]} token - resolved-token (get active-theme-tokens (:name token)) - errors (:errors resolved-token) + (let [{:keys [name type]} token + resolved-token (get active-theme-tokens name) pill-ref (mf/use-ref nil) pill-id (mf/use-id) @@ -208,52 +263,14 @@ tooltip-content* (mf/use-state "") tooltip-content (deref tooltip-content*) - has-selected? (pos? (count selected-shapes)) - is-reference? (cfo/is-reference? token) contains-path? (str/includes? name ".") + is-viewer? (not can-edit) - attributes (as-> (get dwta/token-properties type) $ - (d/nilv (:all-attributes $) (:attributes $))) - - full-applied? - (if has-selected? - (applied-all-attributes? token selected-shapes attributes) - true) - - applied? - (if has-selected? - (cfo/shapes-token-applied? token selected-shapes attributes) - false) - - half-applied? - (and applied? (not full-applied?)) - - disabled? (and - has-selected? - (not applied?) - (not half-applied?) - (not (attributes-match-selection? selected-shapes attributes {:selected-inside-layout? is-selected-inside-layout}))) - - - is-viewer? (not can-edit) - - ref-not-in-active-set - (if (contains? cf/flags :tokenscript) - (seq (:errors resolved-token)) - (and is-reference? - (not (contains-reference-value? value active-theme-tokens)))) - - name-collision (->> errors - (first) - (:error/code) - (= :error.token/name-collision)) - - no-valid-value (seq errors) - - errors? - (or ref-not-in-active-set - no-valid-value - name-collision) + {:keys [is-applied is-disabled has-invalid-value error-message + has-errors status-icon-id] + :as state} + (mf/with-memo [token active-theme-tokens selected-shapes is-selected-inside-layout] + (pill-state token active-theme-tokens selected-shapes is-selected-inside-layout)) color (when (cfo/color-token? token) @@ -262,23 +279,6 @@ status-icon? (contains? token-types-with-status-icon type) - on-click - (mf/use-fn - (mf/deps errors? on-click token) - (fn [event] - (dom/stop-propagation event) - (when (and (not (seq errors)) on-click) - (on-click event token)))) - - token-status-id - (cond - half-applied? - "token-status-partial" - full-applied? - "token-status-full" - :else - "token-status-non-applied") - on-context-menu (mf/use-fn (mf/deps can-edit on-context-menu token) @@ -289,20 +289,19 @@ on-click (mf/use-fn - (mf/deps errors? on-click can-edit) + (mf/deps can-edit has-invalid-value on-click token) (fn [event] (dom/stop-propagation event) - (when (and can-edit (not (seq errors)) on-click) - (on-click event)))) + (when (and can-edit (not has-invalid-value) on-click) + (on-click event token)))) on-hover (mf/use-fn - (mf/deps selected-shapes is-viewer? active-theme-tokens token half-applied? no-valid-value ref-not-in-active-set name-collision errors) + (mf/deps token resolved-token selected-shapes state is-viewer?) (fn [_] - (let [theme-token (get active-theme-tokens name) - title (generate-tooltip is-viewer? (first selected-shapes) theme-token token - half-applied? no-valid-value ref-not-in-active-set name-collision errors)] - (reset! tooltip-content* title)))) + (->> (tooltip-info token resolved-token (first selected-shapes) state is-viewer?) + (render-tooltip) + (reset! tooltip-content*)))) ;; `tooltip-content` may hold rich (hiccup) content, so it's wrapped ;; in a function to satisfy the tooltip's `:content` schema; this @@ -319,20 +318,20 @@ [:button {:ref pill-ref :class (stl/css-case :token-pill true - :token-pill-no-icon (and (not status-icon?) (not errors?)) + :token-pill-no-icon (and (not status-icon?) (not has-errors)) :token-pill-default can-edit - :token-pill-disabled disabled? - :token-pill-applied (and can-edit applied?) - :token-pill-invalid (and can-edit errors?) - :token-pill-invalid-applied (and applied? errors? can-edit) + :token-pill-disabled is-disabled + :token-pill-applied (and can-edit is-applied) + :token-pill-invalid (and can-edit has-errors) + :token-pill-invalid-applied (and is-applied has-errors can-edit) :token-pill-viewer is-viewer? :token-pill-applied-viewer (and is-viewer? - applied?) + is-applied) :token-pill-invalid-viewer (and is-viewer? - errors?) + has-errors) :token-pill-invalid-applied-viewer (and is-viewer? - applied?)) + is-applied)) :id (str "token-pill-" (:id token)) :type "button" :on-focus on-hover @@ -342,13 +341,12 @@ :on-context-menu on-context-menu} (cond - errors? + has-errors [:> icon* {:icon-id i/broken-link :class (stl/css :token-pill-icon) - :aria-label (if name-collision - (wte/resolve-error-message (first errors)) - (tr "workspace.tokens.missing-reference"))}] + :aria-label (or error-message + (tr "workspace.tokens.missing-reference"))}] color [:> swatch* {:background color @@ -356,7 +354,7 @@ status-icon? [:> token-status-icon* - {:icon-id token-status-id + {:icon-id status-icon-id :class (stl/css :token-pill-icon)}]) (if contains-path? diff --git a/frontend/src/app/main/ui/workspace/top_toolbar.cljs b/frontend/src/app/main/ui/workspace/top_toolbar.cljs index f3d843000d..7a7f767621 100644 --- a/frontend/src/app/main/ui/workspace/top_toolbar.cljs +++ b/frontend/src/app/main/ui/workspace/top_toolbar.cljs @@ -15,17 +15,15 @@ [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.drawing.common :as dwdc] - [app.main.data.workspace.mcp :as mcp] [app.main.data.workspace.media :as dwm] [app.main.data.workspace.path.state :as pst] [app.main.data.workspace.shortcuts :as sc] [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] - [app.main.ui.components.dropdown-menu :refer [dropdown-menu* dropdown-menu-item*]] [app.main.ui.components.file-uploader :as file-uploader] + [app.main.ui.components.mcp-menu :refer [mcp-menu*]] [app.main.ui.context :as ctx] - [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] [app.util.dom :as dom] @@ -295,55 +293,6 @@ :ref ref :on-selected on-selected}]])) -(mf/defc mcp-tool* - {::mf/private true - ::mf/wrap [mf/memo]} - [{:keys [is-mcp-connected]}] - (let [menu-open* (mf/use-state false) - menu-open? (deref menu-open*) - - on-toggle-menu - (mf/use-fn - (fn [event] - (dom/stop-propagation event) - (swap! menu-open* not))) - - on-close-menu - (mf/use-fn - #(reset! menu-open* false)) - - on-connect - (mf/use-fn - #(st/emit! (mcp/connect-mcp) - (ev/event {::ev/name "connect-mcp-plugin" - ::ev/origin "workspace:toolbar"})))] - - [:* - [:> button* {:variant "ghost" - :on-click on-toggle-menu - :aria-pressed menu-open? - :data-tool "mcp" - :data-testid "mcp-btn"} - [:div {:class (stl/css-case :toolbar-mcp-button true - :selected menu-open?)} - [:span {:class (stl/css-case :toolbar-mcp-button-dot true - :connected is-mcp-connected)}] - [:span {:class (stl/css-case :toolbar-mcp-button-label true - :connected is-mcp-connected)} - (tr "workspace.toolbar.mcp")]]] - - [:div {:class (stl/css :toolbar-mcp-menu)} - [:> dropdown-menu* {:show menu-open? - :on-close on-close-menu - :class (stl/css :toolbar-mcp-dropdown)} - (if is-mcp-connected - [:li {:class (stl/css :toolbar-mcp-dropdown-info) - :role "presentation"} - (tr "workspace.toolbar.mcp-connected")] - [:> dropdown-menu-item* {:class (stl/css :toolbar-mcp-dropdown-item) - :on-click on-connect} - (tr "workspace.toolbar.mcp-connect-here")])]]])) - (mf/defc top-toolbar* {::mf/wrap [mf/memo]} [{:keys [layout]}] @@ -480,7 +429,9 @@ (when mcp-show? [:li {:class (stl/css :toolbar-option)} - [:> mcp-tool* {:is-mcp-connected mcp-connected?}]])] + [:> mcp-menu* {:is-mcp-connected mcp-connected? + :is-connection-requested (:connection-requested mcp) + :session-id (:session-id mcp)}]])] [:button {:title (tr "workspace.toolbar.toggle-toolbar") :aria-label (tr "workspace.toolbar.toggle-toolbar") diff --git a/frontend/src/app/main/ui/workspace/top_toolbar.scss b/frontend/src/app/main/ui/workspace/top_toolbar.scss index 23581afc99..71ad771b3c 100644 --- a/frontend/src/app/main/ui/workspace/top_toolbar.scss +++ b/frontend/src/app/main/ui/workspace/top_toolbar.scss @@ -83,7 +83,7 @@ inset-inline-start: 50%; transform: translateX(-50%); margin-block-start: var(--sp-xxs); - padding: var(--sp-xxs) 0; + padding: var(--sp-xxs); border-radius: $br-8; border: #{$b-1} solid var(--menu-border-color); background-color: var(--menu-background-color); @@ -101,96 +101,6 @@ } } -.toolbar-mcp-button { - display: flex; - align-items: center; - gap: var(--sp-xs); -} - -.toolbar-mcp-button-label { - &.connected { - color: var(--color-accent-primary); - } -} - -.toolbar-mcp-button-dot { - position: relative; - display: flex; - flex-shrink: 0; - inline-size: px2rem(6); - block-size: px2rem(6); - border-radius: 50%; - background-color: var(--color-background-disabled); - - &.connected { - background-color: var(--color-accent-primary); - } - - // One-shot "blob" ripple that confirms the moment the tab becomes - // connected (e.g. after using "Switch here"). Triggered purely by the - // `.connected` class appearing, in sync with the color change. - &.connected::after { - content: ""; - position: absolute; - inset: 0; - border-radius: 50%; - background-color: var(--color-accent-primary); - opacity: 0; - animation: mcp-status-blob 0.5s ease-out; - } -} - -@keyframes mcp-status-blob { - from { - opacity: 0.5; - transform: scale(1); - } - - to { - opacity: 0; - transform: scale(2.5); - } -} - -.toolbar-mcp-menu { - position: absolute; - left: 0; - top: $sz-36; -} - -.toolbar-mcp-dropdown { - box-shadow: 0 0 $sz-12 0 var(--color-shadow-dark); - z-index: var(--z-index-dropdown); - margin: 0; - padding: var(--sp-xs); - border: $b-1 solid var(--panel-border-color); - border-radius: $br-8; - background-color: var(--menu-background-color); -} - -.toolbar-mcp-dropdown-info { - @include t.use-typography("body-small"); - - padding: var(--sp-s) var(--sp-m); - color: var(--color-foreground-secondary); - white-space: nowrap; -} - -.toolbar-mcp-dropdown-item { - @include t.use-typography("body-small"); - - display: flex; - align-items: center; - padding: var(--sp-s) var(--sp-m); - border-radius: $br-8; - color: var(--menu-foreground-color); - white-space: nowrap; - - &:hover { - background-color: var(--menu-background-color-hover); - } -} - .toolbar-separator { --separator-color: var(--color-background-quaternary); diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index cd2ef3ca88..9c3fcb77fd 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -538,7 +538,9 @@ {:bounds vbox :selected-shapes selected-shapes :frame selected-frame - :hover-shape @measure-hover + ;; The current shape: a rollback may have removed or changed it + ;; since the pointer last moved + :hover-shape (some->> @measure-hover :id (get base-objects)) :zoom zoom}]) ;; Show distances during movement with ALT @@ -592,6 +594,7 @@ [:> widgets/frame-titles* {:objects base-objects + :base-objects base-objects :selected selected :zoom zoom :is-show-artboard-names show-artboard-names? @@ -637,6 +640,7 @@ :page-id page-id :selected selected :objects objects-modified + :base-objects base-objects :focus focus}]) (when show-snap-distance? diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs index d8e1314dc4..c92f565ea3 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs @@ -29,8 +29,8 @@ [app.main.store :as st] [app.main.ui.css-cursors :as cur] [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] [app.main.ui.formats :as fmt] - [app.main.ui.icons :as deprecated-icon] [app.main.ui.workspace.viewport.viewport-ref :as uwvv] [app.util.debug :as dbg] [app.util.dom :as dom] @@ -180,6 +180,12 @@ (when on-drag-delta (on-drag-delta event (gpt/to-vec start pos))) (when on-drag-position (on-drag-position event pt))))))] + ;; Unmounted mid-drag, as when saves fail and edition ends, the drag + ;; never gets its release: its preview is dropped and nothing is committed + (mf/with-effect [] + #(when (mf/ref-val dragging-ref) + (st/emit! (dwt/cancel-transform)))) + {:handle-pointer-down handle-pointer-down :handle-lost-pointer-capture handle-lost-pointer-capture :handle-pointer-move handle-pointer-move})) @@ -918,9 +924,15 @@ :on-key-down handle-keydown-track-input :on-blur handle-blur-track-input}] (when (and (not medium?) (not small?)) - [:button {:class (stl/css :grid-editor-button) + ;; Raw button on purpose: it lives on the canvas and keeps a + ;; fixed screen size at any zoom. + [:button {:type "button" + :class (stl/css :grid-editor-button) :data-testid "grid-track-options-btn" - :on-click handle-show-track-menu} deprecated-icon/menu])]])] + :aria-label (tr "workspace.layout-grid.editor.track-options") + :on-click handle-show-track-menu} + [:> icon* {:icon-id i/menu + :class (stl/css :grid-editor-button-icon)}]])]])] [:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p)))} [:& track-marker diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss index 008f4e8b0d..6215876b48 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss @@ -4,17 +4,23 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/colors.scss" as *; @use "ds/z-index.scss" as *; +// The track editor lives on the canvas: its sizes are divided by the zoom +// so it keeps the same size on screen. + .grid-track-marker { .marker-shape { - fill: var(--grid-editor-marker-color); + fill: var(--color-accent-quaternary); } .marker-text { - fill: var(--app-white); - font-size: calc(deprecated.$s-12 / var(--zoom)); + fill: $white; + font-size: calc(#{px2rem(12)} / var(--zoom)); font-family: "worksans", "vazirmatn", sans-serif; } } @@ -22,8 +28,8 @@ .grid-editor-wrapper { cursor: grab; position: relative; - width: 100%; - height: 80%; + inline-size: 100%; + block-size: 80%; display: flex; flex-direction: column; justify-content: center; @@ -31,21 +37,20 @@ } .grid-editor-header-hover { - fill: var(--grid-editor-marker-color); + fill: var(--color-accent-quaternary); } .grid-editor-label { flex: 1; background: none; border: 0; - color: var(--grid-editor-marker-text); + color: var(--color-accent-quaternary); font-family: "worksans", "vazirmatn", sans-serif; - font-size: calc(deprecated.$fs-12 / var(--zoom)); + font-size: calc(#{px2rem(12)} / var(--zoom)); font-weight: 400; margin: 0; - max-width: calc(deprecated.$s-60 / var(--zoom)); - padding: 0; - padding: calc(deprecated.$s-4 / var(--zoom)); + max-inline-size: calc(#{px2rem(60)} / var(--zoom)); + padding: calc(var(--sp-xs) / var(--zoom)); text-align: center; &:focus { @@ -60,21 +65,23 @@ margin: 0; padding: 0; position: absolute; - top: calc(deprecated.$s-6 / var(--zoom)); - right: calc(deprecated.$s-20 / var(--zoom)); - width: calc(deprecated.$s-20 / var(--zoom)); - height: calc(deprecated.$s-20 / var(--zoom)); + inset-block-start: calc(#{px2rem(6)} / var(--zoom)); + inset-inline-end: calc(#{px2rem(20)} / var(--zoom)); + inline-size: calc(#{px2rem(20)} / var(--zoom)); + block-size: calc(#{px2rem(20)} / var(--zoom)); opacity: 0; pointer-events: none; +} - svg { - position: absolute; - top: 0; - left: 0; - width: calc(deprecated.$s-16 / var(--zoom)); - height: auto; - stroke: var(--grid-editor-marker-color); - } +// The DS icon has a fixed 16px size: scale it down with the zoom, as the +// rest of the track editor, so it keeps the same size on screen. +.grid-editor-button-icon { + position: absolute; + inset-block-start: 0; + inset-inline-start: 0; + transform: scale(calc(1 / var(--zoom))); + transform-origin: top left; + color: var(--color-accent-quaternary); } .grid-editor-wrapper:hover .grid-editor-button { @@ -83,7 +90,7 @@ } .grid-frame { - fill: var(--grid-editor-marker-color); + fill: var(--color-accent-quaternary); fill-opacity: 0.1; } @@ -92,13 +99,13 @@ opacity: 0.5; .grid-plus-shape { - fill: var(--grid-editor-plus-btn-background); - stroke: var(--grid-editor-plus-btn-background); - stroke-width: calc(deprecated.$s-1 / var(--zoom)); + fill: var(--color-accent-quaternary); + stroke: var(--color-accent-quaternary); + stroke-width: calc(#{px2rem(1)} / var(--zoom)); } .grid-plus-icon { - stroke: var(--grid-editor-plus-btn-foreground); + stroke: $white; } &:hover { @@ -108,7 +115,7 @@ .grid-cell-outline { fill: transparent; - stroke: var(--grid-editor-line-color); + stroke: var(--color-accent-quaternary); stroke-opacity: 0.5; stroke-width: calc(1 / var(--zoom)); @@ -122,37 +129,32 @@ .grid-actions { pointer-events: none; position: absolute; - top: deprecated.$s-44; - left: 50%; + inset-block-start: px2rem(44); + inset-inline-start: 50%; z-index: var(--z-index-notifications); } .grid-actions-container { - @include deprecated.flex-row; - - background: var(--panel-background-color); - border-radius: deprecated.$br-12; - box-shadow: 0 0 deprecated.$s-12 0 var(--menu-shadow-color); - gap: deprecated.$s-8; - height: deprecated.$s-48; - margin-left: -50%; - padding: deprecated.$s-8; + display: flex; + align-items: center; + gap: var(--sp-s); + background: var(--color-background-primary); + border-radius: $br-12; + box-shadow: 0 0 px2rem(12) 0 var(--color-shadow-dark); + block-size: $sz-48; + margin-inline-start: -50%; + padding: var(--sp-s); cursor: initial; pointer-events: initial; - width: deprecated.$s-512; + inline-size: $sz-512; } .grid-actions-title { flex: 1; - font-size: deprecated.$fs-12; + + // TODO: No exact DS typography match found. + // Closest match: body-small. + font-size: px2rem(12); color: var(--color-foreground-secondary); - padding-left: deprecated.$s-8; -} - -.close-btn { - @extend %button-tertiary; - - svg { - @extend %button-icon; - } + padding-inline-start: var(--sp-s); } diff --git a/frontend/src/app/main/ui/workspace/viewport/guides.cljs b/frontend/src/app/main/ui/workspace/viewport/guides.cljs index 69e0df5a31..47dae84486 100644 --- a/frontend/src/app/main/ui/workspace/viewport/guides.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/guides.cljs @@ -184,6 +184,16 @@ :new-position new-position :new-frame-id new-frame-id))))))] + ;; A drag in progress when the workspace turns read-only (saves fail) + ;; is dropped, so the guide goes back and its release changes nothing + (mf/with-effect [read-only?] + (when read-only? + (mf/set-ref-val! dragging-ref false) + (mf/set-ref-val! start-ref nil) + (mf/set-ref-val! start-pos-ref nil) + (when (some? (:new-position @state)) + (swap! state assoc :new-position nil)))) + {:on-pointer-enter on-pointer-enter :on-pointer-leave on-pointer-leave :on-pointer-down on-pointer-down diff --git a/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs b/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs index 2ec5a82d17..c9981b0f6c 100644 --- a/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs @@ -11,53 +11,21 @@ [app.main.data.workspace.path.helpers :as path.helpers] [app.main.data.workspace.path.shortcuts :as sc] [app.main.store :as st] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.util.i18n :as i18n :refer [tr]] [app.util.timers :as ts] [rumext.v2 :as mf])) -(def ^:private pentool-icon - (deprecated-icon/icon-xref :pentool (stl/css :pentool-icon :pathbar-icon))) - -(def ^:private move-icon - (deprecated-icon/icon-xref :move (stl/css :move-icon :pathbar-icon))) - -(def ^:private merge-nodes-icon - (deprecated-icon/icon-xref :merge-nodes (stl/css :merge-nodes-icon :pathbar-icon))) - -(def ^:private join-nodes-icon - (deprecated-icon/icon-xref :join-nodes (stl/css :join-nodes-icon :pathbar-icon))) - -(def ^:private separate-nodes-icon - (deprecated-icon/icon-xref :separate-nodes (stl/css :separate-nodes-icon :pathbar-icon))) - -(def ^:private to-corner-icon - (deprecated-icon/icon-xref :to-corner (stl/css :to-corner-icon :pathbar-icon))) - -(def ^:private to-curve-icon - (deprecated-icon/icon-xref :to-curve (stl/css :to-curve-icon :pathbar-icon))) - -(def ^:private snap-icon - (deprecated-icon/icon-xref :snap (stl/css :snap-icon :pathbar-icon))) - ;; Handler behavior icons: mirror, aligned, and independent. -(def ^:private handler-mirror-icon - (deprecated-icon/icon-xref :handlers-equal (stl/css :pathbar-icon))) - -(def ^:private handler-aligned-icon - (deprecated-icon/icon-xref :handlers-mirror (stl/css :pathbar-icon))) - -(def ^:private handler-independent-icon - (deprecated-icon/icon-xref :handlers-independent (stl/css :pathbar-icon))) - (defn- handler-type-icon [type] (case type - :mirror handler-mirror-icon - :aligned handler-aligned-icon - :independent handler-independent-icon + :mirror i/handlers-equal + :aligned i/handlers-mirror + :independent i/handlers-independent ;; Use the independent icon for mixed selections. - :mixed handler-independent-icon - handler-independent-icon)) + :mixed i/handlers-independent + i/handlers-independent)) (defn toolbar-group-visibility [structural-visible? shape-visible? handler-visible?] @@ -69,11 +37,11 @@ (mf/defc topbar-button* "A path node action button." {::mf/private true} - [{:keys [title on-click icon]}] - [:button {:class (stl/css :topbar-btn) - :title title - :on-click on-click} - icon]) + [props] + (let [props (mf/spread-props props {:variant "ghost" + :tooltip-placement "bottom" + :tooltip-class (stl/css :topbar-btn)})] + [:> icon-button* props])) (defn- cancel-timer! [timer-ref*] @@ -144,31 +112,31 @@ [:div {:class (stl/css :handler-menu) :on-pointer-enter on-display-menu :on-pointer-leave on-hide-menu} - [:button {:class (stl/css :topbar-btn :handler-trigger) - :title (tr "workspace.path.actions.handler-type") - :aria-haspopup true - :aria-expanded open? - :on-click on-trigger-click} - (handler-type-icon active-type) - [:svg {:view-box "0 0 6 6" - :aria-hidden true - :class (stl/css :flyout-indicator)} - [:path {:d "M4,2 L4,3.15 C4,3.62 3.62,4 3.15,4 L2,4" - :stroke-linecap "round"}]]] + ;; Like the top toolbar flyouts: the menu opens on hover, so the + ;; trigger has no tooltip. + [:> icon-button* {:variant "ghost" + :class (stl/css :topbar-btn) + :flyout-indicator true + :has-tooltip false + :aria-label (tr "workspace.path.actions.handler-type") + :aria-haspopup true + :aria-expanded open? + :icon (handler-type-icon active-type) + :on-click on-trigger-click}] [:div {:class (stl/css-case :handler-popover true :open open?) :data-dont-clear-path true} - [:button {:class (stl/css-case :is-toggled (= active-type :mirror) :topbar-btn true) - :title (tr "workspace.path.actions.handler-mirror") - :on-click #(select :mirror)} - handler-mirror-icon] - [:button {:class (stl/css-case :is-toggled (= active-type :aligned) :topbar-btn true) - :title (tr "workspace.path.actions.handler-aligned") - :on-click #(select :aligned)} - handler-aligned-icon] - [:button {:class (stl/css-case :is-toggled (= active-type :independent) :topbar-btn true) - :title (tr "workspace.path.actions.handler-independent") - :on-click #(select :independent)} - handler-independent-icon]]])) + [:> topbar-button* {:aria-label (tr "workspace.path.actions.handler-mirror") + :aria-pressed (= active-type :mirror) + :icon (handler-type-icon :mirror) + :on-click #(select :mirror)}] + [:> topbar-button* {:aria-label (tr "workspace.path.actions.handler-aligned") + :aria-pressed (= active-type :aligned) + :icon (handler-type-icon :aligned) + :on-click #(select :aligned)}] + [:> topbar-button* {:aria-label (tr "workspace.path.actions.handler-independent") + :aria-pressed (= active-type :independent) + :icon (handler-type-icon :independent) + :on-click #(select :independent)}]]])) (mf/defc path-actions* [{:keys [shape state]}] @@ -266,14 +234,14 @@ :data-dont-clear-path true} ;; Mode: draw / move (always visible) [:div {:class (stl/css :sub-actions-group)} - [:button {:class (stl/css-case :is-toggled (= edit-mode :draw) :topbar-btn true) - :title (tr "workspace.path.actions.draw-nodes" (sc/get-tooltip :draw-nodes)) - :on-click on-select-draw-mode} - pentool-icon] - [:button {:class (stl/css-case :is-toggled (= edit-mode :move) :topbar-btn true) - :title (tr "workspace.path.actions.move-nodes" (sc/get-tooltip :move-nodes)) - :on-click on-select-edit-mode} - move-icon]] + [:> topbar-button* {:aria-label (tr "workspace.path.actions.draw-nodes" (sc/get-tooltip :draw-nodes)) + :aria-pressed (= edit-mode :draw) + :icon i/pentool + :on-click on-select-draw-mode}] + [:> topbar-button* {:aria-label (tr "workspace.path.actions.move-nodes" (sc/get-tooltip :move-nodes)) + :aria-pressed (= edit-mode :move) + :icon i/move + :on-click on-select-edit-mode}]] [:div {:class (stl/css :separator)}] @@ -281,17 +249,17 @@ (when structural-visible? [:div {:class (stl/css :sub-actions-group)} (when (:merge-nodes enabled-buttons) - [:> topbar-button* {:title (tr "workspace.path.actions.merge-nodes" (sc/get-tooltip :merge-nodes)) + [:> topbar-button* {:aria-label (tr "workspace.path.actions.merge-nodes" (sc/get-tooltip :merge-nodes)) :on-click on-merge-nodes - :icon merge-nodes-icon}]) + :icon i/merge-nodes}]) (when (:join-nodes enabled-buttons) - [:> topbar-button* {:title (tr "workspace.path.actions.join-nodes" (sc/get-tooltip :join-nodes)) + [:> topbar-button* {:aria-label (tr "workspace.path.actions.join-nodes" (sc/get-tooltip :join-nodes)) :on-click on-join-nodes - :icon join-nodes-icon}]) + :icon i/join-nodes}]) (when (:separate-nodes enabled-buttons) - [:> topbar-button* {:title (tr "workspace.path.actions.separate-nodes" (sc/get-tooltip :separate-nodes)) + [:> topbar-button* {:aria-label (tr "workspace.path.actions.separate-nodes" (sc/get-tooltip :separate-nodes)) :on-click on-separate-nodes - :icon separate-nodes-icon}])]) + :icon i/separate-nodes}])]) (when node-groups-separator-visible? [:div {:class (stl/css :separator)}]) @@ -300,13 +268,13 @@ (when shape-visible? [:div {:class (stl/css :sub-actions-group)} (when (:make-corner enabled-buttons) - [:> topbar-button* {:title (tr "workspace.path.actions.make-corner" (sc/get-tooltip :make-corner)) + [:> topbar-button* {:aria-label (tr "workspace.path.actions.make-corner" (sc/get-tooltip :make-corner)) :on-click on-make-corner - :icon to-corner-icon}]) + :icon i/to-corner}]) (when (:make-curve enabled-buttons) - [:> topbar-button* {:title (tr "workspace.path.actions.make-curve" (sc/get-tooltip :make-curve)) + [:> topbar-button* {:aria-label (tr "workspace.path.actions.make-curve" (sc/get-tooltip :make-curve)) :on-click on-make-curve - :icon to-curve-icon}])]) + :icon i/to-curve}])]) ;; Handler behaviour of the selected node(s) (when handler-visible? @@ -318,7 +286,7 @@ ;; Toggle snap (always visible, pinned to the right) [:div {:class (stl/css :sub-actions-group :snap-group)} - [:button {:class (stl/css-case :is-toggled snap-toggled :topbar-btn true) - :title (tr "workspace.path.actions.snap-nodes" (sc/get-tooltip :snap-nodes)) - :on-click on-toggle-snap} - snap-icon]]])) + [:> topbar-button* {:aria-label (tr "workspace.path.actions.snap-nodes" (sc/get-tooltip :snap-nodes)) + :aria-pressed (boolean snap-toggled) + :icon i/snap + :on-click on-toggle-snap}]]])) diff --git a/frontend/src/app/main/ui/workspace/viewport/path_actions.scss b/frontend/src/app/main/ui/workspace/viewport/path_actions.scss index 2bca2d1a11..6c3bf1e0c7 100644 --- a/frontend/src/app/main/ui/workspace/viewport/path_actions.scss +++ b/frontend/src/app/main/ui/workspace/viewport/path_actions.scss @@ -4,7 +4,6 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; @use "ds/_borders.scss" as *; @use "ds/_sizes.scss" as *; @@ -12,20 +11,22 @@ cursor: initial; pointer-events: initial; position: absolute; + + // top and height stay physical: the transition below names them. top: var(--sp-xl); - left: 50%; + inset-inline-start: 50%; transform: translateX(-50%); display: flex; align-items: center; justify-content: flex-start; // Keep the bar stable as actions change. - width: $sz-430; + inline-size: $sz-430; height: $sz-48; padding: var(--sp-s); border-radius: $br-8; gap: var(--sp-s); - border: $b-2 solid var(--panel-border-color); + border: $b-2 solid var(--color-background-quaternary); z-index: var(--z-index-panels); background-color: var(--color-background-primary); transition: @@ -45,15 +46,15 @@ // Pin the snap toggle to the right. .snap-group { - margin-left: auto; + margin-inline-start: auto; } .separator { flex-shrink: 0; - width: $sz-1; - height: $sz-24; + inline-size: $sz-1; + block-size: $sz-24; margin: 0 var(--sp-xs); - background-color: var(--panel-border-color); + background-color: var(--color-background-quaternary); } .handler-menu { @@ -62,34 +63,17 @@ align-items: center; } -// Handler behavior flyout trigger. -.handler-trigger { - position: relative; -} - -// Flyout corner indicator. -.flyout-indicator { - position: absolute; - inset-block-end: var(--sp-xs); - inset-inline-end: var(--sp-xs); - inline-size: $sz-6; - block-size: $sz-6; - stroke: var(--pathbar-icon-color); - fill: none; - pointer-events: none; -} - .handler-popover { position: absolute; - top: calc(100% + var(--sp-s)); - left: 50%; + inset-block-start: calc(100% + var(--sp-s)); + inset-inline-start: 50%; transform: translateX(-50%); display: flex; align-items: center; padding: var(--sp-xs); gap: var(--sp-xxs); border-radius: $br-8; - border: $b-2 solid var(--panel-border-color); + border: $b-2 solid var(--color-background-quaternary); background-color: var(--color-background-primary); z-index: var(--z-index-dropdown); @@ -109,28 +93,6 @@ } .topbar-btn { - --pathbar-icon-color: var(--color-foreground-secondary); - --button-tertiary-background-color-hover: var(--color-background-tertiary); - - @extend %button-tertiary; - - height: $sz-32; - width: $sz-32; flex-shrink: 0; - background-color: transparent; - border-radius: $br-8; - border: none; margin: 0 var(--sp-xxs); - - &.is-toggled { - --pathbar-icon-color: var(--button-radio-foreground-color-active); - - background-color: var(--button-radio-background-color-active); - } - - .pathbar-icon { - @extend %button-icon; - - stroke: var(--pathbar-icon-color); - } } diff --git a/frontend/src/app/main/ui/workspace/viewport/snap_points.cljs b/frontend/src/app/main/ui/workspace/viewport/snap_points.cljs index 409a6ad153..063890d84e 100644 --- a/frontend/src/app/main/ui/workspace/viewport/snap_points.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/snap_points.cljs @@ -158,12 +158,13 @@ (mf/defc snap-points* {::mf/wrap [mf/memo]} - [{:keys [layout zoom objects selected page-id drawing focus]}] + [{:keys [layout zoom objects base-objects selected page-id drawing focus]}] (dm/assert! (set? selected)) (let [shapes (into [] (keep (d/getf objects)) selected) filter-shapes - (into selected (mapcat #(cfh/get-children-ids objects %)) selected) + (mf/with-memo [base-objects selected] + (into #{} (mapcat #(cfh/get-children-ids-with-self base-objects %)) selected)) remove-snap-base? (mf/with-memo [layout filter-shapes objects focus] diff --git a/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs b/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs index 7f59804825..bc0c0b0e61 100644 --- a/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.workspace.viewport.top-bar (:require-macros [app.main.style :as stl]) (:require + [app.main.data.persistence :as dps] [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwc] [app.main.refs :as refs] @@ -29,18 +30,36 @@ (st/emit! :interrupt (dw/set-options-mode :design) (dwc/set-workspace-read-only false)))) - render-context-lost? (mf/deref refs/render-context-lost?)] - [:div {:class (stl/css :viewport-actions)} + render-context-lost? (mf/deref refs/render-context-lost?) + save-locked? (some? (mf/deref refs/persistence-lock)) + persistence (mf/deref refs/persistence)] + [:div {:class (stl/css-case :viewport-actions true + :viewport-actions-save-failed save-locked?)} [:div {:class (stl/css :viewport-actions-container)} - [:div {:class (stl/css :viewport-actions-title)} + [:div {:class (stl/css :viewport-actions-title) + :role (when save-locked? "status")} [:> i18n/tr-html* {:tag-name "span" - :content (if render-context-lost? + :content (cond + save-locked? + (if (dps/save-retrying? persistence) + (tr "workspace.save-failure.retrying") + (tr "workspace.save-failure.stopped")) + + render-context-lost? (tr "workspace.top-bar.webgl-context-lost") + + :else (tr "workspace.top-bar.view-only"))}]] - (if render-context-lost? + (cond + save-locked? + nil + + render-context-lost? [:> button* {:variant "primary" :on-click (fn [] (js/location.reload))} (tr "workspace.top-bar.webgl-context-lost.reload")] + + :else [:> button* {:on-click on-close} (tr "workspace.top-bar.read-only.done")])]])) diff --git a/frontend/src/app/main/ui/workspace/viewport/top_bar.scss b/frontend/src/app/main/ui/workspace/viewport/top_bar.scss index 255ae85080..2bb577514b 100644 --- a/frontend/src/app/main/ui/workspace/viewport/top_bar.scss +++ b/frontend/src/app/main/ui/workspace/viewport/top_bar.scss @@ -4,55 +4,74 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/typography.scss" as *; +@use "ds/_utils.scss" as *; .viewport-actions, .viewport-actions-path { pointer-events: none; position: absolute; - --actions-toolbar-position-y: #{deprecated.$s-28}; - --actions-toolbar-offset-y: #{deprecated.$s-6}; + --actions-toolbar-position-y: #{$sz-28}; + --actions-toolbar-offset-y: #{px2rem(6)}; - top: calc(var(--actions-toolbar-position-y) + var(--actions-toolbar-offset-y)); - left: 50%; + inset-block-start: calc(var(--actions-toolbar-position-y) + var(--actions-toolbar-offset-y)); + inset-inline-start: 50%; transform: translateX(-50%); - z-index: deprecated.$z-index-20; + + // TODO: No DS z-index token for this value. + z-index: 20; } .viewport-actions-path { - z-index: deprecated.$z-index-3; + // TODO: No DS z-index token for this value. + z-index: 3; } .viewport-actions-container { - @include deprecated.flex-row; - - background: var(--panel-background-color); - border-radius: deprecated.$br-12; - box-shadow: 0 0 deprecated.$s-12 0 var(--menu-shadow-color); - gap: deprecated.$s-8; - height: deprecated.$s-48; - padding: deprecated.$s-8; + display: flex; + align-items: center; + gap: var(--sp-s); + background: var(--color-background-primary); + border-radius: $br-12; + box-shadow: 0 0 px2rem(12) 0 var(--color-shadow-dark); + block-size: $sz-48; + padding: var(--sp-s); cursor: initial; pointer-events: initial; - min-width: deprecated.$s-400; - border: deprecated.$s-2 solid var(--panel-border-color); + min-inline-size: $sz-400; + border: $b-2 solid var(--color-background-quaternary); } .viewport-actions-title { + @include use-typography("body-small"); + flex: 1; - font-size: deprecated.$fs-12; color: var(--color-foreground-secondary); - padding-left: deprecated.$s-8; - width: max-content; + padding-inline-start: var(--sp-s); + inline-size: max-content; + + strong { + font-weight: 700; + } } -.done-btn { - @extend %button-primary; +.viewport-actions-save-failed { + inline-size: max-content; + min-inline-size: min(#{$sz-400}, calc(100% - var(--sp-xl))); + max-inline-size: calc(100% - var(--sp-xl)); - text-transform: uppercase; - padding: deprecated.$s-8 deprecated.$s-20; - font-size: deprecated.$fs-11; + .viewport-actions-container { + block-size: auto; + min-block-size: $sz-48; + min-inline-size: 0; + } + + .viewport-actions-title { + inline-size: auto; + } } .viewport-actions-no-rulers { diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index b9806fddda..2c5374ced8 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -259,10 +259,12 @@ (mf/defc frame-titles* {::mf/wrap [mf/memo]} - [{:keys [objects zoom selected focus is-show-artboard-names + [{:keys [objects base-objects zoom selected focus is-show-artboard-names on-frame-enter on-frame-leave on-frame-select]}] (let [selected (or selected #{}) - shapes (ctt/get-frames objects {:skip-copies? true :ignore-index? true}) + frame-ids (mf/with-memo [base-objects] + (vec (ctt/get-frames-ids base-objects {:skip-copies? true :ignore-index? true}))) + shapes (into [] (keep (d/getf objects)) frame-ids) shapes (if (dbg/enabled? :shape-titles) (into (set shapes) (map (d/getf objects)) diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs index 39bbef583b..cfa1a3d38d 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs @@ -11,6 +11,7 @@ [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] [app.common.geom.shapes :as gsh] + [app.common.render-wasm.api.select :as wselect] [app.common.types.color :as clr] [app.common.types.component :as ctk] [app.common.types.shape :as cts] @@ -502,7 +503,7 @@ (let [content (-> active-editor-state (ted/get-editor-current-content) (ted/export-content))] - (wasm.api/use-shape edition) + (wselect/use-shape edition) (wasm.api/set-shape-text-content edition content) (let [dimension (wasm.api/get-text-dimensions)] (st/emit! (dwt/resize-text-editor edition dimension)) @@ -651,12 +652,12 @@ (mf/with-effect [path-editing? edition @initialized?] (when (and path-editing? edition @initialized?) - (wasm.api/use-shape edition) + (wselect/use-shape edition) (wasm.api/set-shape-hidden true) (wasm.api/request-render "start-path-edition") (fn [] (when (wasm.api/initialized?) - (wasm.api/use-shape edition) + (wselect/use-shape edition) (wasm.api/set-shape-hidden false) (wasm.api/request-render "stop-path-edition"))))) @@ -834,7 +835,9 @@ {:bounds vbox :selected-shapes selected-shapes :frame selected-frame - :hover-shape @measure-hover + ;; The current shape: a rollback may have removed or changed it + ;; since the pointer last moved + :hover-shape (some->> @measure-hover :id (get base-objects)) :zoom zoom}]) (when show-padding? @@ -869,6 +872,7 @@ (when-not shapes-loading? [:> widgets/frame-titles* {:objects objects-modified + :base-objects base-objects :selected selected :zoom zoom :is-show-artboard-names show-artboard-names? @@ -917,6 +921,7 @@ :page-id page-id :selected selected :objects objects-modified + :base-objects base-objects :focus focus}]) (when show-snap-distance? diff --git a/frontend/src/app/plugins/api.cljs b/frontend/src/app/plugins/api.cljs index 226b324c1f..92ca262da5 100644 --- a/frontend/src/app/plugins/api.cljs +++ b/frontend/src/app/plugins/api.cljs @@ -43,6 +43,7 @@ [app.plugins.history :as history] [app.plugins.library :as library] [app.plugins.local-storage :as local-storage] + [app.plugins.management :as management] [app.plugins.page :as page] [app.plugins.parser :as parser] [app.plugins.reflow :as wrfp] @@ -64,6 +65,7 @@ ;; (defn create-shape [plugin-id type] + (r/check-writable) (let [page (dsh/lookup-page @st/state) shape (cts/setup-shape {:type type :x 0 :y 0 @@ -88,6 +90,9 @@ :$plugin {:enumerable false :get (fn [] plugin-id)} ;; Public properties + :management + {:get (fn [] (management/create-context plugin-id))} + :version {:this true :get (constantly (:base cf/version))} @@ -201,6 +206,7 @@ :replaceColor (fn [shapes old-color new-color] + (r/check-writable) (let [old-color (parser/parse-color-data old-color) new-color (parser/parse-color-data new-color)] (cond @@ -257,6 +263,7 @@ :uploadMediaUrl (fn [name url] + (r/check-writable) (cond (not (string? name)) (u/not-valid plugin-id :uploadMedia-name name) @@ -270,12 +277,15 @@ (fn [resolve reject] (->> (dwm/upload-media-url name file-id url) (rx/take 1) + ;; Saves may have failed while the upload ran + (rx/tap (fn [_] (r/check-writable))) (rx/map format/format-image) (rx/tap #(st/emit! (se/event plugin-id "add-media"))) (rx/subs! resolve reject))))))) :uploadMediaData (fn [name data mime-type] + (r/check-writable) (let [file-id (:current-file-id @st/state)] (js/Promise. (fn [resolve reject] @@ -303,12 +313,15 @@ :on-image identity :on-svg identity}) (rx/take 1) + ;; Saves may have failed while the upload ran + (rx/tap (fn [_] (r/check-writable))) (rx/map format/format-image) (rx/tap #(st/emit! (se/event plugin-id "add-media"))) (rx/subs! resolve reject)))))) :group (fn [shapes] + (r/check-writable) (let [valid-shapes? (and (array? shapes) (every? shape/shape-proxy? shapes)) file-id (:current-file-id @st/state) page-id (:current-page-id @st/state) @@ -339,6 +352,7 @@ :ungroup (fn [group & rest] + (r/check-writable) (let [valid-group? (shape/shape-proxy? group) valid-rest? (every? shape/shape-proxy? rest) shapes (when (and valid-group? valid-rest?) (concat [group] rest)) @@ -378,6 +392,7 @@ :createPath (fn [] + (r/check-writable) (let [page (dsh/lookup-page @st/state) shape (cts/setup-shape {:type :path @@ -395,6 +410,7 @@ :createText (fn [text] + (r/check-writable) (cond (not (string? text)) (u/not-valid plugin-id :createText text) @@ -432,6 +448,7 @@ :createShapeFromSvg (fn [svg-string] + (r/check-writable) (cond (not (dwm/valid-svg-string? svg-string)) (u/not-valid plugin-id :createShapeFromSvg svg-string) @@ -446,6 +463,7 @@ :createShapeFromSvgWithImages (fn [svg-string] + (r/check-writable) (js/Promise. (fn [resolve reject] (cond @@ -460,12 +478,15 @@ page-id (:current-page-id @st/state)] (st/emit! (dwm/create-svg-shape-with-images file-id id "svg" svg-string (gpt/point 0 0) - #(resolve (shape/shape-proxy plugin-id file-id page-id id)) - reject) + {;; Saves may have failed while the images uploaded + :check-writable r/check-writable + :on-success #(resolve (shape/shape-proxy plugin-id file-id page-id id)) + :on-error reject}) (se/event plugin-id "create-shape" :type :text))))))) :createBoolean (fn [bool-type shapes] + (r/check-writable) (let [bool-type (keyword bool-type)] (cond (not (contains? cts/bool-types bool-type)) @@ -595,6 +616,7 @@ :createPage (fn [] + (r/check-writable) (let [file-id (:current-file-id @st/state) id (uuid/next)] (st/emit! (-> (dw/create-page {:page-id id :file-id file-id}) @@ -632,6 +654,7 @@ :alignHorizontal (fn [shapes direction] + (r/check-writable) (let [dir (case direction "left" :hleft "center" :hcenter @@ -650,6 +673,7 @@ :alignVertical (fn [shapes direction] + (r/check-writable) (let [dir (case direction "top" :vtop "center" :vcenter @@ -668,6 +692,7 @@ :distributeHorizontal (fn [shapes] + (r/check-writable) (cond (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (u/not-valid plugin-id :distributeHorizontal-shapes "Not valid shapes") @@ -678,6 +703,7 @@ :distributeVertical (fn [shapes] + (r/check-writable) (cond (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (u/not-valid plugin-id :distributeVertical-shapes "Not valid shapes") @@ -688,6 +714,7 @@ :flatten (fn [shapes] + (r/check-writable) (cond (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (u/not-valid plugin-id :flatten-shapes "Not valid shapes") @@ -703,6 +730,7 @@ :createVariantFromComponents (fn [shapes] + (r/check-writable) (cond (or (not (array? shapes)) (not (seq shapes)) diff --git a/frontend/src/app/plugins/events.cljs b/frontend/src/app/plugins/events.cljs index 884a1e0bac..389e700a4c 100644 --- a/frontend/src/app/plugins/events.cljs +++ b/frontend/src/app/plugins/events.cljs @@ -8,17 +8,36 @@ (:require [app.common.data.macros :as dm] [app.main.data.helpers :as dsh] + [app.main.data.persistence :as dps] [app.main.store :as st] [app.plugins.file :as file] + [app.plugins.management :as management] [app.plugins.page :as page] [app.plugins.parser :as parser] [app.plugins.shape :as shape] [app.util.object :as obj] [app.util.theme :as theme] - [goog.functions :as gf])) + [beicon.v2.core :as rx] + [goog.functions :as gf] + [potok.v2.core :as ptk])) (defmulti handle-state-change (fn [type _ _ _ _] type)) +(defmethod handle-state-change "workspacechange" + [_ _ old-val new-val _] + (let [old-workspace (management/workspace-context old-val) + new-workspace (management/workspace-context new-val)] + (if (= old-workspace new-workspace) + ::not-changed + (clj->js new-workspace)))) + +(defmethod handle-state-change "logout" + [_ _ old-val new-val _] + (if (and (:profile-id old-val) + (not= (:profile-id old-val) (:profile-id new-val))) + ::void + ::not-changed)) + (defmethod handle-state-change "finish" [_ _ old-val new-val _] (let [old-file-id (:current-file-id old-val) @@ -95,6 +114,25 @@ [props] {:shape-id (-> (obj/get props "shapeId") parser/parse-id)}) +;; Stream subscriptions of the listeners that follow events, not state +(defonce ^:private subscriptions (atom {})) + +(defn- format-rollback + [{:keys [file-ids page-ids shape-ids]}] + #js {:fileIds (apply array (map str file-ids)) + :pageIds (apply array (map str page-ids)) + :shapeIds (apply array (map str shape-ids))}) + +(defn- subscribe-rollbacks + "Calls `callback` after each rollback of rejected changes, once the + reverted data is in the state. Not debounced: every rollback counts." + [callback] + (->> st/stream + (rx/filter (ptk/type? ::dps/rolled-back)) + (rx/map (comp format-rollback deref)) + (rx/observe-on :async) + (rx/subs! callback))) + (defn add-listener [type plugin-id callback props] (let [plugin-id (parser/parse-id plugin-id) @@ -115,21 +153,28 @@ ;; We also debounce the callbacks so we don't get too many at the same time debounced-callback (gf/debounce safe-callback 10)] - (add-watch - st/state key - (fn [_ _ old-val new-val] - ;; The store notifies its watches with a plain iteration: a throw - ;; here starves the watches registered after this one. - (try - (let [result (handle-state-change type plugin-id old-val new-val props)] - (when (not= ::not-changed result) - (debounced-callback result))) - (catch :default cause - (.error js/console cause))))) + (if (= "contentrollback" type) + (swap! subscriptions assoc key (subscribe-rollbacks safe-callback)) + (add-watch + st/state key + (fn [_ _ old-val new-val] + ;; The store notifies its watches with a plain iteration: a throw + ;; here starves the watches registered after this one. + (try + (let [result (handle-state-change type plugin-id old-val new-val props)] + (when (not= ::not-changed result) + (if (contains? #{"workspacechange" "logout"} type) + (safe-callback result) + (debounced-callback result)))) + (catch :default cause + (.error js/console cause)))))) ;; return the generated key key)) (defn remove-listener [key] + (when-let [subscription (get @subscriptions key)] + (rx/dispose! subscription) + (swap! subscriptions dissoc key)) (remove-watch st/state key)) diff --git a/frontend/src/app/plugins/history.cljs b/frontend/src/app/plugins/history.cljs index 68eb122506..54996cddee 100644 --- a/frontend/src/app/plugins/history.cljs +++ b/frontend/src/app/plugins/history.cljs @@ -20,10 +20,12 @@ (obj/reify {:name "HistorySubcontext"} :$plugin {:enumerable false :get (fn [] plugin-id)} + ;; Undo blocks write nothing, so they work in a read-only workspace: + ;; a block opened before a lock can still be closed :undoBlockBegin (fn [] (cond - (not (r/check-permission plugin-id "content:write")) + (not (r/has-permission? plugin-id "content:write")) (u/not-valid plugin-id :resize "Plugin doesn't have 'content:write' permission") :else @@ -34,7 +36,7 @@ :undoBlockFinish (fn [block-id] (cond - (not (r/check-permission plugin-id "content:write")) + (not (r/has-permission? plugin-id "content:write")) (u/not-valid plugin-id :resize "Plugin doesn't have 'content:write' permission") (not block-id) diff --git a/frontend/src/app/plugins/library.cljs b/frontend/src/app/plugins/library.cljs index c22edd3910..faec7fad16 100644 --- a/frontend/src/app/plugins/library.cljs +++ b/frontend/src/app/plugins/library.cljs @@ -936,7 +936,7 @@ :else (let [id-ref (atom nil)] - (st/emit! (-> (dwl/instantiate-component file-id id (gpt/point 0 0) {:id-ref id-ref :origin "plugin"}) + (st/emit! (-> (dwl/instantiate-component file-id id (gpt/point 0 0) {:id-ref id-ref :origin "plugin" :frame-id uuid/zero}) (se/add-event plugin-id))) (shape/shape-proxy plugin-id @id-ref)))) @@ -1293,6 +1293,25 @@ (let [file (u/locate-file file-id)] (apply array (keys (dm/get-in file [:data :plugin-data (keyword "shared" namespace)])))))))) +(defn- loaded-file-ids + "Ids of the files whose data is loaded: the open file and its libraries. + Files left behind by earlier workspaces stay in `:files` without data." + [state] + (into #{} (keep (fn [[id file]] (when (:data file) id))) (:files state))) + +(defn- on-library-loaded + "Calls `f` once the data of `library-id` is in the workspace state." + [library-id f] + (let [key (js/Symbol) + loaded? #(some? (dm/get-in % [:files library-id :data]))] + (if (loaded? @st/state) + (f) + (add-watch st/state key + (fn [_ _ _ state] + (when (loaded? state) + (remove-watch st/state key) + (f))))))) + (defn library-subcontext [plugin-id] (obj/reify {:name "PenpotLibrarySubcontext"} @@ -1304,8 +1323,7 @@ :connected {:get (fn [] - (let [libraries (get @st/state :files)] - (apply array (->> libraries keys (map (partial library-proxy plugin-id))))))} + (apply array (map (partial library-proxy plugin-id) (loaded-file-ids @st/state))))} :tokensSource {:get @@ -1343,7 +1361,7 @@ (let [team-id (:current-team-id @st/state)] (js/Promise. (fn [resolve reject] - (let [current-libs (into #{} (map first) (get @st/state :files))] + (let [current-libs (loaded-file-ids @st/state)] (->> (rp/cmd! :get-team-shared-files {:team-id team-id}) (rx/map (fn [result] (->> result @@ -1374,6 +1392,8 @@ team-id (:current-team-id @st/state) library-id (uuid/parse library-id)] (->> (rp/cmd! :get-team-shared-files {:team-id team-id}) + ;; Saves may have failed while the list loaded + (rx/tap (fn [_] (r/check-writable))) (rx/subs! (fn [shared-files] ;; Only published libraries of the team can be linked @@ -1385,7 +1405,11 @@ (->> st/stream (rx/filter (ptk/type? ::dwl/link-file-to-library-finished)) (rx/take 1) - (rx/subs! #(resolve (library-proxy plugin-id library-id)) reject)) + (rx/subs! (fn [_] + (on-library-loaded + library-id + #(resolve (library-proxy plugin-id library-id)))) + reject)) (st/emit! (-> (dwl/link-file-to-library file-id library-id) (se/add-event plugin-id)))))) reject))))))))) diff --git a/frontend/src/app/plugins/management.cljs b/frontend/src/app/plugins/management.cljs new file mode 100644 index 0000000000..91671991f8 --- /dev/null +++ b/frontend/src/app/plugins/management.cljs @@ -0,0 +1,606 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.plugins.management + (:require + [app.common.data :as d] + [app.common.features :as cfeat] + [app.common.time :as ct] + [app.common.uuid :as uuid] + [app.main.data.dashboard :as dd] + [app.main.data.event :as ev] + [app.main.data.helpers :as dsh] + [app.main.data.team :as dtm] + [app.main.features :as features] + [app.main.repo :as rp] + [app.main.router :as rt] + [app.main.store :as st] + [app.plugins.register :as r] + [app.plugins.system-events :as se] + [app.plugins.utils :as u] + [app.util.object :as obj] + [app.util.sse :as sse] + [beicon.v2.core :as rx] + [clojure.set :as set] + [cuerdas.core :as str] + [potok.v2.core :as ptk])) + +(declare project-proxy) +(declare file-proxy) + +(defn- dashboard-of-team? + "True when the dashboard of `team-id` is open. Outside the dashboard `:files` + holds the workspace files and libraries, so dashboard updates do not apply." + [state team-id] + (and (str/starts-with? (name (or (rt/lookup-name state) "")) "dashboard") + (= team-id (:current-team-id state)))) + +(defn- update-dashboard + "Applies `update-fn` to the state when the dashboard of `team-id` is open." + [team-id update-fn] + (st/emit! (fn [state] + (if (dashboard-of-team? state team-id) + (update-fn state) + state)))) + +(defn- refresh-restored-project + [team-id project-id] + (if (dashboard-of-team? @st/state team-id) + (->> (rx/zip (rp/cmd! :get-projects {:team-id team-id}) + (rp/cmd! :get-project-files {:project-id project-id}) + (rp/cmd! :get-team-deleted-files {:team-id team-id})) + (rx/tap (fn [[projects files deleted-files]] + (update-dashboard + team-id + (fn [state] + (-> state + (update :projects merge (d/index-by :id projects)) + (update :files merge (d/index-by :id (concat files deleted-files))) + (assoc :deleted-files (d/index-by :id deleted-files))))))) + (rx/map (constantly nil))) + (rx/of nil))) + +(defn- restore-deleted-files + "Uses the dashboard's Trash operation and waits for its final result." + [team-id ids] + (->> (rp/cmd! ::sse/restore-deleted-team-files {:team-id team-id :ids ids}) + (rx/filter sse/end-of-stream?) + (rx/take 1) + (rx/map sse/get-payload) + (rx/reduce (fn [_ restored] restored) nil) + (rx/mapcat (fn [restored] + (cond + (nil? restored) + (rx/throw (js/Error. "Restoration ended without a result")) + + (not (set/subset? ids restored)) + (rx/throw (js/Error. "Some files could not be restored; they may no longer exist")) + + :else + (rx/of nil)))))) + +(defn- valid-name + "Returns `value` trimmed when it is a name of 1 to 250 characters." + [value] + (when (string? value) + (let [value (str/trim value)] + (when (<= 1 (count value) 250) + value)))) + +(defn- granted? + "True when the plugin was granted `permission`. Management calls act on + files through the backend, not on the open workspace, so the workspace's + read-only state does not apply to them." + [plugin-id permission] + (r/has-permission? plugin-id permission)) + +(defn- permission-error + [permission] + (js/Error. (str "Permission " permission " is not granted"))) + +(defn- backend-error + "Adds the backend type and code to plugin errors without exposing RPC data." + [error] + (if-let [{:keys [type code hint]} (ex-data error)] + (let [details (str/join "/" (keep #(some-> % name) [type code])) + message (or hint (ex-message error)) + result (js/Error. (str message (when (seq details) (str " (" details ")"))))] + (obj/set! result "type" (some-> type name)) + (obj/set! result "code" (some-> code name)) + result) + error)) + +(defn- request + "Resolves with the single value of the stream built by `make-stream` when + the plugin has `permission`." + [plugin-id permission make-stream] + (if (granted? plugin-id permission) + (js/Promise. + (fn [resolve reject] + (rx/subs! resolve #(reject (backend-error %)) (make-stream)))) + (js/Promise.reject (permission-error permission)))) + +(defn- set-property + "Runs a setter's RPC before updating the proxy and app state. Async failures + are logged even when synchronous validation errors are configured to throw." + [plugin-id data attr value event] + (st/emit! + (se/add-event + (ptk/reify ::set-property + ev/Event + (-data [_] + (assoc (if (satisfies? ev/Event event) (ev/-data event) {}) + ::ev/name (name (ptk/type event)))) + + ptk/WatchEvent + (watch [_ state stream] + (->> (rx/concat + (ptk/watch event state stream) + (rx/of (fn [state] + (swap! data assoc attr value) + (ptk/update event state)))) + (rx/catch (fn [error] + (u/display-not-valid attr (.-message (backend-error error))) + (rx/empty)))))) + plugin-id))) + +(defn- set-project-pin + [{:keys [id is-pinned] :as params}] + (ptk/reify ::set-project-pin + ptk/UpdateEvent + (update [_ state] + (d/update-in-when state [:projects id] assoc :is-pinned is-pinned)) + + ptk/WatchEvent + (watch [_ _ _] + (->> (rp/cmd! :update-project-pin params) + (rx/ignore))))) + +(defn workspace-context + [state] + (let [params (rt/get-params state) + file-id (when (= :workspace (rt/lookup-name state)) + (uuid/parse* (:file-id params))) + ready? (and file-id + (nil? (:exception state)) + (= file-id (:current-file-id state) (:workspace-ready state)) + (some? (:current-page-id state)) + (some? (dsh/lookup-page state file-id (:current-page-id state))))] + {:status (cond ready? "ready" file-id "loading" :else "none") + :fileId (some-> file-id str) + :fileName (when file-id (:name (dsh/lookup-file state file-id))) + :teamId (some-> (:current-team-id state) str)})) + +(defn open-file + [file-id options] + (let [file-id (uuid/parse* file-id) + team-id (if-let [id (obj/get options "teamId")] + (uuid/parse* id) + (:current-team-id @st/state))] + (cond + (or (nil? file-id) (nil? team-id)) + (js/Promise.reject (js/Error. "Expected a file UUID and a team UUID")) + + (and (= "ready" (:status (workspace-context @st/state))) + (= file-id (:current-file-id @st/state)) + (= team-id (:current-team-id @st/state))) + (js/Promise.resolve nil) + + :else + (js/Promise. + (fn [resolve reject] + (let [key (js/Symbol) + initial-route (:route @st/state) + timer (atom nil) + finish (fn [error] + (remove-watch st/state key) + (js/clearTimeout @timer) + (if error (reject error) (resolve nil)))] + (reset! timer (js/setTimeout #(finish (js/Error. "Timed out opening file")) 30000)) + (add-watch + st/state key + (fn [_ _ _ state] + (let [workspace (workspace-context state) + target? (and (= (str file-id) (:fileId workspace)) + (= (str team-id) (:team-id (rt/get-params state))))] + (cond + (:exception state) + (finish (js/Error. "Unable to open file")) + + (and target? (= "ready" (:status workspace))) + (finish nil) + + (and (not= initial-route (:route state)) (not target?)) + (finish (js/Error. "File navigation was superseded")))))) + (st/emit! (rt/nav :workspace {:team-id team-id :file-id file-id})))))))) + +(defn- list-projects + [plugin-id team-id] + (request plugin-id "content:read" + #(->> (rp/cmd! :get-projects {:team-id team-id}) + (rx/map (fn [projects] + (->> projects + (remove :deleted-at) + (map (partial project-proxy plugin-id)) + (into-array))))))) + +(defn- list-deleted-projects + [plugin-id team-id] + (request plugin-id "content:read" + #(->> (rp/cmd! :get-projects {:team-id team-id}) + (rx/map (fn [projects] + (into-array (map (partial project-proxy plugin-id) + (filter (fn [project] + (some-> (:deleted-at project) (ct/is-after? (ct/now)))) + projects)))))))) + +(defn- list-deleted-files + [plugin-id team-id project-id] + (request plugin-id "content:read" + #(->> (rp/cmd! :get-team-deleted-files {:team-id team-id}) + (rx/map (fn [files] + (into-array (map (partial file-proxy plugin-id) + (cond->> (filter (fn [file] + (some-> (:will-be-deleted-at file) (ct/is-after? (ct/now)))) + files) + project-id (filter (fn [file] (= project-id (:project-id file)))))))))))) + +(defn- create-project + [plugin-id team-id options] + (if-let [name (valid-name (obj/get options "name"))] + (request plugin-id "manage:projects" + #(->> (rp/cmd! :create-project {:team-id team-id :name name}) + (rx/map (fn [project] (assoc project :count 0))) + (rx/tap (fn [project] + (update-dashboard team-id + (fn [state] + (assoc-in state [:projects (:id project)] project))))) + (rx/map (partial project-proxy plugin-id)))) + (js/Promise.reject (js/Error. "Expected a name with 1 to 250 characters")))) + +(defn- file-metadata + "Fetches the metadata of a file and its team. A file loaded in the workspace + is read from the state instead of downloading it again." + [file-id] + (let [file (dsh/lookup-file @st/state file-id)] + (->> (if (:project-id file) + (rx/of file) + (rp/cmd! :get-file {:id file-id :features (:features @st/state)})) + (rx/mapcat (fn [file] + (->> (rp/cmd! :get-project {:id (:project-id file)}) + (rx/map (fn [project] + (-> (dissoc file :data) + (assoc :team-id (:team-id project))))))))))) + +(defn- get-file + [plugin-id file-id] + (if-let [file-id (uuid/parse* file-id)] + (request plugin-id "content:read" + #(->> (file-metadata file-id) + (rx/map (partial file-proxy plugin-id)))) + (js/Promise.reject (js/Error. "Expected a file UUID")))) + +(defn team-proxy + [plugin-id team] + (let [data (atom team)] + (obj/reify {:name "TeamProxy"} + :$plugin {:enumerable false :get (fn [] plugin-id)} + + :id + {:get #(str (:id @data))} + + :isDefault + {:get #(boolean (:is-default @data))} + + :name + {:get #(:name @data) + :set + (fn [value] + (let [name (valid-name value)] + (cond + (not (granted? plugin-id "manage:teams")) + (u/not-valid plugin-id :name "Plugin doesn't have 'manage:teams' permission") + + (nil? name) + (u/not-valid plugin-id :name value) + + :else + (set-property plugin-id data :name name + (dtm/update-team {:id (:id @data) :name name})))))} + + :listProjects + (fn [] (list-projects plugin-id (:id @data))) + + :listDeletedProjects + (fn [] (list-deleted-projects plugin-id (:id @data))) + + :listDeletedFiles + (fn [] (list-deleted-files plugin-id (:id @data) nil)) + + :createProject + (fn [options] (create-project plugin-id (:id @data) options))))) + +(defn project-proxy + [plugin-id project] + (let [data (atom project)] + (obj/reify {:name "ProjectProxy"} + :$plugin {:enumerable false :get (fn [] plugin-id)} + + :id + {:get #(str (:id @data))} + + :teamId + {:get #(str (:team-id @data))} + + :isDefault + {:get #(boolean (:is-default @data))} + + :fileCount + {:get #(or (:count @data) 0)} + + :name + {:get #(:name @data) + :set + (fn [value] + (let [name (valid-name value)] + (cond + (not (granted? plugin-id "manage:projects")) + (u/not-valid plugin-id :name "Plugin doesn't have 'manage:projects' permission") + + (nil? name) + (u/not-valid plugin-id :name value) + + :else + (set-property plugin-id data :name name + (dd/rename-project {:id (:id @data) :name name})))))} + + :pinned + {:get #(boolean (:is-pinned @data)) + :set + (fn [value] + (cond + (not (granted? plugin-id "manage:projects")) + (u/not-valid plugin-id :pinned "Plugin doesn't have 'manage:projects' permission") + + (not (boolean? value)) + (u/not-valid plugin-id :pinned value) + + :else + (set-property plugin-id data :is-pinned value + (set-project-pin {:id (:id @data) + :team-id (:team-id @data) + :is-pinned value}))))} + + :listFiles + (fn [] + (request plugin-id "content:read" + #(->> (rp/cmd! :get-project-files {:project-id (:id @data)}) + (rx/map (fn [files] + (into-array (map (fn [file] + (file-proxy plugin-id (assoc file :team-id (:team-id @data)))) + files))))))) + + :listDeletedFiles + (fn [] (list-deleted-files plugin-id (:team-id @data) (:id @data))) + + :restore + (fn [] + (request plugin-id "manage:projects" + #(let [{:keys [id team-id]} @data] + (->> (rp/cmd! :get-team-deleted-files {:team-id team-id}) + (rx/mapcat (fn [files] + (let [ids (into #{} (comp (filter (fn [file] (= id (:project-id file)))) + (map :id)) files)] + (if (seq ids) + (restore-deleted-files team-id ids) + (rx/throw (js/Error. "Cannot restore a project without recoverable files")))))) + (rx/tap (fn [_] (swap! data dissoc :deleted-at))) + (rx/mapcat (fn [_] (refresh-restored-project team-id id))))))) + + :createFile + (fn [options] + (if-let [name (valid-name (obj/get options "name"))] + (request plugin-id "content:write" + #(let [{:keys [id team-id]} @data + features (set/difference (:features @st/state #{}) cfeat/frontend-only-features)] + (->> (rp/cmd! :create-file {:project-id id :name name :features features}) + (rx/map (fn [file] (-> (dissoc file :data) (assoc :team-id team-id)))) + (rx/tap (fn [file] + (update-dashboard team-id (fn [state] (ptk/update (dd/file-created file) state))))) + (rx/map (partial file-proxy plugin-id))))) + (js/Promise.reject (js/Error. "Expected a name with 1 to 250 characters")))) + + :duplicate + (fn [options] + (let [name (obj/get options "name")] + (if (and (some? name) (nil? (valid-name name))) + (js/Promise.reject (js/Error. "Expected a name with 1 to 250 characters")) + (request plugin-id "manage:projects" + #(let [{:keys [id team-id]} @data] + (->> (rp/cmd! :duplicate-project (cond-> {:project-id id} + (some? name) (assoc :name (valid-name name)))) + (rx/mapcat dd/project-with-file-count) + (rx/tap (fn [project] + (update-dashboard team-id (fn [state] (ptk/update (dd/project-duplicated project) state))))) + (rx/map (partial project-proxy plugin-id)))))))) + + :moveTo + (fn [team] + (if-let [team-id (uuid/parse* (obj/get team "id"))] + (request plugin-id "manage:teams" + #(let [{:keys [id] :as project} @data] + (->> (rp/cmd! :move-project {:project-id id :team-id team-id}) + (rx/tap (fn [_] + (swap! data assoc :team-id team-id) + (update-dashboard (:team-id project) (fn [state] (update state :projects dissoc id))))) + (rx/map (constantly nil))))) + (js/Promise.reject (js/Error. "Expected a team")))) + + :remove + (fn [] + (request plugin-id "manage:delete" + #(let [{:keys [id team-id]} @data] + (->> (rp/cmd! :delete-project {:id id}) + (rx/tap (fn [_] + (update-dashboard team-id (fn [state] (update state :projects dissoc id))))) + (rx/map (constantly nil))))))))) + +(defn file-proxy + [plugin-id file] + (let [data (atom file)] + (obj/reify {:name "ProjectFileProxy"} + :$plugin {:enumerable false :get (fn [] plugin-id)} + + :id + {:get #(str (:id @data))} + + :teamId + {:get #(str (:team-id @data))} + + :projectId + {:get #(str (:project-id @data))} + + :modifiedAt + {:get #(.toISOString ^js (:modified-at @data))} + + :name + {:get #(:name @data) + :set + (fn [value] + (let [name (valid-name value)] + (cond + (not (granted? plugin-id "content:write")) + (u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission") + + (nil? name) + (u/not-valid plugin-id :name value) + + :else + (set-property plugin-id data :name name + (dd/rename-file {:id (:id @data) :name name})))))} + + :shared + {:get #(boolean (:is-shared @data)) + :set + (fn [value] + (cond + (not (granted? plugin-id "library:write")) + (u/not-valid plugin-id :shared "Plugin doesn't have 'library:write' permission") + + (not (boolean? value)) + (u/not-valid plugin-id :shared value) + + :else + (set-property plugin-id data :is-shared value + (dd/set-file-shared {:id (:id @data) :is-shared value}))))} + + :open + (fn [] + (if (granted? plugin-id "content:read") + (open-file (str (:id @data)) #js {:teamId (str (:team-id @data))}) + (js/Promise.reject (permission-error "content:read")))) + + :restore + (fn [] + (request plugin-id "content:write" + #(->> (restore-deleted-files (:team-id @data) #{(:id @data)}) + (rx/tap (fn [_] + (swap! data dissoc :deleted-at :will-be-deleted-at))) + (rx/mapcat (fn [_] (refresh-restored-project (:team-id @data) (:project-id @data))))))) + + :moveTo + (fn [project] + (if-let [project-id (uuid/parse* (obj/get project "id"))] + (request plugin-id "content:write" + #(let [{:keys [id team-id]} @data] + (->> (rp/cmd! :get-project {:id project-id}) + (rx/mapcat (fn [target] + (if (= team-id (:team-id target)) + (rp/cmd! :move-files {:ids #{id} :project-id project-id}) + (rx/throw (js/Error. "Cannot move a file to a project in another team"))))) + (rx/tap (fn [_] + (update-dashboard team-id (fn [state] (ptk/update (dd/move-files {:ids #{id} :project-id project-id}) state))) + (swap! data assoc :project-id project-id))) + (rx/map (constantly nil))))) + (js/Promise.reject (js/Error. "Expected a project")))) + + :duplicate + (fn [options] + (let [name (obj/get options "name")] + (if (and (some? name) (nil? (valid-name name))) + (js/Promise.reject (js/Error. "Expected a name with 1 to 250 characters")) + (request plugin-id "content:write" + #(let [{:keys [id team-id]} @data] + (->> (rp/cmd! :duplicate-file (cond-> {:file-id id} + (some? name) (assoc :name (valid-name name)))) + (rx/map (fn [file] (-> (dissoc file :data) (assoc :team-id team-id)))) + (rx/tap (fn [file] + (update-dashboard team-id (fn [state] (ptk/update (dd/file-created file) state))))) + (rx/map (partial file-proxy plugin-id)))))))) + + :remove + (fn [] + (request plugin-id "manage:delete" + #(let [{:keys [id project-id team-id]} @data] + (->> (rp/cmd! :delete-file {:id id}) + (rx/tap (fn [_] + (update-dashboard team-id + (fn [state] + (->> state + (ptk/update (dd/delete-file {:id id :project-id project-id})) + (ptk/update (dd/file-deleted project-id))))))) + (rx/map (constantly nil))))))))) + +(defn create-context + [plugin-id] + (obj/reify {:name "PenpotManagementContext"} + :workspace + {:get (fn [] (clj->js (workspace-context @st/state)))} + + :openFile open-file + + :getFile + (fn [file-id] (get-file plugin-id file-id)) + + :listTeams + (fn [] + (request plugin-id "manage:teams" + #(->> (rp/cmd! :get-teams) + (rx/map (fn [teams] + (into-array (map (partial team-proxy plugin-id) teams))))))) + + :createTeam + (fn [options] + (if-let [name (valid-name (obj/get options "name"))] + (request plugin-id "manage:teams" + #(->> (rp/cmd! :create-team {:name name :features features/global-enabled-features}) + (rx/tap (fn [_] (st/emit! (dtm/fetch-teams)))) + (rx/map (partial team-proxy plugin-id)))) + (js/Promise.reject (js/Error. "Expected a name with 1 to 250 characters")))) + + :listProjects + (fn [options] + (if-let [team-id (if-let [id (obj/get options "teamId")] + (uuid/parse* id) + (:current-team-id @st/state))] + (list-projects plugin-id team-id) + (js/Promise.reject (js/Error. "Expected a team UUID")))) + + :listDeletedProjects + (fn [options] + (if-let [team-id (if-let [id (obj/get options "teamId")] + (uuid/parse* id) + (:current-team-id @st/state))] + (list-deleted-projects plugin-id team-id) + (js/Promise.reject (js/Error. "Expected a team UUID")))) + + :listDeletedFiles + (fn [options] + (if-let [team-id (if-let [id (obj/get options "teamId")] + (uuid/parse* id) + (:current-team-id @st/state))] + (list-deleted-files plugin-id team-id nil) + (js/Promise.reject (js/Error. "Expected a team UUID")))))) diff --git a/frontend/src/app/plugins/register.cljs b/frontend/src/app/plugins/register.cljs index c70b4f9e7a..3c919eed50 100644 --- a/frontend/src/app/plugins/register.cljs +++ b/frontend/src/app/plugins/register.cljs @@ -22,6 +22,13 @@ ;; cause a circular dependency (def mcp-plugin-id "96dfa740-005d-8020-8007-55ede24a2bae") +(def mcp-permissions + #{"user:read" "library:read" "library:write" + "comment:read" "comment:write" "content:read" "content:write" + "allow:global" "manage:projects" "manage:teams" "manage:delete" + ;; Keeps the rollbacks not reported yet across reloads + "allow:localstorage"}) + ;; Promise that resolves when plugins runtime is initialized. ;; Lives here to avoid circular dependency: workspace.mcp -> app.plugins -> app.plugins.api -> workspace (defonce ^:private runtime-ready-promise (p/deferred)) @@ -54,15 +61,20 @@ "Read the manifest.json defined by the plugins definition and transforms it into an object that will be stored in the register." [plugin-url ^js manifest] - (let [name (obj/get manifest "name") - desc (obj/get manifest "description") - code (obj/get manifest "code") - icon (obj/get manifest "icon") - vers (d/nilv (obj/get manifest "version") 1) + (let [name (obj/get manifest "name") + desc (obj/get manifest "description") + code (obj/get manifest "code") + icon (obj/get manifest "icon") + vers (d/nilv (obj/get manifest "version") 1) + scope (obj/get manifest "scope") permissions (into #{} (obj/get manifest "permissions" [])) permissions (cond-> permissions + ;; Global plugins must be explicitly allowed by the user + (= "global" scope) + (conj "allow:global") + (contains? permissions "content:write") (conj "content:read") @@ -102,6 +114,7 @@ {:plugin-id plugin-id :url (str plugin-url) :version vers + :scope scope :name name :description desc :host origin @@ -259,12 +272,54 @@ (.error js/console "Rollback install failed:" err2)))))) (.error js/console "Failed to remove plugin:" err)))))))) -(defn check-permission +(defn has-permission? + "True when the plugin was granted `permission`." [plugin-id permission] - (or (= plugin-id "00000000-0000-0000-0000-000000000000") - (= plugin-id mcp-plugin-id) - (let [{:keys [permissions]} (dm/get-in @registry [:data plugin-id])] - (contains? permissions permission)))) + (cond + (= plugin-id "00000000-0000-0000-0000-000000000000") true + (= plugin-id mcp-plugin-id) (contains? mcp-permissions permission) + :else (let [{:keys [permissions]} (dm/get-in @registry [:data plugin-id])] + (contains? permissions permission)))) + +(defn- read-only-reason + "Why plugin writes must fail in `state`, or nil when they can go ahead. + The read-only flag alone is not a reason: the Inspect tab sets it too, + and it only hides the editing tools." + [state] + (let [wglobal (:workspace-global state)] + (cond + (some? (:persistence-lock wglobal)) + "because changes cannot be saved right now" + + (not (dm/get-in state [:permissions :can-edit])) + "because you cannot edit this file" + + (some? (:preview-id wglobal)) + "while a version is being previewed" + + (dm/get-in state [:render-state :lost]) + "because the canvas stopped rendering"))) + +(defn check-writable + "Throws while saves are failing, the user cannot edit, a version is + being previewed or the render context is lost. A write made then would + be lost or edit the wrong data, so it fails whatever the plugin's + validation flag." + [] + (when-let [reason (read-only-reason @st/state)] + (throw (ex-info (str "[PENPOT PLUGIN] The workspace is read-only " reason) + {:type ::read-only})))) + +(def ^:private write-permissions + #{"content:write" "library:write"}) + +(defn check-permission + "True when the plugin was granted `permission`. A write permission also + runs `check-writable`, which throws while the workspace is read-only." + [plugin-id permission] + (when (contains? write-permissions permission) + (check-writable)) + (has-permission? plugin-id permission)) (defn get-plugin-data [state plugin-id] diff --git a/frontend/src/app/plugins/ruler_guides.cljs b/frontend/src/app/plugins/ruler_guides.cljs index 56dd051f1e..701a6a41d5 100644 --- a/frontend/src/app/plugins/ruler_guides.cljs +++ b/frontend/src/app/plugins/ruler_guides.cljs @@ -124,6 +124,7 @@ :remove (fn [] + (r/check-writable) (cond (not (u/page-active? page-id)) (u/not-valid plugin-id :remove "Cannot modify a page that is not currently active") diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index 408cf84b25..a313ae1ee6 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -66,7 +66,7 @@ [app.plugins.strokes :as strokes] [app.plugins.system-events :as se] [app.plugins.text :as text] - [app.plugins.tokens :refer [applied-tokens-plugin->applied-tokens expand-token-attrs per-side-stroke-attrs-restricted? per-side-stroke-widths-message token-attr-plugin->token-attr token-attr? valid-token-resolution?]] + [app.plugins.tokens :refer [applied-tokens-plugin->applied-tokens expand-token-attrs per-side-stroke-attrs-restricted? per-side-stroke-widths-message token-attr? valid-token-resolution?]] [app.plugins.utils :as u] [app.util.http :as http] [app.util.object :as obj] @@ -365,6 +365,7 @@ (let [data (u/locate-shape file-id page-id id)] (-> (obj/reify {:name "ShapeProxy" + :on-set u/check-shape-writable :on-error (u/handle-error plugin-id)} :$plugin {:enumerable false :get (fn [] plugin-id)} :$id {:enumerable false :get (fn [] id)} @@ -1041,6 +1042,7 @@ ;; Methods :resize (fn [width height] + (u/check-shape-located file-id page-id id) (cond (or (not (sm/valid-safe-number? width)) (<= width 0)) (u/not-valid plugin-id :resize width) @@ -1081,6 +1083,7 @@ :rotate (fn [angle center] + (u/check-shape-located file-id page-id id) (let [center (when center {:x (obj/get center "x") :y (obj/get center "y")})] (cond (not (sm/valid-safe-number? angle)) @@ -1469,18 +1472,22 @@ :bringForward (fn [] + (r/check-writable) (st/emit! (dw/vertical-order-selected (z-order-location plugin-id file-id page-id id :up) [id]))) :sendBackward (fn [] + (r/check-writable) (st/emit! (dw/vertical-order-selected (z-order-location plugin-id file-id page-id id :down) [id]))) :bringToFront (fn [] + (r/check-writable) (st/emit! (dw/vertical-order-selected (z-order-location plugin-id file-id page-id id :top) [id]))) :sendToBack (fn [] + (r/check-writable) (st/emit! (dw/vertical-order-selected (z-order-location plugin-id file-id page-id id :bottom) [id]))) ;; COMPONENTS diff --git a/frontend/src/app/plugins/text.cljs b/frontend/src/app/plugins/text.cljs index 66b2f2fb88..0be157b613 100644 --- a/frontend/src/app/plugins/text.cljs +++ b/frontend/src/app/plugins/text.cljs @@ -431,6 +431,7 @@ :applyTypography (fn [typography] + (r/check-writable) (let [typography (u/proxy->library-typography typography) attrs (-> typography (assoc :typography-ref-file file-id) diff --git a/frontend/src/app/plugins/tokens.cljs b/frontend/src/app/plugins/tokens.cljs index 4e21174bab..62573c232f 100644 --- a/frontend/src/app/plugins/tokens.cljs +++ b/frontend/src/app/plugins/tokens.cljs @@ -264,14 +264,12 @@ k)) (defn- shadow-entry->js - "Converts one resolved shadow entry (a JS Map of field name -> tokenscript - symbol) into a plain JS object using the public member names and the - unit-converted values." - [^js m] + "Converts one resolved shadow entry (a map of field name -> value) into + a plain JS object using the public member names." + [m] (let [out #js {}] - (.forEach m (fn [sym k] - (obj/set! out (shadow-key->camel k) - (ts/tokenscript-symbols->penpot-unit sym)))) + (doseq [[k v] m] + (obj/set! out (shadow-key->camel (name k)) (clj->js v))) out)) (defn- shadow-resolved-value->js @@ -281,41 +279,29 @@ (when (some? entries) (into-array (map shadow-entry->js entries)))) -(defn- font-families-resolved-value->js - "Converts a resolved fontFamilies value (a tokenscript list symbol) into the - documented `string[]` shape rather than leaking the raw tokenscript structure." - [resolved-value] - (let [v (ts/tokenscript-symbols->penpot-unit resolved-value)] - (cond - (nil? v) nil - (sequential? v) (clj->js v) - :else #js [v]))) - (defn- get-resolved-value [token tokens-tree] (let [resolved-tokens (ts/resolve-tokens tokens-tree) - resolved-value (dm/get-in resolved-tokens [(:name token) :resolved-value])] + resolved-token (get resolved-tokens (:name token)) + resolved-value (:resolved-value resolved-token)] (cond (= :font-family (:type token)) - ;; A fontFamilies token resolves to a list of families; expose it as the - ;; documented `string[]` rather than the raw tokenscript list symbol. - (font-families-resolved-value->js resolved-value) + ;; A fontFamilies token resolves to a vector of families; expose it as + ;; the documented `string[]`. + (some-> resolved-value clj->js) (= :typography (:type token)) ;; A typography token resolves to a composite; expose it as the documented - ;; `TokenTypographyValue[]` rather than the raw tokenscript structure. - (typography-resolved-value->js - (ts/tokenscript-symbols->penpot-unit resolved-value)) + ;; `TokenTypographyValue[]` with the line height as resolved. + (typography-resolved-value->js (ts/plugin-value resolved-token)) (= :shadow (:type token)) - ;; A shadow token resolves to a list of composites whose entries the - ;; tokenscript unit conversion leaves as raw symbols; expose them as the + ;; A shadow token resolves to a list of composites; expose them as the ;; documented `TokenShadowValue[]`. - (shadow-resolved-value->js - (ts/tokenscript-symbols->penpot-unit resolved-value)) + (shadow-resolved-value->js resolved-value) :else - (ts/tokenscript-symbols->penpot-unit resolved-value)))) + resolved-value))) (defn token-proxy? [p] (obj/type-of? p "TokenProxy")) diff --git a/frontend/src/app/plugins/utils.cljs b/frontend/src/app/plugins/utils.cljs index 581a41afe2..a9a6479de4 100644 --- a/frontend/src/app/plugins/utils.cljs +++ b/frontend/src/app/plugins/utils.cljs @@ -20,6 +20,7 @@ [app.main.data.helpers :as dsh] [app.main.data.workspace.tokens.library-edit :as dwtl] [app.main.store :as st] + [app.plugins.register :as r] [app.util.object :as obj] [cuerdas.core :as str])) @@ -151,6 +152,29 @@ (when (and (some? file-id) (some? page-id) (some? id)) (locate-shape file-id page-id id)))) +(defn- shape-not-found + [id] + (ex-info (dm/str "[PENPOT PLUGIN] Shape not found: " id) + {:type ::shape-not-found})) + +(defn check-shape-located + "Throws when shape `id` no longer exists (removed by an undo, a peer or + a rejected save), so a write to it does not vanish silently. Always + throws, whatever the plugin's validation flag: `handle-error` lets this + error through." + [file-id page-id id] + (when (nil? (locate-shape file-id page-id id)) + (throw (shape-not-found id)))) + +(defn check-shape-writable + "Throws when the shape behind `proxy` no longer exists (see + `check-shape-located`) or the workspace is read-only (see + `r/check-writable`)." + [proxy] + (when (nil? (proxy->shape proxy)) + (throw (shape-not-found (obj/get proxy "$id")))) + (r/check-writable)) + (defn inside-component-copy? "True when `shape` is nested inside a component copy. The copy root itself is movable as a whole; its descendants are structural copy content and must not @@ -581,6 +605,11 @@ (let [explain (-> cause ex-data ::sm/explain) throw? (throw-validation-errors? plugin-id)] (cond + ;; A write to a removed shape or a read-only workspace always + ;; reaches the plugin + (contains? #{::shape-not-found ::r/read-only} (-> cause ex-data :type)) + (throw cause) + ;; If it's a clojure error we throw as a validation error (and throw? explain) (throw-not-valid :error (error-messages explain)) diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 7bed759057..aab9321c6c 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -17,6 +17,7 @@ [app.common.logging :as log] [app.common.math :as mth] [app.common.render-wasm.api.props :as props] + [app.common.render-wasm.api.select :as wselect] [app.common.render-wasm.helpers :as h] [app.common.render-wasm.mem :as mem] [app.common.render-wasm.mem.heap32 :as mem.h32] @@ -50,6 +51,7 @@ [app.render-wasm.performance :as perf] [app.render-wasm.rulers-state :as rulers-state] [app.render-wasm.text-editor :as text-editor] + [app.render-wasm.text-paste :as text-paste] [app.util.debug :as dbg] [app.util.dom :as dom] [app.util.functions :as fns] @@ -274,8 +276,6 @@ (def text-editor-select-paragraph text-editor/text-editor-select-paragraph) (def text-editor-sync-content text-editor/text-editor-sync-content) -(def dpr - (if use-dpr? (if (exists? js/window) js/window.devicePixelRatio 1.0) 1.0)) (defn get-dpr "Returns the current device pixel ratio. Use instead of `dpr` wherever @@ -344,20 +344,11 @@ (declare request-render-preserving-target) (declare render-pending?) -;; These are the type of frames we have in our -;; render pipeline. -(def ^:const FRAME_TYPE_NONE 0) ;; This type should never "leak". -(def ^:const FRAME_TYPE_PARTIAL 1) ;; A frame needs more render calls to end. -(def ^:const FRAME_TYPE_FULL 2) ;; A frame was full. -(def ^:const FRAME_TYPE_VIEWPORT_READY 3) ;; Viewport presented; interest tiles may still be pending. - (defn- needs-more-render-frames? "True when WASM still has progressive tile work (visible or interest ring)." [] - (or (= wasm/internal-frame-type FRAME_TYPE_PARTIAL) - (= wasm/internal-frame-type FRAME_TYPE_VIEWPORT_READY))) - -(def ^:const RENDER-FLAG-SYNC-TILES 4) ;; Rebuild tile index without ending fast mode (pan/zoom pause). + (or (= wasm/internal-frame-type wasm/FRAME_TYPE_PARTIAL) + (= wasm/internal-frame-type wasm/FRAME_TYPE_VIEWPORT_READY))) (defn- internal-render ([] @@ -455,7 +446,7 @@ ;; one-shot on both sides: WASM clears it when the render loop starts, so ;; the progressive continuation frames behave normally. (if (compare-and-set! preserve-target-render? true false) - (internal-render timestamp (bit-or wasm/internal-frame-type RENDER-FLAG-SYNC-TILES)) + (internal-render timestamp (bit-or wasm/internal-frame-type wasm/RENDER_FLAG_SYNC_TILES)) (internal-render timestamp)) ;; Update text editor blink (so cursor toggles) using the same timestamp @@ -660,7 +651,7 @@ (timers/cancel-af! frame-id) (set! wasm/internal-frame-id nil)) (reset! pending-render false) - (set! wasm/internal-frame-type FRAME_TYPE_NONE)) + (set! wasm/internal-frame-type wasm/FRAME_TYPE_NONE)) (defn- register-deferred-render! [] @@ -727,18 +718,6 @@ (declare get-text-dimensions) -(defn use-shape - [id] - ;; Use `wasm/live?` (not `initialized?`) so context-restore reload can - ;; select shapes while `reloading?` still blocks external app callers. - (when (wasm/live?) - (let [buffer (uuid/get-u32 id)] - (h/call wasm/internal-module "_use_shape" - (aget buffer 0) - (aget buffer 1) - (aget buffer 2) - (aget buffer 3))))) - (defn has-shape [id] (when (wasm/live?) @@ -763,9 +742,14 @@ (let [text (apply str (map :text spans))] (t/write-shape-text spans paragraph text))))) -(defn set-shape-text-content - "This function sets shape text content and returns a stream that loads the needed fonts asynchronously" - [shape-id content] +(defn- text-content-fonts + "Content fonts and fallback faces (emoji, Noto, ...) of a text content." + [content] + {:fonts (f/get-content-fonts content) + :fallback-fonts (fonts-from-text-content content false)}) + +(defn- upload-text-content + [shape-id content {:keys [fonts fallback-fonts]}] ;; Cache content for text editor sync (text-editor/cache-shape-text-content! shape-id content) @@ -779,21 +763,23 @@ (set-shape-vertical-align (get content :vertical-align)) - (let [fonts (f/get-content-fonts content) - fallback-fonts (fonts-from-text-content content false) - all-fonts (concat fonts fallback-fonts) - result (f/store-fonts all-fonts)] + (let [result (f/store-fonts (concat fonts fallback-fonts))] (write-text-content! content) (f/load-fallback-fonts-for-editor! fallback-fonts) (h/call wasm/internal-module "_update_shape_text_layout") result))) +(defn set-shape-text-content + "This function sets shape text content and returns a stream that loads the needed fonts asynchronously" + [shape-id content] + (upload-text-content shape-id content (text-content-fonts content))) + (defn apply-styles-to-selection "Apply style attrs to the currently selected text spans. Updates the cached content, pushes to WASM, and returns {:shape-id :content} for saving. `:with-fills?` also returns the selection's `:fills`." [styles & [opts]] - (let [result (text-editor/apply-styles-to-selection styles use-shape set-shape-text-content opts)] + (let [result (text-editor/apply-styles-to-selection styles wselect/use-shape set-shape-text-content opts)] (request-render "apply-styles-to-selection") result)) @@ -801,7 +787,7 @@ "Apply paragraph attrs to the paragraphs the editor selection touches. Returns {:shape-id :content} for saving." [attrs] - (let [result (text-editor/apply-paragraph-attrs-to-selection attrs use-shape set-shape-text-content)] + (let [result (text-editor/apply-paragraph-attrs-to-selection attrs wselect/use-shape set-shape-text-content)] (request-render "apply-paragraph-attrs-to-selection") result)) @@ -811,11 +797,25 @@ [shape-id range] (when-let [styles (text-editor/get-pending-caret-styles shape-id)] (let [result (text-editor/apply-styles-to-range - shape-id range styles use-shape set-shape-text-content)] + shape-id range styles wselect/use-shape set-shape-text-content)] (text-editor/clear-pending-caret-styles!) (request-render "apply-pending-caret-styles") result))) +(defn apply-paste-styles + "Restyle the text just pasted at `start` with `fragment`: its overrides, or its full + styles when copied in Penpot. Nil when the shape has no cached content." + [shape-id fragment start penpot?] + (when-let [content (text-editor/get-cached-content shape-id)] + (let [content (if penpot? + (text-paste/apply-content-styles content fragment start) + (text-paste/apply-fragment-styles content fragment start))] + (wselect/use-shape shape-id) + (set-shape-text-content shape-id content) + (request-render "apply-paste-styles") + {:shape-id shape-id + :content content}))) + (defn set-parent-id [id] (let [buffer (uuid/get-u32 id)] @@ -865,9 +865,9 @@ (let [children (into [] (filter uuid?) children)] (if (empty? children) (h/call wasm/internal-module "_set_children_0") - (let [heap (mem/get-heap-u32) - size (mem/get-alloc-size children UUID-U8-SIZE) - offset (mem/alloc->offset-32 size)] + (let [size (mem/get-alloc-size children UUID-U8-SIZE) + offset (mem/alloc->offset-32 size) + heap (mem/get-heap-u32)] (reduce (fn [offset id] (mem.h32/write-uuid offset heap id)) @@ -1407,7 +1407,7 @@ (defn get-text-dimensions ([id] - (use-shape id) + (wselect/use-shape id) (get-text-dimensions)) ([] (if-not (initialized?) @@ -1481,7 +1481,7 @@ ;; this implicitly (`zoom_changed`); this extends it to pan/resize-triggered ;; ends (e.g. selecting a shape opens the options panel and resizes the ;; viewport), which previously blanked. - (internal-render (js/performance.now) RENDER-FLAG-SYNC-TILES))) + (internal-render (js/performance.now) wasm/RENDER_FLAG_SYNC_TILES))) (def render-finish (letfn [(do-render [] @@ -1490,7 +1490,7 @@ (when (initialized?) (if (view-gesture-active?) ;; Pan/zoom pause: render without ending the interaction. - (internal-render (js/performance.now) RENDER-FLAG-SYNC-TILES) + (internal-render (js/performance.now) wasm/RENDER_FLAG_SYNC_TILES) (finalize-view-interaction!))))] (fns/debounce do-render DEBOUNCE_DELAY_MS))) @@ -1530,16 +1530,10 @@ (def ^:private empty-text-font-state {:font-index {} :pending-faces #{}}) -(defn- text-layout-fonts - "Content and fallback faces (emoji, Noto, ...) that `set-shape-text-content` - uploads, must match that path for pending-face tracking." - [content] - (into #{} (concat (f/get-content-fonts content) - (fonts-from-text-content content false)))) - (defn- text-font-face-keys-state - "All font-face keys for a text content, and the subset not WASM-ready yet." - [content] + "All font-face keys for the `text-content-fonts` of a text, and the subset + not WASM-ready yet." + [{:keys [fonts fallback-fonts]}] (reduce (fn [acc font] (let [font-data (f/make-font-data font) @@ -1550,7 +1544,7 @@ (cond-> pending? (update :pending-font-face-keys conj key))))) {:font-face-keys #{} :pending-font-face-keys #{}} - (text-layout-fonts content))) + (into #{} (concat fonts fallback-fonts)))) (defn- acc-text-font-state [{:keys [font-index pending-faces]} id font-face-keys pending-font-face-keys] @@ -1567,7 +1561,7 @@ (if (cfh/text-shape? shape) (let [content (ensure-text-content (:content shape)) {:keys [font-face-keys pending-font-face-keys]} - (text-font-face-keys-state content)] + (text-font-face-keys-state (text-content-fonts content))] (acc-text-font-state empty-text-font-state (:id shape) font-face-keys @@ -1608,7 +1602,7 @@ true))] (when needs-current? - (use-shape id)) + (wselect/use-shape id)) (when (and (some? content) (= type :svg-raw)) (set-shape-svg-raw-content (get-static-markup shape))) @@ -1620,9 +1614,10 @@ (set-layout-data shape))) (let [is-text? (= type :text) + text-fonts (when is-text? (text-content-fonts content)) {:keys [font-face-keys pending-font-face-keys]} - (when is-text? (text-font-face-keys-state content)) - text-content-pending (when is-text? (set-shape-text-content id content)) + (when is-text? (text-font-face-keys-state text-fonts)) + text-content-pending (when is-text? (upload-text-content id content text-fonts)) pending-thumbnails (into [] (concat text-content-pending (when is-text? (set-shape-text-images id content true)) @@ -1643,7 +1638,7 @@ {:thumbnails [] :full [] :font-face-keys #{} :pending-font-face-keys #{}} (do (perf/begin-measure "set-object") - (let [prepared (serialize-shape/serialize-shape! shape) + (let [prepared (serialize-shape/serialize-shape shape) result (set-object-host-attrs prepared false)] (perf/end-measure "set-object") result)))) @@ -1759,21 +1754,11 @@ ;; No pending images — complete immediately. (when on-complete (on-complete))))) -(defn process-object - [shape] - (let [{:keys [thumbnails full font-face-keys pending-font-face-keys]} - (set-object shape) - text-font-state (acc-text-font-state empty-text-font-state - (:id shape) - font-face-keys - pending-font-face-keys)] - (process-pending [shape] thumbnails full text-font-state noop-fn))) - (defn process-objects - "Like process-object but for multiple shapes at once. Accumulates all - pending font/image callbacks before calling process-pending, so that - update-text-layouts fires for all text shapes after fonts load — not - just the first shape that triggered the fetch." + "Takes multiple shapes, calling `set-object` for each one and accumulating + their thumbnails, full-image callbacks, and text font state. Then calls + `process-pending` once for the whole batch, so text layouts are updated for + all shapes after pending fonts and images are processed." [shapes] (let [total-shapes (count shapes) {:keys [thumbnails full text-font-state]} @@ -1811,11 +1796,10 @@ end-index (min total (+ start-index BATCH_MAX_SHAPES)) chunk (into [] (subvec (if (vector? shapes) shapes (vec shapes)) start-index end-index)) - prepared (serialize-shape/serialize-shapes-batch! + prepared (serialize-shape/serialize-shapes-batch chunk {:include-layout? true - :include-fills-strokes? true} - use-shape)] + :include-fills-strokes? true})] (loop [xs prepared t-acc (transient thumbnails-acc) @@ -1961,11 +1945,10 @@ (defn- set-objects-sync "Synchronously process all shapes (for small shape counts)." [shapes render-callback on-shapes-ready] - (let [prepared (serialize-shape/serialize-shapes-batch! + (let [prepared (serialize-shape/serialize-shapes-batch shapes {:include-layout? true - :include-fills-strokes? true} - use-shape) + :include-fills-strokes? true}) total-shapes (count prepared) {:keys [thumbnails full text-font-state]} (loop [index 0 @@ -2079,8 +2062,8 @@ [entries] (when (and (initialized?) (not ^boolean (empty? entries))) (let [size (mem/get-alloc-size entries UUID-U8-SIZE) - heap (mem/get-heap-u32) - offset (mem/alloc->offset-32 size)] + offset (mem/alloc->offset-32 size) + heap (mem/get-heap-u32)] (reduce (fn [offset id] (mem.h32/write-uuid offset heap id)) @@ -2103,8 +2086,8 @@ (when (and (initialized?) (seq shape-ids)) (let [ids (vec shape-ids) size (mem/get-alloc-size ids UUID-U8-SIZE) - heap (mem/get-heap-u32) - offset (mem/alloc->offset-32 size)] + offset (mem/alloc->offset-32 size) + heap (mem/get-heap-u32)] (reduce (fn [offset id] (mem.h32/write-uuid offset heap id)) offset @@ -2162,10 +2145,10 @@ [entries snap-pixel? snap-ignore-axis] (if-not (and (initialized?) (not ^boolean (empty? entries))) [] - (let [heapf32 (mem/get-heap-f32) - heapu32 (mem/get-heap-u32) - size (mem/get-alloc-size entries INPUT-MODIFIER-U8-SIZE) - offset (mem/alloc->offset-32 size)] + (let [size (mem/get-alloc-size entries INPUT-MODIFIER-U8-SIZE) + offset (mem/alloc->offset-32 size) + heapf32 (mem/get-heap-f32) + heapu32 (mem/get-heap-u32)] (reduce (fn [offset [id data]] (let [transform (:transform data) @@ -2180,6 +2163,8 @@ (let [precision (pixel-precision-mode snap-pixel? snap-ignore-axis) offset (-> (h/call wasm/internal-module "_propagate_modifiers" precision) (mem/->offset-32)) + heapf32 (mem/get-heap-f32) + heapu32 (mem/get-heap-u32) length (aget heapu32 offset) max-offset (+ offset 1 (* length MODIFIER-U32-SIZE)) result (loop [result (transient []) @@ -2199,8 +2184,7 @@ (when (and (initialized?) (not ^boolean (empty? entries))) (let [size (mem/get-alloc-size entries UUID-U8-SIZE) offset (mem/alloc->offset-32 size) - heapu32 (mem/get-heap-u32) - heapf32 (mem/get-heap-f32)] + heapu32 (mem/get-heap-u32)] (reduce (fn [offset id] (mem.h32/write-uuid offset heapu32 id)) @@ -2209,7 +2193,7 @@ (let [offset (-> (h/call wasm/internal-module "_get_selection_rect") (mem/->offset-32)) - result (dr/read-selection-rect heapf32 offset)] + result (dr/read-selection-rect (mem/get-heap-f32) offset)] (mem/free) result)))) @@ -2336,13 +2320,6 @@ (fn [resolve _reject] (timers/raf (fn [] (resolve nil)))))) -(def ^:private default-context-options - #js {:antialias false - :depth true - :stencil true - :alpha true - "preserveDrawingBuffer" true}) - (defn resize-viewbox "Resizes the WASM viewbox. No-ops unless the GL context is live (`wasm/live?`), so callers cannot hit `_resize_viewbox` during @@ -2377,54 +2354,19 @@ (when (wasm/live?) (h/call wasm/internal-module "_set_render_options" (debug-flags) new-dpr))) -(def ^:private max-surface-size - ;; Must match `gpu_state::MAX_SURFACE_SIZE`. - 8192) - -(defn- clamp-physical-size - "Clamp physical pixel dimensions before assigning `canvas.width/height`. - Rust `resize` applies the same cap and syncs the effective DPR from the - real drawing buffer." - [w h] - (let [w (mth/max 1 w) - h (mth/max 1 h) - scale (mth/min 1 (/ max-surface-size w) (/ max-surface-size h))] - [(mth/max 1 (mth/floor (* scale w))) - (mth/max 1 (mth/floor (* scale h)))])) - (defn resize-offscreen-canvas! "Resize a persistent OffscreenCanvas to new physical-pixel dimensions and update the WASM render surfaces accordingly (via `_resize_viewbox`). The design state (shape pool) is preserved so `set-objects` is not needed again." [canvas new-physical-w new-physical-h] (when (wasm/live?) - (let [dpr (get-dpr) - [pw ph] (clamp-physical-size new-physical-w new-physical-h)] + (let [dpr (get-dpr) + [pw ph] (webgl/clamp-physical-size new-physical-w new-physical-h)] (set! (.-width canvas) pw) (set! (.-height canvas) ph) (set-render-options! dpr) (resize-viewbox (/ new-physical-w dpr) (/ new-physical-h dpr))))) -(defn- wasm-set-param-from-route-params-if-present - [param-name] - (when-let [value (wasm-get-numeric-value param-name)] - (let [setter-name (str/concat "_set_" (name param-name))] - (h/call wasm/internal-module setter-name value)))) - -(defn- canvas-css-size - "Return canvas size in CSS pixels. - - - For DOM canvases: use `clientWidth/clientHeight`. - - For OffscreenCanvas: fall back to `width/height` (physical px) converted by DPR." - [canvas dpr] - (let [cw (.-clientWidth ^js canvas) - ch (.-clientHeight ^js canvas)] - (if (and (number? cw) (pos? cw) - (number? ch) (pos? ch)) - [cw ch] - [(/ (.-width ^js canvas) dpr) - (/ (.-height ^js canvas) dpr)]))) - (defn resize-canvas! "Sizes the canvas drawing buffer, the WASM render surface and the DPR from a single source of truth (the canvas CSS client size) so the GL framebuffer @@ -2436,10 +2378,10 @@ (resize-canvas! canvas (get-dpr))) ([canvas new-dpr] (when (wasm/live?) - (let [[css-w css-h] (canvas-css-size canvas new-dpr) - css-w (mth/max 1 css-w) - css-h (mth/max 1 css-h) - [phys-w phys-h] (clamp-physical-size + (let [[css-w css-h] (webgl/canvas-css-size canvas new-dpr) + css-w (mth/max 1 css-w) + css-h (mth/max 1 css-h) + [phys-w phys-h] (webgl/clamp-physical-size (mth/floor (* css-w new-dpr)) (mth/floor (* css-h new-dpr)))] (set! (.-width ^js canvas) phys-w) @@ -2503,45 +2445,40 @@ [canvas] (if-not (wasm/module-ready?) false - (let [gl (unchecked-get wasm/internal-module "GL") - flags (debug-flags) + (let [flags (debug-flags) context-id (if (dbg/enabled? :wasm-gl-context-init-error) "fail" "webgl2") - context (.getContext ^js canvas context-id default-context-options) - context-init? (not (nil? context)) - browser (sr/translate-browser cf/browser) - dpr (get-dpr) - [css-w css-h] (canvas-css-size canvas dpr) + browser (sr/translate-browser cf/browser) + dpr (get-dpr) + [css-w css-h] (webgl/canvas-css-size canvas dpr) ;; Avoid 0×0 Skia/GL surfaces (crashes on some browsers). - css-w (mth/max 1 css-w) - css-h (mth/max 1 css-h) + css-w (mth/max 1 css-w) + css-h (mth/max 1 css-h) + params {:antialias_threshold (wasm-get-numeric-value :antialias_threshold) + :viewport_interest_area_threshold (wasm-get-numeric-value :viewport_interest_area_threshold) + :max_blocking_time_ms (wasm-get-numeric-value :max_blocking_time_ms) + :node_batch_threshold (wasm-get-numeric-value :node_batch_threshold) + :blur_downscale_threshold (wasm-get-numeric-value :blur_downscale_threshold)} + result (webgl/init-context canvas {:module wasm/internal-module + :context-id context-id + :css-width css-w + :css-height css-h + :dpr dpr + :flags flags + :browser browser + :params params}) + context-init? (some? result) can-listen? (fn? (.-addEventListener ^js canvas))] - (when-not (nil? context) - (let [handle (.registerContext ^js gl context #js {"majorVersion" 2})] - (.makeContextCurrent ^js gl handle) + (when result + (let [{:keys [context handle]} result] (set! wasm/gl-context-handle handle) (set! wasm/gl-context context) - ;; Force the WEBGL_debug_renderer_info extension as emscripten does not enable it - (.getExtension context "WEBGL_debug_renderer_info") - - ;; Initialize Wasm Render Engine - (h/call wasm/internal-module "_init" css-w css-h) - (h/call wasm/internal-module "_set_render_options" flags dpr) - - ;; Configurable parameters. - (wasm-set-param-from-route-params-if-present :antialias_threshold) - (wasm-set-param-from-route-params-if-present :viewport_interest_area_threshold) - (wasm-set-param-from-route-params-if-present :max_blocking_time_ms) - (wasm-set-param-from-route-params-if-present :node_batch_threshold) - (wasm-set-param-from-route-params-if-present :blur_downscale_threshold) - - ;; Set browser after `_init`; mark live before sizing so the + ;; `init-context` already ran `_init` through `_set_browser` in + ;; order. Publish the handles, then mark live before sizing so the ;; guarded `resize-*` helpers can run (they require `wasm/live?`, ;; which is true here even while `reloading?` still blocks app callers). - (h/call wasm/internal-module "_set_browser" browser) - - ;; Add event listeners for WebGL context lost (set! wasm/canvas canvas) + ;; Add event listeners for WebGL context lost (when can-listen? (.addEventListener canvas "webglcontextlost" webgl-context-lost-listener) (.addEventListener canvas "webglcontextrestored" webgl-context-restored-listener)) @@ -2587,19 +2524,18 @@ ;; Ensure the WebGL context is properly disposed so browsers do not keep ;; accumulating active contexts between page switches. - (when-let [gl (unchecked-get wasm/internal-module "GL")] - (when-let [handle wasm/gl-context-handle] - (try - ;; For hard teardown we can explicitly lose browser context. - ;; For reload->reinit flows we skip this because immediate context - ;; recreation may fail on some browsers/GPUs while context is lost. - (when lose-browser-context? - (when-let [ctx wasm/gl-context] - (when-let [lose-ext (.getExtension ^js ctx "WEBGL_lose_context")] - (.loseContext ^js lose-ext)))) - (.deleteContext ^js gl handle) - (catch :default dispose-error - (.error js/console dispose-error))))) + (when-let [handle wasm/gl-context-handle] + (try + ;; For hard teardown we can explicitly lose browser context. + ;; For reload->reinit flows we skip this because immediate context + ;; recreation may fail on some browsers/GPUs while context is lost. + (when lose-browser-context? + (when-let [ctx wasm/gl-context] + (when-let [lose-ext (.getExtension ^js ctx "WEBGL_lose_context")] + (.loseContext ^js lose-ext)))) + (webgl/delete-context! wasm/internal-module handle) + (catch :default dispose-error + (.error js/console dispose-error)))) (wasm-gesture/reset-after-wasm-reload!) (wasm/reset-context-state!) @@ -2779,7 +2715,7 @@ (defn shape-to-path [id] (when (initialized?) - (use-shape id) + (wselect/use-shape id) (try (let [offset (-> (h/call wasm/internal-module "_current_to_path") (mem/->offset-32)) @@ -2803,7 +2739,7 @@ the fill rule itself)." [id stroke-index] (when (initialized?) - (use-shape id) + (wselect/use-shape id) (try (let [offset (-> (h/call wasm/internal-module "_convert_stroke_to_path" stroke-index) (mem/->offset-32)) @@ -2826,8 +2762,8 @@ (defn calculate-bool* [bool-type ids] (let [size (mem/get-alloc-size ids UUID-U8-SIZE) - heap (mem/get-heap-u32) - offset (mem/alloc->offset-32 size)] + offset (mem/alloc->offset-32 size) + heap (mem/get-heap-u32)] (reduce (fn [offset id] (mem.h32/write-uuid offset heap id)) @@ -2839,6 +2775,7 @@ (-> (h/call wasm/internal-module "_calculate_bool" (sr/translate-bool-type bool-type)) (mem/->offset-32)) + heap (mem/get-heap-u32) length (aget heap offset) data (mem/slice heap (+ offset 1) @@ -2893,11 +2830,11 @@ (defn calculate-position-data [shape] (when (initialized?) - (use-shape (:id shape)) - (let [heapf32 (mem/get-heap-f32) - heapu32 (mem/get-heap-u32) - offset (-> (h/call wasm/internal-module "_calculate_position_data") + (wselect/use-shape (:id shape)) + (let [offset (-> (h/call wasm/internal-module "_calculate_position_data") (mem/->offset-32)) + heapf32 (mem/get-heap-f32) + heapu32 (mem/get-heap-u32) length (aget heapu32 offset) max-offset (+ offset 1 (* length POSITION-DATA-U32-SIZE)) @@ -2966,29 +2903,47 @@ (when-let [snapshot wasm/canvas-snapshot] (reset! transition-image* snapshot))))) +(defn- read-raster-result + "Decode `[len][width][height][bytes]` from a `_render_shape_*` raster call." + [offset] + (let [heap (mem/get-heap-u8) + heapu32 (mem/get-heap-u32) + length (aget heapu32 (mem/->offset-32 offset)) + result (dr/read-image-bytes heap (+ offset 12) length)] + (mem/free) + result)) + (defn render-shape-pixels - "Renders a shape subtree to encoded image bytes. `format` is :png, :jpeg or - :webp; jpeg is flattened onto white on the Rust side, since it has no alpha - channel." + "Renders a shape subtree to encoded image bytes on the GPU export surface. + `format` is :png, :jpeg or :webp; jpeg is flattened onto white on the Rust + side, since it has no alpha channel." [shape-id scale format] (when (initialized?) (let [buffer (uuid/get-u32 shape-id) + offset (h/call wasm/internal-module "_render_shape_pixels" + (aget buffer 0) + (aget buffer 1) + (aget buffer 2) + (aget buffer 3) + scale + (sr/translate-raster-format format))] + (read-raster-result offset)))) - offset - (h/call wasm/internal-module "_render_shape_pixels" - (aget buffer 0) - (aget buffer 1) - (aget buffer 2) - (aget buffer 3) - scale - (sr/translate-raster-format format)) - - heap (mem/get-heap-u8) - heapu32 (mem/get-heap-u32) - length (aget heapu32 (mem/->offset-32 offset)) - result (dr/read-image-bytes heap (+ offset 12) length)] - (mem/free) - result))) +(defn render-shape-raster + "Same output as `render-shape-pixels`, but on a CPU Skia surface (no + GPU/WebGL). Used when the export exceeds the GPU max texture size — the + same path the headless exporter uses for wasm rasters." + [shape-id scale format] + (when (initialized?) + (let [buffer (uuid/get-u32 shape-id) + offset (h/call wasm/internal-module "_render_shape_raster" + (aget buffer 0) + (aget buffer 1) + (aget buffer 2) + (aget buffer 3) + scale + (sr/translate-raster-format format))] + (read-raster-result offset)))) (defn get-shape-extrect [shape-id] @@ -3043,6 +2998,66 @@ (mem/free) result))) +(defn render-shapes-pdf + "Renders each shape id as a page of one PDF. IDs are written to the + transfer buffer (same layout as `set-focus-mode`) before the call." + [shape-ids] + (when (and (initialized?) (seq shape-ids)) + (let [ids (vec shape-ids) + size (mem/get-alloc-size ids UUID-U8-SIZE) + offset (mem/alloc->offset-32 size) + heap (mem/get-heap-u32) + + _ (reduce (fn [offset id] + (mem.h32/write-uuid offset heap id)) + offset + ids) + + result-offset + (h/call wasm/internal-module "_render_shapes_pdf") + + heapu8 (mem/get-heap-u8) + heapu32 (mem/get-heap-u32) + length (aget heapu32 (mem/->offset-32 result-offset)) + result (dr/read-image-bytes heapu8 (+ result-offset 4) length)] + (mem/free) + result))) + +(defn pdf-export-begin! + "Opens a progressive multi-page PDF session." + [] + (when (initialized?) + (h/call wasm/internal-module "_pdf_export_begin"))) + +(defn pdf-export-add-page! + "Appends `shape-id` as the next page of the current PDF session." + [shape-id] + (when (initialized?) + (let [buffer (uuid/get-u32 shape-id)] + (h/call wasm/internal-module "_pdf_export_add_page" + (aget buffer 0) + (aget buffer 1) + (aget buffer 2) + (aget buffer 3))))) + +(defn pdf-export-finish + "Closes the PDF session and returns the encoded PDF bytes." + [] + (when (initialized?) + (let [offset (h/call wasm/internal-module "_pdf_export_finish") + heapu8 (mem/get-heap-u8) + heapu32 (mem/get-heap-u32) + length (aget heapu32 (mem/->offset-32 offset)) + result (dr/read-image-bytes heapu8 (+ offset 4) length)] + (mem/free) + result))) + +(defn pdf-export-abort! + "Drops an in-flight PDF session without returning bytes." + [] + (when (initialized?) + (h/call wasm/internal-module "_pdf_export_abort"))) + (defn render-shape-svg [shape-id scale] (when (initialized?) @@ -3062,6 +3077,31 @@ (mem/free) result))) +(defn render-shapes-svg + "Renders the given shape ids into one SVG document (joined viewBox). + IDs are written to the transfer buffer (same layout as `render-shapes-pdf`)." + [shape-ids scale] + (when (and (initialized?) (seq shape-ids)) + (let [ids (vec shape-ids) + size (mem/get-alloc-size ids UUID-U8-SIZE) + offset (mem/alloc->offset-32 size) + heap (mem/get-heap-u32) + + _ (reduce (fn [offset id] + (mem.h32/write-uuid offset heap id)) + offset + ids) + + result-offset + (h/call wasm/internal-module "_render_shapes_svg" scale) + + heapu8 (mem/get-heap-u8) + heapu32 (mem/get-heap-u32) + length (aget heapu32 (mem/->offset-32 result-offset)) + result (dr/read-image-bytes heapu8 (+ result-offset 4) length)] + (mem/free) + result))) + (defn init-wasm-module [module] (let [default-fn (unchecked-get module "default") diff --git a/frontend/src/app/render_wasm/api/webgl.cljs b/frontend/src/app/render_wasm/api/webgl.cljs index 5fedee789f..3fe8a7b0ba 100644 --- a/frontend/src/app/render_wasm/api/webgl.cljs +++ b/frontend/src/app/render_wasm/api/webgl.cljs @@ -5,12 +5,142 @@ ;; Copyright (c) KALEIDOS SUBSIDIARY SL (ns app.render-wasm.api.webgl - "WebGL utilities for pixel capture and rendering" + "WebGL context creation and utilities for pixel capture and rendering" (:require [app.common.logging :as log] + [app.common.math :as mth] + [app.common.render-wasm.helpers :as h] [app.common.render-wasm.wasm :as wasm] [promesa.core :as p])) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Context +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def default-context-options + "Shared WebGL2 context attributes" + #js {:antialias false + :depth true + :stencil true + :alpha true + :preserveDrawingBuffer true}) + +(def ^:const ^:private max-surface-size + ;; Must match `gpu_state::MAX_SURFACE_SIZE`. + 8192) + +(defn canvas-css-size + "Return canvas size in CSS pixels. + + - For DOM canvases: use `clientWidth/clientHeight`. + - For OffscreenCanvas: fall back to `width/height` (physical px) converted by DPR." + [canvas dpr] + (let [cw (.-clientWidth ^js canvas) + ch (.-clientHeight ^js canvas)] + (if (and (number? cw) (pos? cw) + (number? ch) (pos? ch)) + [cw ch] + [(/ (.-width ^js canvas) dpr) + (/ (.-height ^js canvas) dpr)]))) + +(defn clamp-physical-size + "Clamp physical pixel dimensions before assigning `canvas.width/height`. + Rust `resize` applies the same cap and syncs the effective DPR from the + real drawing buffer." + [w h] + (let [w (mth/max 1 w) + h (mth/max 1 h) + scale (mth/min 1 (/ max-surface-size w) (/ max-surface-size h))] + [(mth/max 1 (mth/floor (* scale w))) + (mth/max 1 (mth/floor (* scale h)))])) + +(defn delete-context! + "Deletes an Emscripten GL context by handle.Never throws." + [module handle] + (when (some? handle) + (try + ;; We look the method up by string key bc dot access would let `:advanced` + ;; builds rename the Emscripten method + (let [gl (unchecked-get module "GL") + delete-context (unchecked-get gl "deleteContext")] + (when (fn? delete-context) + (.call ^js delete-context gl handle))) + (catch :default _)))) + +(defn release-context! + "Releases renderer state and a WebGL context owned by the caller. + `handle` may be nil if Emscripten registration failed. Each release is + attempted even when an earlier one throws; cleanup must preserve the + original initialization failure. Writes no global lifecycle state." + [module context handle] + (when (some? handle) + (try + (h/call module "_clean_up") + (catch :default _))) + (try + (when-let [ext (.getExtension ^js context "WEBGL_lose_context")] + (.loseContext ^js ext)) + (catch :default _)) + (delete-context! module handle)) + +(defn init-context + "Create a WebGL2 context on `canvas` and run the WASM renderer init sequence. + + Expects all values resolved by the caller. Writes no `wasm/` atoms and installs + no listeners. + + - `opts` has keys `module`, `context-id`, `css-width`, `css-height`, `dpr`, + `flags`, `browser`, and `params`. + - `module` is the Emscripten module object holding `GL`, `_init`, + `_set_render_options`, and `_set_browser`. `context-id` is `\"webgl2\"` + (or `\"fail\"` to force the debug failure path). + - `css-width` and `css-height` are CSS pixel sizes already probed and clamped + to at least 1. + - `params` maps tunable keywords to values (which pass to `_set_*` unchecked): + - `:antialias_threshold` + - `:viewport_interest_area_threshold` + - `:max_blocking_time_ms` + - `:node_batch_threshold` + - `:blur_downscale_threshold` + + Returns `{:context :handle}` or nil when `getContext` returns nil. + On failure, releases acquired resources before rethrowing. On success, + ownership transfers to the caller." + [canvas {:keys [module context-id css-width css-height dpr flags browser params]}] + (let [gl (unchecked-get module "GL") + context (.getContext ^js canvas context-id default-context-options)] + (when-not (nil? context) + (let [handle (volatile! nil)] + (try + (vreset! handle (.registerContext ^js gl context #js {"majorVersion" 2})) + (.makeContextCurrent ^js gl @handle) + + ;; Force the WEBGL_debug_renderer_info extension as emscripten does not enable it + (.getExtension context "WEBGL_debug_renderer_info") + + ;; Initialize Wasm Render Engine + (h/call module "_init" css-width css-height) + (h/call module "_set_render_options" flags dpr) + + ;; Configurable parameters, fixed order to match the editor path. + (when-let [value (:antialias_threshold params)] + (h/call module "_set_antialias_threshold" value)) + (when-let [value (:viewport_interest_area_threshold params)] + (h/call module "_set_viewport_interest_area_threshold" value)) + (when-let [value (:max_blocking_time_ms params)] + (h/call module "_set_max_blocking_time_ms" value)) + (when-let [value (:node_batch_threshold params)] + (h/call module "_set_node_batch_threshold" value)) + (when-let [value (:blur_downscale_threshold params)] + (h/call module "_set_blur_downscale_threshold" value)) + + (h/call module "_set_browser" browser) + {:context context + :handle @handle} + (catch :default cause + (release-context! module context @handle) + (throw cause))))))) + (defn get-webgl-context "Gets the WebGL context from the WASM module" [] @@ -23,6 +153,10 @@ (when current-ctx (.-GLctx ^js current-ctx))))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Bitmap rendering +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (defn create-webgl-texture-from-image "Creates a WebGL texture from an HTMLImageElement or ImageBitmap and returns the texture object" [gl image-element] diff --git a/frontend/src/app/render_wasm/shape.cljs b/frontend/src/app/render_wasm/shape.cljs index 995ea816cd..dfbcf37bb4 100644 --- a/frontend/src/app/render_wasm/shape.cljs +++ b/frontend/src/app/render_wasm/shape.cljs @@ -9,6 +9,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] + [app.common.render-wasm.api.select :as wselect] [app.common.render-wasm.svg-derived :as svg-derived] [app.common.transit :as t] [app.common.types.shape :as shape] @@ -76,7 +77,17 @@ c/ICounted (-count [_] - (+ 1 (count delegate))) + (+ 2 (count delegate))) + + c/IKVReduce + (-kv-reduce [_ f init] + (let [acc (f init :id id)] + (if (reduced? acc) + @acc + (let [acc (f acc :type type)] + (if (reduced? acc) + @acc + (reduce-kv f acc delegate)))))) c/ILookup (-lookup [coll k] @@ -321,7 +332,7 @@ (let [shape-id (dm/get-prop shape :id)] (if (shape-in-current-page? shape-id) (do - (api/use-shape shape-id) + (wselect/use-shape shape-id) (->> properties (mapcat #(set-wasm-attr! shape %)) (d/index-by :key :callback) @@ -347,10 +358,27 @@ ;; `conj` empty set initialization (def conj* (fnil conj (d/ordered-set))) +(defn- track-change! + [self k] + (when-let [changes shape/*shape-changes*] + (let [id (:id self)] + (.set changes id (conj* (.get changes id) k))))) + +(defn- assoc-delegate + "Keeps an array map delegate as an array map when adding a new key." + [delegate k v] + (if (and (instance? PersistentArrayMap delegate) + (not (contains? delegate k))) + (PersistentArrayMap. (meta delegate) + (inc (count delegate)) + (doto (aclone (.-arr ^PersistentArrayMap delegate)) + (.push k v)) + nil) + (assoc delegate k v))) + (defn- impl-assoc [self k v] - (when shape/*shape-changes* - (vswap! shape/*shape-changes* update (:id self) conj* k)) + (track-change! self k) (case k :id @@ -363,7 +391,7 @@ (.-delegate ^ShapeProxy self)) (let [delegate (.-delegate ^ShapeProxy self) - delegate' (assoc delegate k v)] + delegate' (assoc-delegate delegate k v)] (if (identical? delegate' delegate) self (ShapeProxy. (.-id ^ShapeProxy self) @@ -378,8 +406,7 @@ (defn- impl-dissoc [self k] - (when shape/*shape-changes* - (vswap! shape/*shape-changes* update (:id self) conj* k)) + (track-change! self k) (case k :id @@ -413,18 +440,19 @@ (next es)) (throw (js/Error. "conj on a map takes map entries or seqables of map entries")))))))) -(def ^:private xf:without-id-and-type - (remove (fn [kvpair] - (let [k (key kvpair)] - (or (= k :id) - (= k :type)))))) - (defn create-shape - "Instantiate a shape from a map" + "Instantiate a shape from a map. The delegate is an array map because + it takes far less memory than a hash map for shape-sized maps." [attrs] - (ShapeProxy. (:id attrs) - (:type attrs) - (into {} xf:without-id-and-type attrs))) + (let [arr (reduce-kv (fn [arr k v] + (when-not (or (= k :id) (= k :type)) + (.push arr k v)) + arr) + #js [] + attrs)] + (ShapeProxy. (:id attrs) + (:type attrs) + (PersistentArrayMap. nil (/ (alength arr) 2) arr nil)))) (t/add-handlers! ;; We only add a write handler, read handler uses the dynamic dispatch diff --git a/frontend/src/app/render_wasm/text_editor.cljs b/frontend/src/app/render_wasm/text_editor.cljs index d26088820a..851f08fbb8 100644 --- a/frontend/src/app/render_wasm/text_editor.cljs +++ b/frontend/src/app/render_wasm/text_editor.cljs @@ -118,14 +118,22 @@ d (aget heap-u32 (+ fill-u32-offset 4)) alpha (aget heap-u8 (+ fill-byte-offset 20)) flags (aget heap-u8 (+ fill-byte-offset 21)) + mtype (bit-or (aget heap-u8 (+ fill-byte-offset 22)) + (bit-shift-left (aget heap-u8 (+ fill-byte-offset 23)) 8)) width (aget heap-i32 (+ fill-u32-offset 6)) height (aget heap-i32 (+ fill-u32-offset 7))] {:fill-opacity (u8->opacity alpha) - :fill-image {:id (uuid/from-unsigned-parts a b c d) - :width width - :height height - :keep-aspect-ratio (not (zero? (bit-and flags 0x01))) - :name "sample"}}) + :fill-image (cond-> {:id (uuid/from-unsigned-parts a b c d) + :width width + :height height + :mtype (types.fills.impl/decode-mtype mtype) + :keep-aspect-ratio (not (zero? (bit-and flags 0x01))) + :name "sample"} + (not (zero? (bit-and flags 0x02))) + (assoc :transform {:x (aget heap-f32 (+ fill-u32-offset 8)) + :y (aget heap-f32 (+ fill-u32-offset 9)) + :width (aget heap-f32 (+ fill-u32-offset 10)) + :height (aget heap-f32 (+ fill-u32-offset 11))}))}) nil))) @@ -375,10 +383,9 @@ (wasm/ready?)) (let [encoder (js/TextEncoder.) buf (.encode encoder text) - heapu8 (mem/get-heap-u8) size (mem/size buf) offset (mem/alloc size)] - (mem/write-buffer offset heapu8 buf)))) + (mem/write-buffer offset (mem/get-heap-u8) buf)))) (defn text-editor-encode-text-post [text] @@ -498,10 +505,10 @@ (when (wasm/ready?) (try (let [byte-offset (mem/alloc 16) - u32-offset (mem/->offset-32 byte-offset) - heap (mem/get-heap-u32)] + u32-offset (mem/->offset-32 byte-offset)] (h/call wasm/internal-module "_text_editor_get_active_shape_id" byte-offset) - (let [a (aget heap u32-offset) + (let [heap (mem/get-heap-u32) + a (aget heap u32-offset) b (aget heap (+ u32-offset 1)) c (aget heap (+ u32-offset 2)) d (aget heap (+ u32-offset 3)) @@ -518,10 +525,10 @@ (when (wasm/ready?) (let [byte-offset (mem/alloc 16) u32-offset (mem/->offset-32 byte-offset) - heap (mem/get-heap-u32) has-selection? (h/call wasm/internal-module "_text_editor_get_selection" byte-offset)] (if has-selection? - (let [result {:anchor-para (aget heap u32-offset) + (let [heap (mem/get-heap-u32) + result {:anchor-para (aget heap u32-offset) :anchor-offset (aget heap (+ u32-offset 1)) :focus-para (aget heap (+ u32-offset 2)) :focus-offset (aget heap (+ u32-offset 3))}] @@ -667,6 +674,26 @@ {:start-para focus-para :start-offset focus-offset :end-para anchor-para :end-offset anchor-offset})) +(defn caret-position + "Collapsed caret as {:para :offset} from the WASM selection, or nil." + [] + (when-let [{:keys [focus-para focus-offset]} (text-editor-get-selection)] + {:para focus-para :offset focus-offset})) + +(defn selection-start + "Start of the WASM selection as {:para :offset}: where inserted text goes." + [] + (when-let [selection (text-editor-get-selection)] + (let [{:keys [start-para start-offset]} (normalize-selection selection)] + {:para start-para :offset start-offset}))) + +(defn typed-range + "Normalized range covering the text inserted between `before` and `after`, or nil." + [before after] + (when (and before after) + (normalize-selection {:anchor-para (:para before) :anchor-offset (:offset before) + :focus-para (:para after) :focus-offset (:offset after)}))) + (defn apply-attrs-to-paragraph "Apply `styles` (attrs map, or a fn per span) within [sel-start, sel-end), splitting spans." [para sel-start sel-end styles] @@ -739,7 +766,7 @@ (= 1 (count fills-set)) (first fills-set) :else :multiple))) -(defn- apply-styles-over-range +(defn apply-styles-over-range "Apply `styles` (attrs map or per-span fn) to the char range of `content`, splitting spans." [content {:keys [start-para start-offset end-para end-offset]} styles] (let [paragraph-set (first (:children content)) @@ -769,6 +796,112 @@ paragraphs)] (assoc content :children [(assoc paragraph-set :children new-paragraphs)]))) +(defn- slice-paragraph + "`para` with only the text in [start, end), cutting the spans at both ends. + With no text left it keeps its first span, empty, for the style." + [para start end] + (let [spans (loop [spans (:children para) + pos 0 + acc []] + (if (empty? spans) + acc + (let [span (first spans) + text (:text span) + span-end (+ pos (count text)) + ol-start (max pos start) + ol-end (min span-end end)] + (recur (rest spans) span-end + (cond-> acc + (< ol-start ol-end) + (conj (assoc span :text (subs text (- ol-start pos) (- ol-end pos)))))))))] + (assoc para :children (if (seq spans) + spans + [(assoc (first (:children para)) :text "")])))) + +(defn content-range + "The part of `content` inside the char range, with its paragraph and span styles." + [content {:keys [start-para start-offset end-para end-offset]}] + (let [paragraph-set (first (:children content)) + paragraphs (->> (:children paragraph-set) + (map-indexed vector) + (keep (fn [[idx para]] + (when (<= start-para idx end-para) + (slice-paragraph para + (if (= idx start-para) start-offset 0) + (if (= idx end-para) end-offset (para-char-count para)))))) + (vec))] + (assoc content :children [(assoc paragraph-set :children paragraphs)]))) + +(defn selection-content + "The content of the editor selection in `shape-id`, or nil when it is collapsed." + [shape-id] + (when-let [selection (text-editor-get-selection)] + (let [{:keys [start-para start-offset end-para end-offset] :as range} + (normalize-selection selection)] + (when-let [content (get-cached-content shape-id)] + (when-not (and (= start-para end-para) (= start-offset end-offset)) + (content-range content range)))))) + +(defn span-ranges + "The char range of every span of `content`, a slice that starts at `start` + (`{:para :offset}`) in its shape: one vector of ranges per paragraph." + [content {:keys [para offset]}] + (into [] + (map-indexed + (fn [idx paragraph] + (let [para-idx (+ para idx) + spans (:children paragraph) + starts (reductions + (if (zero? idx) offset 0) (map (comp count :text) spans))] + (mapv (fn [span span-start] + {:start-para para-idx + :start-offset span-start + :end-para para-idx + :end-offset (+ span-start (count (:text span)))}) + spans + starts)))) + (-> content :children first :children))) + +(defn- range-origin + "Where the text in `range` starts in the text `shape-id`, shape-local, with the size + of the box its fills are laid over: {:x :y :box {:width :height}}, or nil." + [shape-id {:keys [start-para start-offset end-para end-offset]}] + (when (wasm/ready?) + (let [id (uuid/get-u32 shape-id) + byte-offset (mem/alloc 16) + f32-offset (mem/->offset-32 byte-offset) + has-text? (h/call wasm/internal-module "_text_get_range_origin" + (aget id 0) (aget id 1) (aget id 2) (aget id 3) + start-para start-offset end-para end-offset byte-offset) + heap (mem/get-heap-f32) + result (when has-text? + {:x (aget heap f32-offset) + :y (aget heap (+ f32-offset 1)) + :box {:width (aget heap (+ f32-offset 2)) + :height (aget heap (+ f32-offset 3))}})] + (mem/free) + result))) + +(defn- box-relative-fill? + [fill] + (or (some? (:fill-color-gradient fill)) + (some? (:fill-image fill)))) + +(defn content-geometry + "Where the gradient or image spans of `content`, a slice starting at `start`, begin + in the text `shape-id`, each with its box size; nil when it has none." + [shape-id content start] + (let [ranges (span-ranges content start) + origins (mapv (fn [paragraph paragraph-ranges] + (mapv (fn [span range] + (when (some box-relative-fill? (:fills span)) + (range-origin shape-id range))) + (:children paragraph) + paragraph-ranges)) + (-> content :children first :children) + ranges)] + (when (some some? (flatten origins)) + {:origins origins}))) + (defn- clean-styles "Drop nil-valued attrs (unlike the DOM path, our merge would keep them and fail the backend schema); a per-span fn is passed through untouched. A nil diff --git a/frontend/src/app/render_wasm/text_paste.cljs b/frontend/src/app/render_wasm/text_paste.cljs new file mode 100644 index 0000000000..bbc5b5d512 --- /dev/null +++ b/frontend/src/app/render_wasm/text_paste.cljs @@ -0,0 +1,190 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.render-wasm.text-paste + "Restyles the text WASM just inserted from a paste fragment (see + `app.util.text.clipboard`), with run overrides or full Penpot styles." + (:require + [app.common.types.text :as txt] + [app.main.fonts :as fonts] + [app.render-wasm.text-editor :as text-editor] + [cuerdas.core :as str])) + +(defn fragment->text + [fragment] + (->> fragment + (map (fn [paragraph] (str/join (map :text (:children paragraph))))) + (str/join "\n"))) + +(defn styled? + [fragment] + (some (fn [paragraph] (some (comp seq :attrs) (:children paragraph))) fragment)) + +(defn- styled-ranges + "The content range of every run with overrides, for a fragment inserted at + `start` (`{:para :offset}`, offsets in UTF-16 units like WASM's)." + [fragment {:keys [para offset]}] + (for [[idx paragraph] (map-indexed vector fragment) + :let [para-idx (+ para idx) + runs (:children paragraph) + starts (reductions + (if (zero? idx) offset 0) + (map (comp count :text) runs))] + [run run-start] (map vector runs starts) + :when (seq (:attrs run))] + {:attrs (:attrs run) + :range {:start-para para-idx + :start-offset run-start + :end-para para-idx + :end-offset (+ run-start (count (:text run)))}})) + +(defn- target-weight + "The weight for `span` given a weight override. \"400\" means not bold, so it + only unbolds: a span below bold keeps its own weight." + [span font-weight] + (let [current (or (:font-weight span) "400")] + (if (and (= font-weight "400") (< (js/parseInt current 10) 600)) + current + (or font-weight current)))) + +(defn- resolve-overrides + "`span` with `overrides`, weight and italic resolved to a variant its font has. + A changed span leaves its typography, which it no longer matches." + [span {:keys [font-weight font-style] :as overrides}] + (let [variant (when (or font-weight font-style) + (some-> (fonts/get-font-data (:font-id span)) + (fonts/find-closest-variant + (target-weight span font-weight) + (or font-style (:font-style span) "normal")))) + result (cond-> (merge span (select-keys overrides [:text-decoration :text-transform])) + (some? variant) + (assoc :font-weight (:weight variant) + :font-style (:style variant) + :font-variant-id (:id variant)))] + (if (= result span) + span + (dissoc result :typography-ref-id :typography-ref-file)))) + +(defn fragment->content + "Penpot content for `fragment`, with `base` as the style its overrides go over." + [fragment base] + {:type "root" + :children + [{:type "paragraph-set" + :children + (mapv (fn [{:keys [children]}] + (merge base + {:type "paragraph" + :children (if (seq children) + (mapv (fn [{:keys [text attrs]}] + (resolve-overrides (assoc base :text text) attrs)) + children) + [(assoc base :text "")])})) + fragment)}]}) + +(defn apply-fragment-styles + "Restyles the `fragment` text WASM inserted at `start` in `content`." + [content fragment start] + (reduce (fn [content {:keys [range attrs]}] + (text-editor/apply-styles-over-range content range #(resolve-overrides % attrs))) + content + (styled-ranges fragment start))) + +;; --- Text copied in Penpot + +(def ^:private style-attrs + (into txt/paragraph-attrs txt/text-node-attrs)) + +(defn content->fragment + "The paste fragment for Penpot `content`. Its attrs are whole paragraph and span + styles, not overrides." + [content] + (->> (-> content :children first :children) + (mapv (fn [paragraph] + {:attrs (dissoc paragraph :type :children) + :children (into [] + (comp (filter (comp seq :text)) + (map (fn [span] {:text (:text span) :attrs (dissoc span :text)}))) + (:children paragraph))})))) + +(defn- restyle + "`node` with its style replaced by `attrs`." + [node attrs] + (merge (apply dissoc node style-attrs) attrs)) + +(defn apply-content-styles + "Restyles the Penpot `fragment` text WASM inserted at `start` in `content`. The + first paragraph keeps the style of the one it went into; the others bring their own." + [content fragment start] + (let [content (reduce (fn [content {:keys [range attrs]}] + (text-editor/apply-styles-over-range content range #(restyle % attrs))) + content + (styled-ranges fragment start))] + (reduce (fn [content [idx {:keys [attrs]}]] + (update-in content [:children 0 :children (+ (:para start) idx)] restyle attrs)) + content + (rest (map-indexed vector fragment))))) + +;; --- Gradients and images of text copied in Penpot + +(defn- move-gradient + "`gradient`, in fractions of the `from` box, in fractions of the `to` box, with its + points at the same pixel offset from the span origin." + [gradient from to] + (let [{x0 :x y0 :y {w0 :width h0 :height} :box} from + {x1 :x y1 :y {w1 :width h1 :height} :box} to + move-x #(/ (+ x1 (- (* % w0) x0)) w1) + move-y #(/ (+ y1 (- (* % h0) y0)) h1)] + (cond-> (-> gradient + (update :start-x move-x) + (update :start-y move-y) + (update :end-x move-x) + (update :end-y move-y)) + ;; A radial width is scaled by the box aspect ratio when drawn. + (= :radial (:type gradient)) + (update :width #(/ (* % w0 h1) (* h0 w1)))))) + +(defn- move-image + "`image`, laid over its transform in the `from` box (the whole box without one), + with a transform in the `to` box at the same pixel offset from the span origin." + [image from to] + (let [{x0 :x y0 :y {w0 :width h0 :height} :box} from + {x1 :x y1 :y {w1 :width h1 :height} :box} to + {:keys [x y width height]} (get image :transform {:x 0 :y 0 :width 1 :height 1})] + (assoc image :transform {:x (/ (+ x1 (- (* x w0) x0)) w1) + :y (/ (+ y1 (- (* y h0) y0)) h1) + :width (/ (* width w0) w1) + :height (/ (* height h0) h1)}))) + +(defn- move-fills + [span from to] + (update span :fills + (fn [fills] + (mapv #(cond-> % + (some? (:fill-color-gradient %)) + (update :fill-color-gradient move-gradient from to) + + (some? (:fill-image %)) + (update :fill-image move-image from to)) + fills)))) + +(defn- sized? + [{{:keys [width height]} :box}] + (and (pos? width) (pos? height))) + +(defn move-span-fills + "`content` with the gradients and images of `pasted`, inserted at `start`, moved from + the `source` geometry to the `target` one (see `text-editor/content-geometry`)." + [content pasted start source target] + (let [entries (map vector + (flatten (text-editor/span-ranges pasted start)) + (flatten (:origins source)) + (flatten (:origins target)))] + (reduce (fn [content [range from to]] + (if (and (some? from) (some? to) (sized? from) (sized? to)) + (text-editor/apply-styles-over-range content range #(move-fills % from to)) + content)) + content + entries))) diff --git a/frontend/src/app/util/clipboard.cljs b/frontend/src/app/util/clipboard.cljs index 3199a6f093..1f0b7decda 100644 --- a/frontend/src/app/util/clipboard.cljs +++ b/frontend/src/app/util/clipboard.cljs @@ -24,16 +24,14 @@ :allowHTMLPaste false}) (defn plain-text->html - "Build a minimal text/html clipboard payload from plain text. - - Windows apps often prefer CF_HTML over CF_UNICODETEXT; writing only - text/plain from a contenteditable copy handler can leave them with the - editor surface's empty/`
` fallback (a lone newline)." - [text] - (let [escaped (-> (or text "") - (dom/escape-html) - (str/replace "\n" "
"))] - (str "" escaped))) + "Minimal text/html clipboard payload for `text`, with `hidden-html` before it. + Windows apps prefer CF_HTML, and may otherwise paste the editor's empty `
`." + ([text] (plain-text->html text nil)) + ([text hidden-html] + (let [escaped (-> (or text "") + (dom/escape-html) + (str/replace "\n" "
"))] + (str "" hidden-html escaped)))) (defn- from-data-transfer "Get clipboard stream from DataTransfer instance" @@ -225,6 +223,34 @@ (.readText ^js clipboard) (unavailable-error)))) +(defn- read-item-type + "Promise of the `mime` text of clipboard `item`, or of nil when it has none." + [^js item mime] + (if (some #{mime} (some-> item .-types array-seq)) + (-> (.getType item mime) (.then #(.text ^js %))) + (js/Promise.resolve nil))) + +(defn read-html-and-text + "Promise of the system clipboard as {:html :text}, either may be nil. Rejects like + `to-clipboard` when the asynchronous Clipboard API is not exposed." + [] + (let [clipboard (get-clipboard)] + (cond + (and clipboard (unchecked-get clipboard "read")) + (-> (.read ^js clipboard) + (.then (fn [items] + (let [item (first items)] + (-> (js/Promise.all #js [(read-item-type item "text/html") + (read-item-type item "text/plain")]) + (.then (fn [[html text]] {:html html :text text}))))))) + + (and clipboard (unchecked-get clipboard "readText")) + (-> (.readText ^js clipboard) + (.then (fn [text] {:html nil :text text}))) + + :else + (unavailable-error)))) + (defn permission-error? "True for the `NotAllowedError` DOMException raised when access is denied." [cause] diff --git a/frontend/src/app/util/code_gen/markup_svg_wasm.cljs b/frontend/src/app/util/code_gen/markup_svg_wasm.cljs new file mode 100644 index 0000000000..4342ced0b6 --- /dev/null +++ b/frontend/src/app/util/code_gen/markup_svg_wasm.cljs @@ -0,0 +1,56 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.util.code-gen.markup-svg-wasm + "SVG markup via the render-wasm export path (inspect / copy-as-svg). + Callers must gate on `render-wasm/v1` and must not fall back to classic + SVG codegen while that feature is on." + (:require + [app.render-wasm.api :as wasm.api] + [app.util.code-beautify :as cb] + [cuerdas.core :as str])) + +(defn- decode-utf8 + [bytes] + (when bytes + (.decode (js/TextDecoder.) bytes))) + +(defn get-svg-markup + "UTF-8 SVG string for the given shape ids via the main-thread WASM module. + Empty string when WASM is not ready, ids are empty, or export throws." + [shape-ids] + (let [ids (vec shape-ids)] + (cond + (empty? ids) + "" + + (not (wasm.api/initialized?)) + "" + + :else + (try + (or (decode-utf8 + (if (= 1 (count ids)) + (wasm.api/render-shape-svg (first ids) 1) + (wasm.api/render-shapes-svg ids 1))) + "") + ;; Missing shape / export failure is a WASM CriticalError (:panic). + ;; Letting it reach the global handler would show Internal Error and + ;; block the workspace; the user can still work without this markup, so + ;; log and return empty instead (same pattern as other non-fatal WASM UI paths). + (catch :default cause + (js/console.error "WASM SVG markup failed:" cause) + ""))))) + +(defn get-formatted-svg-markup + "Beautified SVG markup for inspect / copy-as-svg. + Skips beautify when markup is empty so inspect/copy stay safe without + js-beautify (and avoid a no-op format call)." + [shape-ids] + (let [markup (get-svg-markup shape-ids)] + (if (str/empty? markup) + "" + (cb/format-code markup "svg")))) diff --git a/frontend/src/app/util/code_gen/style_css_values.cljs b/frontend/src/app/util/code_gen/style_css_values.cljs index 4052adba14..e35ef02c44 100644 --- a/frontend/src/app/util/code_gen/style_css_values.cljs +++ b/frontend/src/app/util/code_gen/style_css_values.cljs @@ -12,6 +12,7 @@ [app.common.files.helpers :as cfh] [app.common.geom.matrix :as gmt] [app.common.geom.shapes :as gsh] + [app.common.logging :as log] [app.common.types.shape.layout :as ctl] [app.main.ui.formats :as fmt] [app.util.code-gen.common :as cgc] @@ -24,12 +25,24 @@ :gradient fill-color-gradient :image fill-image}) +(defn- missing-parent? + "True when the parent of `shape` is not in `objects`, which happens in a + damaged file; the position, relative to the parent, cannot be computed." + [objects shape] + (let [missing? (not (contains? objects (:parent-id shape)))] + (when missing? + (log/wrn :hint "shape parent not found" + :shape-id (dm/str (:id shape)) + :parent-id (dm/str (:parent-id shape)))) + missing?)) + (defn get-shape-position [shape objects coord] (when (and (not (cfh/root-frame? shape)) (or (not (ctl/any-layout-immediate-child? objects shape)) - (ctl/position-absolute? shape))) + (ctl/position-absolute? shape)) + (not (missing-parent? objects shape))) (let [parent (get objects (:parent-id shape)) diff --git a/frontend/src/app/util/globals.js b/frontend/src/app/util/globals.js index 5ac6cd3130..8b398ba53d 100644 --- a/frontend/src/app/util/globals.js +++ b/frontend/src/app/util/globals.js @@ -84,6 +84,9 @@ goog.scope(function () { forward() {}, go() {}, }; + // `app.main.data.workspace.mcp` talks to a browser extension through + // window messages; nothing receives them here. + mockWindow.postMessage = function () {}; return mockWindow; } })(); diff --git a/frontend/src/app/util/i18n.cljs b/frontend/src/app/util/i18n.cljs index ce7712ae4e..d36b2872c7 100644 --- a/frontend/src/app/util/i18n.cljs +++ b/frontend/src/app/util/i18n.cljs @@ -55,7 +55,7 @@ {:label "आधुनिक मानक हिन्दी (community)" :value "hi"} {:label "عربي/عربى (community)" :value "ar"} {:label "فارسی (community)" :value "fa"} - {:label "日本語 (Community)" :value "ja_jp"} + {:label "日本語 (Community)" :value "ja"} {:label "简体中文 (community)" :value "zh_cn"} {:label "繁體中文 (community)" :value "zh_hant"}]) diff --git a/frontend/src/app/util/object.cljc b/frontend/src/app/util/object.cljc index 6d1b078df1..40ee9cd8e0 100644 --- a/frontend/src/app/util/object.cljc +++ b/frontend/src/app/util/object.cljc @@ -176,6 +176,7 @@ fn-expr (c/get params :fn) schema-n (c/get params :schema) wrap (c/get params :wrap) + on-set (c/get params :on-set) schema-1 (c/get params :schema-1) this? (c/get params :this false) on-error (c/get params :on-error) @@ -216,6 +217,10 @@ (-> (gensym (str "wrap-fn-" (str/slug pname) "-")) (with-meta {:tag 'function})) + on-set-sym + (-> (gensym (str "on-set-fn-" (str/slug pname) "-")) + (with-meta {:tag 'function})) + val-sym (gensym (str "val-" (str/slug pname) "-")) @@ -232,6 +237,9 @@ (when wrap [wrap-sym wrap]) + (when (and set-expr on-set) + [on-set-sym on-set]) + (when get-expr [(make-sym pname "get-fn") (if this? @@ -258,6 +266,10 @@ (make-sym pname "set-fn") `(fn [~val-sym] + ;; Runs outside the error handling, so what it throws + ;; always reaches the caller + ~@(when on-set + [`(~on-set-sym (~'js* "this"))]) ~(wrap-error-handling `(let [~this-sym (~'js* "this") ~fn-sym ~set-expr @@ -411,6 +423,7 @@ (if (some? definition) (let [definition (if (map? definition) (c/merge {:wrap (:wrap tmeta) + :on-set (:on-set tmeta) :on-error (:on-error tmeta)} definition) (-> {:enumerable false} diff --git a/frontend/src/app/util/text/clipboard.cljs b/frontend/src/app/util/text/clipboard.cljs new file mode 100644 index 0000000000..f8368aa34a --- /dev/null +++ b/frontend/src/app/util/text/clipboard.cljs @@ -0,0 +1,397 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.util.text.clipboard + "Reads clipboard data into paste fragments: `[{:attrs {} :children [{:text :attrs}]}]`, + whose run attrs override the caret style; Penpot text also carries a hidden payload." + (:require + [app.common.data :as d] + [app.common.exceptions :as ex] + [app.common.fonts :as cfnt] + [app.common.schema :as sm] + [app.common.transit :as t] + [app.common.types.shape.text :as cts] + [app.common.types.text :as txt] + [app.common.types.typography :as ctt] + [cuerdas.core :as str])) + +(def ^:private block-tags + #{"ADDRESS" "ARTICLE" "ASIDE" "BLOCKQUOTE" "CAPTION" "DD" "DETAILS" "DIV" "DL" + "DT" "FIELDSET" "FIGCAPTION" "FIGURE" "FOOTER" "FORM" "H1" "H2" "H3" "H4" + "H5" "H6" "HEADER" "HR" "LI" "MAIN" "NAV" "OL" "P" "PRE" "SECTION" + "SUMMARY" "TABLE" "TBODY" "TFOOT" "THEAD" "TR" "UL"}) + +;; Elements whose text is read; any other element is skipped with its children. +;; `O:P` is Word's paragraph filler, which marks its empty lines. +(def ^:private allowed-tags + (into block-tags + #{"A" "ABBR" "B" "BDI" "BDO" "BIG" "BR" "CENTER" "CITE" "CODE" "DATA" + "DEL" "DFN" "EM" "FONT" "I" "INS" "KBD" "LABEL" "MARK" "O:P" "Q" "S" + "SAMP" "SMALL" "SPAN" "STRIKE" "STRONG" "SUB" "SUP" "TD" "TH" "TIME" + "TT" "U" "VAR" "WBR"})) + +(def ^:private bold-tags #{"B" "STRONG" "TH" "H1" "H2" "H3" "H4" "H5" "H6"}) +(def ^:private italic-tags #{"I" "EM" "CITE" "VAR" "DFN"}) +(def ^:private underline-tags #{"U" "INS"}) +(def ^:private strike-tags #{"S" "STRIKE" "DEL"}) +(def ^:private cell-tags #{"TD" "TH"}) + +(defn- style-value + [^js element property] + (some-> (.-style element) (.getPropertyValue property) str/lower)) + +(defn- parse-bold + "True/false for a CSS font-weight, nil when it says nothing." + [weight] + (cond + (empty? weight) nil + (#{"bold" "bolder"} weight) true + (#{"normal" "lighter"} weight) false + :else (let [n (js/parseInt weight 10)] + (when-not (js/isNaN n) (>= n 600))))) + +(defn- parse-italic + [font-style] + (cond + (empty? font-style) nil + (or (= font-style "italic") (str/starts-with? font-style "oblique")) true + (= font-style "normal") false + :else nil)) + +(defn- display-block? + [display] + (some #(str/starts-with? display %) ["block" "list-item" "table" "flex" "grid"])) + +(defn- element-state + "The emphasis state inside `element`, or nil when hidden. Inline styles win over + tags, so Google Docs' `` wrapper is not bold." + [state ^js element tag] + (let [display (or (style-value element "display") "") + weight (parse-bold (style-value element "font-weight")) + italic (parse-italic (style-value element "font-style")) + decoration (str (style-value element "text-decoration-line") " " + (style-value element "text-decoration")) + transform (style-value element "text-transform") + space (style-value element "white-space")] + (when-not (= display "none") + (cond-> state + (contains? bold-tags tag) (assoc :bold? true) + (some? weight) (assoc :bold? weight) + (contains? italic-tags tag) (assoc :italic? true) + (some? italic) (assoc :italic? italic) + (contains? underline-tags tag) (assoc :underline? true) + (str/includes? decoration "underline") (assoc :underline? true) + (contains? strike-tags tag) (assoc :strike? true) + (str/includes? decoration "line-through") (assoc :strike? true) + (= tag "A") (assoc :link? true) + (= tag "PRE") (assoc :pre? true) + (seq space) (assoc :pre? (str/starts-with? space "pre")) + (= transform "none") (dissoc :transform) + (#{"uppercase" "lowercase" "capitalize"} transform) (assoc :transform transform))))) + +(defn- block-element? + [^js element tag] + (let [display (or (style-value element "display") "")] + (if (seq display) + (display-block? display) + (contains? block-tags tag)))) + +(defn- state->attrs + "Penpot text attrs for an emphasis state, with every attr set so the source + replaces the caret's emphasis. Link underlines go with the link." + [{:keys [bold? italic? underline? strike? link? transform]}] + (let [underline? (and underline? (not link?))] + {:font-weight (if bold? "700" "400") + :font-style (if italic? "italic" "normal") + :text-decoration (cond underline? "underline" strike? "line-through" :else "none") + :text-transform (or transform "none")})) + +;; --- Fragment builder +;; +;; `:runs` is the paragraph being built; `:space?` is true when the next +;; collapsible space would be dropped (paragraph start or after a space). + +(def ^:private empty-builder + {:paragraphs [] :runs [] :space? true}) + +(defn- trim-trailing-space + [runs] + (let [idx (dec (count runs)) + run (get runs idx)] + (if (and run (not (:pre? run))) + (update runs idx update :text str/rtrim " ") + runs))) + +(defn- close-paragraph + [{:keys [runs] :as builder}] + (-> builder + (update :paragraphs conj (trim-trailing-space runs)) + (assoc :runs [] :space? true))) + +(defn- soft-break + "Ends the paragraph at a block boundary, unless nothing was written yet." + [{:keys [runs] :as builder}] + (if (some #(seq (:text %)) runs) + (close-paragraph builder) + (assoc builder :runs []))) + +(defn- add-run + [builder text attrs pre?] + (update builder :runs conj {:text text :attrs attrs :pre? pre?})) + +(defn- add-collapsible-text + [{:keys [space?] :as builder} text attrs] + (let [text (cond-> (str/replace text #"[ \t\n\r\f]+" " ") + space? (str/ltrim " "))] + (if (empty? text) + builder + (-> builder + (add-run text attrs false) + (assoc :space? (str/ends-with? text " ")))))) + +(defn- add-pre-text + [builder text attrs] + (let [lines (.split (str/replace text "\r" "") "\n")] + (reduce (fn [builder [idx line]] + (cond-> builder + (pos? idx) (close-paragraph) + (seq line) (add-run line attrs true) + :always (assoc :space? false))) + builder + (map-indexed vector lines)))) + +(defn- add-text + [builder text state] + (let [attrs (state->attrs state)] + (if (:pre? state) + (add-pre-text builder text attrs) + (add-collapsible-text builder text attrs)))) + +(defn- walk-node + [builder ^js node state] + (case (.-nodeType node) + 3 (add-text builder (.-nodeValue node) state) + 1 (let [tag (str/upper (.-tagName node))] + (if-not (contains? allowed-tags tag) + builder + (if-let [state (element-state state node tag)] + (if (= tag "BR") + (close-paragraph builder) + (let [block? (block-element? node tag) + ;; Cells of a row are joined by a tab. + cell? (and (contains? cell-tags tag) + (some? (.-previousElementSibling node))) + builder (cond-> builder + block? (soft-break) + cell? (-> (add-run "\t" (state->attrs state) true) + (assoc :space? true))) + builder (reduce #(walk-node %1 %2 state) + builder + (array-seq (.-childNodes node)))] + (cond-> builder block? (soft-break)))) + builder))) + builder)) + +;; --- Normalizing + +(defn- merge-runs + "Joins neighbouring runs that share attrs and drops empty ones." + [runs] + (reduce (fn [acc {:keys [text attrs]}] + (let [prev (peek acc)] + (cond + (empty? text) acc + (= (:attrs prev) attrs) (conj (pop acc) (update prev :text str text)) + :else (conj acc {:text text :attrs attrs})))) + [] + runs)) + +(defn- runs->paragraph + [runs] + (let [runs (->> runs + (map #(update % :text str/replace " " " ")) + (merge-runs))] + {:attrs {} + :children (if (every? #(str/blank? (:text %)) runs) [] runs)})) + +(defn- empty-paragraph? + [paragraph] + (empty? (:children paragraph))) + +(defn- normalize-paragraphs + "Drops empty paragraphs at both ends and keeps at most one in a row." + [paragraphs] + (let [paragraphs (->> paragraphs + (drop-while empty-paragraph?) + (reverse) + (drop-while empty-paragraph?) + (reverse))] + (->> paragraphs + (partition-by empty-paragraph?) + (mapcat #(if (empty-paragraph? (first %)) [(first %)] %)) + (vec)))) + +(defn document->fragment + "The paste fragment for a parsed HTML document, or nil when it has no text." + [^js document] + (let [paragraphs (->> (array-seq (.-childNodes (.-body document))) + (reduce #(walk-node %1 %2 {}) empty-builder) + (soft-break) + :paragraphs + (map runs->paragraph) + (normalize-paragraphs))] + (when (seq paragraphs) + paragraphs))) + +(defn html->fragment + [html] + (-> (js/DOMParser.) + (.parseFromString html "text/html") + (document->fragment))) + +(defn without-overrides + "`fragment` as one plain run per paragraph, so it takes the style it is pasted + into. External sources keep only their text and paragraphs." + [fragment] + (mapv (fn [{:keys [children] :as paragraph}] + (assoc paragraph :children + (if (seq children) + [{:text (str/join (map :text children)) :attrs {}}] + []))) + fragment)) + +(defn text->fragment + "The paste fragment for plain text, one paragraph per line, or nil when empty." + [text] + (when (seq text) + (mapv (fn [line] + {:attrs {} + :children (if (empty? line) [] [{:text line :attrs {}}])}) + (.split (str/replace text "\r" "") "\n")))) + +;; --- Penpot payload +;; +;; Text copied in Penpot keeps its content in the `data-penpot-text` attr of an +;; empty span in the HTML: the one hidden marker every browser keeps. + +(def ^:private payload-re #"data-penpot-text=\"([A-Za-z0-9+/=]*)\"") + +;; Bigger HTML is pasted as plain text rather than read. +(def max-paste-html-length 1000000) + +(def ^:private schema:origin + [:map {:closed true} + [:x ::sm/safe-number] + [:y ::sm/safe-number] + [:box [:map {:closed true} + [:width ::sm/safe-number] + [:height ::sm/safe-number]]]]) + +(def ^:private valid-geometry? + (sm/lazy-validator + [:map {:closed true} + [:origins [:vector [:vector [:maybe schema:origin]]]]])) + +(defn valid-payload? + "True when the `content` and `geometry` of a payload, which other apps or plugins + may have written, are safe to paste into a text shape." + [{:keys [content geometry]}] + (and (cts/valid-content? content) + (or (nil? geometry) (valid-geometry? geometry)))) + +(defn- encode-base64 + [s] + (->> (.encode (js/TextEncoder.) s) + (.from js/Array) + (map #(.fromCharCode js/String %)) + (str/join) + (js/btoa))) + +(defn- decode-base64 + [s] + (->> (.from js/Uint8Array (js/atob s) #(.charCodeAt % 0)) + (.decode (js/TextDecoder.)))) + +(defn payload->html + "Hidden markup carrying `content`, copied from `file-id` in `team-id`, and the + `geometry` of its spans in their text box (see `text-editor/content-geometry`)." + ([content file-id team-id] + (payload->html content file-id team-id nil)) + ([content file-id team-id geometry] + (let [payload (cond-> {:type :copied-text + :version 1 + :file-id file-id + :team-id team-id + :content content} + (some? geometry) (assoc :geometry geometry))] + (str "")))) + +(defn html->payload + "The Penpot payload in `html`, or nil when it has none." + [html] + (when-let [[_ encoded] (when (< (count html) max-paste-html-length) + (some->> html (re-find payload-re)))] + (let [{:keys [type file-id team-id] :as payload} + (ex/ignoring (t/decode-str (decode-base64 encoded)))] + (when (and (map? payload) + (= :copied-text type) + (uuid? file-id) + (or (nil? team-id) (uuid? team-id)) + (valid-payload? payload)) + payload)))) + +;; --- Pasting into another file or team + +(def ^:private default-font + (d/seek #(= (:id %) (:font-id txt/default-text-attrs)) cfnt/local-fonts)) + +(defn- replace-custom-font + "`node` in the default font, at the closest weight and style it has." + [{:keys [font-weight font-style] :as node}] + (let [variant (or (cfnt/closest-variant (:variants default-font) (str font-weight) font-style) + (d/seek #(= (:id %) (:font-variant-id txt/default-text-attrs)) (:variants default-font)))] + (-> node + (assoc :font-id (:id default-font) + :font-family (:family default-font) + :font-variant-id (:id variant) + :font-weight (:weight variant) + :font-style (:style variant)) + (ctt/remove-typography-from-node)))) + +(defn replace-custom-fonts + "`content` with every custom font replaced by the default one, for pasting it + into another team, where custom fonts have other ids." + [content] + (txt/transform-nodes #(= :custom (cfnt/font-id->backend (:font-id %))) + replace-custom-font + content)) + +(defn- drop-image-fills + "`node` without image fills, or with the default fill when it had only those." + [{:keys [fills] :as node}] + (if (some :fill-image fills) + (let [fills (into [] (remove :fill-image) fills)] + (assoc node :fills (if (seq fills) fills (txt/get-default-text-fills)))) + node)) + +(defn clean-content + "`content` without what the paste target cannot reach: typographies outside + `valid-file-ids`, custom fonts from another team, images from another file." + [content {:keys [valid-file-ids same-team? same-file?]}] + (txt/transform-nodes + (fn [node] + (cond-> node + (and (some? (:typography-ref-file node)) + (not (contains? valid-file-ids (:typography-ref-file node)))) + (ctt/remove-typography-from-node) + + (and (not same-team?) + (= :custom (cfnt/font-id->backend (:font-id node)))) + (replace-custom-font) + + (not same-file?) + (drop-image-fills))) + content)) diff --git a/frontend/src/app/util/theme.cljs b/frontend/src/app/util/theme.cljs index 75c0837d40..b5c0437a6e 100644 --- a/frontend/src/app/util/theme.cljs +++ b/frontend/src/app/util/theme.cljs @@ -17,7 +17,7 @@ (defonce ^:private color-scheme-listeners* (atom #{})) -(def ^:const default "dark") +(def ^:const default "system") (defn get-system-theme [] @@ -47,16 +47,17 @@ (defn resolve-theme "Resolves the profile's theme setting to the effective UI theme ('dark' or - 'light'): 'system' follows the given system theme, 'default' and an unset - theme mean dark, and any other value is taken as-is. Single source of truth - for the app's own theme and the theme reported to plugins." + 'light'). An unset preference or 'system' follows the system theme; + the legacy 'default' preference means dark. Used by the app and plugins." [profile-theme system-theme] - (cond - (= profile-theme "system") system-theme - (= profile-theme "default") "dark" - :else (d/nilv profile-theme "dark"))) + (let [preference (d/nilv profile-theme default)] + (case preference + "system" system-theme + "default" "dark" + preference))) -(defn use-initialize +(defn use-theme + "Return the effective theme and follow system preference changes." [{profile-theme :theme}] (let [system-theme* (mf/use-state get-system-theme) system-theme (deref system-theme*)] @@ -68,6 +69,11 @@ (fn [] (rx/dispose! s)))) - (mf/with-effect [system-theme profile-theme] - (set-color-scheme (resolve-theme profile-theme system-theme)) + (resolve-theme profile-theme system-theme))) + +(defn use-initialize + [profile] + (let [color (use-theme profile)] + (mf/with-effect [color] + (set-color-scheme color) (notify-color-scheme-listeners!)))) diff --git a/frontend/src/app/worker.cljs b/frontend/src/app/worker.cljs index da6f0c8a95..5df69ac365 100644 --- a/frontend/src/app/worker.cljs +++ b/frontend/src/app/worker.cljs @@ -11,8 +11,8 @@ [app.common.schema :as sm] [app.common.types.objects-map] [app.util.object :as obj] + [app.worker.exports] [app.worker.impl :as impl] - [app.worker.import] [app.worker.index] [app.worker.messages :as wm] [app.worker.thumbnails] diff --git a/frontend/src/app/worker/exports.cljs b/frontend/src/app/worker/exports.cljs new file mode 100644 index 0000000000..fa83b4ef29 --- /dev/null +++ b/frontend/src/app/worker/exports.cljs @@ -0,0 +1,260 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.worker.exports + "Local WASM export (single file, ZIP, multi-page PDF) on the worker module. + + Runs through `app.worker.wasm/submit` so it never interleaves with + thumbnail WASM jobs on the same module." + (:require + [app.common.logging :as log] + [app.common.media :as media] + [app.common.render-wasm.wasm :as wasm] + [app.common.types.color :as cc] + [app.common.uuid :as uuid] + [app.main.data.exports.wasm :as wasm.exports] + [app.render-wasm.api :as wasm.api] + [app.util.webapi :as wapi] + [app.util.zip :as uz] + [app.worker.impl :as impl] + [app.worker.wasm :as wwasm] + [beicon.v2.core :as rx] + [promesa.core :as p])) + +(log/set-level! :info) + +(def ^:private ^:const canvas-size 100) + +(defn- export-cancelled + [] + (ex-info "export cancelled" {:type :export-cancelled})) + +(defn- export-mtype + [type] + (case type + :pdf "application/pdf" + (media/format->mtype type))) + +(defn- build-single + "One shape → one downloadable blob (PNG/JPEG/WebP/SVG/PDF)." + [exports job-id on-progress] + (let [export (first exports)] + (when-not export + (throw (ex-info "no export entry" + {:type :validation + :code :empty-export}))) + (when (wwasm/cancelled? job-id) + (throw (export-cancelled))) + (let [bytes (wasm.exports/export-bytes export)] + (when-not bytes + (throw (ex-info "wasm export returned no bytes" + {:type :internal + :code :wasm-export-empty + :object-id (:object-id export)}))) + (on-progress 1 1) + (p/resolved (wapi/create-blob bytes (export-mtype (:type export))))))) + +(defn- build-zip + [exports job-id on-progress] + (let [writer (uz/writer (uz/blob-writer :mtype "application/zip")) + used (volatile! #{}) + entries + (mapv (fn [export] + (let [filename (wasm.exports/sanitize-filename export @used)] + (vswap! used conj filename) + [filename export])) + exports) + total (count entries)] + (->> (p/reduce + (fn [writer [index [filename export]]] + (when (wwasm/cancelled? job-id) + (throw (export-cancelled))) + (let [bytes (wasm.exports/export-bytes export)] + (when-not bytes + (throw (ex-info "wasm export returned no bytes" + {:type :internal + :code :wasm-export-empty + :object-id (:object-id export)}))) + (->> (uz/add writer filename bytes) + (p/fmap (fn [_] + (on-progress (inc index) total) + writer))))) + writer + (map-indexed vector entries)) + (p/mcat (fn [writer] + (uz/close writer))) + (p/merr (fn [cause] + (try (uz/close writer) (catch :default _)) + (p/rejected cause)))))) + +(defn- build-frames-pdf + [exports job-id on-progress] + (let [ids (mapv :object-id exports) + total (count ids)] + (wasm.api/pdf-export-begin!) + (->> (p/reduce + (fn [_ [index id]] + (when (wwasm/cancelled? job-id) + (throw (export-cancelled))) + (wasm.api/pdf-export-add-page! id) + (on-progress (inc index) total) + ;; Yield so a cancel that arrived during add-page can stop + ;; before the next page (nil would run every page in one turn). + (p/resolved nil)) + nil + (map-indexed vector ids)) + (p/mcat + (fn [_] + (let [bytes (wasm.api/pdf-export-finish)] + (when-not bytes + (throw (ex-info "wasm pdf export returned no bytes" + {:type :internal + :code :wasm-export-empty}))) + (p/resolved (wapi/create-blob bytes "application/pdf"))))) + (p/merr + (fn [cause] + (wasm.api/pdf-export-abort!) + (p/rejected cause)))))) + +(defn- export-entry + "Keeps only the fields the worker needs to render and name files." + [{:keys [object-id type scale name suffix]}] + {:object-id object-id + :type type + :scale (or scale 1) + :name name + :suffix suffix}) + +(defn- run-local-export + [{:keys [job-id kind exports objects background] export-name :name}] + (let [exports (mapv export-entry exports)] + (->> (rx/from @wwasm/module) + (rx/mapcat + (fn [_] + (rx/create + (fn [subs] + (let [done? (volatile! false) + ;; Never `rx/error!` here: the main `ask-many!` stream would + ;; hang because worker error replies used to drop `:error` + ;; and never send `completed`. Emit a terminal message instead. + finish! + (fn [result cause] + (when-not @done? + (vreset! done? true) + (try (wasm.api/clear-canvas) (catch :default _)) + (wwasm/clear-cancel job-id) + (cond + (or (wwasm/cancelled? job-id) + (= :export-cancelled (:type (ex-data cause)))) + (do + (rx/push! subs {:type :cancelled}) + (rx/end! subs)) + + cause + (do + (rx/push! subs {:type :error + :hint (ex-message cause) + :data (ex-data cause)}) + (rx/end! subs)) + + :else + (let [entry (first exports) + filename (case kind + :pdf-frames + (str (or export-name "export") ".pdf") + + :single + (wasm.exports/sanitize-filename entry #{}) + + (str (or export-name "export") ".zip")) + mtype (case kind + :pdf-frames "application/pdf" + :single (export-mtype (:type entry)) + "application/zip")] + (rx/push! subs {:type :done + :filename filename + :mtype mtype + :blob result}) + (rx/end! subs))))) + + on-progress + (fn [done total] + (rx/push! subs {:type :progress + :done done + :total total})) + + start-export! + (fn [] + ;; `:on-render` runs after process-pending (images + + ;; fonts). `:on-shapes-ready` fires earlier and would + ;; export empty image fills / missing typefaces. + (when (wwasm/cancelled? job-id) + (finish! nil (export-cancelled))) + (when-not @done? + (try + (let [work (case kind + :pdf-frames + (build-frames-pdf exports job-id on-progress) + + :single + (build-single exports job-id on-progress) + + (build-zip exports job-id on-progress))] + (->> work + (p/fnly (fn [blob cause] + (finish! blob cause))))) + (catch :default cause + (finish! nil cause))))) + + canvas (js/OffscreenCanvas. canvas-size canvas-size) + init? (wasm.api/init-canvas-context canvas)] + (cond + (not init?) + (finish! nil (ex-info "failed to init worker wasm canvas" + {:type :internal + :code :wasm-canvas-init-failed})) + + (not (wasm/live?)) + (finish! nil (ex-info "worker wasm context is not live" + {:type :internal + :code :wasm-not-live})) + + (empty? objects) + (finish! nil (ex-info "no shapes to export" + {:type :validation + :code :empty-export-objects})) + + (wwasm/cancelled? job-id) + (finish! nil (export-cancelled)) + + :else + (try + (let [bgcolor (or background cc/canvas) + vbox {:x 0 :y 0 :width canvas-size :height canvas-size}] + (reset! wasm/disable-request-render? true) + (wasm.api/initialize-viewport + objects 1 vbox + :background bgcolor + :force-sync true + :on-render start-export!)) + (catch :default cause + (finish! nil cause)))) + (fn [] + (wwasm/cancel-job job-id) + (try (wasm.api/pdf-export-abort!) (catch :default _)) + (try (wasm.api/clear-canvas) (catch :default _))))))))))) + +(defmethod impl/handler :exports/run-local + [message _] + (let [job-id (or (:job-id message) (uuid/next)) + msg (assoc message :job-id job-id)] + (wwasm/submit #(run-local-export msg)))) + +(defmethod impl/handler :exports/cancel + [{:keys [job-id]} _] + (when job-id + (wwasm/cancel-job job-id)) + nil) diff --git a/frontend/src/app/worker/import.cljs b/frontend/src/app/worker/import.cljs deleted file mode 100644 index e8310efa89..0000000000 --- a/frontend/src/app/worker/import.cljs +++ /dev/null @@ -1,253 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; Copyright (c) KALEIDOS SUBSIDIARY SL - -(ns app.worker.import - (:refer-clojure :exclude [resolve]) - (:require - [app.common.exceptions :as ex] - [app.common.json :as json] - [app.common.logging :as log] - [app.common.schema :as sm] - [app.common.uuid :as uuid] - [app.main.data.uploads :as uploads] - [app.main.repo :as rp] - [app.util.http :as http] - [app.util.i18n :as i18n :refer [tr]] - [app.util.sse :as sse] - [app.util.zip :as uz] - [app.worker.impl :as impl] - [beicon.v2.core :as rx] - [cuerdas.core :as str])) - -(log/set-level! :warn) - -(defn- import-cause-message - "Prefer the server `:hint` (full text, e.g. SSE error payload), then `:explain` - when present; avoid the generic `stream exception` wrapper when a payload exists." - [cause default-msg] - (let [data (ex-data cause) - hint (some-> data :hint str/trim) - explain (some-> data :explain str/trim)] - (cond - (not (str/blank? hint)) hint - (not (str/blank? explain)) explain - :else - (let [msg (some-> (ex-message cause) str/trim)] - (if (or (str/blank? msg) (= msg "stream exception")) - default-msg - msg))))) - -;; Upload changes batches size -(def ^:const change-batch-size 100) - -(def conjv (fnil conj [])) - -(defn read-zip-manifest - [zip-reader] - (->> (rx/from (uz/get-entry zip-reader "manifest.json")) - (rx/mapcat (fn [entry] - (if (nil? entry) - (rx/throw (ex/error :type :validation - :code :invalid-penpot-file - :hint "Not a valid Penpot file: manifest.json is missing")) - (uz/read-as-text entry)))) - (rx/map json/decode))) - -(defn slurp-uri - ([uri] (slurp-uri uri :text)) - ([uri response-type] - (->> (http/send! - {:uri uri - :response-type response-type - :method :get}) - (rx/map :body)))) - -(defn parse-mtype [ba] - (let [u8 (js/Uint8Array. ba 0 4) - sg (areduce u8 i ret "" (str ret (if (zero? i) "" " ") (.toString (aget u8 i) 8)))] - (case sg - "120 113 3 4" "application/zip" - "1 13 32 206" "application/octet-stream" - "other"))) - -;; NOTE: this is a limited subset schema for the manifest file of -;; binfile-v3 format; is used for partially parse it and read the -;; files referenced inside the exported file - -(def ^:private schema:manifest - [:map {:title "Manifest"} - [:type :string] - [:files - [:vector - [:map - [:id ::sm/uuid] - [:name :string]]]]]) - -(def ^:private decode-manifest - (sm/decoder schema:manifest sm/json-transformer)) - -(defn analyze-file - [{:keys [uri] :as file}] - (let [stream (->> (slurp-uri uri :buffer) - (rx/merge-map - (fn [body] - (let [mtype (parse-mtype body)] - (cond - (= "application/zip" mtype) - (let [zip-reader (uz/reader body)] - (->> (read-zip-manifest zip-reader) - (rx/map - (fn [manifest] - (if (= (:type manifest) "penpot/export-files") - (let [manifest (decode-manifest manifest)] - (assoc file :type :binfile-v3 :files (:files manifest))) - (assoc file :type :unknown)))) - (rx/finalize (partial uz/close zip-reader)))) - - (= "application/octet-stream" mtype) - (rx/of (assoc file :type :binfile-v1)) - - :else - (rx/of (assoc file :type :unknown)))))) - - (rx/share))] - - (->> (rx/merge - (->> stream - (rx/filter (fn [entry] (= :binfile-v1 (:type entry)))) - (rx/map (fn [entry] - (let [file-id (uuid/next)] - (-> entry - (assoc :file-id file-id) - (assoc :name (:name file)) - (assoc :status :success)))))) - - (->> stream - (rx/filter (fn [entry] (= :binfile-v3 (:type entry)))) - (rx/merge-map (fn [{:keys [files] :as entry}] - (->> (rx/from files) - (rx/map (fn [file] - (-> entry - (dissoc :files) - (assoc :name (:name file)) - (assoc :file-id (:id file)) - (assoc :status :success)))))))) - - (->> stream - (rx/filter (fn [data] (= :unknown (:type data)))) - (rx/map (fn [_] - {:uri (:uri file) - :status :error - :error (tr "dashboard.import.analyze-error")})))) - - (rx/catch (fn [cause] - (let [error (import-cause-message cause (tr "dashboard.import.analyze-error"))] - (rx/of (assoc file :error error :status :error)))))))) - -(defmethod impl/handler :analyze-import - [{:keys [files]}] - (->> (rx/from files) - (rx/merge-map analyze-file))) - -(defn- import-blob-via-upload - "Fetches `uri` as a Blob, uploads it using the generic chunked-upload - session API and calls `import-binfile` with the resulting upload-id. - Returns an observable of SSE events from the import stream." - [uri {:keys [name version project-id]}] - (->> (slurp-uri uri :blob) - (rx/mapcat - (fn [blob] - (->> (uploads/upload-blob-chunked blob) - (rx/mapcat - (fn [{:keys [session-id]}] - (rp/cmd! ::sse/import-binfile - {:name name - :upload-id session-id - :version version - :project-id project-id})))))))) - -(defmethod impl/handler :import-files - [{:keys [project-id files]}] - (let [binfile-v1 (filter #(= :binfile-v1 (:type %)) files) - binfile-v3 (filter #(= :binfile-v3 (:type %)) files) - resolutions (volatile! {})] - - (rx/merge - (->> (rx/from binfile-v1) - (rx/merge-map - (fn [data] - (->> (import-blob-via-upload (:uri data) - {:name (str/replace (:name data) #".penpot$" "") - :version 1 - :project-id project-id}) - (rx/tap (fn [event] - (let [payload (sse/get-payload event) - type (sse/get-type event)] - (if (= type "progress") - (log/dbg :hint "import-binfile: progress" - :section (:section payload) - :name (:name payload)) - (log/dbg :hint "import-binfile: end"))))) - (rx/filter sse/end-of-stream?) - (rx/map (fn [_] - {:status :finish - :file-id (:file-id data)})) - (rx/catch - (fn [cause] - (log/error :hint "unexpected error on import process" - :project-id project-id - :cause cause) - (rx/of {:status :error - :error (import-cause-message cause (tr "labels.error")) - :file-id (:file-id data)}))))))) - - - (rx/concat - (->> (rx/from binfile-v3) - (rx/reduce (fn [result file] - (update result (:uri file) (fnil conj []) file)) - {}) - (rx/mapcat identity) - (rx/merge-map - (fn [[uri entries]] - (->> (import-blob-via-upload uri - {:name (-> entries first :name) - :version 3 - :project-id project-id}) - (rx/tap (fn [event] - (let [payload (sse/get-payload event) - type (sse/get-type event)] - (cond - (= type "progress") - (log/dbg :hint "import-binfile: progress" - :section (:section payload) - :name (:name payload)) - - :else - (log/dbg :hint "import-binfile: end"))))) - (rx/filter sse/end-of-stream?) - (rx/mapcat (fn [message] - (let [{:keys [resolution]} (sse/get-payload message)] - (when (seq resolution) - (vswap! resolutions merge resolution)) - (->> (rx/from entries) - (rx/map (fn [entry] - {:status :finish - :file-id (:file-id entry)})))))) - (rx/catch (fn [cause] - (log/error :hint "import-binfile: unexpected error on importing" - :project-id project-id - ::log/sync? true - :cause cause) - (let [err (import-cause-message cause (tr "labels.error"))] - (->> (rx/from entries) - (rx/map (fn [entry] - {:status :error - :error err - :file-id (:file-id entry)})))))))))) - (->> (rx/defer #(rx/of @resolutions)) - (rx/map (fn [resolutions] - {:libraries-resolution resolutions}))))))) diff --git a/frontend/src/app/worker/index.cljs b/frontend/src/app/worker/index.cljs index 1117440563..910fa74fae 100644 --- a/frontend/src/app/worker/index.cljs +++ b/frontend/src/app/worker/index.cljs @@ -7,24 +7,62 @@ (ns app.worker.index "Page index management within the worker." (:require - [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.changes :as ch] [app.common.geom.rect :as grc] [app.common.logging :as log] [app.common.time :as ct] + [app.common.transit :as t] [app.worker.impl :as impl] [app.worker.selection :as selection] [app.worker.snap :as snap] + [app.worker.trim :as trim] [okulary.core :as l])) (log/set-level! :info) (defonce state (l/atom {:pages-index {}})) +;; Changes that edit what the indexes read. Replaying one on a trimmed page +;; needs the attributes it reads in `app.worker.trim`. +(def index-change-types + #{:add-obj :mod-obj :del-obj :fix-obj :mov-objects :reg-objects + :reorder-children :set-guide :del-page}) + +;; Changes that leave the indexes alone. +(def skipped-change-types + #{:set-comment-thread-position :set-flow :set-default-grid + :add-page :mod-page :mov-page :set-plugin-data + :add-color :mod-color :del-color + :add-media :mod-media :del-media + :add-component :mod-component :del-component :restore-component :purge-component + :add-typography :mod-typography :del-typography + :set-tokens-lib :set-token :set-token-set :set-token-theme :set-tokens-status + :set-own-tokens-status :rename-token-set-group :move-token-set + :move-token-set-group :set-base-font-size :set-tokens-source}) + +(defn- index-change? + [{:keys [type]}] + (cond + (contains? index-change-types type) + true + + (contains? skipped-change-types type) + false + + :else + (do (log/wrn :hint "change type unknown to the worker index, skipped" :type type) + false))) + +(defn- changed-object? + [old-object new-object] + (and (not (identical? old-object new-object)) + (not= old-object new-object))) + (defmethod impl/handler :index/initialize [{:keys [page] :as message}] - (let [tpoint (ct/tpoint-ms)] + (let [tpoint (ct/tpoint-ms) + page (trim/trim-page page)] (try (swap! state update :pages-index assoc (:id page) page) (swap! state update ::selection selection/add-page page) @@ -35,27 +73,48 @@ (log/dbg :hint "page indexed" :id (:id page) :elapsed elapsed ::log/sync? true)))) nil)) +(defmethod impl/handler :index/finalize + [{:keys [page-id]}] + (swap! state #(-> % + (update :pages-index dissoc page-id) + (update ::selection dissoc page-id) + (update ::snap dissoc page-id))) + (t/clear-uuid-cache) + (log/dbg :hint "page index dropped" :id page-id ::log/sync? true) + nil) + (defmethod impl/handler :index/update [{:keys [page-id changes] :as message}] (let [tpoint (ct/tpoint-ms)] (try - (let [old-page (dm/get-in @state [:pages-index page-id]) - new-page (-> state - (swap! ch/process-changes changes false) - (dm/get-in [:pages-index page-id])) + (if-let [old-page (dm/get-in @state [:pages-index page-id])] + (let [changes (filterv index-change? changes) + new-page (-> state + (swap! ch/process-changes changes false) + (dm/get-in [:pages-index page-id]))] + (if (nil? new-page) + ;; The changes deleted the page + (swap! state #(-> % + (update ::snap dissoc page-id) + (update ::selection dissoc page-id))) - text-rects (dm/get-in @state [::text-rect page-id]) + (let [old-objects (:objects old-page) + ids (ch/get-candidate-object-ids old-objects (:objects new-page) page-id changes) + new-page (update new-page :objects #(trim/retrim-objects old-objects % ids)) + new-objects (:objects new-page) + ids (into #{} + (filter #(changed-object? (get old-objects %) (get new-objects %))) + ids) + guides? (not (identical? (:guides old-page) (:guides new-page)))] + (swap! state #(cond-> (assoc-in % [:pages-index page-id] new-page) + (or (seq ids) guides?) + (update ::snap snap/update-page old-page new-page ids) - ;; Update page objects with the text data - new-page - (reduce-kv - (fn [page id data] - (update-in page [:objects id] d/patch-object data)) - new-page - text-rects)] + (seq ids) + (update ::selection selection/update-page old-page new-page ids)))))) - (swap! state update ::snap snap/update-page old-page new-page) - (swap! state update ::selection selection/update-page old-page new-page)) + ;; A later `:index/initialize` sends the whole page again + (log/dbg :hint "page not indexed, skipping update" :id page-id ::log/sync? true)) (catch :default cause (log/error :hint "error updating page index" :id page-id :cause cause)) (finally diff --git a/frontend/src/app/worker/messages.cljs b/frontend/src/app/worker/messages.cljs index 2bb1c6a859..ef06f9e102 100644 --- a/frontend/src/app/worker/messages.cljs +++ b/frontend/src/app/worker/messages.cljs @@ -11,26 +11,56 @@ [app.common.transit :as t] [app.util.object :as obj])) -(defn encode [{:keys [sender-id reply-to payload buffer?] :as message}] - #js {:cmd (d/name (:cmd payload)) - :senderId (when sender-id (str sender-id)) - :replyTo (when reply-to (str reply-to)) - :payload (if (= :initialize-indices (:cmd payload)) - (:file-raw payload) - (when (some? payload) (t/encode-str payload))) - :buffer (when (some? buffer?) buffer?)}) +(defn encode + "Serializes a worker message for `postMessage`. + + Transit cannot carry `js/Blob`. If the payload map has `:blob`, or the + message already has a top-level `:blob`, that value is sent as a + structured-clone field beside the Transit string — not inside it." + [{:keys [sender-id reply-to payload buffer? blob completed error]}] + (let [;; Peel `:blob` out of map payloads before Transit (selection/snap + ;; replies are vectors and other non-maps; leave them untouched). + inline-blob (when (map? payload) (:blob payload)) + blob (or blob inline-blob) + payload (cond-> payload + (some? inline-blob) + (dissoc :blob)) + obj #js {:cmd (d/name (:cmd payload)) + :senderId (when sender-id (str sender-id)) + :replyTo (when reply-to (str reply-to)) + :payload (when (some? payload) (t/encode-str payload)) + :buffer (when (some? buffer?) buffer?) + :completed (when completed true)}] + (when (some? blob) + (obj/set! obj "blob" blob)) + (when (some? error) + (obj/set! obj "error" (clj->js error))) + obj)) + +(defn- js->error-map + [^js err] + (when err + (let [m (js->clj err :keywordize-keys true)] + (cond-> m + (string? (:data m)) + (assoc :data nil))))) (defn decode [^js data] - (let [cmd (obj/get data "cmd") - sender-id (obj/get data "senderId") + (let [sender-id (obj/get data "senderId") reply-to (obj/get data "replyTo") payload (obj/get data "payload") - buffer (obj/get data "buffer")] - (d/without-nils - {:sender-id (when sender-id (uuid sender-id)) - :reply-to (when reply-to (uuid reply-to)) - :payload (if (= cmd "initialize-indices") - {:cmd :initialize-indices - :file-raw payload} - (when (some? payload) (t/decode-str payload))) - :buffer? buffer}))) + buffer (obj/get data "buffer") + blob (obj/get data "blob") + completed (obj/get data "completed") + error (obj/get data "error") + decoded + (d/without-nils + {:sender-id (when sender-id (uuid sender-id)) + :reply-to (when reply-to (uuid reply-to)) + :payload (when (some? payload) (t/decode-str payload)) + :buffer? buffer + :completed completed + :error (js->error-map error)})] + (cond-> decoded + (some? blob) + (update :payload assoc :blob blob)))) diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs index d5a5ee06a0..1775bf0773 100644 --- a/frontend/src/app/worker/selection.cljs +++ b/frontend/src/app/worker/selection.cljs @@ -16,8 +16,7 @@ [app.common.geom.shapes.text :as gst] [app.common.types.modifiers :as ctm] [app.common.uuid :as uuid] - [app.util.quadtree :as qdt] - [clojure.set :as set])) + [app.util.quadtree :as qdt])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; IMPL @@ -107,22 +106,13 @@ (dissoc objects uuid/zero))] {:index index :bounds bounds :parents-index parents-index :clip-index clip-index})) -;; FIXME: optimize (defn- update-index - [{index :index :as data} old-objects new-objects] - (let [object-changed? - (fn [id] - (not= (get old-objects id) - (get new-objects id))) - - changed-ids + [{index :index :as data} new-objects object-ids] + (let [changed-ids (into #{} - (comp (filter #(not= % uuid/zero)) - (filter object-changed?) + (comp (remove #(= % uuid/zero)) (mapcat #(into [%] (cfh/get-children-ids new-objects %)))) - - (set/union (set (keys old-objects)) - (set (keys new-objects)))) + object-ids) shapes (->> changed-ids @@ -272,13 +262,13 @@ (assoc state id (create-index objects))) (defn update-page - "Update page index on the state" - [state old-page new-page] + "Update page index on the state. `object-ids` are the objects that + changed between `old-page` and `new-page`." + [state old-page new-page object-ids] (let [page-id (get old-page :id)] (update state page-id (fn [index] - (let [old-objects (:objects old-page) - new-objects (:objects new-page) + (let [new-objects (:objects new-page) old-bounds (:bounds index) new-bounds (objects-bounds new-objects)] @@ -287,7 +277,7 @@ ;; re-create it. (if (and (some? index) (grc/contains-rect? old-bounds new-bounds)) - (update-index index old-objects new-objects) + (update-index index new-objects object-ids) (create-index new-objects))))))) (defn query diff --git a/frontend/src/app/worker/snap.cljs b/frontend/src/app/worker/snap.cljs index afc418b061..2065e78676 100644 --- a/frontend/src/app/worker/snap.cljs +++ b/frontend/src/app/worker/snap.cljs @@ -226,8 +226,9 @@ (assoc snap-data (:id page) page-data))) (defn update-page - "Updates a previously inserted page with new data" - [snap-data old-page page] + "Updates a previously inserted page with new data. Only the objects in + `object-ids` are checked for changes." + [snap-data old-page page object-ids] (if (contains? snap-data (:id page)) ;; Update page @@ -245,7 +246,7 @@ new-frames new-shapes new-guides]} - (diff/calculate-page-diff old-page page snap-attrs)] + (diff/calculate-page-diff old-page page snap-attrs object-ids)] (as-> page-data $ (reduce (partial update-shape objects) $ change-frame-shapes) diff --git a/frontend/src/app/worker/thumbnails.cljs b/frontend/src/app/worker/thumbnails.cljs index 48d35cb0c7..bed81a00e9 100644 --- a/frontend/src/app/worker/thumbnails.cljs +++ b/frontend/src/app/worker/thumbnails.cljs @@ -14,13 +14,13 @@ [app.common.render-wasm.wasm :as wasm] [app.common.types.color :as cc] [app.common.uri :as u] - [app.common.uuid :as uuid] [app.config :as cf] [app.main.fonts :as fonts] [app.main.render :as render] [app.render-wasm.api :as wasm.api] [app.util.http :as http] [app.worker.impl :as impl] + [app.worker.wasm :as wwasm] [beicon.v2.core :as rx] [okulary.core :as l] [promesa.core :as p] @@ -138,23 +138,12 @@ :height height :background background})))))) -(defonce ^:private wasm-module - (delay - (let [module (unchecked-get js/globalThis "WasmModule") - init-fn (unchecked-get module "default") - href (cf/resolve-href "js/render-wasm.wasm")] - (->> (init-fn #js {:locateFile (constantly href)}) - (p/fnly (fn [module cause] - (if cause - (js/console.error cause) - (set! wasm/internal-module module)))))))) - (defn- render-thumbnail-with-wasm - [{:keys [id file-id revn width] :as message}] - (->> (rx/from @wasm-module) + [{:keys [file-id revn width] :as _message}] + (->> (rx/from @wwasm/module) (rx/mapcat #(request-data-for-thumbnail file-id revn false)) (rx/mapcat - (fn [{:keys [page] :as file}] + (fn [{:keys [page] :as _file}] (rx/create (fn [subs] (let [bgcolor (or (:background page) cc/canvas) @@ -187,39 +176,13 @@ (if cause (rx/error! subs cause) (rx/push! subs - {:id id - :data data + {:data data :file-id file-id :revn revn})) (rx/end! subs))))))) (rx/error! subs (ex-info "unable to init wasm canvas" {:file-id file-id}))) nil))))))) -(defonce ^:private - thumbnails-queue - (rx/subject)) - -(defonce ^:private - thumbnails-stream - (->> thumbnails-queue - (rx/mapcat (fn [{:keys [id] :as message}] - (->> (render-thumbnail-with-wasm message) - (rx/catch (fn [cause] - (rx/of {:id id :error cause})))))) - (rx/share))) - (defmethod impl/handler :thumbnails/generate-for-file-wasm [message _] - (rx/create - (fn [subs] - (let [id (uuid/next) - sid (->> thumbnails-stream - (rx/filter #(= id (:id %))) - (rx/subs! - (fn [{:keys [error] :as result}] - (if error - (rx/error! subs error) - (do (rx/push! subs result) - (rx/end! subs))))))] - (rx/push! thumbnails-queue (assoc message :id id)) - #(rx/dispose! sid))))) + (wwasm/submit #(render-thumbnail-with-wasm message))) diff --git a/frontend/src/app/worker/trim.cljs b/frontend/src/app/worker/trim.cljs new file mode 100644 index 0000000000..8c1aecda5b --- /dev/null +++ b/frontend/src/app/worker/trim.cljs @@ -0,0 +1,103 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.worker.trim + "Keeps only the shape attributes the worker reads: its selection and + snap indexes, and the change replay of `process-changes`." + (:require + [app.common.transit :as t] + [app.common.types.objects-map :as omap] + [app.common.types.shape :as cts])) + +;; The attributes the worker keeps. Add one when the selection or snap index, +;; or a change type in `app.worker.index/index-change-types`, reads it. +(def ^:private shape-attrs + [:id :type :parent-id :frame-id :shapes + :x :y :width :height :rotation :selrect :points + :transform :transform-inverse :flip-x :flip-y + :hidden :blocked :show-content :masked-group + ;; The replay refuses to edit copies or to nest a component in itself. + :component-id :shape-ref + ;; The replay rebuilds bool paths from rounded rects. + :r1 :r2 :r3 :r4 + :stroke-width :bool-type :grids :layout]) + +(def ^:private stroke-attrs [:stroke-width :stroke-alignment]) + +(def ^:private position-data-attrs [:x :y :width :height]) + +;; Selection only asks whether a shape has fills. +(def ^:private some-fills [{}]) + +(defn- trim-svg-attrs + [svg-attrs] + (let [style (get svg-attrs :style)] + (cond-> {} + (contains? svg-attrs :fill) + (assoc :fill (get svg-attrs :fill)) + + (contains? style :fill) + (assoc :style {:fill (get style :fill)})))) + +(defn trim-shape + [shape] + (let [type (get shape :type) + fills (get shape :fills) + strokes (get shape :strokes) + svg-attrs (some-> (get shape :svg-attrs) trim-svg-attrs not-empty) + content (get shape :content) + pos-data (get shape :position-data)] + (cts/map->Shape + (cond-> (select-keys shape shape-attrs) + (seq fills) + (assoc :fills some-fills) + + (seq strokes) + (assoc :strokes (mapv #(select-keys % stroke-attrs) strokes)) + + (some? svg-attrs) + (assoc :svg-attrs svg-attrs) + + (and (some? content) (or (= type :path) (= type :bool))) + (assoc :content content) + + (and (some? pos-data) (= type :text)) + (assoc :position-data (mapv #(select-keys % position-data-attrs) pos-data)))))) + +(defn trim-objects + "Trims every object. An `ObjectsMap` is read from its encoded data, so + the decoded shapes are not kept in its cache." + [objects] + (if (omap/objects-map? objects) + (persistent! + (reduce-kv (fn [result id data] + (assoc! result id (trim-shape (t/decode-str data)))) + (transient {}) + (omap/get-data objects))) + (persistent! + (reduce-kv (fn [result id shape] + (assoc! result id (trim-shape shape))) + (transient {}) + objects)))) + +(defn retrim-objects + "Trims again the objects at `ids` that differ from `old-objects`: the + replay may have written attributes the worker does not keep." + [old-objects new-objects ids] + (reduce (fn [objects id] + (let [shape (get objects id)] + (cond-> objects + (and (some? shape) (not (identical? shape (get old-objects id)))) + (assoc id (trim-shape shape))))) + new-objects + ids)) + +(defn trim-page + [page] + (cond-> {:id (get page :id) + :objects (trim-objects (get page :objects))} + (contains? page :guides) + (assoc :guides (get page :guides)))) diff --git a/frontend/src/app/worker/wasm.cljs b/frontend/src/app/worker/wasm.cljs new file mode 100644 index 0000000000..eca6c47071 --- /dev/null +++ b/frontend/src/app/worker/wasm.cljs @@ -0,0 +1,87 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns app.worker.wasm + "Shared render-wasm loader and serial work queue for the web worker. + + Thumbnails and local multi-export share one WASM module instance. Jobs that + touch that module (initialize-viewport, render, clear-canvas) must run through + `submit` so they never interleave." + (:require + [app.common.render-wasm.wasm :as wasm] + [app.config :as cf] + [beicon.v2.core :as rx] + [promesa.core :as p])) + +;; Delay that resolves to the worker's WasmModule instance. +(defonce module + (delay + (let [module (unchecked-get js/globalThis "WasmModule") + init-fn (unchecked-get module "default") + href (cf/resolve-href "js/render-wasm.wasm")] + (->> (init-fn #js {:locateFile (constantly href)}) + (p/fnly (fn [module cause] + (if cause + (js/console.error "worker wasm module init failed" cause) + (set! wasm/internal-module module)))))))) + +(defonce ^:private work-queue + (rx/subject)) + +(defonce ^:private cancelled-jobs* + (atom #{})) + +(defonce ^:private executor-sub* + (atom nil)) + +(defn- ensure-executor! + "Starts the serial drain once. Must be reachable from `submit` so release + builds cannot DCE the subscription (a lone unused `defonce` is removed)." + [] + (when (nil? @executor-sub*) + (reset! executor-sub* + (->> work-queue + (rx/mapcat + (fn [{:keys [bus work]}] + (->> (work) + (rx/tap (fn [value] + (rx/push! bus value))) + (rx/catch (fn [cause] + (rx/error! bus cause) + (rx/empty))) + (rx/finalize + (fn [] + (rx/end! bus)))))) + (rx/subs! + (fn [_]) + (fn [cause] + (js/console.error "worker wasm queue error" cause))))))) + +(defn cancel-job + "Marks `job-id` so an in-flight export stops between items." + [job-id] + (swap! cancelled-jobs* conj job-id) + nil) + +(defn cancelled? + [job-id] + (contains? @cancelled-jobs* job-id)) + +(defn clear-cancel + [job-id] + (swap! cancelled-jobs* disj job-id)) + +(defn submit + "Runs `work` (zero-arg → observable) on the serial WASM queue. + Returns a subject that mirrors `work` emissions and errors. + + Pushes onto the queue immediately (does not wait for a subscriber) so the + job starts even if the caller is slow to subscribe." + [work] + (ensure-executor!) + (let [bus (rx/subject)] + (rx/push! work-queue {:bus bus :work work}) + bus)) diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index 7d71812c19..7a1abb89af 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -9,11 +9,14 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.exceptions :as ex] + [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] [app.common.files.repair :as cfr] [app.common.files.validate :as cfv] [app.common.json :as json] [app.common.logging :as l] + [app.common.logic.libraries :as cll] + [app.common.path-names :as cpn] [app.common.pprint :as pp] [app.common.render-wasm.helpers :as wasm.h] [app.common.render-wasm.mem :as wasm.mem] @@ -28,6 +31,7 @@ [app.main.data.common :as dcm] [app.main.data.dashboard.shortcuts] [app.main.data.helpers :as dsh] + [app.main.data.persistence :as dps] [app.main.data.preview :as dp] [app.main.data.viewer.shortcuts] [app.main.data.workspace :as dw] @@ -614,6 +618,419 @@ [id shape-ref] (st/emit! (set-shape-ref* id shape-ref))) +;; --- Save faults + +(def ^:private rejecting-faults + "Debug faults the backend rejects a commit for." + #{"validation" "runtime" "ex-info" "assertion"}) + +(def ^:private request-faults + "Debug faults that fail the whole save, as an infrastructure error does." + #{"sql" "io" "eof" "interrupted" "not-found" "stack-overflow"}) + +(defn- debug-plugin-data-change + "A change that removes plugin data `key` of the file, which is never set, + so it changes no data." + [key] + {:type :set-plugin-data + :object-type :file + :namespace :penpot-debug + :key key + :value nil}) + +(defn- save-fault-marker + "A change that makes the backend fail with `fault` when it applies it + (`debug-save-faults` backend flag). It changes no data." + [fault] + (debug-plugin-data-change (str "save-fault:" fault))) + +(defn- fault-error + "Why `fault` is not one of `faults`, or nil if it is." + [fault faults] + (when-not (contains? faults fault) + (str "unknown fault " fault ", expected one of " + (str/join ", " (sort faults))))) + +;; Armed `:hold` and `:faults` for update-file calls, and the `:original` +;; method while they wrap it. +(defonce ^:private save-hook (atom {})) + +(defn- mark-save + "Adds the fault armed for the file of `params` to the commit it names, + and counts the save against it." + [params] + (let [file-id (:id params) + [old _] (swap-vals! save-hook + (fn [{:keys [faults] :as hook}] + (let [{:keys [times]} (get faults file-id)] + (cond + (nil? times) hook + (> times 1) (update-in hook [:faults file-id :times] dec) + :else (update hook :faults dissoc file-id)))))] + (if-let [{:keys [fault commit]} (get-in old [:faults file-id])] + (let [marker (save-fault-marker fault) + groups (:changes-with-metadata params)] + (if (seq groups) + (update-in params [:changes-with-metadata (min commit (dec (count groups))) :changes] + conj marker) + (update params :changes (fnil conj []) marker))) + params))) + +(defn- hooked-update-file + "Sends an update-file call through `send` with the armed fault, and holds + its answer while saves are held." + [send id params] + (let [response (send id (mark-save params))] + (if-let [gate (dm/get-in @save-hook [:hold :gate])] + (rx/mapcat (fn [result] + (rx/map (constantly result) (rx/from gate))) + response) + response))) + +(defn- sync-save-hook + "Wraps update-file calls while a hold or a fault is armed, and unwraps + them when none is." + [] + (let [{:keys [hold faults original]} @save-hook + armed? (or (some? hold) (seq faults))] + (cond + (and armed? (nil? original)) + (let [original (get-method rp/cmd! :update-file)] + (swap! save-hook assoc :original original) + (-add-method rp/cmd! :update-file + (fn [id params] (hooked-update-file original id params)))) + + (and (not armed?) (some? original)) + (do + (if (identical? original (get-method rp/cmd! :default)) + (remove-method rp/cmd! :update-file) + (-add-method rp/cmd! :update-file original)) + (swap! save-hook dissoc :original))))) + +(defn ^:export releaseSaves + "Answers the held saves now and stops holding new ones." + [] + (when-let [{:keys [gate timer]} (:hold @save-hook)] + (js/clearTimeout timer) + (p/resolve gate nil)) + (swap! save-hook dissoc :hold) + (sync-save-hook) + "saves are answered as they arrive") + +(defn ^:export holdSaves + "Holds the answers to the saves sent in the next `seconds` (default 5) + until then, or until `debug.releaseSaves()`. The backend applies each + save at once; only the tab waits, so edits made meanwhile stay pending + behind it, as with a slow backend. + + debug.holdSaves() + debug.holdSaves(10)" + ([] (holdSaves 5)) + ([seconds] + (releaseSaves) + (swap! save-hook assoc :hold {:gate (p/deferred) + :timer (js/setTimeout releaseSaves (* 1000 seconds))}) + (sync-save-hook) + (str "holding the answers to saves for " seconds " seconds"))) + +(defn ^:export failNextSave + "Makes the backend fail the next `times` saves (default 1) of a file (by + default the open one) with `fault`, from the commit at index `commit` + (default 0) on. Needs the `debug-save-faults` backend flag. + + Faults 'validation', 'runtime', 'ex-info' and 'assertion' reject the + commits from that one on; 'sql', 'io', 'eof', 'interrupted', 'not-found' + and 'stack-overflow' fail the whole save, as an infrastructure error + does. Of these, 'sql', 'io' and 'interrupted' pass on their own (503, + retried); the rest do not. A retry is a new save and counts against + `times`. + + debug.failNextSave({fault: 'sql'}) + debug.failNextSave({fault: 'runtime', commit: 2, times: 3, fileId: '…'})" + [options] + (let [{:keys [fault times commit fileId] + :or {times 1 commit 0}} (js->clj options :keywordize-keys true) + file-id (if fileId (uuid/parse fileId) (:current-file-id @st/state))] + (or (fault-error fault (into rejecting-faults request-faults)) + (do + (swap! save-hook assoc-in [:faults file-id] + {:fault fault :times times :commit commit}) + (sync-save-hook) + (str "the next " times " saves of " file-id " fail with " fault + " from commit " commit " on"))))) + +(defn ^:export clearSaveFaults + "Disarms every fault armed with `debug.failNextSave`." + [] + (swap! save-hook dissoc :faults) + (sync-save-hook) + "no save fault armed") + +(defn- pending-of-file + "Applies `f` to the pending commits of `file-id`, queued and buffered." + [pstate file-id f] + (let [file? #(= file-id (:file-id %)) + queued (filter #(file? (get-in pstate [:index %])) (:queue pstate)) + buffered #(if (file? %) (f %) %)] + (as-> pstate $ + (reduce (fn [pstate id] (update-in pstate [:index id :commits] #(mapv f %))) $ queued) + (update $ :buffer #(mapv buffered %))))) + +(defn ^:export failNextRollback + "Makes the undo of the commits pending now in the open file fail if a + rollback reverts them, so the rollback falls back to reloading the + file. Pair it with `debug.holdSaves` to have commits pending. + + debug.holdSaves(); debug.commitInvalidChange(); debug.failNextRollback()" + [] + (let [state @st/state + file-id (:current-file-id state)] + (if (empty? (concat (:queue (:persistence state)) (:buffer (:persistence state)))) + "nothing is pending: hold the saves and commit something first" + (do + (st/emit! + (ptk/reify ::fail-next-rollback + ptk/UpdateEvent + (update [_ state] + ;; A change type with no handler, so applying it throws + (update state :persistence pending-of-file file-id + #(update % :undo-changes (fn [changes] + (into [{:type :debug-fail}] changes))))))) + "the next rollback of the pending commits fails and reloads the file")))) + +(defn- get-persistence-summary + "What is pending in the tab and what could block the user." + [state] + (let [{:keys [status queue index buffer saved remote-changes]} (:persistence state) + queued (count (mapcat #(get-in index [% :commits]) queue)) + warn? (contains? @st/ongoing-tasks :persistence) + crash? (some? (:exception state))] + {:status status + :queued queued + :buffered (count buffer) + :saved (count saved) + :remoteChanges (count (mapcat val remote-changes)) + :leaveWarning warn? + :undoItems (count (dm/get-in state [:workspace-undo :items])) + :edition (some-> (dm/get-in state [:workspace-local :edition]) str) + :crashed crash? + :held (some? (:hold @save-hook)) + :faults (count (:faults @save-hook)) + :clean (and (contains? #{nil :saved} status) + (zero? queued) + (empty? buffer) + (empty? saved) + (empty? remote-changes) + (not warn?) + (not crash?))})) + +(defn ^:export persistenceState + "The save state of the tab: what is pending, the leave-page warning, the + undo history, the edited shape and a crash. `clean` is true when nothing + is pending and nothing blocks the user." + [] + (clj->js (get-persistence-summary @st/state))) + +;; --- Invalid changes + +(def ^:private invalid-change-kinds + "Ways to break page objects that backend validation rejects, with the + error code each raises." + {"parent" {:code :parent-not-found + :break (fn [objects shape] + (assoc-in objects [(:id shape) :parent-id] (uuid/next)))} + "child" {:code :child-not-found + :break (fn [objects shape] + (update-in objects [(:parent-id shape) :shapes] conj (uuid/next)))}}) + +(defn- changed-ids + [objects objects'] + (into [] (remove #(identical? (get objects %) (get objects' %))) (keys objects'))) + +(defn- renamed-main-instances + "Main instances whose name differs between `objects` and `objects'`." + [objects objects'] + (into [] + (comp (map #(get objects' %)) + (filter ctk/main-instance?) + (remove #(= (:name %) (:name (get objects (:id %)))))) + (changed-ids objects objects'))) + +(defn- rename-components + "Renames in `data` the components of the main instances renamed in + `objects'`." + [data objects objects'] + (reduce (fn [data {:keys [component-id name]}] + (let [[path name] (cpn/split-group-name name)] + (update-in data [:components component-id] assoc :path path :name name))) + data + (renamed-main-instances objects objects'))) + +(defn- objects-changes + "Changes for the shapes that differ between `objects` and `objects'`." + [origin page-id objects objects'] + (-> (pcb/empty-changes origin page-id) + (pcb/with-objects objects) + (pcb/update-shapes (changed-ids objects objects') #(get objects' (:id %))))) + +(defn- commit-objects-change + "Commits the shapes that differ between `objects` and `objects'`. A + renamed main instance renames its component too, as validation requires. + `params` go to `commit-changes`." + [origin page-id data objects objects' params] + (dwc/commit-changes + (merge (reduce (fn [changes {:keys [component-id name]}] + (cll/generate-rename-component changes component-id name data)) + (objects-changes origin page-id objects objects') + (renamed-main-instances objects objects')) + params))) + +(defn- objects-step + "A step that commits the shapes `f` changes in the page objects, with + `params` for `commit-changes`." + ([f] (objects-step f nil)) + ([f params] + (fn [origin page-id data objects] + (let [objects' (f objects)] + [(rename-components data objects objects') + objects' + (commit-objects-change origin page-id data objects objects' params)])))) + +(defn- fault-step + "A step that commits a marker the backend fails on with `fault`, with + `params` for `commit-changes`." + [fault params] + (fn [origin _ data objects] + [data + objects + ;; A real undo change, so the commit gets an undo entry as others do + (dwc/commit-changes (merge {:origin origin + :redo-changes [(save-fault-marker fault)] + :undo-changes [(debug-plugin-data-change "save-fault-undo")]} + params))])) + +(defn ^:export commitInvalidChange + "Commits a change that the backend rejects, on the selected shape or the + first one of the page, with optional valid renames before and after it, + all in one save. + + Kinds: 'parent' (the shape points to a missing parent, the default) and + 'child' (its parent lists a missing child). With `fault` ('validation', + 'runtime', 'ex-info' or 'assertion') the bad commit changes nothing and + the backend fails on it with that error instead (`debug-save-faults` + backend flag). With `undo: false` the bad commit gets no undo entry, so + rolling it back clears the whole undo history. + + debug.commitInvalidChange() + debug.commitInvalidChange({kind: 'child', before: 2, after: 1}) + debug.commitInvalidChange({fault: 'runtime', before: 1}) + debug.commitInvalidChange({undo: false})" + ([] (commitInvalidChange nil)) + ([options] + (let [{:keys [kind fault before after undo] + :or {kind "parent" before 0 after 0 undo true}} (js->clj options :keywordize-keys true) + + state @st/state + objects (dsh/lookup-page-objects state) + shape-id (or (first (dm/get-in state [:workspace-local :selected])) + (first (dm/get-in objects [uuid/zero :shapes]))) + shape (get objects shape-id) + {:keys [code break]} (get invalid-change-kinds kind)] + + (cond + (nil? break) + (str "unknown kind " kind ", expected one of " + (str/join ", " (keys invalid-change-kinds))) + + (and (some? fault) (fault-error fault rejecting-faults)) + (fault-error fault rejecting-faults) + + (nil? shape) + "select a shape, or open a page that has one" + + :else + (let [rename (fn [label] + (objects-step #(assoc-in % [shape-id :name] (str (:name shape) " [" label "]")))) + steps (concat (map #(rename (str "before " (inc %))) (range before)) + [(if fault + (fault-step fault {:save-undo? undo}) + (objects-step #(break % shape) {:save-undo? undo}))] + (map #(rename (str "after " (inc %))) (range after)))] + (st/emit! + (ptk/reify ::commit-invalid-change + ptk/WatchEvent + (watch [it state _] + (let [page-id (:current-page-id state) + commits (->> steps + (reduce (fn [[data objects commits] step] + (let [[data objects commit] (step it page-id data objects)] + [data objects (conj commits commit)])) + [(dsh/lookup-file-data state) (dsh/lookup-page-objects state) []]) + (last))] + (rx/concat + (rx/from commits) + ;; Saves once all commits are buffered and the 200ms pending debounce passed + (->> (rx/timer 300) + (rx/map (constantly ::dps/force-persist)))))))) + (str "committed " (+ before 1 after) " changes on '" (:name shape) + "'; commit " (inc before) " should fail with " (or fault (name code)))))))) + +(defn- broken-component-change + "A change that points the component of `shape`, or the first one of the + file, to a main instance that does not exist." + [data shape] + (when-let [component-id (or (:component-id shape) + (:id (first (ctkl/components-seq data))))] + {:type :mod-component + :id component-id + :main-instance-id (uuid/next)})) + +(defn ^:export saveBrokenData + "Saves, skipping backend validation, data that fails it: the kinds of + `debug.commitInvalidChange` on the selected shape or the first one of the + page, or 'component' (the component of the selected shape, or the first + of the file, points to a missing main instance). Then reloads the tab so + it opens the broken file. Undo with `debug.repair()`. + + debug.saveBrokenData({kind: 'child'}) + debug.saveBrokenData({kind: 'component'})" + ([] (saveBrokenData nil)) + ([options] + (let [{:keys [kind] :or {kind "parent"}} (js->clj options :keywordize-keys true) + state @st/state + file (dsh/lookup-file state) + page-id (:current-page-id state) + objects (dsh/lookup-page-objects state) + shape (get objects (or (first (dm/get-in state [:workspace-local :selected])) + (first (dm/get-in objects [uuid/zero :shapes])))) + break (get-in invalid-change-kinds [kind :break]) + changes (cond + (= "component" kind) + (some-> (broken-component-change (:data file) shape) vector) + + (and (some? break) (some? shape)) + (:redo-changes (objects-changes nil page-id objects (break objects shape))))] + (cond + (not (:clean (get-persistence-summary state))) + "wait until every change is saved" + + (empty? changes) + (str "nothing to break: kinds are " (str/join ", " (keys invalid-change-kinds)) + " and component, and they need a shape or a component") + + :else + (do + (->> (rp/cmd! :update-file {:id (:id file) + :revn (:revn file) + :vern (:vern file) + :session-id (:session-id state) + :features (:features state) + :changes changes + :skip-validate true}) + (rx/subs! (fn [_] (dom/reload-current-window)) + (fn [cause] (ex/print-throwable cause)))) + (str "saving broken " kind " data, then reloading")))))) + (defn ^:export network-averages [] (.log js/console (clj->js @http/network-averages))) diff --git a/frontend/test/frontend_tests/data/dashboard_test.cljs b/frontend/test/frontend_tests/data/dashboard_test.cljs index f59b5f83e7..8bd965eca5 100644 --- a/frontend/test/frontend_tests/data/dashboard_test.cljs +++ b/frontend/test/frontend_tests/data/dashboard_test.cljs @@ -11,6 +11,8 @@ [app.main.data.common :as dcm] [app.main.data.dashboard :as dd] [app.main.data.notifications :as ntf] + [app.main.data.persistence :as dps] + [app.main.data.team :as dtm] [app.main.data.websocket :as dws] [app.main.repo :as rp] [app.main.router :as rt] @@ -47,7 +49,8 @@ rt/get-current-href (mock/stub (constantly current-url))} (await (async/observe (ptk/watch event state nil) :on-next (fn [ev] - (t/is (= ::ntf/show (ptk/type ev))))))) + (t/is (contains? #{::ntf/show ::dtm/fetch-teams} + (ptk/type ev))))))) (.then (fn [_] @tr-calls))))) (t/deftest ^:async team-org-change-toast-names-team-when-joining-org (let [tr-calls (await (tr-args-for-team-org-toast "dashboard.team-belong-organization" @@ -62,6 +65,56 @@ (t/is (= [["dashboard.team-no-longer-belong-organization" ["Team-Apple" "OrgA"]]] tr-calls)))) +(t/deftest ^:async team-org-change-keeps-restrictions-met-of-the-same-organization + (let [team-id (uuid/next) + organization-id (uuid/next) + state {:teams {team-id {:id team-id + :organization {:id organization-id + :name "Acme" + :restrictions-met {:send-invitations false}}}}} + event (dcm/handle-change-team-organization + {:team {:id team-id + :organization {:id organization-id + :name "Acme 2" + :permissions {:send-invitations "owners"}}} + :notification nil})] + (await (mock/with-mocks* + {cf/flags (conj cf/flags :admin-console)} + (let [organization (get-in (ptk/update event state) [:teams team-id :organization])] + (t/is (= "Acme 2" (:name organization))) + (t/is (= {:send-invitations false} (:restrictions-met organization)))))))) + +(t/deftest ^:async team-org-change-refetches-teams-for-the-current-profile + (let [team-id (uuid/next) + state {:teams {team-id {:id team-id}}} + event (dcm/handle-change-team-organization + {:team {:id team-id :organization {:id (uuid/next) :name "Acme"}} + :notification nil}) + types (atom [])] + (await (mock/with-mocks* + {cf/flags (conj cf/flags :admin-console)} + (await (async/observe (ptk/watch event state nil) + :on-next #(swap! types conj (ptk/type %)))))) + (t/is (= [::dtm/fetch-teams] @types)))) + +(t/deftest ^:async duplicated-project-has-a-file-count-and-is-unpinned + (let [id (uuid/next) + copy-id (uuid/next) + team-id (uuid/next) + copied {:id copy-id :team-id team-id :name "Copy"} + state {:projects {id {:id id :name "Source" :count 9 :is-pinned true}}} + result (atom state)] + (await + (mock/with-mocks* + {rp/cmd! (mock/stub (fn [cmd _] + (->> (rx/of (case cmd + :duplicate-project copied + :get-project-files [{:id (uuid/next)} {:id (uuid/next)}])) + (rx/observe-on :async))))} + (await (async/observe (ptk/watch (dd/duplicate-project {:id id :name "Source"}) state (rx/empty)) + :on-next #(swap! result (partial ptk/update %)))))) + (t/is (= (assoc copied :count 2 :is-pinned false) (get-in @result [:projects copy-id]))))) + (t/deftest moving-current-team-into-sso-organization-redirects (t/async done (let [team-id (uuid/next) @@ -172,3 +225,76 @@ (await (async/observe (ptk/watch (dd/initialize team-id) state stream) :on-next #(swap! processed conj (ptk/type %)))))) (t/is (= 1 (count (filter #{::dcm/handle-organization-change-sso} @processed)))))) + +;; Scenario: the user duplicates a file whose workspace still has a change +;; waiting to be saved. Proves: the copy is requested once the save is done, +;; and only then is the user told, with a warning that the copy misses what +;; could not be saved, never replaced by the success toast. +(t/deftest ^:async duplicating-a-file-waits-for-its-pending-changes + (let [file-id (uuid/next) + commit-id (uuid/next) + calls (atom []) + answer (rx/subject) + store (ptk/store {:state {:files {} + :persistence {:queue (conj #queue [] commit-id) + :index {commit-id {:id commit-id :file-id file-id}}}}})] + (await + (mock/with-mocks* + {dps/force-persist-files-and-wait (mock/stub (fn [file-ids _] + (swap! calls conj [:wait file-ids]) + (rx/empty))) + rp/cmd! (mock/stub (fn [cmd _] + (swap! calls conj [cmd]) + (->> answer (rx/take 1) (rx/observe-on :async)))) + i18n/tr (fn ([key] (str "translated:" key)) ([key & _] (str "translated:" key)))} + (ptk/emit! store (dd/duplicate-files [{:id file-id :name "File"}])) + (await (async/wait-for #(= 2 (count @calls)) "the copy is requested")) + (t/is (= [[:wait #{file-id}] [:duplicate-file]] @calls)) + (t/is (nil? (:notification @store)) "nothing is told before the copy exists") + (rx/push! answer {:id (uuid/next)}) + (await (async/wait-for #(some? (:notification @store)) "the user is told")) + (await (async/settle)) + (t/is (= "translated:dashboard.duplicate-unsaved-changes" + (get-in @store [:notification :content]))) + (t/is (= :warning (get-in @store [:notification :level]))))))) + +;; Scenario: the user duplicates a file with everything saved. Proves: the +;; success toast shows once the copy exists, not when it is requested. +(t/deftest ^:async duplicating-a-saved-file-tells-once-the-copy-exists + (let [file-id (uuid/next) + answer (rx/subject) + sent (atom 0) + store (ptk/store {:state {:files {}}})] + (await + (mock/with-mocks* + {dps/force-persist-files-and-wait (mock/stub (fn [_ _] (rx/empty))) + rp/cmd! (mock/stub (fn [_ _] + (swap! sent inc) + (->> answer (rx/take 1) (rx/observe-on :async)))) + i18n/tr (fn ([key] (str "translated:" key)) ([key & _] (str "translated:" key)))} + (ptk/emit! store (dd/duplicate-files [{:id file-id :name "File"}])) + (await (async/wait-for #(= 1 @sent) "the copy is requested")) + (t/is (nil? (:notification @store))) + (rx/push! answer {:id (uuid/next)}) + (await (async/wait-for #(some? (:notification @store)) "the user is told")) + (t/is (= "translated:dashboard.success-duplicate-file" + (get-in @store [:notification :content]))) + (t/is (= :success (get-in @store [:notification :level]))))))) + +;; Scenario: the server fails to copy the file. Proves: no success toast +;; claims a copy that does not exist. +(t/deftest ^:async a-failed-copy-tells-no-success + (let [errors (atom []) + store (ptk/store {:state {:files {}} + :on-error #(swap! errors conj %)})] + (await + (mock/with-mocks* + {dps/force-persist-files-and-wait (mock/stub (fn [_ _] (rx/empty))) + rp/cmd! (mock/stub (fn [_ _] + (->> (rx/throw (ex-info "copy failed" {:type :internal})) + (rx/observe-on :async)))) + i18n/tr (fn ([key] (str "translated:" key)) ([key & _] (str "translated:" key)))} + (ptk/emit! store (dd/duplicate-files [{:id (uuid/next) :name "File"}])) + (await (async/wait-for #(seq @errors) "the copy fails")) + (await (async/settle)) + (t/is (nil? (:notification @store))))))) diff --git a/frontend/test/frontend_tests/data/event_test.cljs b/frontend/test/frontend_tests/data/event_test.cljs index 9add050dd4..04a184e94f 100644 --- a/frontend/test/frontend_tests/data/event_test.cljs +++ b/frontend/test/frontend_tests/data/event_test.cljs @@ -6,12 +6,14 @@ (ns frontend-tests.data.event-test (:require + [app.common.uuid :as uuid] [app.main.data.event :as ev] [app.main.repo :as rp] [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] [frontend-tests.helpers.async :as async] - [frontend-tests.helpers.mock :as mock])) + [frontend-tests.helpers.mock :as mock] + [potok.v2.core :as ptk])) (t/deftest events-enabled?-truth-table (t/testing "enabled when audit-log is present" @@ -41,3 +43,39 @@ {rp/cmd! (mock/stub (fn [_cmd] (rx/throw (ex-info "boom" {}))))} (await (async/->promise (ev/fetch-environment-data)))))] (t/is (= {:flags #{:telemetry}} data)))) + +(t/deftest snapshot-performance-info-counts-libraries + (let [file-id (uuid/next) + lib-id (uuid/next) + other-id (uuid/next) + unlinked (uuid/next) + sized (fn [n] (zipmap (repeatedly n uuid/next) (repeat {}))) + data (fn [components colors typographies] + {:components (sized components) + :colors (sized colors) + :typographies (sized typographies)}) + state {:files + {file-id {:id file-id + :data (-> (data 1 1 1) + (assoc :pages-index (zipmap (repeatedly 2 uuid/next) + [{:objects (sized 4)} + {:objects (sized 2)}])))} + lib-id {:id lib-id + :library-of file-id + :data (data 2 3 1)} + other-id {:id other-id + :library-of file-id + :data (data 1 2 2)} + unlinked {:id unlinked + :library-of (uuid/next) + :data (data 3 3 3)}}} + state' (ptk/update (#'ev/snapshot-performance-info {:file-id file-id}) state) + counters (get-in state' [:performance-info :counters])] + + (t/testing "counts shapes of the current file" + (t/is (= 6 (:total-shapes counters)))) + + (t/testing "sums the data of the file and its libraries, not other files" + (t/is (= 4 (:total-components counters))) + (t/is (= 6 (:total-colors counters))) + (t/is (= 4 (:total-typographies counters)))))) diff --git a/frontend/test/frontend_tests/data/exports_assets_test.cljs b/frontend/test/frontend_tests/data/exports_assets_test.cljs index a9c9393f25..18277c2f37 100644 --- a/frontend/test/frontend_tests/data/exports_assets_test.cljs +++ b/frontend/test/frontend_tests/data/exports_assets_test.cljs @@ -8,12 +8,16 @@ (:require [app.common.uuid :as uuid] [app.main.data.exports.assets :as de] + [app.main.data.exports.wasm :as wasm.exports] [app.main.data.persistence :as dwp] [app.main.errors :as errors] [app.main.repo :as repo] [app.main.store :as st] + [app.main.worker :as mw] + [app.render-wasm.api :as wasm.api] [app.util.dom :as dom] [app.util.i18n :as i18n] + [app.util.webapi :as wapi] [app.util.websocket :as ws] [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] @@ -21,7 +25,6 @@ [frontend-tests.helpers.events :as the] [frontend-tests.helpers.mock :as mock] [potok.v2.core :as ptk])) - (def ^:private export {:id (uuid/next) :object-id (uuid/next) :type :png @@ -37,6 +40,17 @@ {:profile-id (:id export) :ws-conn nil}) +(defn- wasm-state + [] + (assoc (test-state) :features #{"render-wasm/v1"})) + +(t/deftest sanitize-filename-strips-reserved-chars-and-dedupes + (t/is (= "a_b.png" + (wasm.exports/sanitize-filename {:name "a/b" :suffix "" :type :png} #{}))) + (t/is (= "board-2.pdf" + (wasm.exports/sanitize-filename {:name "board" :suffix "" :type :pdf} + #{"board.pdf"})))) + (t/deftest normalize-export-preserves-existing-name (t/is (= (export-with-name "Layer 1") (de/normalize-export (export-with-name "Layer 1"))))) @@ -203,3 +217,286 @@ (await (async/wait-for #(true? @downloaded) "the export runs")) (await (async/settle)) (t/is (nil? (:notification @store))))))) + +;; Scenario: WASM is on and the user exports one shape. Proves: the worker +;; builds a single blob (no ZIP); the exporter is not called. +;; Stub `export-target`: without a module it is nil; routing still stays +;; local (never the server API under wasm). +(t/deftest ^:async wasm-simple-export-stays-local + (let [cmd-calls (atom []) + worker-kind (atom nil) + downloaded (atom nil) + store (export-store (wasm-state) (atom [])) + page-id (uuid/next) + file-id (uuid/next) + one (assoc export :enabled true :name "layer" + :page-id page-id :file-id file-id)] + (await + (mock/with-mocks* + {repo/cmd! (mock/stub (fn [cmd params] + (swap! cmd-calls conj [cmd params]) + (rx/throw (ex-info "exporter must not run" {})))) + wasm.api/export-target (mock/stub (constantly :client)) + de/collect-export-objects (mock/stub (fn [_ _] {})) + mw/ask-many! (mock/stub (fn [msg] + (reset! worker-kind (:kind msg)) + (rx/of {:type :done + :filename "layer.png" + :mtype "image/png" + :blob (js/Blob.)}))) + wapi/create-uri (mock/stub (constantly "blob:png")) + dom/trigger-download-uri (mock/stub (fn [filename mtype uri] + (reset! downloaded [filename mtype uri]))) + st/ongoing-tasks (atom #{})} + (ptk/emit! store (de/request-simple-export {:export one})) + (await (async/wait-for #(some? @downloaded) "the file downloads")) + (t/is (empty? @cmd-calls) "no exporter RPC") + (t/is (= :single @worker-kind)) + (t/is (= ["layer.png" "image/png" "blob:png"] @downloaded)))))) + +;; Scenario: WASM says the raster is too big for the GPU (legacy `:exporter` +;; code). Proves: still local worker download, no server RPC — CPU raster +;; runs inside the worker via `export-bytes`. +(t/deftest ^:async wasm-oversized-for-gpu-stays-local + (let [cmd-calls (atom []) + worker-kind (atom nil) + downloaded (atom nil) + store (export-store (wasm-state) (atom [])) + one (assoc export :enabled true :name "huge")] + (await + (mock/with-mocks* + {repo/cmd! (mock/stub (fn [cmd params] + (swap! cmd-calls conj [cmd params]) + (rx/throw (ex-info "exporter must not run" {})))) + wasm.api/export-target (mock/stub (constantly :exporter)) + de/collect-export-objects (mock/stub (fn [_ _] {})) + mw/ask-many! (mock/stub (fn [msg] + (reset! worker-kind (:kind msg)) + (rx/of {:type :done + :filename "huge.png" + :mtype "image/png" + :blob (js/Blob.)}))) + wapi/create-uri (mock/stub (constantly "blob:png")) + dom/trigger-download-uri (mock/stub (fn [filename mtype uri] + (reset! downloaded [filename mtype uri]))) + st/ongoing-tasks (atom #{})} + (ptk/emit! store (de/request-simple-export {:export one})) + (await (async/wait-for #(some? @downloaded) "the file downloads")) + (t/is (empty? @cmd-calls) "no exporter RPC") + (t/is (= :single @worker-kind)) + (t/is (= ["huge.png" "image/png" "blob:png"] @downloaded)))))) + +;; Scenario: WASM reports true `:too-large` (beyond raster limits). Proves: +;; toast and no RPC — the exporter would fail the same way. +(t/deftest ^:async wasm-true-too-large-shows-toast + (let [cmd-calls (atom []) + store (export-store (wasm-state) (atom [])) + one (assoc export :enabled true :name "impossible")] + (await + (mock/with-mocks* + {repo/cmd! (mock/stub (fn [cmd params] + (swap! cmd-calls conj [cmd params]) + (rx/throw (ex-info "exporter must not run" {})))) + wasm.api/export-target (mock/stub (constantly :too-large)) + i18n/tr (mock/stub #(str "translated:" %))} + (ptk/emit! store (de/request-simple-export {:export one})) + (await (async/wait-for #(some? (:notification @store)) "the toast shows")) + (t/is (empty? @cmd-calls) "no exporter RPC") + (t/is (= "translated:errors.export-too-large" + (get-in @store [:notification :content]))) + (t/is (= :error (get-in @store [:notification :level]))))))) + +;; Scenario: WASM is on and the user exports several shapes. Proves: nothing +;; hits the exporter; the worker builds a ZIP and the client downloads it. +(t/deftest ^:async wasm-multiple-export-stays-local + (let [cmd-calls (atom []) + worker-cmd (atom nil) + downloaded (atom nil) + store (export-store (wasm-state) (atom [])) + page-id (uuid/next) + file-id (uuid/next) + exports [(assoc export :enabled true :name "a" :page-id page-id :file-id file-id) + (assoc export :id (uuid/next) + :object-id (uuid/next) + :page-id page-id + :file-id file-id + :enabled true + :name "b")]] + (await + (mock/with-mocks* + {repo/cmd! (mock/stub (fn [cmd params] + (swap! cmd-calls conj [cmd params]) + (rx/throw (ex-info "exporter must not run" {})))) + de/collect-export-objects (mock/stub (fn [_ _] {})) + mw/ask-many! (mock/stub (fn [msg] + (reset! worker-cmd (:cmd msg)) + (rx/of {:type :done + :filename "page.zip" + :mtype "application/zip" + :blob (js/Blob.)}))) + wapi/create-uri (mock/stub (constantly "blob:zip")) + dom/trigger-download-uri (mock/stub (fn [filename mtype uri] + (reset! downloaded [filename mtype uri]))) + st/ongoing-tasks (atom #{})} + (ptk/emit! store (de/request-multiple-export {:exports exports :name "page"})) + (await (async/wait-for #(some? @downloaded) "the zip downloads")) + (t/is (empty? @cmd-calls) "no exporter RPC") + (t/is (= :exports/run-local @worker-cmd)) + (t/is (= ["page.zip" "application/zip" "blob:zip"] @downloaded)))))) + +;; Scenario: WASM is on and the user exports boards as PDF. Proves: worker +;; multi-page PDF, no exporter call. +(t/deftest ^:async wasm-export-frames-stays-local + (let [cmd-calls (atom []) + worker-kind (atom nil) + downloaded (atom nil) + store (export-store (wasm-state) (atom [])) + page-id (uuid/next) + file-id (uuid/next) + exports [(assoc export :enabled true :name "board-1" + :page-id page-id :file-id file-id) + (assoc export :id (uuid/next) + :object-id (uuid/next) + :page-id page-id + :file-id file-id + :enabled true + :name "board-2")]] + (await + (mock/with-mocks* + {repo/cmd! (mock/stub (fn [cmd params] + (swap! cmd-calls conj [cmd params]) + (rx/throw (ex-info "exporter must not run" {})))) + de/collect-export-objects (mock/stub (fn [_ _] {})) + mw/ask-many! (mock/stub (fn [msg] + (reset! worker-kind (:kind msg)) + (rx/of {:type :progress :done 1 :total 2} + {:type :progress :done 2 :total 2} + {:type :done + :filename "page.pdf" + :mtype "application/pdf" + :blob (js/Blob.)}))) + wapi/create-uri (mock/stub (constantly "blob:pdf")) + dom/trigger-download-uri (mock/stub (fn [filename mtype uri] + (reset! downloaded [filename mtype uri]))) + st/ongoing-tasks (atom #{})} + (ptk/emit! store (de/request-multiple-export {:exports exports + :cmd :export-frames + :name "page"})) + (await (async/wait-for #(some? @downloaded) "the pdf downloads")) + (t/is (empty? @cmd-calls) "no exporter RPC") + (t/is (= :pdf-frames @worker-kind)) + (t/is (= ["page.pdf" "application/pdf" "blob:pdf"] @downloaded)))))) + +;; Scenario: the user cancels a local WASM multi-export. Proves: the widget +;; stays cancelled and late worker :progress/:done do not download or flip +;; status back to running. +(t/deftest ^:async wasm-local-cancel-ignores-late-worker-messages + (let [downloaded (atom nil) + cancel-cmds (atom []) + worker-bus (rx/subject) + store (export-store (wasm-state) (atom [])) + page-id (uuid/next) + file-id (uuid/next) + exports [(assoc export :enabled true :name "a" + :page-id page-id :file-id file-id) + (assoc export :id (uuid/next) + :object-id (uuid/next) + :page-id page-id + :file-id file-id + :enabled true + :name "b")]] + (await + (mock/with-mocks* + {repo/cmd! (mock/stub (fn [_ _] + (rx/throw (ex-info "exporter must not run" {})))) + de/collect-export-objects (mock/stub (fn [_ _] {})) + mw/ask-many! (mock/stub (fn [_] worker-bus)) + mw/emit! (mock/stub (fn [msg] + (swap! cancel-cmds conj msg))) + wapi/create-uri (mock/stub (constantly "blob:zip")) + dom/trigger-download-uri (mock/stub (fn [filename mtype uri] + (reset! downloaded [filename mtype uri]))) + st/ongoing-tasks (atom #{})} + (ptk/emit! store (de/request-multiple-export {:exports exports :name "page"})) + (await (async/wait-for #(= "running" (get-in @store [:export :status])) + "export is running")) + (rx/push! worker-bus {:type :progress :done 1 :total 2}) + (await (async/wait-for #(= 1 (get-in @store [:export :progress])) + "first progress lands")) + (ptk/emit! store (de/cancel-export)) + (await (async/wait-for #(= "cancelled" (get-in @store [:export :status])) + "export is cancelled")) + (rx/push! worker-bus {:type :progress :done 2 :total 2}) + (rx/push! worker-bus {:type :done + :filename "page.zip" + :mtype "application/zip" + :blob (js/Blob.)}) + (await (async/settle)) + (t/is (= "cancelled" (get-in @store [:export :status]))) + (t/is (nil? @downloaded) "late :done does not download") + (t/is (= :exports/cancel (:cmd (first @cancel-cmds)))) + (rx/end! worker-bus))))) + +(t/deftest collect-export-objects-keeps-only-roots-and-descendants + (let [page-id (uuid/next) + file-id (uuid/next) + root (uuid/next) + child (uuid/next) + other (uuid/next) + objects {root {:id root :parent-id uuid/zero :shapes [child]} + child {:id child :parent-id root :shapes []} + other {:id other :parent-id uuid/zero :shapes []}} + state {:files {file-id {:id file-id + :data {:pages-index + {page-id {:id page-id + :objects objects}}}}}} + exports [{:object-id root :page-id page-id :file-id file-id}] + selected (de/collect-export-objects state exports)] + (t/is (= #{root child} (set (keys selected)))) + (t/is (not (contains? selected other))))) + +;; Scenario: a shape is drawn in the browser while its file has a change not +;; saved yet. Proves: the export waits for the save first, and the user is +;; told the export includes changes the server does not have. +(t/deftest ^:async client-export-waits-for-the-save-and-warns-of-unsaved-changes + (let [file-id (uuid/next) + calls (atom []) + store (export-store (assoc (unsaved-state file-id) :features #{"render-wasm/v1"}) + (atom []))] + (await + (mock/with-mocks* + {dwp/force-persist-files-and-wait (mock/stub (fn [file-ids _] + (swap! calls conj [:wait file-ids]) + (rx/empty))) + wasm.api/export-target (mock/stub (constantly :client)) + de/collect-export-objects (mock/stub (fn [_ _] {})) + mw/ask-many! (mock/stub (fn [_] + (swap! calls conj [:export]) + (rx/empty))) + i18n/tr (mock/stub #(str "translated:" %)) + st/ongoing-tasks (atom #{})} + (ptk/emit! store (de/request-simple-export {:export (assoc export :file-id file-id)})) + (await (async/wait-for #(= 2 (count @calls)) "the export runs")) + (t/is (= [[:wait #{file-id}] [:export]] @calls) + "the export starts once the save is done") + (t/is (= "translated:workspace.export.unsaved-changes-included" + (get-in @store [:notification :content]))))))) + +;; Scenario: a shape is drawn in the browser with every change saved. +;; Proves: no warning is shown. +(t/deftest ^:async client-export-shows-no-warning-when-the-file-is-saved + (let [exported (atom false) + store (export-store (wasm-state) (atom []))] + (await + (mock/with-mocks* + {dwp/force-persist-files-and-wait (mock/stub (fn [_ _] (rx/empty))) + wasm.api/export-target (mock/stub (constantly :client)) + de/collect-export-objects (mock/stub (fn [_ _] {})) + mw/ask-many! (mock/stub (fn [_] + (reset! exported true) + (rx/empty))) + st/ongoing-tasks (atom #{})} + (ptk/emit! store (de/request-simple-export {:export (assoc export :file-id (uuid/next))})) + (await (async/wait-for #(true? @exported) "the export runs")) + (await (async/settle)) + (t/is (nil? (:notification @store))))))) diff --git a/frontend/test/frontend_tests/data/exports_files_test.cljs b/frontend/test/frontend_tests/data/exports_files_test.cljs index fa545900d6..9e7f15ebd5 100644 --- a/frontend/test/frontend_tests/data/exports_files_test.cljs +++ b/frontend/test/frontend_tests/data/exports_files_test.cljs @@ -2,23 +2,300 @@ ;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; file, You can obtain one at http://mozilla.org/MPL/2.0/. ;; -;; Copyright (c) KALEIDOS INC Sucursal en España SL +;; Copyright (c) KALEIDOS SUBSIDIARY SL (ns frontend-tests.data.exports-files-test - "Tests for opening the `.penpot` download dialog: the pending changes are - sent first, and the dialog is told when some are still not saved." + "Exporting files as jobs (one file, one job and one answer, with the progress + of the job reported while it runs), and opening the `.penpot` download dialog + (the pending changes are sent first, and the dialog is told when some are + still not saved)." (:require [app.common.uuid :as uuid] [app.main.data.exports.files :as fexp] [app.main.data.modal :as modal] [app.main.data.persistence :as dps] [app.main.repo :as rp] + [app.main.store :as st] + [app.util.websocket :as ws] [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] - [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.async :as hva] [frontend-tests.helpers.mock :as mock] [potok.v2.core :as ptk])) +(defn- file + [name] + {:id (uuid/next) + :name name + :project-id (uuid/next) + :is-shared false}) + +(defn- job + [job-id status & {:as extra}] + (merge {:id job-id :status status :name "export-binfile"} extra)) + +(defn- event + [job-id kind payload] + {:type :job-event + :job-id job-id + :kind kind + :payload payload}) + +(defn- message + [payload] + {:type :message :payload payload}) + +(defn- fake-server + "The backend of the export: the creation answers with a fresh job the + test drives, and the read answers the row of that job." + [calls rows] + (mock/stub + (fn [id params] + (case id + :create-export-binfile-job + (let [job-id (uuid/next)] + (swap! calls conj {:cmd id :params params :job-id job-id}) + (swap! rows assoc job-id (job job-id "running")) + (rx/of {:id job-id})) + + :get-job + (do + (swap! calls conj {:cmd id :params params}) + (rx/of (get @rows (:id params)))) + + (rx/of nil))))) + +(def ^:private created? + (comp #{:create-export-binfile-job} :cmd)) + +(defn- answered? + "True when the file already has its answer: the artifact or an error." + [results file-id] + (some (fn [result] + (and (= file-id (:file-id result)) + (or (:uri result) (:error result)))) + results)) + +(t/deftest ^:async a-file-is-exported-as-its-own-job + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + target (file "My file")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + rp/cmd! (fake-server calls rows) + ws/get-rcv-stream (mock/stub (fn [_] ws-stream))} + + (let [done (hva/observe (fexp/export-files :files [target] :type :detach-libraries) + {:on-next #(swap! seen conj %)}) + job-id (:job-id (first @calls))] + + (t/testing "the file is exported by a job of its own" + (t/is (= :create-export-binfile-job (:cmd (first @calls)))) + (t/is (= {:params {:file-ids #{(:id target)} + :export-type :detach-libraries}} + (:params (first @calls))))) + + (t/testing "the milestone of the job is reported while it runs" + (rx/push! ws-stream + (message (event job-id :progress + {:stage :pages + :counters {:files {:current 1 :total 1} + :pages {:current 1 :total 2}}}))) + (await (hva/wait-for #(some :progress @seen) "the milestone")) + + (t/is (= {:file-id (:id target) + :progress {:stage :pages + :counters {:files {:current 1 :total 1} + :pages {:current 1 :total 2}}}} + (last @seen)))) + + (t/testing "the artifact of the job answers when it is over" + (swap! rows assoc job-id + (job job-id "completed" + :result {:resource-uri "http://assets/export" + :filename "export.penpot" + :mtype "application/zip" + :size 120})) + (rx/push! ws-stream (message (event job-id :end {:outcome "completed"}))) + (await done) + + (t/is (= {:file-id (:id target) + :uri "http://assets/export" + :filename "My file" + :size 120} + (last @seen))))))))) + +(t/deftest ^:async a-failure-fails-only-its-own-file + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + one (file "First") + other (file "Second")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + rp/cmd! (fake-server calls rows) + ws/get-rcv-stream (mock/stub (fn [_] ws-stream))} + + (let [done (hva/observe (fexp/export-files :files [one other] :type :detach-libraries) + {:on-next #(swap! seen conj %)})] + + (t/testing "the first file answers with its artifact" + (let [job-id (:job-id (first (filter created? @calls)))] + (swap! rows assoc job-id + (job job-id "completed" :result {:resource-uri "http://assets/first"})) + (rx/push! ws-stream (message (event job-id :end {:outcome "completed"}))) + (await (hva/wait-for #(answered? @seen (:id one)) "the first answer")))) + + (t/testing "and the second one fails on its own" + (await (hva/wait-for #(= 2 (count (filter created? @calls))) "the second job")) + (let [job-id (:job-id (second (filter created? @calls)))] + (swap! rows assoc job-id + (job job-id "failed" :error {:type :internal + :code :boom + :hint "boom"})) + (rx/push! ws-stream (message (event job-id :end {:outcome "failed"}))) + (await done))) + + (t/testing "the failure does not touch the file that worked" + (t/is (= {:file-id (:id one) + :uri "http://assets/first" + :filename "First" + :size nil} + (first (filter #(and (= (:id one) (:file-id %)) + (:uri %)) + @seen))))) + + (t/is (= {:file-id (:id other) + :error {:type :internal :code :boom :hint "boom"}} + (last @seen)))))))) + +(t/deftest ^:async the-created-jobs-reach-the-caller-tagged-with-their-file + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen-jobs (atom []) + one (file "First") + other (file "Second")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + rp/cmd! (fake-server calls rows) + ws/get-rcv-stream (mock/stub (fn [_] ws-stream))} + + (let [done (hva/observe (fexp/export-files :files [one other] + :type :detach-libraries + :on-job #(swap! seen-jobs conj %)) + {:on-next (constantly nil)})] + + (await (hva/wait-for #(= 1 (count (filter created? @calls))) "the first job")) + + (t/testing "the server recorded the creation with its job id" + (let [creates (filterv created? @calls)] + (t/is (= 1 (count creates))) + (t/is (= :create-export-binfile-job (:cmd (first creates)))) + (t/is (uuid? (:job-id (first creates)))))) + + (t/testing "the caller learns that same job tagged with its file" + (t/is (= [{:job-id (:job-id (first (filterv created? @calls))) + :file-id (:id one)}] + @seen-jobs))) + + ;; files export one after the other: the second job is only + ;; created once the first one is over + (let [{job-1 :job-id} (first @seen-jobs)] + (swap! rows assoc job-1 (job job-1 "completed" :result {:resource-uri "http://assets/first"})) + (rx/push! ws-stream (message (event job-1 :end {:outcome "completed"})))) + + (await (hva/wait-for #(= 2 (count (filter created? @calls))) "the second job")) + + (t/testing "the caller learns the second job too, tagged and in order" + (t/is (= 2 (count @seen-jobs))) + (t/is (= (:job-id (second (filterv created? @calls))) (:job-id (second @seen-jobs)))) + (t/is (= (:id other) (:file-id (second @seen-jobs))))) + + (let [{job-2 :job-id} (second @seen-jobs)] + (swap! rows assoc job-2 (job job-2 "completed" :result {:resource-uri "http://assets/second"})) + (rx/push! ws-stream (message (event job-2 :end {:outcome "completed"}))) + (await done))))))) + +(t/deftest ^:async every-file-is-queued-until-its-first-milestone + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + one (file "First") + other (file "Second")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + rp/cmd! (fake-server calls rows) + ws/get-rcv-stream (mock/stub (fn [_] ws-stream))} + + (let [done (hva/observe (fexp/export-files :files [one other] :type :detach-libraries) + {:on-next #(swap! seen conj %)})] + + (t/testing "every file queues up front, before any of its jobs exists" + (await (hva/wait-for #(= 2 (count (filter :queued @seen))) "the queued messages")) + (t/is (= [{:file-id (:id one) :queued true} + {:file-id (:id other) :queued true}] + (vec (filter :queued @seen))))) + + (t/testing "a worker picking a job up is not a milestone and ends nothing" + (let [job-id (:job-id (first (filter created? @calls)))] + (rx/push! ws-stream (message (event job-id :start {:attempt 1}))) + (t/is (not-any? :started @seen)))) + + ;; finish the first job so the second one is created and the run ends + (let [job-1 (:job-id (first (filter created? @calls)))] + (swap! rows assoc job-1 + (job job-1 "completed" :result {:resource-uri "http://assets/first"})) + (rx/push! ws-stream (message (event job-1 :end {:outcome "completed"})))) + + (await (hva/wait-for #(= 2 (count (filter created? @calls))) "the second job")) + + (let [job-2 (:job-id (second (filter created? @calls)))] + (swap! rows assoc job-2 + (job job-2 "completed" :result {:resource-uri "http://assets/second"})) + (rx/push! ws-stream (message (event job-2 :end {:outcome "completed"}))) + (await done))))))) + +(t/deftest ^:async a-cancelled-job-marks-the-file-cancelled-without-artifact + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + target (file "My file")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + rp/cmd! (fake-server calls rows) + ws/get-rcv-stream (mock/stub (fn [_] ws-stream))} + + (let [done (hva/observe (fexp/export-files :files [target] :type :detach-libraries) + {:on-next #(swap! seen conj %)}) + job-id (:job-id (first @calls))] + + (t/testing "the cancelled outcome is an explicit message, never an artifact" + (swap! rows assoc job-id (job job-id "cancelled")) + (rx/push! ws-stream (message (event job-id :end {:outcome "cancelled"}))) + (await done) + (t/is (= {:file-id (:id target) :cancelled true} (last @seen))) + (t/is (not-any? :uri @seen)))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; DOWNLOAD DIALOG +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (defn- file-param [file-id] {:id file-id :name "File" :project-id (uuid/next) :is-shared false}) @@ -43,8 +320,8 @@ (reset! waited file-ids) (rx/empty)))} (ptk/emit! store (fexp/open-export-dialog [(file-param file-id)])) - (await (async/wait-for #(some? (get-in @store [::modal/modal :props])) - "the dialog opens")))) + (await (hva/wait-for #(some? (get-in @store [::modal/modal :props])) + "the dialog opens")))) (assoc (get-in @store [::modal/modal :props]) ::waited @waited))) ;; Scenario: the user downloads a file whose changes are all saved. Proves: diff --git a/frontend/test/frontend_tests/data/imports_test.cljs b/frontend/test/frontend_tests/data/imports_test.cljs new file mode 100644 index 0000000000..e173ec3671 --- /dev/null +++ b/frontend/test/frontend_tests/data/imports_test.cljs @@ -0,0 +1,774 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.data.imports-test + "Reading a penpot package and importing it as a job." + (:require + [app.common.json :as json] + [app.common.uuid :as uuid] + [app.main.data.imports :as imp] + [app.main.data.jobs :as dj] + [app.main.data.uploads :as uploads] + [app.main.repo :as rp] + [app.main.store :as st] + [app.util.http :as http] + [app.util.i18n :refer [tr]] + [app.util.websocket :as ws] + [app.util.zip :as uz] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as hva] + [frontend-tests.helpers.mock :as mock] + [promesa.core :as p])) + +(def ^:private project-id + (uuid/next)) + +(def ^:private uri + "blob:package") + +(def ^:private zip-magic + (js/Uint8Array. #js [0x50 0x4b 0x03 0x04])) + +(defn- entry + "An entry of the wizard, as the analysis leaves it." + [& {:keys [id name type] + :or {type :binfile-v3}}] + {:file-id (or id (uuid/next)) + :uri uri + :name (or name "My file") + :type type}) + +(defn- job + [job-id status & {:as extra}] + (merge {:id job-id :status status :name "import-binfile"} extra)) + +(defn- event + [job-id kind payload] + {:type :job-event + :job-id job-id + :kind kind + :payload payload}) + +(defn- message + [payload] + {:type :message :payload payload}) + +(defn- slurp-stub + "The transport of a request that answers with `body`." + [body] + (mock/stub (fn [_] (rx/of {:body body})))) + +(defn- upload-stub + "The chunked upload of a package. It is variadic, so it is a plain fn." + [session-id] + (fn [_ & _] (rx/of {:session-id session-id}))) + +(defn- upload-with-progress-stub + "The chunked upload of a package that reports two chunks before + emitting the session, so the test sees the upload phase. It is + variadic, so it is a plain fn." + [session-id] + (fn [_ & {:keys [on-progress]}] + (when (fn? on-progress) + (on-progress {:current 1 :total 2}) + (on-progress {:current 2 :total 2})) + (rx/of {:session-id session-id}))) + +(defn- ws-stub + [stream] + (mock/stub (fn [_] stream))) + +(defn- fake-server + "The backend of the import: the creation answers with a fresh job the + test drives, and the read answers the row of that job." + [calls rows create-row] + (mock/stub + (fn [id params] + (case id + :create-import-binfile-job + (let [job-id (uuid/next)] + (swap! calls conj {:cmd id :params params :job-id job-id}) + (swap! rows assoc job-id (create-row job-id)) + (rx/of {:id job-id})) + + :get-job + (do + (swap! calls conj {:cmd id :params params}) + (rx/of (get @rows (:id params)))) + + (rx/of nil))))) + +(defn- manifest + [& files] + (json/encode {:type "penpot/export-files" + :files (mapv (fn [[id name]] {:id id :name name}) files)})) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; READING A PACKAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest ^:async a-package-without-a-manifest-is-rejected + (let [seen (atom [])] + + (await + (mock/with-mocks* + {http/send! (slurp-stub zip-magic) + uz/get-entry (mock/stub (fn [_ _] (p/resolved nil))) + uz/close (mock/stub (fn [_] nil))} + + (await (hva/observe (imp/analyze [{:uri uri :name "broken.penpot"}]) + {:on-next #(swap! seen conj %)})) + + (t/testing "the entry carries the error of the reader" + (t/is (= 1 (count @seen))) + (t/is (= :error (:status (first @seen)))) + (t/is (string? (:error (first @seen))))))))) + +(t/deftest ^:async a-version-3-package-answers-one-entry-per-file + (let [first-id (uuid/next) + second-id (uuid/next) + seen (atom [])] + + (await + (mock/with-mocks* + {http/send! (slurp-stub zip-magic) + uz/get-entry (mock/stub (fn [_ _] (p/resolved ::entry))) + uz/get-entries (mock/stub (fn [_] (p/resolved #js []))) + uz/read-as-text (mock/stub (fn [_] (p/resolved (manifest [first-id "First"] + [second-id "Second"])))) + uz/close (mock/stub (fn [_] nil))} + + (await (hva/observe (imp/analyze [{:uri uri :name "package.penpot"}]) + {:on-next #(swap! seen conj %)})) + + (t/testing "every file of the manifest is an entry of the wizard" + (t/is (= [first-id second-id] (mapv :file-id @seen))) + (t/is (= ["First" "Second"] (mapv :name @seen))) + (t/is (every? #(= :binfile-v3 (:type %)) @seen)) + (t/is (every? #(= :success (:status %)) @seen))) + + (t/testing "all of them share the upload of the file that was dropped" + (t/is (every? #(= uri (:uri %)) @seen))))))) + +(t/deftest ^:async a-version-3-package-sizes-every-file-with-what-it-occupies + (let [first-id (uuid/next) + second-id (uuid/next) + unknown-id (uuid/next) + object-id (str (uuid/next)) + seen (atom []) + ;; the entries of a container: every file keeps its own under + ;; `files/`, the binaries of its media shared under `objects/`, + ;; and the manifest belongs to the container itself + entries + #js [#js {:filename "manifest.json" + :compressedSize 100} + #js {:filename (str "files/" first-id ".json") + :compressedSize 200} + #js {:filename (str "files/" first-id "/pages/one.json") + :compressedSize 300} + #js {:filename (str "files/" first-id "/media/one.json") + :compressedSize 10 + :content (str "{\"mediaId\": \"" object-id "\"}")} + #js {:filename (str "objects/" object-id ".json") + :compressedSize 20} + #js {:filename (str "objects/" object-id ".png") + :compressedSize 1000} + #js {:filename (str "files/" second-id ".json") + :compressedSize 400}]] + + (await + (mock/with-mocks* + {http/send! (slurp-stub zip-magic) + ;; the manifest is read through the same reader as the rest of the + ;; entries, so the fake carries its text and the mock answers what + ;; every entry holds + uz/get-entry (mock/stub (fn [_ _] + (p/resolved #js {:content (manifest [first-id "First"] + [second-id "Second"] + [unknown-id "Unknown"])}))) + uz/get-entries (mock/stub (fn [_] (p/resolved entries))) + uz/read-as-text (mock/stub (fn [entry] (p/resolved (.-content entry)))) + uz/close (mock/stub (fn [_] nil))} + + (await (hva/observe (imp/analyze [{:uri uri :name "package.penpot" :size 10000}]) + {:on-next #(swap! seen conj %)})))) + + (t/testing "a file occupies its own entries and the objects it references" + ;; 200 + 300 + 10 of its own, and 20 + 1000 of the object its media + ;; points at; the 100 of the manifest belong to the container + (t/is (= [1530 400 nil] (mapv :size @seen)))) + + (t/testing "a file the container does not hold has no size" + ;; the manifest lists it, but no entry of the zip belongs to it: the + ;; row says nothing rather than borrowing what the package weighs + (t/is (nil? (:size (first (filter #(= unknown-id (:file-id %)) @seen)))))) + + (t/testing "what the container occupies is kept apart" + (t/is (= [10000 10000 10000] (mapv :container-size @seen)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IMPORTING A PACKAGE +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest ^:async a-package-is-imported-as-its-own-job + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + session-id (uuid/next) + resolution {:done [{:id (str (uuid/next)) :name "Published"}]} + one (entry :name "First") + other (entry :name "Second")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + http/send! (slurp-stub ::blob) + uploads/upload-blob-chunked (upload-stub session-id) + rp/cmd! (fake-server calls rows (fn [job-id] (job job-id "running"))) + ws/get-rcv-stream (ws-stub ws-stream)} + + (let [done (hva/observe (imp/import-files {:project-id project-id + :entries [one other]}) + {:on-next #(swap! seen conj %)}) + job-id (:job-id (first @calls))] + + (t/testing "the package is uploaded and imported as one job" + (t/is (= :create-import-binfile-job (:cmd (first @calls)))) + (t/is (= {:project-id project-id :name "First" :version 3} + (:params (:params (first @calls))))) + (t/is (= session-id (:upload-id (:params (first @calls)))))) + + (t/testing "the milestone of the job reaches every entry of the package" + (rx/push! ws-stream + (message (event job-id :progress + {:stage :pages + :counters {:files {:current 1 :total 2} + :pages {:current 1 :total 8}}}))) + (await (hva/wait-for #(= 2 (count (filter (comp #{:progress} :status) @seen))) + "the milestone")) + + (t/is (= #{(:file-id one) (:file-id other)} + (into #{} (comp (filter (comp #{:progress} :status)) (map :file-id)) @seen))) + + (t/is (every? (fn [message] + (= {:stage :pages + :counters {:files {:current 1 :total 2} + :pages {:current 1 :total 8}}} + (:progress message))) + (filter (comp #{:progress} :status) @seen)))) + + (t/testing "the entries finish with the resolution of the package" + (swap! rows assoc job-id + (job job-id "completed" + :result {:file-ids (mapv (comp str :file-id) [one other]) + :resolution resolution + :name "First" + :version 3})) + (rx/push! ws-stream (message (event job-id :end {:outcome "completed"}))) + (await done) + + (t/is (= #{(:file-id one) (:file-id other)} + (into #{} (comp (filter (comp #{:finish} :status)) (map :file-id)) @seen))) + + (t/is (= {:libraries-resolution resolution} (last @seen))))))))) + +(t/deftest ^:async the-job-reports-queued-then-started-before-progress + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + session-id (uuid/next) + one (entry :name "First") + other (entry :name "Second")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + http/send! (slurp-stub ::blob) + uploads/upload-blob-chunked (upload-stub session-id) + rp/cmd! (fake-server calls rows (fn [job-id] (job job-id "running"))) + ws/get-rcv-stream (ws-stub ws-stream)} + + (let [done (hva/observe (imp/import-files {:project-id project-id + :entries [one other]}) + {:on-next #(swap! seen conj %)}) + job-id (:job-id (first @calls))] + + (t/testing "every entry is queued while the job waits for a worker" + (await (hva/wait-for #(= 2 (count (filter (comp #{:queued} :status) @seen))) + "the queued messages")) + (t/is (= #{(:file-id one) (:file-id other)} + (into #{} (comp (filter (comp #{:queued} :status)) (map :file-id)) @seen)))) + + (t/testing "a started job moves the entries to progress without a milestone" + (rx/push! ws-stream (message (event job-id :start {:attempt 1}))) + (await (hva/wait-for #(= 2 (count (filter (comp #{:started} :status) @seen))) + "the started messages")) + (t/is (= #{(:file-id one) (:file-id other)} + (into #{} (comp (filter (comp #{:started} :status)) (map :file-id)) @seen)))) + + (t/testing "the milestone still arrives on top" + (rx/push! ws-stream + (message (event job-id :progress + {:stage :pages + :counters {:pages {:current 1 :total 8}}}))) + (await (hva/wait-for #(some :progress (filter (comp #{:progress} :status) @seen)) + "the milestone")) + (swap! rows assoc job-id + (job job-id "completed" + :result {:file-ids (mapv (comp str :file-id) [one other]) + :resolution {} + :name "First" + :version 3})) + (rx/push! ws-stream (message (event job-id :end {:outcome "completed"}))) + (await done))))))) + +(t/deftest ^:async a-failed-job-fails-its-entries + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + target (entry :name "Only")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + http/send! (slurp-stub ::blob) + uploads/upload-blob-chunked (upload-stub (uuid/next)) + rp/cmd! (fake-server calls rows (fn [job-id] (job job-id "running"))) + ws/get-rcv-stream (ws-stub ws-stream)} + + (let [done (hva/observe (imp/import-files {:project-id project-id + :entries [target]}) + {:on-next #(swap! seen conj %)}) + job-id (:job-id (first @calls))] + + (swap! rows assoc job-id + (job job-id "failed" :error {:type :internal + :code :boom + :hint "The package is broken"})) + (rx/push! ws-stream (message (event job-id :end {:outcome "failed"}))) + (await done) + + (t/testing "the entry fails with the message of the job" + (t/is (= [{:status :queued + :file-id (:file-id target)} + {:status :error + :file-id (:file-id target) + :error "The package is broken"} + {:libraries-resolution {}}] + @seen)))))))) + +(t/deftest ^:async a-package-that-cannot-be-created-fails-its-entries + (let [ws-stream (rx/subject) + seen (atom []) + target (entry :name "Only")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + http/send! (slurp-stub ::blob) + uploads/upload-blob-chunked (upload-stub (uuid/next)) + rp/cmd! (mock/stub (fn [_ _] + (rx/throw (ex-info "cannot create the job" + {:type :validation + :code :boom + :hint "Not now"})))) + ws/get-rcv-stream (ws-stub ws-stream)} + + (await (hva/observe (imp/import-files {:project-id project-id + :entries [target]}) + {:on-next #(swap! seen conj %)})) + + (t/testing "the failure reaches every entry of the package" + (t/is (= [{:status :error + :file-id (:file-id target) + :error "Not now"} + {:libraries-resolution {}}] + @seen))))))) + +(t/deftest ^:async a-job-that-already-ended-does-not-leave-the-entries-spinning + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + target (entry :name "Only")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + http/send! (slurp-stub ::blob) + uploads/upload-blob-chunked (upload-stub (uuid/next)) + ;; the job is over before the client reads it for the first time + rp/cmd! (fake-server calls rows + (fn [job-id] (job job-id "completed" :result {:resolution {}}))) + ws/get-rcv-stream (ws-stub ws-stream)} + + (await (hva/observe (imp/import-files {:project-id project-id + :entries [target]}) + {:on-next #(swap! seen conj %)})) + + (t/testing "the entry finishes without waiting for an event" + (t/is (= [{:status :queued + :file-id (:file-id target)} + {:status :finish + :file-id (:file-id target)} + {:libraries-resolution {}}] + @seen))))))) + +(t/deftest ^:async the-upload-reports-chunks-before-the-job-runs + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + session-id (uuid/next) + target (entry :name "Only")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + http/send! (slurp-stub ::blob) + uploads/upload-blob-chunked (upload-with-progress-stub session-id) + rp/cmd! (fake-server calls rows (fn [job-id] (job job-id "running"))) + ws/get-rcv-stream (ws-stub ws-stream)} + + (let [done (hva/observe (imp/import-files {:project-id project-id + :entries [target]}) + {:on-next #(swap! seen conj %)}) + job-id (:job-id (first @calls))] + + (await (hva/wait-for #(>= (count (filter (comp #{:progress} :status) @seen)) 2) + "the upload progress")) + + (t/testing "every uploaded chunk reaches the entry as an upload milestone" + (t/is (= [{:status :progress + :file-id (:file-id target) + :progress {:stage :upload + :counters {:upload {:current 1 :total 2}}}} + {:status :progress + :file-id (:file-id target) + :progress {:stage :upload + :counters {:upload {:current 2 :total 2}}}}] + (filterv (comp #{:progress} :status) @seen)))) + + (t/testing "the entry still finishes with the job" + (swap! rows assoc job-id (job job-id "completed" :result {:resolution {}})) + (rx/push! ws-stream (message (event job-id :end {:outcome "completed"}))) + (await done) + + (t/is (= {:libraries-resolution {}} (last @seen))) + (t/is (= :finish (:status (nth @seen 3)))))))))) + +(t/deftest ^:async a-cancelled-job-closes-its-entries-as-cancelled + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + target (entry :name "Only")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + http/send! (slurp-stub ::blob) + uploads/upload-blob-chunked (upload-stub (uuid/next)) + rp/cmd! (fake-server calls rows (fn [job-id] (job job-id "running"))) + ws/get-rcv-stream (ws-stub ws-stream)} + + (let [done (hva/observe (imp/import-files {:project-id project-id + :entries [target]}) + {:on-next #(swap! seen conj %)}) + job-id (:job-id (first @calls))] + + (t/testing "the cancelled ending terminates the stream" + (swap! rows assoc job-id (job job-id "cancelled")) + (rx/push! ws-stream (message (event job-id :end {:outcome "cancelled"}))) + (await done) + + (t/testing "the cancelled ending closes the entries and terminates the stream" + (t/is (= [{:status :queued + :file-id (:file-id target)} + {:status :error + :file-id (:file-id target) + :error (tr "jobs.import-cancelled")} + {:libraries-resolution {}}] + @seen))))))))) + +(t/deftest ^:async a-cancelled-job-closes-in-progress-entries-as-cancelled + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen (atom []) + target (entry :name "Only")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + http/send! (slurp-stub ::blob) + uploads/upload-blob-chunked (upload-stub (uuid/next)) + rp/cmd! (fake-server calls rows (fn [job-id] (job job-id "running"))) + ws/get-rcv-stream (ws-stub ws-stream)} + + (let [done (hva/observe (imp/import-files {:project-id project-id + :entries [target]}) + {:on-next #(swap! seen conj %)}) + job-id (:job-id (first @calls))] + + ;; drive the entries to progress first, so the cancel + ;; interrupts a run in course instead of a queued job + (rx/push! ws-stream (message (event job-id :progress {:stage :manifest}))) + (await (hva/wait-for #(some (fn [message] (= :progress (:status message))) @seen) + "the progress")) + + (t/testing "an external cancel closes every entry as cancelled" + (swap! rows assoc job-id (job job-id "cancelled")) + (rx/push! ws-stream (message (event job-id :end {:outcome "cancelled"}))) + (await done) + (t/is (= [{:status :queued + :file-id (:file-id target)} + {:status :progress + :file-id (:file-id target) + :progress {:stage :manifest}} + {:status :error + :file-id (:file-id target) + :error (tr "jobs.import-cancelled")} + {:libraries-resolution {}}] + @seen)))))))) + +(t/deftest ^:async the-created-job-id-reaches-the-caller + (let [ws-stream (rx/subject) + calls (atom []) + rows (atom {}) + seen-jobs (atom []) + target (entry :name "Only")] + + (await + (mock/with-mocks* + {st/state (atom {:ws-conn ws-stream}) + http/send! (slurp-stub ::blob) + uploads/upload-blob-chunked (upload-stub (uuid/next)) + rp/cmd! (fake-server calls rows (fn [job-id] (job job-id "running"))) + ws/get-rcv-stream (ws-stub ws-stream)} + + (let [done (hva/observe (imp/import-files {:project-id project-id + :entries [target] + :on-job #(swap! seen-jobs conj %)}) + {:on-next (constantly nil)}) + job-id (:job-id (first @calls))] + + (t/testing "the caller learns the id of the package job" + (t/is (= [job-id] @seen-jobs))) + + (swap! rows assoc job-id (job job-id "completed" :result {:resolution {}})) + (rx/push! ws-stream (message (event job-id :end {:outcome "completed"}))) + (await done)))))) + +(t/deftest format-file-size-writes-human-sizes + (t/testing "unknown sizes show nothing" + (t/is (nil? (imp/format-file-size nil))) + (t/is (nil? (imp/format-file-size -1)))) + + (t/testing "bytes, kilobytes, megabytes and gigabytes" + (t/is (= "0B" (imp/format-file-size 0))) + (t/is (= "512B" (imp/format-file-size 512))) + (t/is (= "1KB" (imp/format-file-size 1024))) + (t/is (= "1.5KB" (imp/format-file-size 1536))) + (t/is (= "512KB" (imp/format-file-size (* 512 1024)))) + (t/is (= "234MB" (imp/format-file-size (* 234 1024 1024)))) + (t/is (= "1.2GB" (imp/format-file-size (* 1.2 1024 1024 1024)))))) + +(defn- uploading + "A file of the wizard while the container it came in is uploaded: the + chunks sent out of the chunks the container has." + [uri size current total] + {:file-id (uuid/next) + :uri uri + :size (* 1024 1024) + :container-size size + :type :binfile-v3 + :status :import-progress + :progress {:stage :upload :counters {:upload {:current current :total total}}}}) + +(t/deftest upload-pct-weighs-every-container-by-its-size + (let [small (* 100 1024 1024) + big (* 300 1024 1024)] + + (t/testing "the bytes sent of every container over the bytes of all of them" + ;; 100MB of 100MB and half of 300MB: 250 of 400, not the average of + ;; 100% and 50% + (t/is (= 63 (imp/upload-pct [(uploading "a" small 4 4) + (uploading "b" big 1 2)])))) + + (t/testing "a container that has not started counts as nothing sent" + (t/is (= 25 (imp/upload-pct [(uploading "a" small 4 4) + {:uri "b" :container-size big :type :binfile-v3 + :status :import-ready}])))) + + (t/testing "the files of one container are one container" + (t/is (= 100 (imp/upload-pct [(uploading "a" big 1 1) + (uploading "a" big 1 1)])))) + + (t/testing "a container with no size leaves the bar indeterminate" + (t/is (nil? (imp/upload-pct []))) + (t/is (nil? (imp/upload-pct [{:uri "a" :type :binfile-v3 :status :import-ready}])))))) + +(t/deftest a-container-past-the-upload-counts-whole + ;; the job of a container is created with its upload as its input, so the + ;; moment the entries leave the upload stage the container is sent + (t/is (= 100 (imp/upload-pct [{:uri "a" :name "a.penpot" :container-size (* 100 1024 1024) + :type :binfile-v1 :status :import-queued} + {:uri "b" :name "b.penpot" :container-size (* 300 1024 1024) + :type :binfile-v1 :status :import-success}])))) + +(t/deftest upload-done?-tells-when-the-chunks-of-a-container-are-sent + (t/testing "the last chunk closes the upload" + (t/is (true? (imp/upload-done? (uploading "a" 100 3 3)))) + (t/is (false? (imp/upload-done? (uploading "a" 100 2 3))))) + + (t/testing "a file that is not uploading yet is not done" + (t/is (false? (imp/upload-done? {:status :import-ready}))) + (t/is (false? (imp/upload-done? {}))))) + +(t/deftest imported-count-counts-the-files-the-job-left-behind + (let [file (fn [uri status progress] + (cond-> {:uri uri :type :binfile-v3 :status status} + (some? progress) (assoc :progress progress)))] + + (t/testing "a container that finished counts all of its files" + (t/is (= 2 (imp/imported-count [(file "a" :import-success nil) + (file "a" :import-success nil)])))) + + (t/testing "a running container counts the files it left behind" + ;; its milestone says it is working on the second file of three + (t/is (= 1 (imp/imported-count [(file "a" :import-progress + {:stage :pages + :counters {:files {:current 2 :total 3} + :pages {:current 1 :total 5}}})])))) + + (t/testing "a container that has not started has nothing done" + (t/is (= 0 (imp/imported-count [(file "a" :import-queued nil)]))) + (t/is (= 0 (imp/imported-count [(file "a" :import-progress + {:stage :pages :counters {}})])))) + + (t/testing "the files of every container add up" + (t/is (= 3 (imp/imported-count [(file "a" :import-success nil) + (file "a" :import-success nil) + (file "b" :import-progress + {:stage :pages + :counters {:files {:current 2 :total 4}}})])))) + + (t/testing "no files, nothing done" + (t/is (= 0 (imp/imported-count [])))))) + +(t/deftest waiting-slot?-is-true-until-a-worker-picks-the-jobs-up + (t/testing "every file queued is a wizard waiting for a slot" + (t/is (true? (imp/waiting-slot? [{:status :import-queued} + {:status :import-queued}])))) + + (t/testing "a job in course leaves the wait behind" + (t/is (false? (imp/waiting-slot? [{:status :import-queued} + {:status :import-progress}])))) + + (t/testing "a file already imported is not waiting either" + (t/is (false? (imp/waiting-slot? [{:status :import-success}])))) + + (t/testing "no files, no wait" + (t/is (false? (imp/waiting-slot? []))))) + +(t/deftest awaiting-analysis-hides-the-rows-while-checking + (t/testing "only a clean analysis shows the placeholder" + (t/is (true? (imp/awaiting-analysis? :analyze false))) + (t/is (false? (imp/awaiting-analysis? :analyze true)))) + (t/testing "any later stage shows the rows" + (t/is (false? (imp/awaiting-analysis? :import-ready false))) + (t/is (false? (imp/awaiting-analysis? :import-progress false))) + (t/is (false? (imp/awaiting-analysis? :import-success false))))) + +(t/deftest ready-to-import?-waits-for-the-whole-analysis + (t/testing "every file found and ready, with the analysis over" + (t/is (true? (imp/ready-to-import? [{:status :import-ready} + {:status :import-ready}] + true)))) + + (t/testing "a file that has not arrived keeps the wizard checking" + ;; the files of a container are only known once its manifest is read, so + ;; offering the import step here would leave the missing one behind + (t/is (false? (imp/ready-to-import? [{:status :import-ready}] false)))) + + (t/testing "a file that cannot be imported is not ready either" + (t/is (false? (imp/ready-to-import? [{:status :import-ready} + {:status :analyze-error}] + true)))) + + (t/testing "no files at all is not ready" + (t/is (false? (imp/ready-to-import? [] true))))) + +(t/deftest import-step-follows-the-wizard-status + (t/testing "analysis paints the check step" + (t/is (= :check (imp/import-step :analyze []))) + (t/is (= :check (imp/import-step :import-ready [])))) + + (t/testing "an upload milestone paints the upload step" + (let [entries [{:file-id (uuid/next) + :status :import-progress + :progress {:stage :upload :counters {:upload {:current 1 :total 2}}}}]] + (t/is (= :upload (imp/import-step :import-progress entries))))) + + (t/testing "a file that has not reported anything yet is still uploading" + ;; the wizard is uploading from the click, before the first chunk arrives + (let [entries [{:file-id (uuid/next) :status :import-ready} + {:file-id (uuid/next) + :status :import-progress + :progress {:stage :upload :counters {:upload {:current 1 :total 2}}}}]] + (t/is (= :upload (imp/import-step :import-progress entries))))) + + (t/testing "a finished upload with its job still to come is uploading" + (let [entries [{:file-id (uuid/next) + :status :import-progress + :progress {:stage :upload :counters {:upload {:current 2 :total 2}}}} + {:file-id (uuid/next) + :status :import-progress + :progress {:stage :upload :counters {:upload {:current 1 :total 4}}}}]] + (t/is (= :upload (imp/import-step :import-progress entries))))) + + (t/testing "queued or job milestones paint the import step" + (let [queued [{:file-id (uuid/next) :status :import-queued}] + started [{:file-id (uuid/next) :status :import-progress}] + pages [{:file-id (uuid/next) + :status :import-progress + :progress {:stage :pages :counters {:pages {:current 1 :total 8}}}}]] + (t/is (= :import (imp/import-step :import-progress queued))) + (t/is (= :import (imp/import-step :import-progress started))) + (t/is (= :import (imp/import-step :import-progress pages))))) + + (t/testing "terminal and resolution stages paint the import step" + (t/is (= :import (imp/import-step :import-success []))) + (t/is (= :import (imp/import-step :import-error []))) + (t/is (= :import (imp/import-step :library-resolution []))) + (t/is (= :import (imp/import-step :library-summary []))))) + +(t/deftest ^:async cancel-job-asks-the-server-and-ignores-failures + (let [calls (atom []) + job-id (uuid/next)] + + (t/testing "the cancel command reaches the server" + (await + (mock/with-mocks* + {rp/cmd! (mock/stub (fn [id params] + (swap! calls conj {:cmd id :params params}) + (rx/of {:id job-id :status "cancelled"})))} + (dj/cancel-job job-id) + (await (hva/settle)) + (t/is (= [{:cmd :cancel-job :params {:id job-id}}] @calls))))) + + (t/testing "a failure is swallowed: the job may have just finished" + (await + (mock/with-mocks* + {rp/cmd! (mock/stub (fn [_ _] + (rx/throw (ex-info "gone" {}))))} + (dj/cancel-job job-id) + (await (hva/settle))))))) diff --git a/frontend/test/frontend_tests/data/jobs_test.cljs b/frontend/test/frontend_tests/data/jobs_test.cljs new file mode 100644 index 0000000000..996d9d1f0b --- /dev/null +++ b/frontend/test/frontend_tests/data/jobs_test.cljs @@ -0,0 +1,176 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.data.jobs-test + "Following a job: the progress the websocket publishes and the outcome + the row holds." + (:require + [app.common.uuid :as uuid] + [app.main.data.jobs :as dj] + [app.main.repo :as rp] + [app.util.http :as http] + [app.util.websocket :as ws] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [cuerdas.core :as str] + [frontend-tests.helpers.async :as hva] + [frontend-tests.helpers.mock :as mock])) + +(def ^:private job-id + (uuid/next)) + +(defn- job + "The row of a job, as `get-job` answers it." + [status & {:as extra}] + (merge {:id job-id :status status :name "export-binfile"} extra)) + +(defn- event + ([kind payload] (event job-id kind payload)) + ([id kind payload] + {:type :job-event + :job-id id + :kind kind + :payload payload})) + +(defn- message + [payload] + {:type :message :payload payload}) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; FOLLOWING A JOB +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest ^:async progress-events-are-emitted-and-the-outcome-ends-the-stream + (let [ws-stream (rx/subject) + row (atom (job "running")) + seen (atom [])] + + (await + (mock/with-mocks* + {rp/cmd! (mock/stub (fn [_ _] (rx/of @row))) + ws/get-rcv-stream (mock/stub (fn [_] ws-stream))} + + (let [done (hva/observe (dj/watch-job nil job-id) + {:on-next #(swap! seen conj %)})] + + (rx/push! ws-stream + (message (event :progress {:stage :pages + :counters {:pages {:current 1 :total 2}}}))) + + (t/testing "an event of another job is not part of this stream" + (rx/push! ws-stream + (message (event (uuid/next) :progress {:stage :media}))) + (await (hva/settle)) + (t/is (= [{:kind :progress + :payload {:stage :pages + :counters {:pages {:current 1 :total 2}}}}] + @seen))) + + (t/testing "the closing event makes the stream read the row and end" + (reset! row (job "completed" :result {:resource-uri "uri"})) + (rx/push! ws-stream (message (event :end {:outcome "completed"}))) + (await done) + + (t/is (= [{:kind :progress + :payload {:stage :pages + :counters {:pages {:current 1 :total 2}}}} + {:status "completed" :result {:resource-uri "uri"}}] + @seen)))))))) + +(t/deftest ^:async a-job-that-already-ended-needs-no-event + (let [ws-stream (rx/subject) + seen (atom [])] + + (await + (mock/with-mocks* + {rp/cmd! (mock/stub (fn [_ _] + (rx/of (job "completed" :result {:resource-uri "uri"})))) + ws/get-rcv-stream (mock/stub (fn [_] ws-stream))} + + (let [done (hva/observe (dj/watch-job nil job-id) + {:on-next #(swap! seen conj %)})] + (await done) + + (t/testing "the first read of the row is the outcome" + (t/is (= [{:status "completed" :result {:resource-uri "uri"}}] + @seen)))))))) + +(t/deftest ^:async a-reconnect-reads-the-row-again + (let [ws-stream (rx/subject) + row (atom (job "running")) + seen (atom [])] + + (await + (mock/with-mocks* + {rp/cmd! (mock/stub (fn [_ _] (rx/of @row))) + ws/get-rcv-stream (mock/stub (fn [_] ws-stream))} + + (let [done (hva/observe (dj/watch-job nil job-id) + {:on-next #(swap! seen conj %)})] + (await (hva/settle)) + + (t/testing "a job that is still running reports nothing" + (t/is (= [] @seen))) + + (t/testing "a reconnect answers for a job that failed while away" + (reset! row (job "failed" :error {:type :internal + :code :boom + :hint "boom"})) + (rx/push! ws-stream {:type :opened}) + (await done) + + (t/is (= [{:status "failed" + :error {:type :internal :code :boom :hint "boom"}}] + @seen)))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; THE COMMANDS THAT FEED IT +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn- capture-requests! + [requests] + (fn [request] + (swap! requests conj request) + (rx/of {:status 200 + :uri (:uri request) + :headers {"content-type" "application/json"} + :body "{}"}))) + +(t/deftest ^:async the-commands-point-at-the-jobs-api + (let [requests (atom [])] + + (await + (mock/with-mocks* + {http/fetch (mock/stub (capture-requests! requests)) + http/response->map (mock/stub identity) + http/process-response-type (mock/stub (fn [_ response] (rx/of response)))} + + (await (hva/->promise (rp/cmd! :create-export-binfile-job + {:file-ids #{job-id} :export-type :detach-libraries}))) + (await (hva/->promise (rp/cmd! :create-import-binfile-job {:project-id (str job-id)}))) + (await (hva/->promise (rp/cmd! :get-job {:id (str job-id)}))) + (await (hva/->promise (rp/cmd! :create-export-job {}))) + + (let [[export import read exporter] @requests] + (t/testing "the binfile export asks the backend, not the exporter service" + (t/is (= :post (:method export))) + (t/is (str/ends-with? (:path (:uri export)) + "/api/main/methods/create-export-binfile-job"))) + + (t/testing "the import job needs no override: its name is the method" + (t/is (= :post (:method import))) + (t/is (str/ends-with? (:path (:uri import)) + "/api/main/methods/create-import-binfile-job"))) + + (t/testing "the read is a get with the id of the job" + (t/is (= :get (:method read))) + (t/is (str/ends-with? (:path (:uri read)) + "/api/main/methods/get-job")) + (t/is (= (str job-id) (:id (:query read))))) + + (t/testing "and the exporter keeps its own name" + (t/is (= :post (:method exporter))) + (t/is (str/ends-with? (:path (:uri exporter)) "/api/export/jobs")))))))) diff --git a/frontend/test/frontend_tests/data/nitrate_test.cljs b/frontend/test/frontend_tests/data/nitrate_test.cljs index 365715d2e4..2ea4442157 100644 --- a/frontend/test/frontend_tests/data/nitrate_test.cljs +++ b/frontend/test/frontend_tests/data/nitrate_test.cljs @@ -15,10 +15,12 @@ [app.main.data.notifications :as ntf] [app.main.data.team :as dt] [app.main.repo :as rp] + [app.main.router :as rt] [app.main.store :as st] [app.main.ui.auth.verify-token :as verify-token] [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] [frontend-tests.helpers.mock :as mock] [potok.v2.core :as ptk])) @@ -352,3 +354,43 @@ {:callback callback}) parsed (-> href u/uri :query u/query-string->map :callback)] (t/is (= callback parsed))))) + +(defn- ^:async observe-add-team-rejected-by-permissions + "Runs `add-team-to-organization` for `team` while the backend rejects it + with `:not-allowed`, as when another session changed the permissions + after the modal opened. Resolves to the emitted events." + [team] + (let [emitted (atom []) + event (dnt/add-team-to-organization {:team-id (:id team) + :organization-id "org-2" + :skip-audit? true}) + state {:teams {(:id team) team}}] + (await + (mock/with-mocks* + {rt/get-current-href (mock/stub (constantly "http://localhost/#/dashboard")) + rp/cmd! (mock/stub + (fn [cmd _params] + (case cmd + :check-nitrate-sso (rx/of {:authorized true}) + ::dnt/add-team-to-organization + (rx/throw (ex-info "not allowed" {:type :validation :code :not-allowed}))))) + modal/show (mock/stub (fn [& args] {:modal-show args}))} + (await (async/observe (ptk/watch event state nil) + :on-next #(swap! emitted conj %))))) + @emitted)) + +(t/deftest ^:async add-team-to-organization-shows-no-permission-on-stale-move + (let [emitted (await (observe-add-team-rejected-by-permissions + {:id "team-1" :organization {:id "org-1"}}))] + (t/is (= 2 (count emitted))) + (t/is (= ::dt/fetch-teams (ptk/type (first emitted)))) + (t/is (= {:modal-show [:no-permission-modal {:type :no-organizations-change}]} + (second emitted))))) + +(t/deftest ^:async add-team-to-organization-shows-no-permission-on-stale-add + (let [emitted (await (observe-add-team-rejected-by-permissions + {:id "team-1"}))] + (t/is (= 2 (count emitted))) + (t/is (= ::dt/fetch-teams (ptk/type (first emitted)))) + (t/is (= {:modal-show [:no-permission-modal {:type :no-organizations-create}]} + (second emitted))))) diff --git a/frontend/test/frontend_tests/data/persistence_commits_test.cljs b/frontend/test/frontend_tests/data/persistence_commits_test.cljs new file mode 100644 index 0000000000..2a85f3e4f3 --- /dev/null +++ b/frontend/test/frontend_tests/data/persistence_commits_test.cljs @@ -0,0 +1,175 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.data.persistence-commits-test + "Tests for the pure commit bookkeeping of persistence + (`app.main.data.persistence.commits`)." + (:require + [app.common.uuid :as uuid] + [app.main.data.persistence.commits :as pcm] + [cljs.test :as t :include-macros true])) + +(defn- commit + [file-id undo-group] + {:id (uuid/next) + :file-id file-id + :file-revn 0 + :file-vern 0 + :features #{} + :source :local + :undo-group undo-group + :redo-changes [{:type :mod-page :id (uuid/next) :name "redo"}] + :undo-changes [{:type :mod-page :id (uuid/next) :name "undo"}] + :remote-seq 0}) + +(defn- queued + "Persistence state with `entries` queued, in order." + [& entries] + {:queue (into #queue [] (map :id) entries) + :index (into {} (map (juxt :id identity)) entries)}) + +(t/deftest a-queue-entry-keeps-only-what-a-save-needs + (let [file-id (uuid/next) + c1 (commit file-id (uuid/next)) + c2 (commit file-id (uuid/next)) + entry (pcm/queue-entry [c1 c2])] + (t/is (= (:id c1) (:id entry))) + (t/is (= file-id (:file-id entry))) + (t/is (not (contains? entry :redo-changes))) + (t/is (= [(:id c1) (:id c2)] (mapv :id (:commits entry)))) + (t/is (not (contains? (first (:commits entry)) :source))))) + +(t/deftest pending-commits-are-the-queued-then-the-buffered-of-one-file + (let [file-id (uuid/next) + other-id (uuid/next) + c1 (commit file-id nil) + c2 (commit other-id nil) + c3 (commit file-id nil) + pstate (assoc (queued (pcm/queue-entry [c1]) (pcm/queue-entry [c2])) + :buffer [c3])] + (t/is (= [(:id c1) (:id c3)] (mapv :id (pcm/pending-commits pstate file-id)))))) + +(t/deftest a-rollback-plan-reverts-whole-undo-groups-across-files + (let [file-id (uuid/next) + lib-id (uuid/next) + group (uuid/next) + kept (commit file-id (uuid/next)) + bad (commit file-id (uuid/next)) + lib-1 (commit lib-id (uuid/next)) + lib-2 (commit lib-id (uuid/next)) + linked (commit lib-id group) + later (commit file-id group) + saved (assoc (commit lib-id group) :file-id lib-id) + head (pcm/queue-entry [kept bad]) + pstate (-> (queued head (pcm/queue-entry [lib-1 linked lib-2])) + (assoc :buffer [later] + :saved [saved])) + plan (pcm/rollback-plan pstate head {:commit-ids [(:id bad)]})] + (t/testing "this file reverts from the rejected commit on" + (t/is (= [(:id bad) (:id later)] + (mapv :id (get-in plan [:files file-id :reverted]))))) + (t/testing "the library reverts from the commit that shares a group on" + (t/is (= [(:id linked) (:id lib-2)] + (mapv :id (get-in plan [:files lib-id :reverted])))) + (t/is (= [(:id lib-1)] (mapv :id (get-in plan [:files lib-id :redo]))))) + (t/testing "saved commits of a reverted group are compensated" + (t/is (= [(:id saved)] (mapv :id (:compensate plan))))) + (t/is (= {:reverted 5 :files 2} (pcm/rollback-summary plan))) + + (t/testing "the rolled back commits leave the queue and the buffer" + (let [pstate' (pcm/without-rolled-back pstate head plan)] + (t/is (= [(:id kept)] (mapv :id (get-in pstate' [:index (:id head) :commits])))) + (t/is (= [(:id lib-1)] + (mapv :id (get-in pstate' [:index (:id lib-1) :commits])))) + (t/is (empty? (:buffer pstate'))) + (t/is (empty? (:saved pstate'))))))) + +(t/deftest an-entry-left-without-commits-leaves-the-queue + (let [file-id (uuid/next) + lib-id (uuid/next) + group (uuid/next) + bad (commit file-id group) + linked (commit lib-id group) + head (pcm/queue-entry [bad]) + lib (pcm/queue-entry [linked]) + pstate (queued head lib) + plan (pcm/rollback-plan pstate head {:commit-ids [(:id bad)]}) + pstate' (pcm/without-rolled-back pstate head plan)] + (t/is (= [(:id head)] (vec (:queue pstate'))) "the in-flight entry stays until it is discarded") + (t/is (not (contains? (:index pstate') (:id lib)))))) + +(t/deftest compensation-undoes-newest-first + (let [c1 {:redo-changes [:r1] :undo-changes [:u1]} + c2 {:redo-changes [:r2] :undo-changes [:u2]}] + (t/is (= {:redo-changes [:u2 :u1] :undo-changes [:r1 :r2]} + (pcm/compensation-changes [c1 c2]))))) + +(t/deftest a-rollback-plan-names-the-index-before-the-oldest-reverted-undo + (let [file-id (uuid/next) + bad (commit file-id (uuid/next)) + undo-1 (assoc (commit file-id (uuid/next)) :undo-index 4) + undo-2 (assoc (commit file-id (uuid/next)) :undo-index 3) + head (pcm/queue-entry [bad undo-1]) + pstate (assoc (queued head) :buffer [undo-2])] + (t/is (= 4 (:undo-index (pcm/rollback-plan pstate head {:commit-ids [(:id bad) (:id undo-1)]})))) + (t/is (nil? (:undo-index (pcm/rollback-plan (queued (pcm/queue-entry [bad])) + (pcm/queue-entry [bad]) + {:commit-ids [(:id bad)]})))))) + +(t/deftest saved-commits-are-kept-while-a-pending-commit-shares-their-group + (let [file-id (uuid/next) + group (uuid/next) + shared (commit file-id group) + alone (commit file-id (uuid/next)) + pending (commit file-id group) + old (commit file-id (uuid/next)) + entry (pcm/queue-entry [shared alone]) + pstate (pcm/keep-saved-commits {:queue #queue [] :index {} :buffer [pending] :saved [old]} + entry)] + (t/is (= [(:id shared)] (mapv :id (:saved pstate)))) + (t/is (= file-id (:file-id (first (:saved pstate)))) + "a saved commit carries the file it was saved to"))) + +(t/deftest remote-changes-older-than-the-oldest-pending-commit-are-forgotten + (let [file-id (uuid/next) + pending (assoc (commit file-id nil) :remote-seq 2) + pstate {:queue #queue [] :index {} :buffer [pending] + :remote-changes {file-id [{:seq 1 :changes [:old]} {:seq 3 :changes [:new]}]}}] + (t/is (= [{:seq 3 :changes [:new]}] + (get-in (pcm/prune-remote-changes pstate file-id) [:remote-changes file-id]))) + (t/is (not (contains? (:remote-changes (pcm/prune-remote-changes (dissoc pstate :buffer) file-id)) + file-id)) + "a file with nothing pending keeps no remote changes"))) + +(t/deftest a-release-plan-reverts-every-pending-commit-of-the-file-and-its-groups + (let [file-id (uuid/next) + lib-id (uuid/next) + group (uuid/next) + edit (commit file-id (uuid/next)) + linked (commit file-id group) + lib (commit lib-id group) + pstate (assoc (queued (pcm/queue-entry [edit linked])) + :buffer [lib]) + plan (pcm/release-plan pstate file-id)] + (t/is (= [(:id edit) (:id linked)] (mapv :id (get-in plan [:files file-id :reverted])))) + (t/is (empty? (get-in plan [:files file-id :redo]))) + (t/is (= [(:id lib)] (mapv :id (get-in plan [:files lib-id :reverted]))) + "the rest of the undo group goes too"))) + +(t/deftest rollback-targets-name-the-files-pages-and-page-shapes-of-the-reverted-changes + (let [[f1 f2 p1 p2 p3 s1 s2 s3 s4 s5 c1] (repeatedly 11 uuid/next) + plan {:files {f1 {:reverted [{:redo-changes [{:type :add-obj :page-id p1 :id s1} + {:type :mov-objects :page-id p1 :shapes [s2 s3]} + {:type :mod-obj :component-id (uuid/next) :id s4} + {:type :mod-page :id p2} + {:type :set-comment-thread-position :page-id p1 + :comment-thread-id c1}]}]}} + :compensate [{:file-id f2 :redo-changes [{:type :del-obj :page-id p3 :id s5}]}]}] + (t/is (= {:file-ids #{f1 f2} + :page-ids #{p1 p2 p3} + :shape-ids #{s1 s2 s3 s5} + :comment-thread-ids #{c1}} + (pcm/rollback-targets plan))))) diff --git a/frontend/test/frontend_tests/data/persistence_lock_test.cljs b/frontend/test/frontend_tests/data/persistence_lock_test.cljs new file mode 100644 index 0000000000..41ec6d38f9 --- /dev/null +++ b/frontend/test/frontend_tests/data/persistence_lock_test.cljs @@ -0,0 +1,209 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.data.persistence-lock-test + "Tests for the commits made while saves fail and the workspace is + locked (`app.main.data.persistence.lock`): user edits are refused, the + undo of rolled-back commits and derived data still commit." + (:require + [app.common.test-helpers.components :as cthc] + [app.common.test-helpers.compositions :as ctho] + [app.common.test-helpers.files :as cthf] + [app.common.test-helpers.ids-map :as cthi] + [app.common.test-helpers.shapes :as cths] + [app.common.uuid :as uuid] + [app.main.data.changes :as dch] + [app.main.data.persistence :as dps] + [app.main.data.persistence.lock :as plk] + [app.main.data.workspace :as dw] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.workspace.undo :as dwu] + [app.main.repo :as rp] + [app.render-wasm.api :as wasm.api] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.mock :as mock] + [frontend-tests.helpers.persistence :as thp] + [frontend-tests.helpers.state :as ths] + [potok.v2.core :as ptk])) + +(t/use-fixtures :each {:before cthi/reset-idmap!}) + +(defn- server-error + "A save answer that fails for good: an unexpected server error." + [] + (rp/handle-response {:status 500 + :body {:type :server-error :code :unhandled} + :uri "https://penpot.example.com/api/main/methods/update-file"})) + +(defn- answers + "A transport that gives each save, in order, the next of `responses` + (functions returning the answer's observable)." + [& responses] + (let [pending (atom responses)] + (fn [_ _] + (let [respond (first @pending)] + (swap! pending rest) + (respond))))) + +(defn- saved + [revn] + #(rx/of {:revn revn})) + +(def ^:private lock + "The lock a failed save leaves, set by hand." + #(assoc-in % [:workspace-global :persistence-lock] {:read-only? false})) + +(defn- record-commits + "Records, for each commit on `store`, whether the workspace was locked + when it was made. Answers the subscription and the atom." + [store] + (let [made (atom []) + sub (->> (ptk/input-stream store) + (rx/filter dch/commit?) + (rx/subs! (fn [_] (swap! made conj (plk/locked? @store)))))] + [sub made])) + +;; Scenario: a save fails for good and the workspace locks; the user then +;; renames the shape through some UI the lock did not hide. Proves: the +;; edit makes no commit, so nothing queues behind the failed save and the +;; shape keeps its name. +(t/deftest ^:async an-edit-while-locked-makes-no-commit + (await + (thp/with-persistence + (^:async fn [{:keys [file-id store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + [sub made] (record-commits store)] + (ptk/emit! store + (thp/open-file file-id page-id (thp/file-data page-id shape-id "B")) + (thp/rename-commit file-id page-id shape-id "A" "B") + ::dps/force-persist) + (await (async/wait-for #(plk/locked? @store) "the failed save locks the workspace")) + (reset! made []) + (ptk/emit! store + (dch/commit-changes {:redo-changes [(thp/rename page-id shape-id "C")] + :undo-changes [(thp/rename page-id shape-id "B")]}) + ::dps/force-persist) + (await (async/settle)) + (rx/dispose! sub) + (t/is (empty? @made) "no commit is made") + (t/is (= "B" (thp/shape-name store file-id page-id shape-id))) + (t/is (empty? (get-in @store [:persistence :buffer]))) + (t/is (= 1 (count (get-in @store [:persistence :queue]))) + "the queue holds only the failed edit"))) + (answers server-error)))) + +;; Scenario: the user presses Ctrl+Z while the workspace is locked. +;; Proves: the history stays where it was, since its commit would be +;; refused after the index moved. +(t/deftest ^:async undo-and-redo-do-nothing-while-locked + (let [file (-> (cthf/sample-file :file1) + (ctho/add-rect :rect1 :name "A")) + store (ths/setup-store file {:renderer :svg}) + page-id (cthf/current-page-id file) + rect-id (cthi/id :rect1) + rename (fn [from to] + (dch/commit-changes {:redo-changes [(thp/rename page-id rect-id to)] + :undo-changes [(thp/rename page-id rect-id from)]})) + group (uuid/next) + entry {:undo-changes [(thp/rename page-id rect-id "A")] + :redo-changes [(thp/rename page-id rect-id "B")] + :undo-group group + :undo-groups #{group} + :tags #{}} + name-of #(:name (cths/get-shape (ths/get-file-from-state @store) :rect1))] + (ptk/emit! store (rename "A" "B") #(assoc % :workspace-undo {:items [entry] :index 0})) + (await (async/wait-for #(= "B" (name-of)) "the shape is renamed")) + (ptk/emit! store lock dwu/undo) + (await (async/settle)) + (t/is (= "B" (name-of)) "undo changes nothing") + (t/is (= 0 (get-in @store [:workspace-undo :index]))) + (ptk/emit! store #(update % :workspace-global dissoc :persistence-lock) dwu/undo) + (await (async/wait-for #(= "A" (name-of)) "undo works once unlocked")) + (ptk/emit! store lock dwu/redo) + (await (async/settle)) + (t/is (= "A" (name-of)) "redo changes nothing") + (t/is (= -1 (get-in @store [:workspace-undo :index]))))) + +;; Scenario: one action renames a shape in a library and in this file; +;; the library's save lands, this file's fails for good and the workspace +;; locks. Edit rights come back with a role change, which releases the +;; failed edit while the lock still holds. Proves: the undo of the +;; library's saved rename still commits and is saved, so the library goes +;; back with the released edit. +(t/deftest ^:async a-release-undoes-saved-commits-while-locked + (await + (thp/with-persistence + (^:async fn [{:keys [file-id store requests]}] + (let [lib-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next) + lshape-id (uuid/next) + group (uuid/next) + linked (ptk/data-event ::dch/commit + (assoc @(thp/rename-commit lib-id page-id lshape-id "X" "Y") + :undo-group group)) + commit (ptk/data-event ::dch/commit + (assoc @(thp/rename-commit file-id page-id shape-id "A" "B") + :undo-group group)) + [sub made] (record-commits store)] + (ptk/emit! store (thp/open-file-with-library file-id lib-id page-id + (thp/file-data page-id shape-id "B") + (thp/file-data page-id lshape-id "Y"))) + (ptk/emit! store linked commit ::dps/force-persist) + (await (async/wait-for #(plk/locked? @store) "this file's failed save locks the workspace")) + (reset! made []) + (ptk/emit! store (ptk/data-event :app.main.data.common/change-team-role)) + (await (async/wait-for #(seq @made) "the library's rename is undone")) + (t/is (= [true] @made) "the undo commits while the lock holds") + (t/is (= "X" (thp/shape-name store lib-id page-id lshape-id))) + (t/is (= "A" (thp/shape-name store file-id page-id shape-id))) + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the undo is saved")) + (rx/dispose! sub) + (t/is (= 3 (count @requests))) + (t/is (= lib-id (get-in (nth @requests 2) [1 :id]))) + (t/is (not (plk/locked? @store))))) + (answers (saved 1) server-error (saved 2))))) + +;; Scenario: texts without layout data are measured while the workspace is +;; locked, as on a reload during the lock. Proves: the derived layout still +;; commits, so the text and its layout data stay in step. +(t/deftest ^:async text-layout-data-commits-while-locked + (let [file (-> (cthf/sample-file :file1) + (ctho/add-text :text1 "hello")) + store (ths/setup-store file {:renderer :svg}) + position-data [{:x 0 :y 0 :width 80 :height 16 :fills [] :text "hello"}] + [sub made] (record-commits store)] + (await + (mock/with-mocks* + {wasm.api/calculate-position-data (mock/stub (fn [_] position-data))} + (ptk/emit! store lock (dw/update-page-position-data)) + (await (async/wait-for #(seq @made) "the layout data is committed")))) + (rx/dispose! sub) + (t/is (= [true] @made)) + (t/is (= position-data + (:position-data (cths/get-shape (ths/get-file-from-state @store) :text1)))))) + +;; Scenario: a main component changes while the workspace is locked, by the +;; undo of a rolled-back commit. Proves: its modification time still +;; commits, so other files see the component changed again. +(t/deftest ^:async a-component-touch-commits-while-locked + (let [file (-> (cthf/sample-file :file1) + (ctho/add-simple-component :component1 :main-root :main-child)) + comp-id (cthi/id :component1) + ;; No modification time yet, so the touch shows whatever the clock + file (update-in file [:data :components comp-id] dissoc :modified-at) + store (ths/setup-store file {:renderer :svg}) + [sub made] (record-commits store)] + (ptk/emit! store lock (dwl/touch-component comp-id)) + (await (async/wait-for #(seq @made) "the touch is committed")) + (rx/dispose! sub) + (t/is (= [true] @made)) + (t/is (some? (:modified-at (cthc/get-component (ths/get-file-from-state @store) :component1)))))) diff --git a/frontend/test/frontend_tests/data/persistence_retry_test.cljs b/frontend/test/frontend_tests/data/persistence_retry_test.cljs index 1eba1bd1b5..1f79811018 100644 --- a/frontend/test/frontend_tests/data/persistence_retry_test.cljs +++ b/frontend/test/frontend_tests/data/persistence_retry_test.cljs @@ -15,7 +15,6 @@ (:require [app.common.time :as ct] [app.common.uuid :as uuid] - [app.main.data.changes :as dch] [app.main.data.event :as ev] [app.main.data.persistence :as dps] [app.main.errors :as errors] @@ -27,62 +26,9 @@ [cljs.test :as t :include-macros true] [frontend-tests.helpers.async :as async] [frontend-tests.helpers.mock :as mock] + [frontend-tests.helpers.persistence :refer [local-commit with-persistence]] [potok.v2.core :as ptk])) -(defn- local-commit - "Builds a synthetic local commit event for `file-id`: a page edit as redo, - no undo, from the `:local` source." - [file-id] - (ptk/data-event ::dch/commit - {:id (uuid/next) - :file-id file-id - :file-revn 0 - :file-vern 0 - :source :local - :features #{} - :redo-changes [{:type :mod-page :id (uuid/next) :name "Edited"}] - :undo-changes []})) - -(defn- with-persistence - "Async fixture for the persistence tests: mocks the transport, flash and - the leave-page task set (`:tasks`), and runs `f` with persistence - initialized. - - Evaluates to a promise resolving once `f` settles and teardown completes; - `await` it from an `^:async` test. - - By default the transport responds through the `:response` subject. Pass a - `respond` function (`(fn [cmd params] observable)`) to drive the transport - response directly instead. Delivery through the subject is asynchronous - (`observe-on :async`): await each effect — via `wait-for` — before - asserting it." - [f & [respond]] - (let [file-id (uuid/next) - response (rx/subject) - failures (atom []) - requests (atom []) - tasks (atom #{}) - store (ptk/store {:state {:current-file-id file-id - :permissions {:can-edit true} - :files {file-id {:id file-id :revn 0}}} - :on-error #(t/is false (str %))})] - (mock/with-mocks* - {rp/cmd! (mock/stub (fn [cmd params] - (swap! requests conj [cmd params]) - (if respond - (respond cmd params) - (->> response (rx/take 1) (rx/observe-on :async))))) - errors/flash (fn [& {:keys [cause]}] - (swap! failures conj cause)) - st/ongoing-tasks tasks} - (try - (ptk/emit! store (dps/initialize-persistence)) - (await (f {:file-id file-id :response response :failures failures - :requests requests :store store :tasks tasks})) - (finally - (rx/dispose! store) - (rx/end! response)))))) - (defn- instant-backoff "Timer double: the backoff delays fire at once and the slow cycle never does, so a failure that outlasts the backoff settles in `:error`." @@ -242,6 +188,42 @@ :code :idle-in-transaction-timeout :hint "The transaction was idle too long and was cancelled"}}))) +;; Scenario: every save answer is a 429, the profile's save permit being +;; held by another save. The connection is fine. Proves: the reconnect +;; notice and, once the backoff is spent, the slow-cycle notice both say the +;; server is busy instead of that the connection was lost. +(t/deftest ^:async a-busy-server-gets-its-own-retry-notices + (let [timer-s (rx/subject) + hints (atom [])] + (await + (with-persistence + (^:async fn [{:keys [file-id requests store]}] + (await + (mock/with-mocks* + {rx/timer (mock/stub (fn [ms] + (if (= ms dps/slow-retry-delay-ms) + (rx/subject) + timer-s))) + errors/flash (fn [& {:keys [hint]}] + (swap! hints conj hint) + (js/Promise.resolve nil)) + i18n/tr (fn ([key] (str "translated:" key)) ([key & _] (str "translated:" key)))} + (ptk/emit! store (local-commit file-id) ::dps/force-persist) + (await (async/wait-for #(= 1 (get-in @store [:persistence :attempts])) + "the first answer starts the retries")) + (t/is (= "translated:errors.save-retrying-busy" + (get-in @store [:notification :content]))) + (doseq [n [2 3 4]] + (rx/push! timer-s :tick) + (await (async/wait-for #(= n (count @requests)) "the retry is sent"))) + (await (async/wait-for #(seq @hints) "the slow-cycle notice")) + (t/is (= ["translated:errors.save-still-retrying-busy"] @hints)) + (rx/end! timer-s)))) + (fn [_ _] + (rp/handle-response {:status 429 + :body nil + :uri "https://penpot.example.com/api/main/methods/update-file"})))))) + ;; Scenario: the backend fails the save for any other reason. Proves: only ;; the busy-server codes retry; a server defect stops saving at once. (t/deftest ^:async other-server-errors-stay-terminal @@ -685,6 +667,44 @@ (rx/throw (ex-info "offline" {:type :offline})) (rx/of {:revn 1}))))))) +;; Scenario: the network stays down past the backoff, so the slow cycle +;; takes over. The workspace stays read-only through each slow attempt, +;; including while one is in flight, and goes back to editable once one +;; lands. Proves: the lock follows the failure, not the status flips of +;; the slow cycle. +(t/deftest ^:async the-slow-cycle-keeps-the-workspace-locked-until-a-save-lands + (let [calls (atom 0) + cycles (atom []) + attempt-s (rx/subject)] + (await + (with-persistence + (^:async fn [{:keys [file-id store]}] + (await + (mock/with-mocks* + {rx/timer (mock/stub (slow-cycle-timer cycles))} + (ptk/emit! store (local-commit file-id) ::dps/force-persist) + (await (async/wait-for #(and (= 1 (count @cycles)) + (some? (get-in @store [:workspace-global :persistence-lock]))) + "the spent backoff locks the workspace")) + (t/is (true? (get-in @store [:workspace-global :read-only?]))) + (t/is (dps/save-retrying? (:persistence @store))) + + (rx/push! (last @cycles) :tick) + (await (async/wait-for #(= 5 @calls) "the slow attempt is sent")) + (await (async/settle)) + (t/is (= :saving (get-in @store [:persistence :status]))) + (t/is (some? (get-in @store [:workspace-global :persistence-lock])) + "a slow attempt in flight keeps the lock") + + (rx/push! attempt-s {:revn 1}) + (await (async/wait-for #(nil? (get-in @store [:workspace-global :persistence-lock])) + "the landed save lifts the lock")) + (t/is (false? (get-in @store [:workspace-global :read-only?])))))) + (fn [_ _] + (if (< (swap! calls inc) 5) + (rx/throw (ex-info "offline" {:type :offline})) + (->> attempt-s (rx/take 1) (rx/observe-on :async)))))))) + ;; Scenario: a gateway error outlasts the backoff, then the proxy recovers. ;; Proves: each gateway status takes the slow cycle and saves the held edit. (t/deftest ^:async a-gateway-error-arms-the-slow-cycle @@ -737,7 +757,8 @@ ;; Scenario: a gateway answers 504 only after its own timeout, while the ;; backend may still be applying the save. Proves: slow failures below the ;; limit still arm the slow cycle; at the limit the save stops being sent, -;; and opening a file releases it instead of sending it again. +;; even with backoff retries left, and recovery releases it instead of +;; sending it again. (t/deftest ^:async slow-failures-stop-the-slow-cycle-at-the-limit (doseq [[slow-count armed?] [[(dec dps/max-slow-failures) true] [dps/max-slow-failures false]]] @@ -755,7 +776,8 @@ (await (async/wait-for #(= :error (get-in @store [:persistence :status])) (str slow-count " slow failures spend the backoff"))) (await (async/settle)) - (t/is (= 4 (count @requests))) + (t/is (= (if armed? 4 slow-count) (count @requests)) + "the backoff sends no attempt past the limit") (t/is (= slow-count (get-in @store [:persistence :slow-failures]))) (t/is (= (if armed? 1 0) (count @cycles)) (str slow-count " slow failures " (if armed? "arm" "do not arm") " the slow cycle")) @@ -764,7 +786,7 @@ (ptk/emit! store (ptk/data-event :app.main.data.common/change-team-role)) (await (async/wait-for #(empty? (get-in @store [:persistence :queue])) "recovery releases the held edit")) - (t/is (= 4 (count @requests)) "the released edit is not sent again"))))) + (t/is (= slow-count (count @requests)) "the released edit is not sent again"))))) (fn [_ _] (when (<= (swap! calls inc) slow-count) (swap! clock + dps/slow-failure-ms)) @@ -792,9 +814,48 @@ "the attempt fails")) (await (async/settle)) (t/is (= 1 (count @cycles)) "no further cycle is armed") - (t/is (= 1 (count (get-in @store [:persistence :queue]))))))) + (t/is (= 1 (count (get-in @store [:persistence :queue])))) + (t/is (not (dps/save-retrying? (:persistence @store))) + "the stopped cycle no longer reads as retrying")))) (fn [_ _] (rx/throw (ex-info "offline" {:type :offline}))))))) +;; Scenario: the backoff fails fast (offline), then every slow-cycle attempt +;; fails slowly until the limit. Proves: the cycle stops at the limit and +;; the failure no longer reads as retrying, so the banner shows the stopped +;; copy. +(t/deftest ^:async a-slow-cycle-stopped-at-the-limit-is-no-longer-retrying + (let [clock (atom 0) + calls (atom 0) + cycles (atom [])] + (await + (with-persistence + (^:async fn [{:keys [file-id requests store]}] + (await + (mock/with-mocks* + {rx/timer (mock/stub (slow-cycle-timer cycles)) + ct/now (mock/stub #(ct/inst @clock))} + (ptk/emit! store (local-commit file-id) ::dps/force-persist) + (await (async/wait-for #(= 1 (count @cycles)) "the fast backoff arms the slow cycle")) + (doseq [n (range 1 dps/max-slow-failures)] + (rx/push! (last @cycles) :tick) + (await (async/wait-for #(= (inc n) (count @cycles)) + (str "slow failure " n " arms the next cycle")))) + (t/is (dps/save-retrying? (:persistence @store))) + (rx/push! (last @cycles) :tick) + (await (async/wait-for #(= (+ 4 dps/max-slow-failures) (count @requests)) + "the last slow attempt is sent")) + (await (async/wait-for #(= :error (get-in @store [:persistence :status])) + "the last slow attempt fails")) + (await (async/settle)) + (t/is (= dps/max-slow-failures (count @cycles)) "no further cycle is armed") + (t/is (nil? (get-in @store [:persistence :recovering]))) + (t/is (not (dps/save-retrying? (:persistence @store))))))) + (fn [_ _] + ;; The four backoff sends fail at once, the slow-cycle ones slowly + (when (> (swap! calls inc) 4) + (swap! clock + dps/slow-failure-ms)) + (rx/throw (ex-info "offline" {:type :offline}))))))) + ;; Scenario: the backoff is spent and the user leaves the workspace, which ;; stops the slow cycle, then opens a file again. Recovery sends the queue ;; once more instead of releasing it, and it saves. Proves: edits held by a @@ -823,6 +884,35 @@ (rx/throw (ex-info "offline" {:type :offline})) (rx/of {:revn 1}))))))) +;; Scenario: saves keep failing on a transport error, so the slow cycle +;; holds the edit, and the user is made a viewer; the role event comes again +;; while the user is still a viewer. Proves: the edit stays queued and the +;; leave warning stays on until edit rights return, instead of being dropped +;; while the canvas still shows it. +(t/deftest ^:async a-viewer-keeps-the-edit-held-by-the-slow-cycle + (let [cycles (atom [])] + (await + (with-persistence + (^:async fn [{:keys [file-id requests store]}] + (await + (mock/with-mocks* + {rx/timer (mock/stub (slow-cycle-timer cycles))} + (ptk/emit! store (local-commit file-id) ::dps/force-persist) + (await (async/wait-for #(= 1 (count @cycles)) "the slow cycle is armed")) + (ptk/emit! store + #(assoc-in % [:permissions :can-edit] false) + (ptk/data-event :app.main.data.common/change-team-role)) + (await (async/wait-for #(= :save-permission-denied + (get-in @store [:persistence :error :code])) + "the viewer cannot send the edit")) + (ptk/emit! store (ptk/data-event :app.main.data.common/change-team-role)) + (await (async/settle)) + (t/is (= :error (get-in @store [:persistence :status]))) + (t/is (= 1 (count (get-in @store [:persistence :queue]))) + "the edit stays queued while the user is a viewer") + (t/is (= 4 (count @requests)) "a viewer sends nothing")))) + (fn [_ _] (rx/throw (ex-info "offline" {:type :offline}))))))) + ;; Leaving the workspace tests. ;; Scenario: a save fails on a transport error and the user leaves the diff --git a/frontend/test/frontend_tests/data/persistence_rollback_test.cljs b/frontend/test/frontend_tests/data/persistence_rollback_test.cljs new file mode 100644 index 0000000000..3efff3cb85 --- /dev/null +++ b/frontend/test/frontend_tests/data/persistence_rollback_test.cljs @@ -0,0 +1,843 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.data.persistence-rollback-test + "Tests for the rollback of a save the server partly rejected + (`app.main.data.persistence`): which commits are reverted, how peers' + changes survive, and how the UI state follows. The pure plan behind it + is tested in `frontend-tests.data.persistence-commits-test`." + (:require + [app.common.geom.point :as gpt] + [app.common.time :as ct] + [app.common.types.shape :as cts] + [app.common.uuid :as uuid] + [app.main.data.changes :as dch] + [app.main.data.modal :as-alias modal] + [app.main.data.persistence :as dps] + [app.main.data.workspace :as dw] + [app.main.data.workspace.comments :as dwcm] + [app.main.data.workspace.undo :as dwu] + [app.main.errors :as errors] + [app.util.i18n :as i18n] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.mock :as mock] + [frontend-tests.helpers.persistence :as thp] + [potok.v2.core :as ptk])) + +(defn- grouped + "`commit` with its undo group set to `group`: one action that makes + several commits gives them the same undo group." + [commit group] + (ptk/data-event ::dch/commit (assoc @commit :undo-group group))) + +(defn- add-shape-commit + "A local commit of `file-id` that adds `shape` to the root of `page-id`." + [file-id page-id {:keys [id] :as shape}] + (ptk/data-event ::dch/commit + {:id (uuid/next) + :file-id file-id + :file-revn 0 + :file-vern 0 + :source :local + :features #{} + :undo-group (uuid/next) + :redo-changes [{:type :add-obj :id id :page-id page-id + :parent-id uuid/zero :frame-id uuid/zero :obj shape}] + :undo-changes [{:type :del-obj :id id :page-id page-id}]})) + +(defn- sent-changes + "The changes of each group of the `n`th save request." + [requests n] + (mapv :changes (get-in (nth @requests n) [1 :changes-with-metadata]))) + +;; Scenario: the first save carries two commits; while it is in flight a +;; third one is queued, an edit of another file is queued too, and a fourth +;; one waits in the buffer. The server keeps the first commit and rejects +;; the second. The file goes back to the first commit: the rejected one and +;; every later one of that file are undone and dropped from the queue, the +;; buffer and the undo history, while the other file's edit is still sent. +;; Proves: a rejection leaves the file as the server holds it and the queue +;; keeps going. +(t/deftest ^:async a-rejected-save-undoes-the-rejected-commit-and-every-later-one + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store failures]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + other-id (uuid/next) + commit1 (thp/rename-commit file-id page-id shape-id "A" "B") + commit2 (thp/rename-commit file-id page-id shape-id "B" "C") + commit3 (thp/rename-commit file-id page-id shape-id "C" "D") + commit4 (thp/rename-commit file-id page-id shape-id "D" "E") + other (thp/local-commit other-id)] + ;; The local data already holds the four renames + (ptk/emit! store + (thp/open-file file-id page-id (thp/file-data page-id shape-id "E")) + #(-> % + (assoc-in [:files other-id] {:id other-id :revn 0}) + (assoc :workspace-undo {:items (mapv thp/undo-entry [commit1 commit2 commit3 commit4]) + :index 3}) + (assoc-in [:workspace-local :selected] #{shape-id}))) + (ptk/emit! store commit1 commit2 ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the first two commits are sent")) + (ptk/emit! store commit3 other ::dps/force-persist) + (await (async/wait-for #(= 3 (count (get-in @store [:persistence :queue]))) + "the third commit and the other file's edit are queued")) + (ptk/emit! store commit4) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) + "the fourth commit is buffered")) + + (rx/push! response (thp/rejection (:id @commit2))) + (await (async/wait-for #(= 2 (count @requests)) "the other file's edit is sent next")) + + (t/is (= "B" (thp/shape-name store file-id page-id shape-id)) + "the file holds the commits the server kept") + (t/is (= [other-id] (mapv #(get-in @store [:persistence :index % :file-id]) + (get-in @store [:persistence :queue]))) + "only the other file's edit is still queued") + (t/is (empty? (get-in @store [:persistence :buffer]))) + (t/is (= [(:undo-group @commit1)] + (mapv :undo-group (get-in @store [:workspace-undo :items])))) + (t/is (= 0 (get-in @store [:workspace-undo :index]))) + (t/is (= 1 (count @failures)) "the user is told once") + (t/is (= other-id (get-in (second @requests) [1 :id]))) + (rx/push! response {:revn 1}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the queue drains"))))))) + +;; Scenario: an edit is buffered while a save is in flight and the save comes +;; back rejected, so the rollback drops the edit; the 200 ms pending notice +;; of that edit fires afterwards. Proves: with nothing left to save the +;; notice is ignored and the status stays :saved, so waiters such as "Pin +;; this version" are not left hanging. +(t/deftest ^:async a-pending-notice-after-a-rollback-leaves-the-status-saved + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + commit1 (thp/rename-commit file-id page-id shape-id "A" "B") + commit2 (thp/rename-commit file-id page-id shape-id "B" "C")] + (ptk/emit! store (thp/open-file file-id page-id (thp/file-data page-id shape-id "C"))) + (ptk/emit! store commit1 ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the first commit is sent")) + (ptk/emit! store commit2) + (rx/push! response (thp/rejection (:id @commit1))) + (await (async/wait-for #(and (= :saved (get-in @store [:persistence :status])) + (empty? (get-in @store [:persistence :buffer]))) + "the rollback drops both commits")) + ;; The notice the dropped edit scheduled + (ptk/emit! store (#'dps/update-status :pending)) + (await (async/settle)) + (t/is (= :saved (get-in @store [:persistence :status])))))))) + +;; Scenario: two edits wait in the buffer, the second one changing the +;; opacity, when a peer's opacity change arrives. The server keeps the first +;; edit and rejects the second. The server holds the peer's opacity, so the +;; local file must too, not the opacity from before the rejected edit. +;; Proves: the rollback applies the peer's changes again over the undone +;; commits. +(t/deftest ^:async a-rollback-keeps-a-peer-change-to-the-same-attribute + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + commit1 (thp/rename-commit file-id page-id shape-id "A" "B") + commit2 (thp/attr-commit file-id page-id shape-id :opacity 0.5 0.9)] + (ptk/emit! store (thp/open-file file-id page-id (thp/file-data page-id shape-id "B" :opacity 0.9))) + (ptk/emit! store commit1 commit2) + (await (async/wait-for #(= 2 (count (get-in @store [:persistence :buffer]))) + "both edits are buffered")) + (ptk/emit! store (thp/remote-commit file-id 1 [(thp/set-attr page-id shape-id :opacity 0.3)])) + (await (async/settle)) + (t/is (= 0.9 (thp/shape-attr store file-id page-id shape-id :opacity)) + "the local edit stays on top of the peer's while pending") + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the edits are sent")) + (rx/push! response (thp/rejection (:id @commit2))) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the rollback settles")) + (t/is (= 0.3 (thp/shape-attr store file-id page-id shape-id :opacity)) + "the peer's opacity, as on the server") + (t/is (= "B" (thp/shape-name store file-id page-id shape-id)) + "the kept edit stays")))))) + +;; Scenario: a peer's opacity change arrives while an opacity edit is +;; pending; that edit is saved (on the server it lands after the peer's). +;; Later two new edits are sent and the second is rejected. The rollback +;; must not apply the old peer change again, which would undo the saved +;; edit. Proves: remote changes older than the oldest pending commit are +;; forgotten once the commits pending before them are saved. +(t/deftest ^:async a-rollback-ignores-peer-changes-older-than-the-pending-commits + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + commit0 (thp/attr-commit file-id page-id shape-id :opacity 0.5 0.7) + commit1 (thp/rename-commit file-id page-id shape-id "A" "B") + commit2 (thp/rename-commit file-id page-id shape-id "B" "C")] + (ptk/emit! store (thp/open-file file-id page-id (thp/file-data page-id shape-id "A" :opacity 0.7))) + (ptk/emit! store commit0) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) "the edit is buffered")) + (ptk/emit! store (thp/remote-commit file-id 1 [(thp/set-attr page-id shape-id :opacity 0.3)])) + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the opacity edit is sent")) + (rx/push! response {:revn 1}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the opacity edit is saved")) + (t/is (empty? (get-in @store [:persistence :remote-changes file-id])) + "nothing pending, nothing kept") + (ptk/emit! store + #(assoc-in % [:files file-id :data :pages-index page-id :objects shape-id :name] "C") + commit1 commit2 ::dps/force-persist) + (await (async/wait-for #(= 2 (count @requests)) "the renames are sent")) + (rx/push! response (thp/rejection (:id @commit2))) + (await (async/wait-for #(= "B" (thp/shape-name store file-id page-id shape-id)) + "the rejected rename is undone")) + (t/is (= 0.7 (thp/shape-attr store file-id page-id shape-id :opacity)) + "the saved opacity edit stays")))))) + +;; Undo groups across files and saves. + +;; Scenario: one action makes a commit in this file and one in a library +;; (as updating a main component from a copy does). The library also has an +;; unrelated edit before the action and another after it. This file's save +;; comes back with the action's commit rejected while the library's commits +;; are still queued. Proves: the rejection reverts the whole action, so the +;; library drops its commit of the action and every later one, and keeps +;; the edit made before it. +(t/deftest ^:async a-rejection-reverts-the-pending-commits-of-the-same-action-in-other-files + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [lib-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next) + lshape-id (uuid/next) + group (uuid/next) + commit1 (thp/rename-commit file-id page-id shape-id "A" "B") + commit2 (grouped (thp/rename-commit file-id page-id shape-id "B" "C") group) + before (thp/rename-commit lib-id page-id lshape-id "X" "Y") + linked (grouped (thp/rename-commit lib-id page-id lshape-id "Y" "Z") group) + after (thp/rename-commit lib-id page-id lshape-id "Z" "W")] + (ptk/emit! store (thp/open-file-with-library file-id lib-id page-id + (thp/file-data page-id shape-id "C") + (thp/file-data page-id lshape-id "W"))) + (ptk/emit! store commit1 commit2 before linked after ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "this file's save is sent")) + (rx/push! response (thp/rejection (:id @commit2))) + (await (async/wait-for #(= 2 (count @requests)) "the library's save is sent next")) + (t/is (= "B" (thp/shape-name store file-id page-id shape-id))) + (t/is (= "Y" (thp/shape-name store lib-id page-id lshape-id)) + "the library goes back to the edit made before the action") + (t/is (= lib-id (get-in (second @requests) [1 :id]))) + (t/is (= [[(thp/rename page-id lshape-id "Y")]] (sent-changes requests 1)) + "only the library's edit made before the action is saved") + (rx/push! response {:revn 1}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the queue drains"))))))) + +;; Scenario: one action makes a commit in a library and one in this file. +;; The library's is saved first; then this file's save comes back with the +;; action's commit rejected. The library's commit can no longer be dropped. +;; Proves: a new commit undoes the saved part of the rejected action. +(t/deftest ^:async a-rejection-undoes-the-saved-commits-of-the-same-action-with-a-new-commit + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [lib-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next) + lshape-id (uuid/next) + group (uuid/next) + linked (grouped (thp/rename-commit lib-id page-id lshape-id "X" "Y") group) + commit (grouped (thp/rename-commit file-id page-id shape-id "A" "B") group)] + (ptk/emit! store (thp/open-file-with-library file-id lib-id page-id + (thp/file-data page-id shape-id "B") + (thp/file-data page-id lshape-id "Y"))) + (ptk/emit! store linked commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the library's save is sent first")) + (t/is (= lib-id (get-in (first @requests) [1 :id]))) + (rx/push! response {:revn 1}) + (await (async/wait-for #(= 2 (count @requests)) "this file's save is sent")) + (rx/push! response (thp/rejection (:id @commit))) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) + "a new commit undoes the library's")) + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(= 3 (count @requests)) "the new commit is sent")) + (t/is (= lib-id (get-in (nth @requests 2) [1 :id]))) + (t/is (= [[(thp/rename page-id lshape-id "X")]] (sent-changes requests 2))) + (t/is (= "A" (thp/shape-name store file-id page-id shape-id))) + (t/is (= "X" (thp/shape-name store lib-id page-id lshape-id))) + (rx/push! response {:revn 2}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the queue drains"))))))) + +;; Scenario: as above, while the user has an undo transaction open. Proves: +;; the undo commit gets its own group, not the transaction's, so a later +;; rejection in the transaction does not revert it. +(t/deftest ^:async the-undo-of-a-saved-commit-stays-out-of-an-open-transaction + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [lib-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next) + lshape-id (uuid/next) + group (uuid/next) + linked (grouped (thp/rename-commit lib-id page-id lshape-id "X" "Y") group) + commit (grouped (thp/rename-commit file-id page-id shape-id "A" "B") group)] + (ptk/emit! store (thp/open-file-with-library file-id lib-id page-id + (thp/file-data page-id shape-id "B") + (thp/file-data page-id lshape-id "Y"))) + (ptk/emit! store linked commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the library's save is sent first")) + (rx/push! response {:revn 1}) + (await (async/wait-for #(= 2 (count @requests)) "this file's save is sent")) + (ptk/emit! store (dwu/start-undo-transaction (js/Symbol) :timeout 0)) + (let [tx-group (get-in @store [:workspace-undo :transaction :commit-group])] + (rx/push! response (thp/rejection (:id @commit))) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) + "a new commit undoes the library's")) + (t/is (some? tx-group)) + (t/is (not= tx-group (get-in @store [:persistence :buffer 0 :undo-group]))) + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(= 3 (count @requests)) "the new commit is sent")) + (rx/push! response {:revn 2}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the queue drains")))))))) + +;; Scenario: as above, but the library is no longer loaded when this file's +;; save comes back. Proves: the undo of the library's saved commit is still +;; sent, without touching local data the library no longer has. +(t/deftest ^:async a-rejection-queues-the-undo-of-a-saved-commit-of-a-file-not-loaded + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [lib-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next) + lshape-id (uuid/next) + group (uuid/next) + linked (grouped (thp/rename-commit lib-id page-id lshape-id "X" "Y") group) + commit (grouped (thp/rename-commit file-id page-id shape-id "A" "B") group)] + (ptk/emit! store (thp/open-file-with-library file-id lib-id page-id + (thp/file-data page-id shape-id "B") + (thp/file-data page-id lshape-id "Y"))) + (ptk/emit! store linked commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the library's save is sent first")) + (rx/push! response {:revn 1}) + (await (async/wait-for #(= 2 (count @requests)) "this file's save is sent")) + (ptk/emit! store #(update % :files dissoc lib-id)) + (rx/push! response (thp/rejection (:id @commit))) + (await (async/wait-for #(= 3 (count @requests)) "the undo is queued and sent")) + (t/is (= lib-id (get-in (nth @requests 2) [1 :id]))) + (t/is (= [[(thp/rename page-id lshape-id "X")]] (sent-changes requests 2))) + (t/is (empty? (get-in @store [:persistence :buffer]))) + (t/is (nil? (get-in @store [:files lib-id])) "no local data is made up") + (t/is (= "A" (thp/shape-name store file-id page-id shape-id))) + (rx/push! response {:revn 2}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the queue drains"))))))) + +;; Scenario: one action makes two commits in this file and the save keeps +;; the first and rejects the second. Proves: the kept commit of the action +;; is undone too, with a new commit, so no action is left half done. +(t/deftest ^:async a-rejection-undoes-the-kept-commits-of-the-same-action + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + group (uuid/next) + commit1 (grouped (thp/rename-commit file-id page-id shape-id "A" "B") group) + commit2 (grouped (thp/attr-commit file-id page-id shape-id :opacity 1 0.5) group)] + (ptk/emit! store (thp/open-file file-id page-id (thp/file-data page-id shape-id "B" :opacity 0.5))) + (ptk/emit! store commit1 commit2 ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the action is sent")) + (rx/push! response (thp/rejection (:id @commit2))) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) + "a new commit undoes the kept one")) + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(= 2 (count @requests)) "the new commit is sent")) + (t/is (= [[(thp/rename page-id shape-id "A")]] (sent-changes requests 1))) + (t/is (= "A" (thp/shape-name store file-id page-id shape-id))) + (t/is (= 1 (thp/shape-attr store file-id page-id shape-id :opacity))) + (rx/push! response {:revn 1}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the queue drains"))))))) + +;; Scenario: applying a token to five shapes makes three commits inside one +;; undo transaction, all sent in one save; the server keeps the first two +;; and rejects the third. Proves: the whole action is reverted, the kept +;; commits by a new commit, so opacity and applied tokens are back to their +;; starting values on every shape. +(t/deftest ^:async a-rejection-reverts-every-commit-of-an-undo-transaction + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-ids (into [] (repeatedly 5 uuid/next)) + shapes (map #(cts/setup-shape {:id % :type :rect :name "R" :opacity 1 + :parent-id uuid/zero :frame-id uuid/zero}) + shape-ids) + apply-to (fn [ids] + (dch/commit-changes + {:redo-changes (into [] + (mapcat (fn [id] + [(thp/set-attr page-id id :opacity 0.5) + (thp/set-attr page-id id :applied-tokens {:opacity "half"})])) + ids) + :undo-changes (into [] + (mapcat (fn [id] + [(thp/set-attr page-id id :opacity 1) + (thp/set-attr page-id id :applied-tokens nil)])) + ids)})) + tx-id (js/Symbol)] + (ptk/emit! store (thp/open-file file-id page-id + {:pages [page-id] + :pages-index {page-id {:id page-id + :name "Page" + :objects (into {uuid/zero (cts/setup-shape {:id uuid/zero :type :frame + :shapes shape-ids})} + (map (juxt :id identity)) + shapes)}}})) + (ptk/emit! store + (dwu/start-undo-transaction tx-id :timeout 0) + (apply-to (subvec shape-ids 0 2)) + (apply-to (subvec shape-ids 2 4)) + (apply-to (subvec shape-ids 4 5)) + (dwu/commit-undo-transaction tx-id) + ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the action is sent")) + (let [groups (get-in (first @requests) [1 :changes-with-metadata])] + (t/is (= 3 (count groups)) "the save carries the three commits") + (rx/push! response (thp/rejection (:commit-id (nth groups 2))))) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) + "a new commit undoes the kept ones")) + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(= 2 (count @requests)) "the new commit is sent")) + (doseq [id shape-ids] + (t/is (= 1 (thp/shape-attr store file-id page-id id :opacity))) + (t/is (nil? (thp/shape-attr store file-id page-id id :applied-tokens)))) + (t/is (= #{1} + (into #{} + (comp (mapcat :changes) + (mapcat :operations) + (filter #(= :opacity (:attr %))) + (map :val)) + (get-in (second @requests) [1 :changes-with-metadata]))) + "the new commit puts the kept opacity back on the server") + (rx/push! response {:revn 1}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the queue drains"))))))) + +;; UI state after a rollback. + +;; Scenario: the rejected commit added the text being edited. Proves: the +;; rollback leaves edition mode, so the editor does not keep pointing at a +;; shape that no longer exists. +(t/deftest ^:async a-rollback-leaves-the-edition-of-a-removed-shape + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + text-id (uuid/next) + text (cts/setup-shape {:id text-id :type :text :parent-id uuid/zero :frame-id uuid/zero}) + commit (add-shape-commit file-id page-id text)] + (ptk/emit! store + (thp/open-file file-id page-id (-> (thp/file-data page-id shape-id "A") + (thp/add-shape page-id text))) + #(assoc-in % [:workspace-local :edition] text-id)) + (ptk/emit! store commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the new text is sent")) + (rx/push! response (thp/rejection (:id @commit))) + (await (async/wait-for #(nil? (get-in @store [:workspace-local :edition])) + "the rollback leaves edition mode")) + (t/is (nil? (thp/shape-attr store file-id page-id text-id :id)))))))) + +;; Scenario: the user edits the nodes of a saved path and the rejected commit +;; changed that path. The path editor works on its own copy of the shape, +;; which still holds the rejected content. Proves: the rollback leaves the +;; edition and drops that copy, so the editor neither shows nor saves the +;; reverted content. +(t/deftest ^:async a-rollback-leaves-the-edition-of-a-changed-path + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + commit (thp/rename-commit file-id page-id shape-id "A" "B")] + (ptk/emit! store + (thp/open-file file-id page-id (thp/file-data page-id shape-id "B")) + #(-> % + (assoc-in [:workspace-local :edition] shape-id) + (assoc-in [:workspace-local :edit-path shape-id] + {:edit-mode :move :old-content [:rejected]}) + (assoc-in [:workspace-drawing :object] + {:id shape-id :content [:rejected]}))) + (ptk/emit! store commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the edit is sent")) + (rx/push! response (thp/rejection (:id @commit))) + (await (async/wait-for #(nil? (get-in @store [:workspace-local :edition])) + "the rollback leaves edition mode")) + (t/is (= "A" (thp/shape-name store file-id page-id shape-id))) + (t/is (nil? (get-in @store [:workspace-local :edit-path shape-id])) + "the editor's state is dropped") + (t/is (nil? (get-in @store [:workspace-drawing :object])) + "the editor's copy of the path is dropped") + (await (async/settle)) + (t/is (= 1 (count @requests)) "closing the editor saves nothing")))))) + +;; Scenario: a rollback changes a shape other than the one being edited. +;; Proves: the edition stays open. +(t/deftest ^:async a-rollback-keeps-the-edition-of-an-unchanged-shape + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + other-id (uuid/next) + other (cts/setup-shape {:id other-id :type :rect :name "Other" + :parent-id uuid/zero :frame-id uuid/zero}) + commit (thp/rename-commit file-id page-id shape-id "A" "B")] + (ptk/emit! store + (thp/open-file file-id page-id (-> (thp/file-data page-id shape-id "B") + (thp/add-shape page-id other))) + #(assoc-in % [:workspace-local :edition] other-id)) + (ptk/emit! store commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the edit is sent")) + (rx/push! response (thp/rejection (:id @commit))) + (await (async/wait-for #(= "A" (thp/shape-name store file-id page-id shape-id)) + "the rollback lands")) + (await (async/settle)) + (t/is (= other-id (get-in @store [:workspace-local :edition])))))))) + +;; Scenario: the color picker is open on a new rectangle when the save that +;; created it is rejected. Proves: the rollback closes the picker, which +;; would otherwise edit a shape that no longer exists. +(t/deftest ^:async a-rollback-closes-the-color-picker-of-a-removed-shape + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + rect-id (uuid/next) + rect (cts/setup-shape {:id rect-id :type :rect :parent-id uuid/zero :frame-id uuid/zero}) + commit (add-shape-commit file-id page-id rect)] + (ptk/emit! store + (thp/open-file file-id page-id (-> (thp/file-data page-id shape-id "A") + (thp/add-shape page-id rect))) + #(-> % + (assoc-in [:workspace-local :selected] #{rect-id}) + (assoc ::modal/modal {:id (uuid/next) :type :colorpicker :props {}}))) + (ptk/emit! store commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the new rectangle is sent")) + (rx/push! response (thp/rejection (:id @commit))) + (await (async/wait-for #(empty? (get-in @store [:workspace-local :selected])) + "the rollback deselects the removed shape")) + (t/is (nil? (::modal/modal @store)) "the color picker is closed")))))) + +;; Scenario: the user undoes a rename with Ctrl+Z and the server rejects the +;; commit the undo makes. Proves: the rollback puts the rename back on +;; screen and the history back where it was, instead of clearing it. +(t/deftest ^:async a-rejected-undo-puts-the-change-and-the-history-back + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + item (thp/undo-entry (thp/rename-commit file-id page-id shape-id "A" "B"))] + (ptk/emit! store + (thp/open-file file-id page-id (thp/file-data page-id shape-id "B")) + #(assoc % :workspace-undo {:items [item] :index 0})) + (ptk/emit! store dwu/undo) + (await (async/wait-for #(= "A" (thp/shape-name store file-id page-id shape-id)) + "the rename is undone")) + (t/is (= -1 (get-in @store [:workspace-undo :index]))) + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the undo is sent")) + (rx/push! response (thp/rejection + (get-in (first @requests) [1 :changes-with-metadata 0 :commit-id]))) + (await (async/wait-for #(= "B" (thp/shape-name store file-id page-id shape-id)) + "the rename is back")) + (t/is (= [item] (get-in @store [:workspace-undo :items])) "the history stays") + (t/is (= 0 (get-in @store [:workspace-undo :index])) "it points at the rename again") + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the rollback settles"))))))) + +;; Version restores. + +(defn- restore-version + "The load of `file-id` that a peer's restore causes: another version + with `data`." + [file-id data] + (#'dw/bundle-fetched {:file {:id file-id :revn 0 :vern 1 :data data} + :file-id file-id + :thumbnails {}})) + +;; Scenario: the undo of a rejected rename cannot be applied, so the +;; rollback reloads the file. Proves: `::rolled-back` waits for the +;; reloaded data, so a listener that reads the listed shapes sees the +;; server data, never the rejected one. +(t/deftest ^:async a-rollback-that-reloads-the-file-is-reported-after-the-reload + (let [reloads (atom 0) + original (get-method ptk/resolve ::dw/reload-current-file)] + (-add-method ptk/resolve ::dw/reload-current-file + (fn [_ _] + (swap! reloads inc) + (ptk/data-event ::reloaded {}))) + (try + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + ;; A change type with no handler, so its undo throws + commit (ptk/data-event ::dch/commit + (update @(thp/rename-commit file-id page-id shape-id "A" "B") + :undo-changes #(into [{:type :debug-fail}] %))) + seen (atom []) + sub (->> (ptk/input-stream store) + (rx/filter (ptk/type? ::dps/rolled-back)) + (rx/subs! (fn [event] + (swap! seen conj + [(:shape-ids @event) + (thp/shape-name store file-id page-id shape-id)]))))] + (ptk/emit! store (thp/open-file file-id page-id (thp/file-data page-id shape-id "B"))) + (ptk/emit! store commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the rename is sent")) + (rx/push! response (thp/rejection (:id @commit))) + (await (async/wait-for #(= 1 @reloads) "the rollback reloads the file")) + (await (async/settle)) + (t/is (empty? @seen) "nothing is reported before the reload") + (ptk/emit! store (#'dw/bundle-fetched {:file {:id file-id :revn 0 + :data (thp/file-data page-id shape-id "A")} + :file-id file-id + :thumbnails {}})) + (await (async/wait-for #(seq @seen) "the rollback is reported")) + (t/is (= [[#{shape-id} "A"]] @seen) "the listener reads the reloaded data") + (rx/dispose! sub))))) + (finally + (-add-method ptk/resolve ::dw/reload-current-file original))))) + +;; Scenario: a save of two renames is in flight and a third rename waits in +;; the queue when a peer restores an earlier version; the file reloads to +;; it and a peer's change to it arrives. The answer then comes: the server +;; kept the first rename and rejected the second, before the restore. +;; Proves: neither the peer's change, the answer nor its rollback replays +;; the renames on the reloaded data, the user gets no rejection warning, +;; and the queued rename, made on the replaced version, is dropped unsent. +(t/deftest ^:async an-answer-from-before-a-restore-leaves-the-reloaded-file-alone + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store failures]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + commit1 (thp/rename-commit file-id page-id shape-id "A" "B") + commit2 (thp/rename-commit file-id page-id shape-id "B" "C") + commit3 (thp/rename-commit file-id page-id shape-id "C" "D")] + (ptk/emit! store + (thp/open-file file-id page-id (thp/file-data page-id shape-id "D")) + #(assoc-in % [:files file-id :vern] 0)) + (ptk/emit! store commit1 commit2 ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the first two renames are sent")) + (ptk/emit! store commit3 ::dps/force-persist) + (await (async/wait-for #(= 2 (count (get-in @store [:persistence :queue]))) + "the third rename is queued")) + + (ptk/emit! store (restore-version file-id (thp/file-data page-id shape-id "Restored"))) + (ptk/emit! store (thp/remote-commit file-id 1 [(thp/set-attr page-id shape-id :opacity 0.5)])) + (await (async/settle)) + (t/is (= "Restored" (thp/shape-name store file-id page-id shape-id)) + "a peer's change does not replay the renames") + (rx/push! response (thp/rejection (:id @commit2))) + (await (async/wait-for #(and (= :saved (get-in @store [:persistence :status])) + (empty? (get-in @store [:persistence :queue]))) + "the queue drains")) + (t/is (= "Restored" (thp/shape-name store file-id page-id shape-id)) + "the file keeps the restored version") + (t/is (= 0.5 (thp/shape-attr store file-id page-id shape-id :opacity))) + (t/is (empty? @failures) "no rejection warning for a replaced version") + (t/is (= 1 (count @requests)) "the rename made on the replaced version is not sent")))))) + +;; Scenario: a save is in flight when a peer restores an earlier version and +;; the file reloads; the server handled the save after the restore, so it +;; refuses it for its old version. Proves: the failure of a save made on a +;; replaced version drops it instead of halting the queue and locking the +;; workspace. +(t/deftest ^:async a-save-refused-after-a-restore-is-dropped + (await + (thp/with-persistence + (^:async fn [{:keys [file-id response requests store failures]}] + (let [page-id (uuid/next) + shape-id (uuid/next)] + (ptk/emit! store + (thp/open-file file-id page-id (thp/file-data page-id shape-id "B")) + #(assoc-in % [:files file-id :vern] 0)) + (ptk/emit! store (thp/rename-commit file-id page-id shape-id "A" "B") ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the rename is sent")) + (ptk/emit! store (restore-version file-id (thp/file-data page-id shape-id "Restored"))) + (rx/error! response (ex-info "A different version has been restored for the file." + {:type :validation :code :vern-conflict})) + (await (async/wait-for #(and (= :saved (get-in @store [:persistence :status])) + (empty? (get-in @store [:persistence :queue]))) + "the refused save is dropped")) + (t/is (empty? @failures)) + (t/is (nil? (get-in @store [:workspace-global :persistence-lock]))) + (t/is (= "Restored" (thp/shape-name store file-id page-id shape-id)))))))) + +(defn- comment-thread + "A comment thread of `file-id` on `page-id`, pinned on `frame-id`." + [file-id page-id frame-id position] + {:id (uuid/next) :page-id page-id :file-id file-id :project-id (uuid/next) + :owner-id (uuid/next) :file-name "File" :seqn 1 :content "Note" + :participants #{} :created-at (ct/now) :modified-at (ct/now) + :position position :frame-id frame-id}) + +;; Scenario: comment #1 sits on board Y. During a save Y is moved, which +;; moves pin #1 in the same action, and board R is drawn and gets comment +;; #2. The server rejects the move, so the move, the pin move and R are +;; rolled back. Comment positions are saved by their own requests, so the +;; server already holds the moved pin and #2 on R. Proves: pin #1 goes back +;; to its spot on screen and on the server, and #2, whose board is gone, +;; moves to the page root, as deleting the board does. +(t/deftest ^:async a-rollback-puts-comment-pins-back-and-detaches-them-from-removed-boards + (let [answer (rx/subject) + respond (fn [cmd _] + (if (= :update-file cmd) + (->> answer (rx/take 1) (rx/observe-on :async)) + (rx/of nil)))] + (await + (thp/with-persistence + (^:async fn [{:keys [file-id requests store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + board-y (cts/setup-shape {:id (uuid/next) :type :frame :name "Y" + :parent-id uuid/zero :frame-id uuid/zero}) + board-r (cts/setup-shape {:id (uuid/next) :type :frame :name "R" + :parent-id uuid/zero :frame-id uuid/zero}) + [p0 p1 p2] [(gpt/point 610 10) (gpt/point 760 10) (gpt/point 20 20)] + thread-1 (comment-thread file-id page-id (:id board-y) p1) + thread-2 (comment-thread file-id page-id (:id board-r) p2) + group (uuid/next) + move (grouped (thp/rename-commit file-id page-id (:id board-y) "Y" "Y moved") group) + pin (grouped (ptk/data-event + ::dch/commit + {:id (uuid/next) :file-id file-id :file-revn 0 :file-vern 0 + :source :local :features #{} + :redo-changes [{:type :set-comment-thread-position :page-id page-id + :comment-thread-id (:id thread-1) + :frame-id (:id board-y) :position p1}] + :undo-changes [{:type :set-comment-thread-position :page-id page-id + :comment-thread-id (:id thread-1) + :frame-id (:id board-y) :position p0}]}) + group) + draw (add-shape-commit file-id page-id board-r) + data (-> (thp/file-data page-id shape-id "A") + (thp/add-shape page-id (assoc board-y :name "Y moved")) + (thp/add-shape page-id board-r) + (assoc-in [:pages-index page-id :comment-thread-positions] + {(:id thread-1) {:frame-id (:id board-y) :position p1} + (:id thread-2) {:frame-id (:id board-r) :position p2}})) + sent (fn [cmd] (into [] (comp (filter #(= cmd (first %))) (map second)) @requests))] + ;; The local data and the rendered pins already hold the edits + (ptk/emit! store + (thp/open-file file-id page-id data) + #(assoc % :comment-threads {(:id thread-1) thread-1 (:id thread-2) thread-2}) + (dwcm/watch-comment-thread-position-changes (rx/subject))) + (ptk/emit! store move pin draw ::dps/force-persist) + (await (async/wait-for #(seq (sent :update-file)) "the edits are sent")) + (rx/push! answer (thp/rejection (:id @move))) + (await (async/wait-for #(seq (sent :update-comment-thread-frame)) + "the rollback updates the comments")) + (t/is (nil? (thp/shape-attr store file-id page-id (:id board-r) :id)) "R is gone") + (t/is (= p0 (get-in @store [:comment-threads (:id thread-1) :position])) + "pin #1 is back on screen") + (t/is (= [{:id (:id thread-1) :position p0 :frame-id (:id board-y)}] + (sent :update-comment-thread-position)) + "and on the server") + (t/is (= uuid/zero (get-in @store [:comment-threads (:id thread-2) :frame-id])) + "comment #2 leaves the removed board") + (t/is (= p2 (get-in @store [:comment-threads (:id thread-2) :position])) + "and keeps its spot") + (t/is (= [{:id (:id thread-2) :frame-id uuid/zero}] + (sent :update-comment-thread-frame))) + (rx/end! answer))) + respond)))) + + +(defn- rejection-notice + "Saves a rename of the open file and answers it with `answer` applied to + a rejection of it, after `prepare` updates the state (given the file + id) while the save is in flight. Resolves with the params the notice was + flashed with; `tr` answers each key with its arguments." + [prepare answer] + (let [flashes (atom [])] + (-> (thp/with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (await + (mock/with-mocks* + {errors/flash (fn [& {:as params}] + (swap! flashes conj params) + (js/Promise.resolve nil)) + i18n/tr (fn ([key] [key]) ([key & args] (into [key] args)))} + (let [page-id (uuid/next) + shape-id (uuid/next) + commit1 (thp/rename-commit file-id page-id shape-id "A" "B")] + (ptk/emit! store (thp/open-file file-id page-id (thp/file-data page-id shape-id "B"))) + (ptk/emit! store commit1 ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the rename is sent")) + (ptk/emit! store #(prepare % file-id)) + (rx/push! response (answer (thp/rejection (:id @commit1)))) + (await (async/wait-for #(seq @flashes) "the notice is flashed"))))))) + (.then (fn [_] (first @flashes)))))) + +;; Scenario: an edit made while the save was in flight is undone with the +;; rejected one. Proves: the notice says the latest changes were undone, +;; and goes away by itself. +(t/deftest ^:async a-rejection-notice-says-the-latest-changes-were-undone + (let [{:keys [hint tag timeout]} (await (rejection-notice (fn [state _] state) identity))] + (t/is (= ["errors.save-rejected-changes-undone"] hint)) + (t/is (= dps/rejected-tag tag)) + (t/is (some? timeout)))) + +;; Scenario: the user went to another file before the answer came back. +;; Proves: the notice names the file it is about. +(t/deftest ^:async a-rejection-in-a-file-not-open-names-it + (let [{:keys [hint]} (await (rejection-notice + (fn [state file-id] + (-> state + (assoc-in [:files file-id :name] "Brand kit") + (assoc :current-file-id (uuid/next)))) + identity))] + (t/is (= ["errors.save-rejected-changes-undone-in-file" "Brand kit"] hint)))) + +;; Scenario: the page holds data that was invalid before the change, so the +;; backend flags the rejection. Proves: the notice says the data is damaged. +(t/deftest ^:async a-rejection-on-damaged-data-says-so + (let [{:keys [hint]} (await (rejection-notice + (fn [state _] state) + #(assoc-in % [:rejected :preexisting-damage] true)))] + (t/is (= ["errors.save-rejected-damaged-data"] hint)))) + +;; Scenario: the repair notice of the open file is still on screen when a +;; save of it comes back rejected. The store holds one toast. Proves: the +;; notice tells both, and stays like the repair notice did. +(t/deftest ^:async a-rejection-keeps-the-repair-notice-on-screen + (let [{:keys [hint tag timeout]} + (await (rejection-notice + (fn [state _] + (assoc state :notification {:content "repaired" :tag dps/file-repaired-tag})) + identity))] + (t/is (= ["workspace.notifications.file-repaired-changes-undone"] hint)) + (t/is (= dps/repaired-rejected-tag tag)) + (t/is (nil? timeout)))) diff --git a/frontend/test/frontend_tests/data/persistence_test.cljs b/frontend/test/frontend_tests/data/persistence_test.cljs index de85818c38..544a41ee03 100644 --- a/frontend/test/frontend_tests/data/persistence_test.cljs +++ b/frontend/test/frontend_tests/data/persistence_test.cljs @@ -9,7 +9,8 @@ read-only saves, the stalled-save watchdog, the core save pipeline (queueing, failures, recovery), and the transient/terminal failure classification. Retry-episode behavior lives in - `frontend-tests.data.persistence-retry-test`. + `frontend-tests.data.persistence-retry-test`, the rollback of a partly + rejected save in `frontend-tests.data.persistence-rollback-test`. The watchdog and read-only tests are fully async: `mock/with-mocks*` installs the doubles, `^:async` bodies `await` observable effects (never @@ -23,8 +24,10 @@ [app.main.data.changes :as dch] [app.main.data.event :as ev] [app.main.data.persistence :as dps] + [app.main.data.persistence.commits :as pcm] [app.main.data.render-wasm :as drw] [app.main.data.workspace :as dw] + [app.main.data.workspace.common :as dwc] [app.main.errors :as errors] [app.main.refs :as refs] [app.main.repo :as rp] @@ -34,6 +37,7 @@ [cljs.test :as t :include-macros true] [frontend-tests.helpers.async :as async] [frontend-tests.helpers.mock :as mock] + [frontend-tests.helpers.persistence :as thp :refer [local-commit with-persistence]] [okulary.core :as l] [potok.v2.core :as ptk])) @@ -58,20 +62,17 @@ :pages-index {page-id {:id page-id :name "Page" :objects {uuid/zero (cts/setup-shape {:id uuid/zero :type :frame})}}}}} - rename (fn [name] - {:type :mod-obj :id shape-id :page-id page-id - :operations [{:type :set :attr :name :val name}]}) pstate {:status status :error {:cause-type cause-type :commit-id first-id} :queue (into #queue [] [first-id next-id other-id]) :index {first-id {:file-id file-id :file-vern 0 - :redo-changes [{:type :add-obj :id shape-id :obj shape - :page-id page-id :parent-id uuid/zero - :frame-id uuid/zero}]} + :commits [{:redo-changes [{:type :add-obj :id shape-id :obj shape + :page-id page-id :parent-id uuid/zero + :frame-id uuid/zero}]}]} next-id {:file-id file-id :file-vern 0 - :redo-changes [(rename "Retained rectangle")]} + :commits [{:redo-changes [(thp/rename page-id shape-id "Retained rectangle")]}]} other-id {:file-id (uuid/next) :file-vern 0 - :redo-changes [(rename "Another file")]}}} + :commits [{:redo-changes [(thp/rename page-id shape-id "Another file")]}]}}} state {:persistence pstate} loaded (ptk/update (#'dw/bundle-fetched {:file file :file-id file-id :thumbnails {}}) state) @@ -80,8 +81,36 @@ (when restored? (t/is (= "Retained rectangle" (:name restored)) "queued edits apply in order and exclude other files")) - (t/is (identical? pstate (:persistence loaded)) - "restoring the editor must preserve the save queue"))))) + (if (zero? vern) + (t/is (identical? pstate (:persistence loaded)) + "restoring the editor must preserve the save queue") + (t/is (= [[] [] (get-in pstate [:index other-id :commits])] + (mapv #(get-in loaded [:persistence :index % :commits]) [first-id next-id other-id])) + "the commits made on the replaced version are forgotten")))))) + +;; Scenario: a file loads again while an edit of it waits in the buffer. +;; Proves: buffered edits apply too, after the queued ones, and those of +;; other files do not. +(t/deftest reopening-restores-the-buffered-edits-after-the-queued-ones + (let [file-id (uuid/next) + page-id (uuid/next) + commit-id (uuid/next) + file {:id file-id + :vern 0 + :data {:pages [page-id] + :pages-index {page-id {:id page-id :name "Page" + :objects {uuid/zero + (cts/setup-shape {:id uuid/zero :type :frame})}}}}} + pstate {:status :saving + :queue (into #queue [] [commit-id]) + :index {commit-id {:file-id file-id :file-vern 0 + :commits [{:redo-changes [(thp/rename page-id uuid/zero "Queued")]}]}} + :buffer [{:file-id file-id :file-vern 0 :redo-changes [(thp/rename page-id uuid/zero "Buffered")]} + {:file-id (uuid/next) :file-vern 0 :redo-changes [(thp/rename page-id uuid/zero "Another file")]}]} + loaded (ptk/update (#'dw/bundle-fetched {:file file :file-id file-id :thumbnails {}}) + {:persistence pstate})] + (t/is (= "Buffered" + (get-in loaded [:files file-id :data :pages-index page-id :objects uuid/zero :name]))))) ;; Scenario: a queued edit no longer applies to the data the server sent, so ;; replaying it throws. Proves: the file still opens, as the server sent it, @@ -98,27 +127,13 @@ pstate {:status :saving :queue (into #queue [] [commit-id]) :index {commit-id {:file-id file-id :file-vern 0 - :redo-changes [{:type :mod-obj :id uuid/zero :page-id page-id - :operations [{:type :no-longer-supported}]}]}}} + :commits [{:redo-changes [{:type :mod-obj :id uuid/zero :page-id page-id + :operations [{:type :no-longer-supported}]}]}]}}} loaded (ptk/update (#'dw/bundle-fetched {:file file :file-id file-id :thumbnails {}}) {:persistence pstate})] (t/is (identical? data (get-in loaded [:files file-id :data]))) (t/is (identical? pstate (:persistence loaded))))) -(defn- local-commit - "Builds a synthetic local commit event for `file-id`: a page edit as redo, - no undo, from the `:local` source." - [file-id] - (ptk/data-event ::dch/commit - {:id (uuid/next) - :file-id file-id - :file-revn 0 - :file-vern 0 - :source :local - :features #{} - :redo-changes [{:type :mod-page :id (uuid/next) :name "Edited"}] - :undo-changes []})) - (defn- ^:async run-read-only-phases "Drives the read-only save scenario presuming asynchronous APIs: every phase emits its triggers, awaits the transport round-trip, and only @@ -508,46 +523,8 @@ ;; ;; Same async pattern as the watchdog tests: triggers stay bare, every ;; assert block is preceded by `wait-for` on its leading signal (or a bare -;; `settle` tick when it asserts only absence). -(defn- with-persistence - "Async fixture for the persistence tests: mocks the transport, flash and - the leave-page task set (`:tasks`), and runs `f` with persistence - initialized. - - Evaluates to a promise resolving once `f` settles and teardown completes; - `await` it from an `^:async` test. - - By default the transport responds through the `:response` subject. Pass a - `respond` function (`(fn [cmd params] observable)`) to drive the transport - response directly instead. Delivery through the subject is asynchronous - (`observe-on :async`): await each effect — via `wait-for` — before - asserting it." - [f & [respond]] - (let [file-id (uuid/next) - response (rx/subject) - failures (atom []) - requests (atom []) - tasks (atom #{}) - store (ptk/store {:state {:current-file-id file-id - :permissions {:can-edit true} - :files {file-id {:id file-id :revn 0}}} - :on-error #(t/is false (str %))})] - (mock/with-mocks* - {rp/cmd! (mock/stub (fn [cmd params] - (swap! requests conj [cmd params]) - (if respond - (respond cmd params) - (->> response (rx/take 1) (rx/observe-on :async))))) - errors/flash (fn [& {:keys [cause]}] - (swap! failures conj cause)) - st/ongoing-tasks tasks} - (try - (ptk/emit! store (dps/initialize-persistence)) - (await (f {:file-id file-id :response response :failures failures - :requests requests :store store :tasks tasks})) - (finally - (rx/dispose! store) - (rx/end! response)))))) +;; `settle` tick when it asserts only absence). The fixture is +;; `with-persistence` (`frontend-tests.helpers.persistence`). ;; Scenario: edit permission is revoked with an edit queued. The save fails ;; without sending anything, the queue keeps the edit and the user is @@ -583,10 +560,12 @@ (await (async/wait-for #(and (= :error (get-in @store [:persistence :status])) (= 1 (count (get-in @store [:persistence :queue])))) "the save fails terminally")) - ;; The user acts again: a team-role change is one of the two real + ;; Edit rights come back: a team-role change is one of the two real ;; recovery triggers (the other is opening a file, whose event does ;; real work and is not usable as a bare signal here). - (ptk/emit! store (ptk/data-event :app.main.data.common/change-team-role)) + (ptk/emit! store + #(assoc-in % [:permissions :can-edit] true) + (ptk/data-event :app.main.data.common/change-team-role)) (await (async/wait-for #(and (= :saved (get-in @store [:persistence :status])) (empty? (get-in @store [:persistence :queue]))) "re-entering the file settles the queue")) @@ -596,6 +575,125 @@ "the edit that cannot be saved is not replayed on re-entry") (t/is (= 1 (count @failures)) "the user is still notified exactly once"))))) +;; Scenario: the user renames a shape and is made a viewer before the save, +;; so it fails for lost permission; the role event comes again, then edit +;; rights return. Proves: the edit is kept while the user is a viewer, and +;; released with edit rights back, the file goes back to the server's data +;; and the history loses the edit, so the canvas never shows a change the +;; server does not have. +(t/deftest ^:async regaining-edit-rights-reverts-the-released-edit + (await + (with-persistence + (^:async fn [{:keys [file-id requests failures store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + commit (thp/rename-commit file-id page-id shape-id "A" "B")] + (ptk/emit! store + (thp/open-file file-id page-id (thp/file-data page-id shape-id "B")) + #(assoc % :workspace-undo {:items [(thp/undo-entry commit)] :index 0}) + commit + #(assoc-in % [:permissions :can-edit] false) + ::dps/force-persist) + (await (async/wait-for #(= :error (get-in @store [:persistence :status])) + "lost permission fails the save")) + + (ptk/emit! store (ptk/data-event :app.main.data.common/change-team-role)) + (await (async/settle)) + (t/is (= 1 (count (get-in @store [:persistence :queue]))) + "a viewer keeps the edit") + (t/is (= "B" (thp/shape-name store file-id page-id shape-id))) + (t/is (= 2 (count @failures)) + "the count notice shows again over the role change notice") + + (ptk/emit! store + #(assoc-in % [:permissions :can-edit] true) + (ptk/data-event :app.main.data.common/change-team-role)) + (await (async/wait-for #(and (= :saved (get-in @store [:persistence :status])) + (empty? (get-in @store [:persistence :queue]))) + "edit rights back release the edit")) + (t/is (= "A" (thp/shape-name store file-id page-id shape-id)) + "the file shows the server's data") + (t/is (empty? (get-in @store [:workspace-undo :items])) + "the released edit leaves the history") + (t/is (empty? @requests) "the released edit is not sent")))))) + +;; Answers every save with an unexpected server error, a terminal failure. +(defn- server-error + [_ _] + (rp/handle-response {:status 500 + :body {:type :server-error :code :unhandled} + :uri "https://penpot.example.com/api/main/methods/update-file"})) + +;; Scenario: the user is in the Inspect tab (read-only) when a save fails +;; terminally, then goes back to Design while the dialog is up. The +;; workspace stays read-only until the failure is released, and then takes +;; the mode the user last asked for. Proves: the lock does not lose the +;; read-only mode the workspace had. +(t/deftest ^:async a-failed-save-locks-the-workspace-until-it-recovers + (await + (with-persistence + (^:async fn [{:keys [file-id store]}] + (ptk/emit! store #(assoc-in % [:workspace-global :read-only?] true) + (local-commit file-id) + ::dps/force-persist) + (await (async/wait-for #(some? (get-in @store [:workspace-global :persistence-lock])) + "the failed save locks the workspace")) + (t/is (true? (get-in @store [:workspace-global :read-only?]))) + (t/is (true? (get-in @store [:workspace-global :persistence-lock :read-only?]))) + + (ptk/emit! store (dwc/set-workspace-read-only false)) + (await (async/wait-for #(false? (get-in @store [:workspace-global :persistence-lock :read-only?])) + "the requested mode is kept for later")) + (t/is (true? (get-in @store [:workspace-global :read-only?])) + "the workspace stays read-only while saves fail") + + (ptk/emit! store (ptk/data-event :app.main.data.common/change-team-role)) + (await (async/wait-for #(nil? (get-in @store [:workspace-global :persistence-lock])) + "recovery lifts the lock")) + (t/is (false? (get-in @store [:workspace-global :read-only?])) + "the workspace takes the mode the user asked for")) + server-error))) + +;; Scenario: the user is made a viewer while an edit waits to be saved. The +;; save fails for lost permission, but the workspace is already read-only +;; for a viewer: no lock and no dialog, only the failure toast. Proves: a +;; viewer is never blocked from looking at the file. +(t/deftest ^:async a-viewer-is-not-locked-by-a-failed-save + (await + (with-persistence + (^:async fn [{:keys [file-id failures store]}] + (ptk/emit! store (local-commit file-id) + #(assoc-in % [:permissions :can-edit] false) + ::dps/force-persist) + (await (async/wait-for #(= :error (get-in @store [:persistence :status])) + "lost permission fails the save")) + (await (async/settle)) + (t/is (nil? (get-in @store [:workspace-global :persistence-lock]))) + (t/is (nil? (get-in @store [:workspace-global :read-only?]))) + (t/is (= 1 (count @failures)) "the user is told by the toast"))))) + +;; Scenario: saves fail, then the WebGL context is lost, then saves recover +;; before the context comes back. Proves: lifting the save lock does not +;; unlock a workspace the lost context still holds read-only. +(t/deftest ^:async a-lost-render-context-outlasts-the-save-lock + (await + (with-persistence + (^:async fn [{:keys [file-id store]}] + (ptk/emit! store (local-commit file-id) ::dps/force-persist) + (await (async/wait-for #(some? (get-in @store [:workspace-global :persistence-lock])) + "the failed save locks the workspace")) + (ptk/emit! store (drw/context-lost) + (ptk/data-event :app.main.data.common/change-team-role)) + (await (async/wait-for #(nil? (get-in @store [:workspace-global :persistence-lock])) + "recovery lifts the save lock")) + (t/is (true? (get-in @store [:workspace-global :read-only?])) + "the lost context keeps the workspace read-only") + + (ptk/emit! store (drw/context-restored)) + (await (async/wait-for #(false? (get-in @store [:workspace-global :read-only?])) + "the restored context brings back the first mode"))) + server-error))) + ;; Scenario: two files have pending edits and the first send fails ;; terminally. Opening a file again releases the failed file, the other file ;; still saves, and the editor settles saved instead of staying halted. @@ -740,7 +838,10 @@ (t/is (not (dps/terminal-status? {:status :retrying :queue [1]}))) (t/is (not (dps/terminal-status? {:status :saving :queue [1]}))) (t/is (not (dps/terminal-status? {:status :pending :queue [1]}))) - (t/is (not (dps/terminal-status? {:status :saved :queue [1]})))) + (t/is (not (dps/terminal-status? {:status :saved :queue [1]}))) + (t/is (not (dps/terminal-status? {:status :saved :queue [] :buffer [{:id 1}]})) + "commits still buffered keep the waiters waiting") + (t/is (dps/terminal-status? {:status :error :queue [] :buffer [{:id 1}]}))) ;; Scenario: the queue references a commit id with no matching commit ;; (plus a dangling run id) when persistence initializes. It must error @@ -770,7 +871,7 @@ (await (with-persistence (^:async fn [{:keys [file-id response requests store]}] - (let [commit (assoc @(local-commit file-id) :changes []) + (let [commit (pcm/queue-entry [@(local-commit file-id)]) id (:id commit)] (ptk/emit! store #(assoc % :persistence {:queue (conj #queue [] id) @@ -889,7 +990,7 @@ (await (with-persistence (^:async fn [{:keys [file-id response requests store]}] - (let [commit (assoc @(local-commit file-id) ::dps/request-id (uuid/next)) + (let [commit (assoc (pcm/queue-entry [@(local-commit file-id)]) ::dps/request-id (uuid/next)) id (:id commit)] (ptk/emit! store #(assoc % :persistence {:queue (conj #queue [] id) @@ -998,7 +1099,9 @@ "the save fails terminally")) (await (async/settle)) (t/is (contains? @tasks :persistence) "the retained edit keeps the guard on") - (ptk/emit! store (ptk/data-event :app.main.data.common/change-team-role)) + (ptk/emit! store + #(assoc-in % [:permissions :can-edit] true) + (ptk/data-event :app.main.data.common/change-team-role)) (await (async/wait-for #(empty? (get-in @store [:persistence :queue])) "recovery releases the edit")) (await (async/wait-for #(not (contains? @tasks :persistence)) @@ -1136,3 +1239,268 @@ [:cause-type :code :retrying :queued-commits]))))) (fn [_ _] (rx/throw (ex-info "invalid" {:type :validation :code :referential-integrity})))))) + +;; Buffered commits. + +;; Scenario: an edit waits in the buffer for the next flush, then a flush +;; moves it to the queue. Proves: buffered edits are visible in the store +;; until they are queued, so a rollback can reach them. +(t/deftest ^:async buffered-commits-stay-in-the-store-until-queued + (await + (with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [commit (local-commit file-id)] + (ptk/emit! store commit) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) + "the edit is buffered")) + (t/is (= [(:id @commit)] (mapv :id (get-in @store [:persistence :buffer])))) + (t/is (empty? (get-in @store [:persistence :queue]))) + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(seq @requests) "the flush sends the edit")) + (t/is (empty? (get-in @store [:persistence :buffer]))) + (t/is (= [(:id @commit)] + (->> (get-in @store [:persistence :queue]) + (mapcat #(get-in @store [:persistence :index % :commits])) + (mapv :id)))) + (rx/push! response {:revn 1}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the save lands"))))))) + +;; Scenario: an edit waits in the buffer when another user's change to the +;; same attribute arrives. The server will apply the remote change first and +;; the buffered edit after it, so the local file must end the same way. +;; Proves: a remote commit is applied under buffered edits too. +(t/deftest ^:async a-remote-commit-goes-under-buffered-edits + (await + (with-persistence + (^:async fn [{:keys [file-id store]}] + (let [page-id (uuid/next) + shape-id (uuid/next) + local (thp/rename-commit file-id page-id shape-id "Original" "Local")] + (ptk/emit! store (thp/open-file file-id page-id (thp/file-data page-id shape-id "Local"))) + (ptk/emit! store local) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) + "the local edit is buffered")) + (ptk/emit! store (thp/remote-commit file-id 1 [(thp/rename page-id shape-id "Remote")])) + (await (async/settle)) + (t/is (= "Local" (thp/shape-name store file-id page-id shape-id)))))))) + +;; Scenario: a library edit waits to be saved when a peer's change to this +;; file arrives, and both files have a shape with the same id. Proves: a +;; remote commit goes under the pending commits of its own file only. +(t/deftest ^:async a-remote-commit-ignores-the-pending-commits-of-other-files + (await + (with-persistence + (^:async fn [{:keys [file-id store]}] + (let [lib-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next)] + (ptk/emit! store (thp/open-file-with-library file-id lib-id page-id + (thp/file-data page-id shape-id "A") + (thp/file-data page-id shape-id "Y"))) + (ptk/emit! store (thp/rename-commit lib-id page-id shape-id "X" "Y")) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) + "the library edit is buffered")) + (ptk/emit! store (thp/remote-commit file-id 1 [(thp/set-attr page-id shape-id :opacity 0.3)])) + (await (async/settle)) + (t/is (= 0.3 (thp/shape-attr store file-id page-id shape-id :opacity))) + (t/is (= "A" (thp/shape-name store file-id page-id shape-id)) + "the library edit is not replayed on this file")))))) + +;; Changes the server stored before a save. + +(def ^:private repair-session-id + #uuid "a11c0de5-0000-4000-8000-000000000001") + +(defn- add-child-commit + "A local commit of `file-id` that adds `shape` as the last child of the + root of `page-id`." + [file-id page-id {:keys [id] :as shape}] + (ptk/data-event ::dch/commit + {:id (uuid/next) + :file-id file-id + :file-revn 0 + :file-vern 0 + :source :local + :features #{} + :undo-group (uuid/next) + :redo-changes [{:type :add-obj :id id :page-id page-id + :parent-id uuid/zero :frame-id uuid/zero :obj shape}] + :undo-changes [{:type :del-obj :id id :page-id page-id}]})) + +(defn- set-root-children + "A change that sets the children of the root of `page-id`." + [page-id ids] + {:type :mod-obj :id uuid/zero :page-id page-id + :operations [{:type :set :attr :shapes :val ids}]}) + +(defn- lagged-row + "A row of a save answer's `:lagged`: a change stored at `revn` by + `session-id`." + [file-id revn session-id changes] + {:id (uuid/next) :file-id file-id :revn revn :session-id session-id :changes changes}) + +(defn- root-children + [store file-id page-id] + (thp/shape-attr store file-id page-id uuid/zero :shapes)) + +(defn- open-with-new-child + "Opens a page whose root holds `shape-id` and the new child `child`, as + the local data does once the user adds it." + [store file-id session-id page-id shape-id child] + (ptk/emit! store + (thp/open-file file-id page-id + (thp/add-shape (thp/file-data page-id shape-id "S") page-id child)) + #(assoc % :session-id session-id))) + +;; Scenario: the user adds a child to a page with old damage. The server +;; repairs the page first, storing a repair that sets the root's children +;; without the new child, then stores the user's change, which adds it. The +;; save answer reaches the tab before the repair's message. Proves: the +;; saved commit stays pending until the repair arrives, so the repair goes +;; under it and the tab ends as the server does. +(t/deftest ^:async a-repair-answered-before-its-message-goes-under-the-saved-commit + (await + (with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [session-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next) + child (cts/setup-shape {:id (uuid/next) :type :rect :name "Rectangle" + :parent-id uuid/zero :frame-id uuid/zero}) + commit (add-child-commit file-id page-id child) + repair [(set-root-children page-id [shape-id])]] + (open-with-new-child store file-id session-id page-id shape-id child) + (ptk/emit! store commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the new child is sent")) + (rx/push! response {:revn 0 + :lagged [(lagged-row file-id 1 repair-session-id repair) + (lagged-row file-id 2 session-id (:redo-changes @commit))]}) + (await (async/wait-for #(get-in @store [:persistence :index (:id @commit) ::dps/awaiting]) + "the saved commit waits for the repair")) + (t/is (= [(:id @commit)] (vec (get-in @store [:persistence :queue])))) + + (ptk/emit! store (thp/remote-commit file-id 1 repair)) + (await (async/wait-for #(and (= :saved (get-in @store [:persistence :status])) + (empty? (get-in @store [:persistence :queue]))) + "the saved commit is acknowledged once the repair arrives")) + (t/is (= [shape-id (:id child)] (root-children store file-id page-id)) + "the repair goes under the new child, as on the server")))))) + +;; Scenario: the repair's message reaches the tab before the save answer. +;; Proves: the answer does not wait for a change the tab already applied. +(t/deftest ^:async a-save-answer-does-not-wait-for-changes-already-received + (await + (with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [session-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next) + child (cts/setup-shape {:id (uuid/next) :type :rect :name "Rectangle" + :parent-id uuid/zero :frame-id uuid/zero}) + commit (add-child-commit file-id page-id child) + repair [(set-root-children page-id [shape-id])]] + (open-with-new-child store file-id session-id page-id shape-id child) + (ptk/emit! store commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the new child is sent")) + (ptk/emit! store (thp/remote-commit file-id 1 repair)) + (rx/push! response {:revn 0 + :lagged [(lagged-row file-id 1 repair-session-id repair) + (lagged-row file-id 2 session-id (:redo-changes @commit))]}) + (await (async/wait-for #(and (= :saved (get-in @store [:persistence :status])) + (empty? (get-in @store [:persistence :queue]))) + "the save lands at once")) + (t/is (= [shape-id (:id child)] (root-children store file-id page-id)))))))) + +;; Scenario: the save answer lists a repair whose message never reaches the +;; tab. Proves: the wait is bounded; the saved commit is acknowledged and +;; the file reloads to get the server's data. +(t/deftest ^:async a-change-that-never-arrives-reloads-the-file + (let [reloads (atom 0) + timeout-s (rx/subject) + original (get-method ptk/resolve ::dw/reload-current-file)] + (-add-method ptk/resolve ::dw/reload-current-file + (fn [_ _] + (swap! reloads inc) + (ptk/data-event ::reloaded {}))) + (try + (await + (with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (await + (mock/with-mocks* + {rx/timer (mock/stub (fn [ms] + (if (= ms @#'dps/unseen-changes-timeout-ms) + timeout-s + (rx/subject))))} + (let [session-id (uuid/next) + page-id (uuid/next) + shape-id (uuid/next) + child (cts/setup-shape {:id (uuid/next) :type :rect :name "Rectangle" + :parent-id uuid/zero :frame-id uuid/zero}) + commit (add-child-commit file-id page-id child)] + (open-with-new-child store file-id session-id page-id shape-id child) + (ptk/emit! store commit ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the new child is sent")) + (rx/push! response {:revn 0 + :lagged [(lagged-row file-id 1 repair-session-id + [(set-root-children page-id [shape-id])]) + (lagged-row file-id 2 session-id (:redo-changes @commit))]}) + (await (async/wait-for #(get-in @store [:persistence :index (:id @commit) ::dps/awaiting]) + "the saved commit waits for the repair")) + (t/is (zero? @reloads)) + (rx/push! timeout-s :tick) + (await (async/wait-for #(= 1 @reloads) "the file reloads")) + (t/is (empty? (get-in @store [:persistence :queue])) + "the saved commit is acknowledged"))))))) + (finally + (-add-method ptk/resolve ::dw/reload-current-file original))))) + +;; Commit boundaries. + +;; Scenario: two edits are buffered and saved together. The request carries +;; one group per edit, each with its commit id and its own changes, so the +;; backend can accept or reject them one by one. Proves: merging a buffer +;; for one request keeps the boundaries between commits. +(t/deftest ^:async a-save-sends-each-commit-as-its-own-group + (await + (with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [commit1 (local-commit file-id) + commit2 (local-commit file-id)] + (ptk/emit! store commit1 commit2 ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "both edits go in one save")) + (let [[_ params] (first @requests)] + (t/is (not (contains? params :changes))) + (t/is (= [{:commit-id (:id @commit1) :changes (:redo-changes @commit1)} + {:commit-id (:id @commit2) :changes (:redo-changes @commit2)}] + (:changes-with-metadata params)))) + (rx/push! response {:revn 1}) + (await (async/wait-for #(= :saved (get-in @store [:persistence :status])) + "the save lands"))))))) + +;; Scenario: a save is in flight while a later edit waits in the buffer, and +;; something waits for the edits to be saved (as a library sync does before +;; marking the library synced). The save lands and the queue empties, so the +;; status turns :saved. Proves: the waiter is released only once the +;; buffered edit is saved too. +(t/deftest ^:async waiting-for-the-save-covers-the-buffered-edits + (await + (with-persistence + (^:async fn [{:keys [file-id response requests store]}] + (let [released? #(dps/terminal-status? (:persistence @store))] + (ptk/emit! store (local-commit file-id) ::dps/force-persist) + (await (async/wait-for #(= 1 (count @requests)) "the first edit is sent")) + (ptk/emit! store (local-commit file-id)) + (await (async/wait-for #(seq (get-in @store [:persistence :buffer])) + "the second edit waits in the buffer")) + (rx/push! response {:revn 1}) + (await (async/wait-for #(and (= :saved (get-in @store [:persistence :status])) + (empty? (get-in @store [:persistence :queue]))) + "the first save lands")) + (t/is (seq (get-in @store [:persistence :buffer]))) + (t/is (not (released?)) "the buffered edit is not saved yet") + (ptk/emit! store ::dps/force-persist) + (await (async/wait-for #(= 2 (count @requests)) "the buffered edit is sent")) + (rx/push! response {:revn 2}) + (await (async/wait-for released? "the waiters are released"))))))) diff --git a/frontend/test/frontend_tests/data/pointer_map_test.cljs b/frontend/test/frontend_tests/data/pointer_map_test.cljs new file mode 100644 index 0000000000..8902d6b517 --- /dev/null +++ b/frontend/test/frontend_tests/data/pointer_map_test.cljs @@ -0,0 +1,126 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.data.pointer-map-test + "Unit tests for the fragment resolvers (workspace and viewer): resolved + data comes back whole, and a failing fragment rejects instead of + silently dropping the page." + (:require + [app.common.transit :as tr] + [app.common.uuid :as uuid] + [app.main.data.viewer :as dv] + [app.main.features.pointer-map :as fpmap] + [app.main.repo :as rp] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as a] + [frontend-tests.helpers.mock :as mock])) + +(def ^:private file-id (uuid/custom 10 10)) +(def ^:private frag-id (uuid/custom 20 20)) +(def ^:private page-id (uuid/custom 30 30)) + +(def ^:private fragment-content {:objects {}}) + +(defn- file-with-pointers + [] + {:id file-id + :data {:options {} + :components (tr/->Pointer frag-id {}) + :pages-index {page-id (tr/->Pointer frag-id {})}}}) + +(defn- cmd-stub + [respond] + (mock/stub + (fn + ([command] (respond command nil)) + ([command params] (respond command params))))) + +(t/deftest ^:async resolve-file-resolves-all-fragments + (let [seen (atom nil)] + (await + (mock/with-mocks* + {rp/cmd! (cmd-stub (fn [command _params] + (t/is (= :get-file-fragment command)) + (rx/of {:data fragment-content})))} + (await (a/observe (fpmap/resolve-file (file-with-pointers)) + :on-next #(reset! seen %))))) + (t/is (some? @seen)) + (t/is (= fragment-content (get-in @seen [:data :components]))) + (t/is (= fragment-content (get-in @seen [:data :pages-index page-id]))))) + +(t/deftest ^:async resolve-viewer-fragment-rejects-with-original-cause + (let [failure (ex-info "fragment exploded" {}) + seen (atom ::none) + caught (atom nil)] + (await + (mock/with-mocks* + {rp/cmd! (cmd-stub (fn [_command _params] (rx/throw failure)))} + (await (a/observe (dv/resolve-viewer-fragment file-id nil [:k (tr/->Pointer frag-id {})]) + :on-next #(reset! seen %) + :on-error #(reset! caught %))))) + (t/is (= ::none @seen)) + (t/is (= failure @caught)))) + +(t/deftest ^:async resolve-viewer-fragment-pairs-key-with-data + (let [seen (atom ::none)] + (await + (mock/with-mocks* + {rp/cmd! (cmd-stub (fn [command params] + (t/is (= :get-file-fragment command)) + (t/is (= {:file-id file-id :fragment-id frag-id} params)) + (rx/of {:data fragment-content})))} + (await (a/observe (dv/resolve-viewer-fragment file-id nil [:k (tr/->Pointer frag-id {})]) + :on-next #(reset! seen %))))) + (t/is (= [:k fragment-content] @seen)))) + +(t/deftest ^:async resolve-viewer-fragment-forwards-share-id + (let [share-id (uuid/custom 40 40) + seen (atom ::none)] + (await + (mock/with-mocks* + {rp/cmd! (cmd-stub (fn [command params] + (t/is (= :get-file-fragment command)) + (t/is (= {:file-id file-id :fragment-id frag-id :share-id share-id} params)) + (rx/of {:data fragment-content})))} + (await (a/observe (dv/resolve-viewer-fragment file-id share-id [:k (tr/->Pointer frag-id {})]) + :on-next #(reset! seen %))))) + (t/is (= [:k fragment-content] @seen)))) + +(t/deftest ^:async resolve-file-rejects-unresolved-leftover-pointer + ;; A resolved fragment whose content is itself a pointer means the + ;; data still holds an unresolvable reference after the merge: the + ;; stream must reject with the local ex-info (not the transport + ;; cause), and deliver nothing. + (let [seen (atom ::none) + caught (atom nil)] + (await + (mock/with-mocks* + {rp/cmd! (cmd-stub (fn [_command _params] + (rx/of {:data (tr/->Pointer frag-id {})})))} + (await (a/observe (fpmap/resolve-file (file-with-pointers)) + :on-next #(reset! seen %) + :on-error #(reset! caught %))))) + (t/is (= ::none @seen)) + (t/is (some? @caught)) + (let [data (ex-data @caught)] + (t/is (= file-id (:file-id data))) + (t/is (seq (:keys data)))))) + +(t/deftest ^:async resolve-file-rejects-on-fragment-failure + (let [failure (ex-info "fragment exploded" {}) + seen (atom ::none) + caught (atom nil)] + (await + (mock/with-mocks* + {rp/cmd! (cmd-stub (fn [_command _params] (rx/throw failure)))} + (await (a/observe (fpmap/resolve-file (file-with-pointers)) + :on-next #(reset! seen %) + :on-error #(reset! caught %))))) + ;; The page is never delivered half-resolved and the original cause + ;; propagates instead of being swallowed. + (t/is (= ::none @seen)) + (t/is (= failure @caught)))) diff --git a/frontend/test/frontend_tests/data/uploads_test.cljs b/frontend/test/frontend_tests/data/uploads_test.cljs index 0a7806b5cc..5dcec770e4 100644 --- a/frontend/test/frontend_tests/data/uploads_test.cljs +++ b/frontend/test/frontend_tests/data/uploads_test.cljs @@ -13,6 +13,7 @@ [app.main.data.uploads :as uploads] [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as hva] [frontend-tests.helpers.http :as http])) ;; --------------------------------------------------------------------------- @@ -115,3 +116,42 @@ (http/restore-fetch! orig) (t/is (= 3 @chunk-calls)) (done)))))))) + +(t/deftest ^:async upload-blob-chunked-reports-progress-per-chunk + (let [session-id (uuid/next) + ;; Three chunks of 10 bytes: 2 full + 1 partial + blob (make-blob 25) + seen (atom []) + + fetch-mock + (fn [url _opts] + (let [cmd (http/url->cmd url)] + (js/Promise.resolve + (case cmd + :create-upload-session + (http/make-transit-response + {:session-id session-id}) + + :upload-chunk + (http/make-transit-response + {:session-id session-id :index 0}) + + (http/make-json-response + {:error (str "unexpected cmd: " cmd)}))))) + + orig (http/install-fetch-mock! fetch-mock)] + + (await + (hva/observe (uploads/upload-blob-chunked blob + :chunk-size 10 + :on-progress #(swap! seen conj %)) + {:on-next (fn [{:keys [session-id]}] + (t/is (uuid? session-id)))})) + (http/restore-fetch! orig) + + (t/testing "every uploaded chunk is reported with uploaded/total" + ;; chunks upload one at a time, so they are reported in order + (t/is (= [{:current 1 :total 3} + {:current 2 :total 3} + {:current 3 :total 3}] + @seen))))) diff --git a/frontend/test/frontend_tests/data/viewer_test.cljs b/frontend/test/frontend_tests/data/viewer_test.cljs index 5c3b0ca128..94f43904b2 100644 --- a/frontend/test/frontend_tests/data/viewer_test.cljs +++ b/frontend/test/frontend_tests/data/viewer_test.cljs @@ -131,9 +131,13 @@ ;; because update-zoom-querystring navigated unconditionally. ;; At HEAD the guard breaks the cycle, so a bundle fetch must not ;; emit any navigation. - (let [state (-> (base-state {:frames [{:selrect {:width 100 :height 100}}] - :index 0}) - (assoc-in [:route :query-params :zoom] "fill"))] + (let [state (as-> (base-state {:frames [{:selrect {:width 100 :height 100}}] + :index 0}) state + ;; The router stores the same query map under both keys. + (let [query (assoc (rt/get-params state) :zoom "fill")] + (-> state + (assoc-in [:route :params :query] query) + (assoc-in [:route :query-params] query))))] (let [events (watch-events (dv/bundle-fetched {:file {:id page-id :data {:pages [page-id] @@ -151,3 +155,49 @@ ;; ones that can restart the zoom cycle. navigations (filter #(= :app.main.router/navigate (ptk/type %)) events)] (t/is (empty? navigations))))) + +(defn- minimal-bundle + [page-ids] + {:file {:id (uuid/custom 2 2) + :data {:pages (vec page-ids) + :pages-index (into {} (map (fn [id] [id {:objects {}}])) page-ids)}} + :project {} + :team {:features []} + :share-links [] + :libraries [] + :users [] + :permissions {} + :thumbnails {}}) + +(defn- route-without-page-id + [] + (let [query {:file-id (str (uuid/custom 2 2)) :section "interactions" :index "0"}] + {:route {:params {:query query} :query-params query}})) + +(t/deftest initialize-without-page-id-fetches-the-bundle + ;; The viewer route passes `:page-id nil` when the URL has none; the + ;; params check used to reject it before any fetch. + (let [events (watch-events (dv/initialize {:file-id (uuid/custom 2 2) + :page-id nil + :share-id nil}) + {})] + (t/is (= [::dv/fetch-bundle] (map ptk/type (remove nil? events)))))) + +(t/deftest bundle-fetched-without-page-id-names-the-first-page + (t/testing "the route is replaced by one naming the first page" + (let [first-page (uuid/custom 3 1) + events (watch-events (dv/bundle-fetched + (minimal-bundle [first-page (uuid/custom 3 2)])) + (route-without-page-id)) + {:keys [id params options]} (some-> (first events) deref)] + (t/is (= 1 (count events))) + (t/is (= :viewer id)) + (t/is (= first-page (:page-id params))) + (t/is (= "interactions" (:section params))) + (t/is (true? (::rt/replace options))))) + + (t/testing "a file without pages keeps the route" + (let [events (watch-events (dv/bundle-fetched (minimal-bundle [])) + (route-without-page-id)) + navigations (filter #(= :app.main.router/navigate (ptk/type %)) events)] + (t/is (empty? navigations))))) diff --git a/frontend/test/frontend_tests/data/workspace_focus_test.cljs b/frontend/test/frontend_tests/data/workspace_focus_test.cljs new file mode 100644 index 0000000000..ee1d3435c3 --- /dev/null +++ b/frontend/test/frontend_tests/data/workspace_focus_test.cljs @@ -0,0 +1,87 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.data.workspace-focus-test + "Tests for focus mode when the focused shapes go away." + (:require + [app.common.data :as d] + [app.common.types.shape :as cts] + [app.common.uuid :as uuid] + [app.main.data.workspace.selection :as dws] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] + [potok.v2.core :as ptk])) + +(defn- focus-store + "A store on a page holding `ids`, in focus mode on `focused`, entered + with `selected` selected." + [ids focused selected] + (let [file-id (uuid/next) + page-id (uuid/next) + objects (into {uuid/zero (cts/setup-shape {:id uuid/zero :type :frame :shapes (vec ids)})} + (map (fn [id] + [id (cts/setup-shape {:id id :type :rect :name "Rect" + :parent-id uuid/zero :frame-id uuid/zero})])) + ids) + local {:selected (into (d/ordered-set) selected) :zoom 1}] + (ptk/store {:state {:current-file-id file-id + :current-page-id page-id + :files {file-id {:id file-id + :data {:pages [page-id] + :pages-index {page-id {:id page-id :objects objects}}}}} + :workspace-local (assoc local :selected (d/ordered-set)) + :workspace-pre-focus local + :workspace-focus-selected (set focused)} + :on-error #(t/is false (str %))}))) + +(defn- remove-shape + "Takes `id` out of the page of `store`, as a delete or a rollback does." + [store id] + (let [{:keys [current-file-id current-page-id]} @store] + (ptk/emit! store #(update-in % [:files current-file-id :data :pages-index current-page-id :objects] + dissoc id)))) + +;; Scenario: focus mode is on R, entered with R selected, and R goes away +;; (a rollback or a delete); later a new shape is drawn. Proves: focus mode +;; ends when its last shape goes, the selection from before it comes back +;; without R, and the new shape does not bring focus mode back. +(t/deftest ^:async focus-mode-ends-when-its-last-shape-is-removed + (let [[r s n] (repeatedly 3 uuid/next) + store (focus-store [r s] [r] [r]) + left (atom 0) + sub (->> (ptk/input-stream store) + (rx/filter (ptk/type? ::dws/focus-mode-left)) + (rx/subs! (fn [_] (swap! left inc))))] + (remove-shape store r) + (ptk/emit! store (dws/update-focus-shapes #{} #{r})) + (await (async/settle)) + (t/is (nil? (:workspace-focus-selected @store)) "focus mode is off") + (t/is (nil? (:workspace-pre-focus @store))) + (t/is (empty? (get-in @store [:workspace-local :selected])) + "the removed shape is not selected again") + (t/is (= 1 (:zoom (:workspace-local @store))) "the viewport from before comes back") + (ptk/emit! store (dws/update-focus-shapes #{n} #{})) + (await (async/settle)) + (t/is (nil? (:workspace-focus-selected @store)) "a new shape does not turn it on") + (t/is (= 1 @left) "the focus watcher is told to stop once") + (rx/dispose! sub))) + +;; Scenario: focus mode is on R and S, entered with both selected; R goes +;; away and the user leaves focus mode with F. Proves: focus mode stays on +;; while a focused shape is left, and leaving it selects only the shapes +;; that still exist. +(t/deftest ^:async leaving-focus-mode-selects-only-shapes-that-exist + (let [[r s] (repeatedly 2 uuid/next) + store (focus-store [r s] [r s] [r s])] + (remove-shape store r) + (ptk/emit! store (dws/update-focus-shapes #{} #{r})) + (await (async/settle)) + (t/is (= #{s} (:workspace-focus-selected @store))) + (ptk/emit! store (dws/toggle-focus-mode)) + (await (async/settle)) + (t/is (nil? (:workspace-focus-selected @store))) + (t/is (= [s] (vec (get-in @store [:workspace-local :selected])))))) diff --git a/frontend/test/frontend_tests/data/workspace_libraries_sync_test.cljs b/frontend/test/frontend_tests/data/workspace_libraries_sync_test.cljs new file mode 100644 index 0000000000..1990099695 --- /dev/null +++ b/frontend/test/frontend_tests/data/workspace_libraries_sync_test.cljs @@ -0,0 +1,146 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.data.workspace-libraries-sync-test + "Tests for marking a library as synced after `dwl/sync-file`." + (:require + [app.common.time :as ct] + [app.common.types.file :as ctf] + [app.common.uuid :as uuid] + [app.main.data.persistence :as dps] + [app.main.data.workspace.libraries :as dwl] + [app.main.repo :as rp] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.mock :as mock] + [potok.v2.core :as ptk])) + +(def ^:private synced-at + (ct/inst 1000)) + +(defn- library-store + "A store with the open file `file-id` and its library `library-id`, last + synced at `synced-at`." + [file-id library-id] + (ptk/store {:state {:current-file-id file-id + :permissions {:can-edit true} + :files {file-id {:id file-id + :data (ctf/make-file-data file-id)} + library-id {:id library-id + :library-of file-id + :synced-at synced-at + :modified-at synced-at + :data (ctf/make-file-data library-id)}}} + :on-error #(t/is false (str %))})) + +(defn- ^:async sync-library + "Syncs the library and lets the save end with `saved`, an observable for + `dps/wait-persisted`; `before-save` runs once the sync started. Waits until + `settled?`, given the commands sent and the library sync date, is true, + then calls `check` with them, the store and the file ids, and awaits + what it returns." + [saved before-save settled? check] + (let [file-id (uuid/next) + library-id (uuid/next) + store (library-store file-id library-id) + commands (atom []) + result #(hash-map :commands @commands + :synced-at (get-in @store [:files library-id :synced-at]) + :store store + :file-id file-id + :library-id library-id)] + (await + (mock/with-mocks* + {rx/timer (mock/stub (fn [& _] (rx/of 0))) + dps/wait-persisted (mock/stub (fn [& _] saved)) + rp/cmd! (mock/stub (fn [cmd _] + (swap! commands conj cmd) + (rx/of nil)))} + (ptk/emit! store (dwl/sync-file file-id library-id)) + (await (async/wait-for #(not= synced-at (:synced-at (result))) + "the sync moves the sync date forward")) + (before-save store file-id) + (await (async/wait-for #(settled? (result)) "the sync settles")) + (await (async/settle)) + (await (check (result))))))) + +;; Scenario: the sync's commits are saved. Proves: the library is marked as +;; synced on the server. +(t/deftest ^:async a-saved-sync-marks-the-library-as-synced + (await + (sync-library (rx/of :saved) + (fn [_ _]) + #(seq (:commands %)) + (fn [{:keys [commands]}] + (t/is (= [:update-file-library-sync-status] commands)))))) + +;; Scenario: the server rejects the save of the sync's commits and the +;; rollback undoes them. Proves: the library is not marked as synced and its +;; sync date goes back, so the update notice can show again. +(t/deftest ^:async a-rejected-sync-leaves-the-library-to-sync-again + (await + (sync-library (rx/subject) + (fn [store file-id] + (ptk/emit! store (ptk/data-event ::dps/rolled-back + {:file-ids #{file-id}}))) + #(= synced-at (:synced-at %)) + (fn [{:keys [commands] :as result}] + (t/is (empty? commands)) + (t/is (= synced-at (:synced-at result))))))) + +;; Scenario: the save of the sync's commits fails for good, so waiting for +;; it ends with no value; later an edit of the same file is rejected. +;; Proves: the library is not marked as synced, its sync date goes back, +;; and the later rollback leaves the date alone. +(t/deftest ^:async a-failed-sync-save-ends-the-sync + (let [saved (rx/subject)] + (await + (sync-library saved + ;; The save fails for good: the wait ends with no value + (fn [_ _] (rx/end! saved)) + #(= synced-at (:synced-at %)) + (^:async fn [{:keys [commands store file-id library-id]}] + (t/is (empty? commands)) + (let [later (ct/inst 5000)] + (ptk/emit! store + #(assoc-in % [:files library-id :synced-at] later) + (ptk/data-event ::dps/rolled-back {:file-ids #{file-id}})) + (await (async/settle)) + (t/is (= later (get-in @store [:files library-id :synced-at])) + "a later rollback does not wake the ended sync"))))))) + +;; Scenario: the user becomes a viewer right after "Update", so the save of +;; the sync's commits fails for good; when edit rights come back, the +;; release of the failed save undoes the sync. Proves: the update notice is +;; checked again, as after a rejected sync, instead of waiting for a reload. +(t/deftest ^:async a-released-sync-checks-the-update-notice-again + (let [file-id (uuid/next) + library-id (uuid/next) + store (library-store file-id library-id) + waits (atom 0) + notified (atom 0)] + (await + (mock/with-mocks* + {rx/timer (mock/stub (fn [& _] (rx/of 0))) + ;; The first wait ends with the failed save, the next ones saved + dps/wait-persisted (mock/stub (fn [& _] + (if (= 1 (swap! waits inc)) + (rx/empty) + (rx/of :saved)))) + dwl/notify-sync-file (mock/stub (fn [] + (swap! notified inc) + (ptk/data-event ::notified {}))) + rp/cmd! (mock/stub (fn [_ _] (rx/of nil)))} + (ptk/emit! store (dwl/sync-file file-id library-id)) + (await (async/wait-for #(= 1 @waits) "the save of the sync fails")) + (await (async/settle)) + (t/is (= synced-at (get-in @store [:files library-id :synced-at]))) + (t/is (zero? @notified) "a failed save alone shows no notice") + + ;; The role change that brings edit rights back releases the save + (ptk/emit! store (ptk/data-event ::dps/rolled-back {:file-ids #{file-id}})) + (await (async/wait-for #(= 1 @notified) "the release checks the update notice")))))) diff --git a/frontend/test/frontend_tests/data/workspace_mcp_test.cljs b/frontend/test/frontend_tests/data/workspace_mcp_test.cljs index 76b8fbd948..77c685036d 100644 --- a/frontend/test/frontend_tests/data/workspace_mcp_test.cljs +++ b/frontend/test/frontend_tests/data/workspace_mcp_test.cljs @@ -8,11 +8,44 @@ (:require [app.common.time :as ct] [app.common.uuid :as uuid] + [app.config :as cf] + [app.main.data.plugins :as dp] [app.main.data.profile :as du] [app.main.data.workspace.mcp :as mcp] + [app.plugins.register :as preg] + [app.util.globals :as globals] + [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as a] + [frontend-tests.helpers.mock :as mock] [potok.v2.core :as ptk])) +(t/deftest ^:async test-initialize-once-per-authenticated-session + (let [stream (rx/subject) + events (atom []) + profile {:id (uuid/next)}] + (await + (mock/with-mocks* + {cf/flags #{:mcp} + preg/wait-for-runtime (fn [] (js/Promise.resolve true))} + (let [subscription (rx/subs! #(swap! events conj (ptk/type %)) + (ptk/watch (mcp/initialize) {} stream))] + (try + (rx/push! stream (du/set-profile profile)) + (await (a/wait-for #(= 1 (count @events)))) + (rx/push! stream (du/set-profile profile)) + (rx/push! stream (ptk/data-event :app.main.data.workspace/finalize-workspace)) + (await (a/settle)) + (t/is (= [:app.main.data.workspace.mcp/init] @events)) + (rx/push! stream (ptk/data-event :app.main.data.auth/logged-out)) + (rx/push! stream (du/set-profile profile)) + (await (a/wait-for #(= 2 (count @events)))) + (t/is (= [:app.main.data.workspace.mcp/init + :app.main.data.workspace.mcp/init] @events)) + (finally + (rx/dispose! subscription) + (rx/end! stream)))))))) + (t/deftest test-update-mcp-status (t/testing "enables MCP in profile props and mcp state" (let [state {:profile {:props {:mcp-enabled false}} :mcp {}} @@ -26,14 +59,26 @@ (t/is (false? (get-in result [:profile :props :mcp-enabled]))) (t/is (false? (get-in result [:mcp :enabled])))))) +(t/deftest test-extension-status + (t/testing "keeps a session the plugin is reconnecting" + (t/is (= "connecting" (mcp/extension-status "disconnected" true)))) + + (t/testing "reports the disconnect once the user has stopped the connection" + (t/is (= "disconnected" (mcp/extension-status "disconnected" false))) + (t/is (= "disconnected" (mcp/extension-status "disconnected" nil)))) + + (t/testing "passes other statuses through" + (t/is (= "connected" (mcp/extension-status "connected" true))) + (t/is (= "error" (mcp/extension-status "error" true))))) + (t/deftest test-update-mcp-connection-status (t/testing "sets connection status to connected" - (let [state {:mcp {:connection-status "disconnected"}} + (let [state {:mcp {:connection-requested true :connection-status "disconnected"}} result (ptk/update (mcp/update-mcp-connection-status "connected") state)] (t/is (= "connected" (get-in result [:mcp :connection-status]))))) (t/testing "sets connection status to disconnected" - (let [state {:mcp {:connection-status "connected"}} + (let [state {:mcp {:connection-requested true :connection-status "connected"}} result (ptk/update (mcp/update-mcp-connection-status "disconnected") state)] (t/is (= "disconnected" (get-in result [:mcp :connection-status])))))) @@ -123,3 +168,106 @@ event (du/delete-access-token {:id (uuid/next)}) result (ptk/update event state)] (t/is (= 2 (count (:access-tokens result)))))))) + +(t/deftest ^:async test-enable-does-not-connect + (let [events (atom [])] + (await (a/observe (ptk/watch (mcp/update-mcp-status true) {} (rx/empty)) + :on-next #(swap! events conj %))) + (t/is (empty? @events)))) + +(t/deftest ^:async test-connect-is-local + (let [event (mcp/connect-mcp) + state (ptk/update event {:mcp {:enabled true :token-valid true}}) + events (atom [])] + (t/is (true? (get-in state [:mcp :connection-requested]))) + (await (a/observe (ptk/watch event state (rx/empty)) + :on-next #(swap! events conj (ptk/type %)))) + (t/is (= [:app.main.data.workspace.mcp/connect] @events)))) + +(defn- connect + "Runs the effect of a connection request made with the given MCP state." + [mcp-state] + (let [event (mcp/connect-mcp)] + (ptk/effect event (ptk/update event {:mcp mcp-state}) nil))) + +(t/deftest ^:async test-session-lock-follows-connection-request + (let [requests (atom []) + released (atom 0) + locks #js {:request (fn [lock-name options callback] + (swap! requests conj [lock-name (.-mode options)]) + (.then (callback nil) #(swap! released inc)))}] + (await + (mock/with-mocks* + {globals/navigator #js {:locks locks}} + (connect {:enabled false}) + (t/is (empty? @requests)) + (connect {:enabled true :token-valid true}) + (connect {:enabled true :token-valid true}) + (t/is (= [["penpot-mcp-session" "shared"]] @requests)) + (ptk/effect (mcp/user-disconnect-mcp) nil nil) + (await (a/settle)) + (t/is (= 1 @released)) + (ptk/effect (mcp/user-disconnect-mcp) nil nil) + (await (a/settle)) + (t/is (= 1 @released)))))) + +(t/deftest ^:async test-session-lock-retried-after-failed-request + (let [requests (atom 0) + locks #js {:request (fn [_ _ _] + (swap! requests inc) + (js/Promise.reject (js/Error. "lock request denied")))}] + (await + (mock/with-mocks* + {globals/navigator #js {:locks locks}} + (connect {:enabled true :token-valid true}) + (await (a/settle)) + (connect {:enabled true :token-valid true}) + (await (a/settle)) + (t/is (= 2 @requests)))))) + +(t/deftest test-disconnect-clears-connection-intent + (let [state {:mcp {:connection-requested true + :connection-status "connecting" + :session-id "pq3gxqddgj"}} + result (ptk/update (mcp/user-disconnect-mcp) state)] + (t/is (false? (get-in result [:mcp :connection-requested]))) + (t/is (= "disconnected" (get-in result [:mcp :connection-status]))) + (t/is (nil? (get-in result [:mcp :session-id]))))) + +(t/deftest test-init-clears-previous-app-connection + (let [state {:profile {:props {:mcp-enabled true}} + :mcp {:connection-requested true + :connection-status "connected" + :session-id "pq3gxqddgj"}} + result (ptk/update (mcp/init) state)] + (t/is (false? (get-in result [:mcp :connection-requested]))) + (t/is (= "disconnected" (get-in result [:mcp :connection-status]))) + (t/is (nil? (get-in result [:mcp :session-id]))))) + +(t/deftest test-late-status-cannot-reconnect + (let [state {:mcp {:connection-requested false :connection-status "disconnected"}}] + (t/is (= state (ptk/update (mcp/update-mcp-connection-status "connected") state))))) + +(t/deftest test-plugin-callbacks-stop-with-app-session + (doseq [stop-event [:app.main.data.auth/logged-out + :app.main.data.workspace.mcp/init]] + (let [stream (rx/subject) + extension (atom nil) + calls (atom 0) + closed (atom 0)] + (with-redefs [dp/start-plugin! (fn [_ extensions] + (reset! extension (.-mcp extensions))) + dp/close-plugin! (fn [_] (swap! closed inc))] + (ptk/effect (#'mcp/init-mcp-plugin {:token "test-token"}) nil stream) + (.on @extension "connect" #(swap! calls inc)) + (rx/push! stream (ptk/data-event :app.main.data.workspace.mcp/connect)) + (t/is (= 1 @calls)) + (rx/push! stream (ptk/data-event :app.main.data.workspace/finalize-workspace)) + (t/is (zero? @closed)) + (rx/push! stream (ptk/data-event stop-event)) + (t/is (= 1 @closed)) + (t/is (false? (.isConnectionRequested @extension))) + (.on @extension "connect" #(swap! calls inc)) + (rx/push! stream (ptk/data-event :app.main.data.workspace.mcp/connect)) + (t/is (= 1 @calls)) + (rx/end! stream))))) diff --git a/frontend/test/frontend_tests/data/workspace_notifications_test.cljs b/frontend/test/frontend_tests/data/workspace_notifications_test.cljs index aceafe6bed..0ad8a12697 100644 --- a/frontend/test/frontend_tests/data/workspace_notifications_test.cljs +++ b/frontend/test/frontend_tests/data/workspace_notifications_test.cljs @@ -6,15 +6,20 @@ (ns frontend-tests.data.workspace-notifications-test (:require - [app.common.data :as d] + [app.common.time :as ct] [app.common.uuid :as uuid] [app.main.data.changes :as dch] + [app.main.data.modal :as modal] [app.main.data.notifications :as ntf] + [app.main.data.persistence :as dps] + [app.main.data.websocket :as dws] + [app.main.data.workspace :as-alias dw] [app.main.data.workspace.notifications :as dwntf] [app.util.i18n :as i18n] [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.mock :as mock] [potok.v2.core :as ptk])) (defn- file-change-msg @@ -35,18 +40,223 @@ :on-next #(swap! events conj %))) @events)) +(defn- ^:async shown-notice + "The notification the watch of `event` shows on `state`." + [event state] + (let [events (atom [])] + (await (async/observe (ptk/watch event state (rx/empty)) + :on-next #(swap! events conj %))) + (some #(when (= ::ntf/show (ptk/type %)) + (:notification (ptk/update % {}))) + @events))) + (t/deftest ^:async repaired-file-change-applies-and-notifies (let [events (await (watch-file-change (file-change-msg {:repaired true}))) - types (mapv ptk/type events) - show (d/seek #(= ::ntf/show (ptk/type %)) events) - state (ptk/update show {})] - (t/is (= [::dch/commit ::ntf/show] types)) - (t/is (= :info (get-in state [:notification :level]))) - (t/is (nil? (get-in state [:notification :timeout])) - "the toast stays until the user closes it") - (t/is (= (i18n/tr "workspace.notifications.file-repaired") - (get-in state [:notification :content]))))) + notice (await (shown-notice (second events) {}))] + (t/is (= [::dch/commit ::dps/notify-repaired] (mapv ptk/type events))) + (t/is (= :info (:level notice))) + (t/is (nil? (:timeout notice)) "the toast stays until the user closes it") + (t/is (= dps/file-repaired-tag (:tag notice))) + (t/is (= (i18n/tr "workspace.notifications.file-repaired") (:content notice))))) + +;; Scenario: a save of the open file came back rejected and its notice is +;; still on screen when the backend repairs the file. The store holds one +;; toast. Proves: the repair notice tells both and keeps the report link. +(t/deftest ^:async a-repair-notice-keeps-a-rejection-notice-on-screen + (let [link {:label "report.txt" :callback identity} + notice (await (shown-notice (dps/notify-repaired) + {:notification {:content "rejected" + :tag dps/rejected-tag + :links [link]}}))] + (t/is (= (i18n/tr "workspace.notifications.file-repaired-changes-undone") + (:content notice))) + (t/is (= :warning (:level notice))) + (t/is (= dps/repaired-rejected-tag (:tag notice))) + (t/is (= [link] (:links notice))) + (t/is (nil? (:timeout notice))))) (t/deftest ^:async ordinary-file-change-does-not-notify (let [events (await (watch-file-change (file-change-msg {})))] (t/is (= [::dch/commit] (mapv ptk/type events))))) + +(defn- restored-events + "Events the watch of `handle-file-restored` emits for `msg` on `state`." + [state msg] + (let [events (atom [])] + (-> (async/observe (ptk/watch (dwntf/handle-file-restored msg) state nil) + :on-next #(swap! events conj %)) + (.then (fn [_] @events))))) + +(defn- shown-modal + [events] + (some #(when (= ::modal/show-modal (ptk/type %)) + (::modal/modal (ptk/update % {}))) + events)) + +;; Scenario: a peer restores an earlier version of the open file. The file +;; reloads and a modal names the peer. Proves: the reload is not silent. +(t/deftest ^:async a-peer-restore-reloads-and-names-the-peer + (let [file-id (uuid/next) + session-id (uuid/next) + profile-id (uuid/next) + state {:current-file-id file-id + :files {file-id {:id file-id :vern 1}} + :workspace-presence {session-id {:profile-id profile-id}} + :profiles {profile-id {:id profile-id :fullname "Ada"}}} + events (await (restored-events state {:type :file-restored + :file-id file-id + :session-id session-id + :vern 2})) + modal (shown-modal events)] + (t/is (some #(= ::dw/reload-current-file (ptk/type %)) events)) + (t/is (= :file-restored (:type modal))) + (t/is (= "Ada" (get-in modal [:props :fullname]))) + (t/is (false? (get-in modal [:props :unsaved]))))) + +;; Scenario: a peer restores a version they named, while this tab has a +;; rename not saved yet and the restorer has no presence. Proves: the modal +;; names the restorer by profile and the version by its label, and says the +;; unsaved rename is lost. +(t/deftest ^:async a-peer-restore-names-the-version-and-the-lost-edits + (let [file-id (uuid/next) + profile-id (uuid/next) + created-at (ct/now) + commit-id (uuid/next) + state {:current-file-id file-id + :files {file-id {:id file-id :vern 1}} + :profiles {profile-id {:id profile-id :fullname "Ada"}} + :persistence {:queue [commit-id] + :index {commit-id {:id commit-id + :file-id file-id + :file-vern 1 + :commits [{:redo-changes [{:type :mod-page}]}]}}}} + events (await (restored-events state {:type :file-restored + :file-id file-id + :session-id (uuid/next) + :profile-id profile-id + :label "Hand-off v2" + :created-at created-at + :vern 2})) + props (:props (shown-modal events))] + (t/is (= "Ada" (:fullname props))) + (t/is (= "Hand-off v2" (:label props))) + (t/is (= created-at (:created-at props))) + (t/is (true? (:unsaved props))))) + +;; Scenario: the restoring session left before its message arrived, so it +;; has no presence. Proves: the modal still shows, without a name. +(t/deftest ^:async a-restore-from-an-unknown-session-shows-no-name + (let [file-id (uuid/next) + state {:current-file-id file-id + :files {file-id {:id file-id :vern 1}}} + events (await (restored-events state {:type :file-restored + :file-id file-id + :session-id (uuid/next) + :vern 2})) + modal (shown-modal events)] + (t/is (= :file-restored (:type modal))) + (t/is (nil? (get-in modal [:props :fullname]))))) + +;; Scenario: the file already shows the restored version. Proves: no reload +;; and no modal for a version the user already sees. +(t/deftest ^:async a-restore-of-the-loaded-version-does-nothing + (let [file-id (uuid/next) + state {:current-file-id file-id + :files {file-id {:id file-id :vern 2}}} + events (await (restored-events state {:type :file-restored + :file-id file-id + :session-id (uuid/next) + :vern 2}))] + (t/is (empty? events)))) + +(defn- ^:async subscription-messages + "Runs `scenario` with a store over `team-id`, `dws/send` and the pointer + handler stubbed. `scenario` gets the store and a function that delivers + a peer's pointer message for a file. Resolves to the messages sent over + the websocket and the number of handled pointer messages." + [team-id scenario] + (let [sent (atom []) + handled (atom 0) + result (atom nil) + store (ptk/store {:state {:profile-id (uuid/next) + :session-id (uuid/next) + :teams {team-id {:id team-id}}} + :on-error #(t/is false (str %))}) + pointer (fn [file-id] + (ptk/emit! store (ptk/data-event ::dws/message + {:type :pointer-update + :topic file-id + :file-id file-id + :session-id (uuid/next)})))] + (await + (mock/with-mocks* + {dws/send (mock/stub (fn [msg] + (swap! sent conj msg) + (ptk/data-event ::sent msg))) + dwntf/handle-pointer-update (mock/stub (fn [_] + (swap! handled inc) + (ptk/data-event ::handled {})))} + (try + (await (scenario store pointer)) + ;; Taken before the store ends, which ends the subscriptions too + (reset! result {:sent (mapv #(select-keys % [:type :file-id]) @sent) + :handled @handled}) + (finally + (rx/dispose! store))))) + @result)) + +;; Scenario: the open file reloads three times (a peer's restore, the +;; restorer's own version reload, the rollback fallback), each starting a +;; new subscription, and then a peer's message arrives. Proves: one live +;; subscription per open file, so each message is handled once, and no +;; unsubscribe ends the server's subscription of the reloaded file. +(t/deftest ^:async a-reload-replaces-the-file-subscription + (let [team-id (uuid/next) + file-id (uuid/next) + result (await + (subscription-messages + team-id + (^:async fn [store pointer] + (ptk/emit! store + (dwntf/initialize team-id file-id) + (dwntf/initialize team-id file-id) + (dwntf/initialize team-id file-id)) + (await (async/settle)) + (pointer file-id) + (await (async/settle)))))] + (t/is (= 1 (:handled result)) "the message is handled once") + (t/is (= 3 (count (filter #(= :subscribe-file (:type %)) (:sent result))))) + (t/is (not-any? #(= :unsubscribe-file (:type %)) (:sent result))))) + +;; Scenario: the user leaves a file, and in another case opens another file +;; without leaving the first one. Proves: the subscription ends with an +;; unsubscribe of the file it was for, and the old file's messages are no +;; longer handled. +(t/deftest ^:async leaving-or-switching-files-ends-the-file-subscription + (let [team-id (uuid/next) + file-a (uuid/next) + file-b (uuid/next) + left (await + (subscription-messages + team-id + (^:async fn [store _] + (ptk/emit! store + (dwntf/initialize team-id file-a) + (dwntf/finalize file-a)) + (await (async/settle))))) + switch (await + (subscription-messages + team-id + (^:async fn [store pointer] + (ptk/emit! store + (dwntf/initialize team-id file-a) + (dwntf/initialize team-id file-b)) + (await (async/settle)) + (pointer file-a) + (pointer file-b) + (await (async/settle)))))] + (t/is (= [{:type :unsubscribe-file :file-id file-a}] + (filterv #(= :unsubscribe-file (:type %)) (:sent left)))) + (t/is (= [{:type :unsubscribe-file :file-id file-a}] + (filterv #(= :unsubscribe-file (:type %)) (:sent switch)))) + (t/is (= 1 (:handled switch)) "only the new file's message is handled"))) diff --git a/frontend/test/frontend_tests/data/workspace_texts_test.cljs b/frontend/test/frontend_tests/data/workspace_texts_test.cljs index 9fe41428e7..d2772ebd36 100644 --- a/frontend/test/frontend_tests/data/workspace_texts_test.cljs +++ b/frontend/test/frontend_tests/data/workspace_texts_test.cljs @@ -16,8 +16,10 @@ [app.main.data.workspace.texts :as dwt] [app.main.data.workspace.texts-events :as dwte] [app.main.ui.workspace.shapes.text.viewport-texts-html :as vth] + [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] - [frontend-tests.helpers.state :as ths])) + [frontend-tests.helpers.state :as ths] + [potok.v2.core :as ptk])) ;; --------------------------------------------------------------------------- ;; Helpers @@ -297,6 +299,20 @@ ;; schema check runs. ;; --------------------------------------------------------------------------- +;; Scenario: the text editor saves its content after its shape is gone (a +;; rollback removed it while it was being edited). Proves: nothing is +;; committed for a shape that no longer exists. +(t/deftest updating-the-content-of-a-removed-text-commits-nothing + (let [state (-> @(ths/setup-store (cthf/sample-file :file1)) + (assoc :features #{})) + event (dwt/v2-update-text-shape-content (uuid/next) (txt/change-text nil "hello") + :finalize? true) + events (atom [])] + (rx/subs! #(swap! events conj %) (ptk/watch event state (rx/subject))) + (t/is (not-any? #(and (ptk/event? %) + (= :app.main.data.workspace.shapes/update-shapes (ptk/type %))) + @events)))) + (t/deftest add-typography-normalises-float-line-height (t/async done @@ -626,3 +642,86 @@ result (dwt/merge-live-text-dimensions :auto-width selrect {:width 0 :height 0})] (t/is (= 50 (:width result))) (t/is (= 25 (:height result)))))) + +;; --------------------------------------------------------------------------- +;; copied-shapes->payload +;; --------------------------------------------------------------------------- + +(defn- copied-text + [id y & spans] + {:id id + :type :text + :x 0 + :y y + :content {:type "root" + :children [{:type "paragraph-set" + :children [{:type "paragraph" + :children (mapv #(hash-map :text %) spans)}]}]}}) + +(t/deftest copied-shapes-payload-joins-text-geometry + (let [top-id (uuid/next) + bottom-id (uuid/next) + origin {:x 4 :y 2 :box {:width 100 :height 20}} + data {:file-id (uuid/next) + :selected #{bottom-id top-id} + :objects {top-id (copied-text top-id 0 "a" "b") + bottom-id (copied-text bottom-id 50 "c")}}] + (t/testing "the origins follow the joined paragraphs, nil for texts without geometry" + (t/is (= {:origins [[nil nil] [origin]]} + (:geometry (#'dwt/copied-shapes->payload + (assoc data :text-geometry {bottom-id {:origins [[origin]]}})))))) + (t/testing "a copy without geometry gives a payload without it" + (t/is (not (contains? (#'dwt/copied-shapes->payload data) :geometry)))))) + +;; --------------------------------------------------------------------------- +;; Tests: values read from a disposed v2 editor +;; --------------------------------------------------------------------------- + +(defn- disposed-v2-editor + "A v2 editor after `dispose!`: reading its style throws." + [] + (let [editor #js {}] + (js/Object.defineProperty editor "currentStyle" + #js {:get (fn [] (throw (js/TypeError. "Cannot read properties of null (reading 'currentStyle')")))}) + editor)) + +;; Scenario: the sidebar renders once more with the editor it saw last, +;; after closing the text editor disposed it. Proves: the values come from +;; the shape instead of crashing the workspace. +(t/deftest current-values-of-a-disposed-v2-editor-come-from-the-shape + (let [shape (make-text-shape) + editor (disposed-v2-editor)] + (t/is (= (dwt/current-text-values {:shape shape :attrs txt/text-node-attrs}) + (dwt/current-text-values {:editor-instance editor + :shape shape + :attrs txt/text-node-attrs}))) + (t/is (= (dwt/current-paragraph-values {:shape shape :attrs txt/paragraph-attrs}) + (dwt/current-paragraph-values {:editor-instance editor + :shape shape + :attrs txt/paragraph-attrs}))))) + +;; --------------------------------------------------------------------------- +;; same-styled-text? +;; --------------------------------------------------------------------------- + +(defn- text-content + "Text content of one paragraph holding `runs`, each a map with `:text` + and its styles." + [& runs] + {:type "root" + :children [{:type "paragraph-set" + :children [{:type "paragraph" + :key (str (uuid/next)) + :children (mapv #(assoc % :key (str (uuid/next))) runs)}]}]}) + +(t/deftest same-styled-text-ignores-node-keys-and-nil-styles + (t/is (dwt/same-styled-text? (text-content {:text "Hello" :font-weight "400"}) + (text-content {:text "Hello" :font-weight "400" :font-style nil})))) + +(t/deftest same-styled-text-sees-a-style-change-on-the-same-text + (t/is (not (dwt/same-styled-text? (text-content {:text "Hello" :font-weight "400"}) + (text-content {:text "Hello" :font-weight "700"}))))) + +(t/deftest same-styled-text-sees-a-text-change + (t/is (not (dwt/same-styled-text? (text-content {:text "Hello" :font-weight "400"}) + (text-content {:text "Hello!" :font-weight "400"}))))) diff --git a/frontend/test/frontend_tests/data/workspace_undo_test.cljs b/frontend/test/frontend_tests/data/workspace_undo_test.cljs new file mode 100644 index 0000000000..55a1ae0cca --- /dev/null +++ b/frontend/test/frontend_tests/data/workspace_undo_test.cljs @@ -0,0 +1,274 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.data.workspace-undo-test + "Tests for the undo groups of commits and history entries, and for + forgetting them after a rollback." + (:require + [app.common.test-helpers.files :as cthf] + [app.common.test-helpers.ids-map :as cthi] + [app.common.uuid :as uuid] + [app.main.data.changes :as dch] + [app.main.data.comments :as dcmt] + [app.main.data.fonts :as df] + [app.main.data.workspace :as dw] + [app.main.data.workspace.undo :as dwu] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.mock :as mock] + [frontend-tests.helpers.state :as ths] + [potok.v2.core :as ptk])) + +(t/use-fixtures :each {:before cthi/reset-idmap!}) + +(defn- rename-page + "Commits a page rename, with `params` for `commit-changes`." + [page-id name & {:as params}] + (dch/commit-changes (merge {:redo-changes [{:type :mod-page :id page-id :name name}] + :undo-changes [{:type :mod-page :id page-id :name "Page 1"}]} + params))) + +(defn- run-commits + "Runs `events` on a store of a sample file and calls `check` with the + undo groups of the commits made and the final state." + [done events check] + (let [file (cthf/sample-file :file1 :page-label :page1) + store (ths/setup-store file) + groups (atom []) + sub (->> (ptk/input-stream store) + (rx/filter dch/commit?) + (rx/subs! #(swap! groups conj (:undo-group (deref %)))))] + (ths/run-store store done (events (cthf/current-page-id file)) + (fn [state] + (rx/dispose! sub) + (check @groups state))))) + +;; Scenario: an action commits twice inside one undo transaction (as applying +;; a token to several shapes does). Proves: both commits carry the undo group +;; of the transaction, which its history entry takes too, so a rejected save +;; reverts the whole Ctrl+Z step, not half of it. +(t/deftest commits-inside-a-transaction-share-its-undo-group + (t/async + done + (let [tx (js/Symbol) + tx-group (atom nil) + spy (ptk/reify ::spy-transaction + ptk/EffectEvent + (effect [_ state _] + (reset! tx-group (get-in state [:workspace-undo :transaction :commit-group]))))] + (run-commits done + (fn [page-id] + [(dwu/start-undo-transaction tx) + spy + (rename-page page-id "A") + (rename-page page-id "B") + (dwu/commit-undo-transaction tx)]) + (fn [groups _] + (t/is (some? @tx-group) "the transaction has its own group") + (t/is (= [@tx-group @tx-group] groups))))))) + +;; Scenario: commits made with no transaction open. Proves: each one keeps +;; its own undo group. +(t/deftest commits-outside-a-transaction-get-their-own-undo-group + (t/async + done + (run-commits done + (fn [page-id] + [(rename-page page-id "A") + (rename-page page-id "B")]) + (fn [groups _] + (t/is (= 2 (count (distinct groups)))))))) + +;; Scenario: inside a transaction, a commit names its undo group (e.g. a +;; component update that also writes the library). Proves: the explicit group +;; is kept. +(t/deftest an-explicit-undo-group-is-kept-inside-a-transaction + (t/async + done + (let [tx (js/Symbol) + group (uuid/next)] + (run-commits done + (fn [page-id] + [(dwu/start-undo-transaction tx) + (rename-page page-id "A" :undo-group group) + (dwu/commit-undo-transaction tx)]) + (fn [groups _] + (t/is (= [group] groups))))))) + +;; Scenario: a rollback forgets undo groups while a transaction is open, then +;; the transaction commits again. Proves: the later commit keeps the group of +;; the earlier one, so the transaction stays one rollback unit. +(t/deftest a-transaction-keeps-its-undo-group-after-forget-undo-groups + (t/async + done + (let [tx (js/Symbol)] + (run-commits done + (fn [page-id] + [(dwu/start-undo-transaction tx) + (rename-page page-id "A") + (ptk/reify ::forget-the-transaction-group + ptk/UpdateEvent + (update [_ state] + (let [group (get-in state [:workspace-undo :transaction :commit-group])] + ;; The transaction holds the first commit, as `append-undo` records it + (->> (assoc-in state [:workspace-undo :transaction :undo-groups] #{group}) + (ptk/update (dwu/forget-undo-groups #{group} nil)))))) + (rename-page page-id "B") + (dwu/commit-undo-transaction tx)]) + (fn [[first-group second-group :as groups] _] + (t/is (= 2 (count groups))) + (t/is (some? first-group)) + (t/is (= first-group second-group))))))) + +(defn- entry + [page-id name group] + {:undo-changes [{:type :mod-page :id page-id :name "Page 1"}] + :redo-changes [{:type :mod-page :id page-id :name name}] + :undo-group group + :tags #{}}) + +;; Scenario: inside one transaction, a commit names another undo group (as a +;; component update does). Proves: the history entry records both groups, +;; so a rollback of either one finds it. +(t/deftest a-transaction-entry-records-every-group-it-holds + (let [page-id (uuid/next) + [g1 g2] (repeatedly 2 uuid/next) + tx (js/Symbol) + state (reduce #(ptk/update %2 %1) + {} + [(dwu/start-undo-transaction tx) + (dwu/append-undo (entry page-id "A" g1) false) + (dwu/append-undo (entry page-id "B" g2) false) + (dwu/commit-undo-transaction tx)])] + (t/is (= #{g1 g2} (get-in state [:workspace-undo :items 0 :undo-groups]))))) + +;; Scenario: a commit stacks onto the last history entry. Proves: the entry +;; records the stacked commit's group too. +(t/deftest a-stacked-commit-adds-its-group-to-the-entry + (let [page-id (uuid/next) + [g1 g2] (repeatedly 2 uuid/next) + state (reduce #(ptk/update %2 %1) + {} + [(dwu/append-undo (entry page-id "A" g1) false) + (dwu/append-undo (entry page-id "B" g2) true)])] + (t/is (= 1 (count (get-in state [:workspace-undo :items])))) + (t/is (= #{g1 g2} (get-in state [:workspace-undo :items 0 :undo-groups]))))) + +;; Scenario: the user presses Ctrl+Z on a page rename. Proves: the commit it +;; makes can be undone (its undo changes redo the rename) and names the index +;; the history moved from, so a rollback can put both back. +(t/deftest an-undo-commit-can-be-undone + (t/async + done + (let [file (cthf/sample-file :file1 :page-label :page1) + page-id (cthf/current-page-id file) + store (ths/setup-store file) + commits (atom []) + sub (->> (ptk/input-stream store) + (rx/filter dch/commit?) + (rx/subs! #(swap! commits conj (deref %)))) + item (assoc (entry page-id "A" (uuid/next)) :undo-groups #{})] + (ths/run-store store done + [#(assoc % :workspace-undo {:items [item] :index 0}) + dwu/undo] + (fn [state] + (rx/dispose! sub) + (let [[commit] @commits] + (t/is (= 1 (count @commits))) + (t/is (= (:undo-changes item) (:redo-changes commit))) + (t/is (= (:redo-changes item) (:undo-changes commit))) + (t/is (= 0 (:undo-index commit))) + (t/is (= -1 (get-in state [:workspace-undo :index]))))))))) + +;; Forgetting the undo groups of a rollback. + +(defn- entry-of-groups + "An undo entry holding commits of `groups`, the first one naming it." + [& groups] + {:undo-group (first groups) :undo-groups (set groups)}) + +(defn- forget + "The undo state after forgetting `groups` from `undo-state`." + [undo-state groups undo-index] + (:workspace-undo (ptk/update (dwu/forget-undo-groups groups undo-index) + {:workspace-undo undo-state}))) + +(t/deftest forgetting-undo-groups-cuts-the-history-at-the-first-match + (let [[g1 g2 g3] (repeatedly 3 uuid/next) + undo (forget {:items [(entry-of-groups g1) (entry-of-groups g2) (entry-of-groups g3)] + :index 2 + :transaction {:undo-group g3 :undo-changes [{:type :mod-page}]}} + #{g2 g3} nil)] + (t/is (= [g1] (mapv :undo-group (:items undo)))) + (t/is (= 0 (:index undo))) + (t/is (empty? (get-in undo [:transaction :undo-changes])) "the open transaction is emptied") + (t/is (= g3 (get-in undo [:transaction :commit-group])) + "its later commits keep the group of its earlier ones"))) + +(t/deftest forgetting-undo-groups-matches-any-group-an-entry-holds + (let [[g1 g2 g3] (repeatedly 3 uuid/next) + undo (forget {:items [(entry-of-groups g1) (entry-of-groups g2 g3)] :index 1} + #{g3} nil)] + (t/is (= [g1] (mapv :undo-group (:items undo)))) + (t/is (= 0 (:index undo))))) + +(t/deftest forgetting-undo-groups-keeps-the-history-without-a-match + (let [[g1 g2] (repeatedly 2 uuid/next) + tx {:undo-group g2 :undo-groups #{g2} :undo-changes [{:type :mod-page}]} + undo (forget {:items [(entry-of-groups g1)] :index 0 :transaction tx} + #{(uuid/next)} nil)] + (t/is (= [g1] (mapv :undo-group (:items undo)))) + (t/is (= 0 (:index undo))) + (t/is (= tx (:transaction undo)) "an unrelated open transaction stays"))) + +(t/deftest forgetting-undo-groups-empties-a-transaction-that-holds-one + (let [[g1 g2] (repeatedly 2 uuid/next) + tx {:undo-group g2 :undo-groups #{g2} :undo-changes [{:type :mod-page}]} + undo (forget {:items [(entry-of-groups g1)] :index 0 :transaction tx} + #{g2} nil)] + (t/is (= [g1] (mapv :undo-group (:items undo))) "the history stays") + (t/is (empty? (get-in undo [:transaction :undo-changes]))) + (t/is (= g2 (get-in undo [:transaction :commit-group]))))) + +(t/deftest forgetting-undo-groups-points-back-to-the-index-of-an-undone-undo + (let [[g1 g2 g3] (repeatedly 3 uuid/next) + undo-state {:items [(entry-of-groups g1) (entry-of-groups g2) (entry-of-groups g3)] + :index 0}] + (t/is (= 2 (:index (forget undo-state #{(uuid/next)} 2)))) + (t/is (= 0 (:index (forget undo-state #{g2} 2))) + "a cut history cannot point past its end"))) + +;; Scenario: the workspace opens and then reloads twice (a peer's restore, +;; the rollback fallback), each reload running `initialize-workspace` again +;; without leaving the workspace; then one commit is made. Proves: each +;; reload replaces the workspace streams, so the commit adds one undo entry, +;; not one per reload. +(t/deftest ^:async a-workspace-reload-adds-one-undo-entry-per-commit + (let [file (cthf/sample-file :file1 :page-label :page1) + team-id (uuid/next) + store (ths/setup-store file {:renderer :svg}) + loaded (fn [type] (mock/stub (fn [& _] (ptk/data-event type {}))))] + (await + (mock/with-mocks* + {dw/initialize-file (loaded ::file-requested) + dcmt/retrieve-comment-threads (loaded ::threads-requested) + dcmt/fetch-profiles (loaded ::profiles-requested) + df/fetch-fonts (loaded ::fonts-requested)} + (try + (ptk/emit! store + #(assoc % :current-team-id team-id) + (dw/initialize-workspace team-id (:id file)) + (ptk/event ::dw/reload-current-file) + (ptk/event ::dw/reload-current-file)) + (await (async/settle)) + (ptk/emit! store (rename-page (cthf/current-page-id file) "Renamed")) + (await (async/wait-for #(seq (get-in @store [:workspace-undo :items])) + "the commit reaches the undo history")) + (await (async/settle)) + (t/is (= 1 (count (get-in @store [:workspace-undo :items])))) + (finally + (rx/dispose! store))))))) diff --git a/frontend/test/frontend_tests/data/workspace_versions_test.cljs b/frontend/test/frontend_tests/data/workspace_versions_test.cljs index 5ec7fa87bb..7753ba4164 100644 --- a/frontend/test/frontend_tests/data/workspace_versions_test.cljs +++ b/frontend/test/frontend_tests/data/workspace_versions_test.cljs @@ -6,14 +6,17 @@ (ns frontend-tests.data.workspace-versions-test (:require + [app.common.time :as ct] [app.common.uuid :as uuid] [app.main.data.changes :as dch] + [app.main.data.notifications :as ntf] [app.main.data.persistence :as dps] [app.main.data.workspace :as dw] [app.main.data.workspace.pages :as dwpg] [app.main.data.workspace.versions :as versions] [app.main.refs :as refs] [app.main.repo :as rp] + [app.main.ui.workspace.sidebar.versions :as versions-ui] [app.util.timers :as tm] [beicon.v2.core :as rx] [cljs.test :as t :include-macros true] @@ -27,6 +30,7 @@ (let [file-id (uuid/next) resolved (atom []) rejected (atom []) + emitted (atom []) requests (atom []) state {:current-file-id file-id} event (case operation @@ -42,12 +46,41 @@ rx/timeout (mock/stub (fn [_ fallback source] (if (= failure :timeout) fallback source)))] (->> (ptk/watch event state (rx/empty)) - (rx/subs! (fn [_]) #(swap! rejected conj %))) + (rx/subs! #(swap! emitted conj %) #(swap! rejected conj %))) (t/is (empty? @requests) "Do not create a version without saving") (t/is (empty? @resolved) "Do not resolve the plugin promise on failure") - (t/is (= 1 (count @rejected))) - (t/is (= (if (= failure :error) :save-permission-denied :save-timeout) - (:code (ex-data (first @rejected)))))))))) + (if (= operation :create) + ;; The save failure is reported on its own; the user is told + ;; why the version is missing instead + (let [notice (some #(when (= ::ntf/show (ptk/type %)) + (:notification (ptk/update % {}))) + @emitted)] + (t/is (empty? @rejected)) + (t/is (= "workspace.versions.create-failed-unsaved" (:content notice))) + (t/is (= :warning (:level notice)))) + (do + (t/is (= 1 (count @rejected))) + (t/is (= (if (= failure :error) :save-permission-denied :save-timeout) + (:code (ex-data (first @rejected)))))))))))) + +(t/deftest repair-snapshots-are-told-apart-from-autosaves + (t/is (versions/repair-snapshot? {:label "internal/repair/5" :created-by "system"})) + (t/is (not (versions/repair-snapshot? {:label "internal/snapshot/20" :created-by "system"}))) + (t/is (not (versions/repair-snapshot? {:label "My version" :created-by "user"}))) + (t/is (not (versions/repair-snapshot? {:created-by "user"})))) + +;; Scenario: on one day the backend kept two autosaves and the copy from +;; before a repair. Proves: the history shows the repair copy as its own +;; entry, above the day's autosaves, so it can be found by its label. +(t/deftest the-history-lists-the-copy-before-a-repair-apart + (let [at (fn [hh] (ct/inst (str "2026-11-03T" hh ":00:00Z"))) + data [{:id (uuid/next) :created-by "system" :label "internal/snapshot/0" :created-at (at "10")} + {:id (uuid/next) :created-by "system" :label "internal/repair/5" :created-at (at "11")} + {:id (uuid/next) :created-by "system" :label "internal/snapshot/20" :created-at (at "12")}] + entries (#'versions-ui/group-snapshots data)] + (t/is (= [:repair :autosave] (mapv :kind entries))) + (t/is (= [["internal/repair/5"] ["internal/snapshot/0" "internal/snapshot/20"]] + (mapv #(mapv :label (:snapshots %)) entries))))) (t/deftest leaving-the-workspace-clears-the-preview-flags (let [team-id (uuid/next) diff --git a/frontend/test/frontend_tests/helpers/persistence.cljs b/frontend/test/frontend_tests/helpers/persistence.cljs new file mode 100644 index 0000000000..70e3db744a --- /dev/null +++ b/frontend/test/frontend_tests/helpers/persistence.cljs @@ -0,0 +1,181 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.helpers.persistence + "Fixture and builders shared by the persistence tests: a store with + persistence initialized over a mocked transport, synthetic commits, and + one-page file data to roll back." + (:require + [app.common.types.shape :as cts] + [app.common.uuid :as uuid] + [app.main.data.changes :as dch] + [app.main.data.persistence :as dps] + [app.main.errors :as errors] + [app.main.repo :as rp] + [app.main.store :as st] + [app.main.worker :as mw] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.mock :as mock] + [potok.v2.core :as ptk])) + +(defn local-commit + "Builds a synthetic local commit event for `file-id`: a page edit as redo, + no undo, from the `:local` source." + [file-id] + (ptk/data-event ::dch/commit + {:id (uuid/next) + :file-id file-id + :file-revn 0 + :file-vern 0 + :source :local + :features #{} + :redo-changes [{:type :mod-page :id (uuid/next) :name "Edited"}] + :undo-changes []})) + +(defn with-persistence + "Async fixture for the persistence tests: mocks the transport, flash, the + worker and the leave-page task set (`:tasks`), and runs `f` with + persistence initialized. + + Evaluates to a promise resolving once `f` settles and teardown completes; + `await` it from an `^:async` test. + + By default the transport responds through the `:response` subject. Pass a + `respond` function (`(fn [cmd params] observable)`) to drive the transport + response directly instead. Delivery through the subject is asynchronous + (`observe-on :async`): await each effect — via `wait-for` — before + asserting it." + [f & [respond]] + (let [file-id (uuid/next) + response (rx/subject) + failures (atom []) + requests (atom []) + tasks (atom #{}) + store (ptk/store {:state {:current-file-id file-id + :permissions {:can-edit true} + :files {file-id {:id file-id :revn 0}}} + :on-error #(t/is false (str %))})] + (mock/with-mocks* + {rp/cmd! (mock/stub (fn [cmd params] + (swap! requests conj [cmd params]) + (if respond + (respond cmd params) + (->> response (rx/take 1) (rx/observe-on :async))))) + errors/flash (fn [& {:keys [cause]}] + (swap! failures conj cause)) + ;; Index updates of applied changes go to a worker the tests lack + mw/ask! (mock/stub (fn [_] (rx/empty))) + st/ongoing-tasks tasks} + (try + (ptk/emit! store (dps/initialize-persistence)) + (await (f {:file-id file-id :response response :failures failures + :requests requests :store store :tasks tasks})) + (finally + (rx/dispose! store) + (rx/end! response)))))) + +(defn set-attr + "A change that sets `attr` of shape `shape-id` to `val`." + [page-id shape-id attr val] + {:type :mod-obj :id shape-id :page-id page-id + :operations [{:type :set :attr attr :val val}]}) + +(defn rename + "A change that renames shape `shape-id` to `name`." + [page-id shape-id name] + (set-attr page-id shape-id :name name)) + +(defn attr-commit + "A local commit of `file-id` that sets `attr` of the shape from `from` to + `to`, with its own undo group." + [file-id page-id shape-id attr from to] + (ptk/data-event ::dch/commit + {:id (uuid/next) + :file-id file-id + :file-revn 0 + :file-vern 0 + :source :local + :features #{} + :undo-group (uuid/next) + :redo-changes [(set-attr page-id shape-id attr to)] + :undo-changes [(set-attr page-id shape-id attr from)]})) + +(defn rename-commit + "A local commit of `file-id` that renames the shape from `from` to `to`, + with its own undo group." + [file-id page-id shape-id from to] + (attr-commit file-id page-id shape-id :name from to)) + +(defn remote-commit + "A peer's commit of `changes` to `file-id` at revision `revn`." + [file-id revn changes] + (dch/commit {:file-id file-id + :file-revn revn + :file-vern 0 + :source :remote + :save-undo? false + :redo-changes changes + :undo-changes []})) + +(defn file-data + "File data with one page holding one rectangle named `name`, with `attrs` + on top of its defaults." + [page-id shape-id name & {:as attrs}] + {:pages [page-id] + :pages-index {page-id {:id page-id + :name "Page" + :objects {uuid/zero (cts/setup-shape {:id uuid/zero :type :frame :shapes [shape-id]}) + shape-id (merge (cts/setup-shape {:id shape-id :type :rect :name name + :parent-id uuid/zero :frame-id uuid/zero}) + attrs)}}}}) + +(defn add-shape + "`data` with `shape` added as the last child of the root of `page-id`." + [data page-id shape] + (-> data + (assoc-in [:pages-index page-id :objects (:id shape)] shape) + (update-in [:pages-index page-id :objects uuid/zero :shapes] conj (:id shape)))) + +(defn open-file + "A state update that opens `page-id` of `file-id` with `data` loaded." + [file-id page-id data] + #(-> % + (assoc :current-page-id page-id) + (assoc-in [:files file-id :data] data))) + +(defn open-file-with-library + "`open-file` that also loads library `lib-id` with `lib-data`." + [file-id lib-id page-id data lib-data] + (comp #(assoc-in % [:files lib-id] {:id lib-id :revn 0 :data lib-data}) + (open-file file-id page-id data))) + +(defn shape-attr + [store file-id page-id shape-id attr] + (get-in @store [:files file-id :data :pages-index page-id :objects shape-id attr])) + +(defn shape-name + [store file-id page-id shape-id] + (shape-attr store file-id page-id shape-id :name)) + +(defn undo-entry + "The undo history entry `commit` would add." + [commit] + (let [{:keys [undo-group undo-changes redo-changes]} @commit] + {:undo-group undo-group + :undo-groups #{undo-group} + :undo-changes undo-changes + :redo-changes redo-changes + :tags #{}})) + +(defn rejection + "A save answer that rejects `commit-ids`, the first one failing + validation." + [& commit-ids] + {:revn 0 + :rejected {:commit-ids (vec commit-ids) + :failed-commit-id (first commit-ids) + :error {:type :validation :code :referential-integrity}}}) diff --git a/frontend/test/frontend_tests/helpers/wasm.cljs b/frontend/test/frontend_tests/helpers/wasm.cljs index 82d4f8ab99..1c955c4d82 100644 --- a/frontend/test/frontend_tests/helpers/wasm.cljs +++ b/frontend/test/frontend_tests/helpers/wasm.cljs @@ -20,6 +20,7 @@ verify the WASM code path was exercised." (:require [app.common.data :as d] + [app.common.render-wasm.api.select :as wselect] [app.render-wasm.api :as wasm.api] [app.render-wasm.api.fonts :as wasm.fonts])) @@ -198,7 +199,7 @@ ;; Save originals (reset! originals {:initialized? wasm.api/initialized? - :use-shape wasm.api/use-shape + :use-shape wselect/use-shape :calculate-position-data wasm.api/calculate-position-data :request-render wasm.api/request-render :clean-modifiers wasm.api/clean-modifiers @@ -217,7 +218,7 @@ :get-content-fonts wasm.fonts/get-content-fonts}) ;; Install mocks (set! wasm.api/initialized? mock-initialized?) - (set! wasm.api/use-shape mock-use-shape) + (set! wselect/use-shape mock-use-shape) (set! wasm.api/calculate-position-data mock-calculate-position-data) (set! wasm.api/request-render mock-request-render) (set! wasm.api/clean-modifiers mock-clean-modifiers) @@ -248,7 +249,7 @@ [] (when-let [orig (not-empty @originals)] (set! wasm.api/initialized? (:initialized? orig)) - (set! wasm.api/use-shape (:use-shape orig)) + (set! wselect/use-shape (:use-shape orig)) (set! wasm.api/calculate-position-data (:calculate-position-data orig)) (set! wasm.api/request-render (:request-render orig)) (set! wasm.api/clean-modifiers (:clean-modifiers orig)) diff --git a/frontend/test/frontend_tests/logic/bool_move_test.cljs b/frontend/test/frontend_tests/logic/bool_move_test.cljs new file mode 100644 index 0000000000..5191d4e98e --- /dev/null +++ b/frontend/test/frontend_tests/logic/bool_move_test.cljs @@ -0,0 +1,99 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.logic.bool-move-test + "Moving shapes must only recalculate booleans whose children moved + relative to them, and leave every boolean equal to a fresh calculation." + (:require + [app.common.geom.point :as gpt] + [app.common.test-helpers.compositions :as ctho] + [app.common.test-helpers.files :as cthf] + [app.common.test-helpers.ids-map :as cthi] + [app.common.test-helpers.shapes :as cths] + [app.common.types.modifiers :as ctm] + [app.common.types.pages-list :as ctpl] + [app.common.types.path :as path] + [app.common.types.shape-tree :as ctst] + [app.main.data.workspace.modifiers :as dwm] + [app.render-wasm.api :as wasm.api] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.state :as ths] + [frontend-tests.helpers.wasm :as thw])) + +(def ^:private bool-calculations (atom 0)) + +(defn- counting-calc-bool-content + [shape objects] + (swap! bool-calculations inc) + (path/calc-bool-content shape objects)) + +(def ^:private original-calculate-bool wasm.api/calculate-bool) + +(t/use-fixtures :each + {:before (fn [] + (cthi/reset-idmap!) + (thw/setup-wasm-mocks!) + (reset! bool-calculations 0) + (set! wasm.api/calculate-bool counting-calc-bool-content) + (set! path/wasm:calc-bool-content counting-calc-bool-content)) + :after (fn [] + (set! wasm.api/calculate-bool original-calculate-bool) + (set! path/wasm:calc-bool-content nil) + (thw/teardown-wasm-mocks!))}) + +(defn- file-with-bool-in-board + [] + (let [file (-> (cthf/sample-file :file1) + (ctho/add-frame :board :x 0 :y 0 :width 400 :height 400) + (cths/add-sample-shape :bool {:type :bool + :bool-type :union + :parent-label :board}) + (ctho/add-rect :rect1 :x 10 :y 10 :width 100 :height 100 :parent-label :bool) + (ctho/add-rect :rect2 :x 60 :y 60 :width 100 :height 100 :parent-label :bool)) + page (cthf/current-page file) + bool (path/update-bool-shape (cths/get-shape file :bool) (:objects page))] + (update file :data ctpl/update-page (:id page) #(ctst/set-shape % bool)))) + +(defn- disable-pixel-grid + [] + (fn [state] + (update state :workspace-layout disj :snap-pixel-grid))) + +(defn- run-move + [done labels delta expected-calculations & {:keys [pixel-grid?] :or {pixel-grid? true}}] + (let [file (file-with-bool-in-board) + store (ths/setup-store file) + ids (map #(:id (cths/get-shape file %)) labels) + modif-tree (dwm/create-modif-tree ids (ctm/move-modifiers delta)) + events (cond->> [(dwm/apply-wasm-modifiers modif-tree)] + (not pixel-grid?) (cons (disable-pixel-grid)))] + (reset! bool-calculations 0) + (ths/run-store + store done events + (fn [new-state] + (let [calculations @bool-calculations + file' (ths/get-file-from-state new-state) + objects' (:objects (cthf/current-page file')) + after (cths/get-shape file' :bool) + fresh (path/update-bool-shape after objects')] + (t/is (= expected-calculations calculations)) + (t/is (= (:selrect fresh) (:selrect after))) + (t/is (= (vec (:content fresh)) (vec (:content after))))))))) + +(t/deftest moving-a-board-does-not-recalculate-the-booleans-inside + (t/async + done + (run-move done [:board :bool :rect1 :rect2] (gpt/point 10 20) 0))) + +(t/deftest moving-a-boolean-child-recalculates-the-boolean + (t/async + done + (run-move done [:rect1] (gpt/point 10 20) 1))) + +(t/deftest moving-a-board-without-pixel-grid-does-not-recalculate-its-booleans + (t/async + done + (run-move done [:board] (gpt/point 10.5 20.25) 0 :pixel-grid? false))) diff --git a/frontend/test/frontend_tests/logic/comp_watcher_test.cljs b/frontend/test/frontend_tests/logic/comp_watcher_test.cljs index f8d3bc8783..b9767480ba 100644 --- a/frontend/test/frontend_tests/logic/comp_watcher_test.cljs +++ b/frontend/test/frontend_tests/logic/comp_watcher_test.cljs @@ -11,6 +11,7 @@ [app.common.test-helpers.files :as cthf] [app.common.test-helpers.ids-map :as cthi] [app.common.test-helpers.shapes :as cths] + [app.main.data.changes :as dch] [app.main.data.helpers :as dsh] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.pages :as dwpg] @@ -98,3 +99,34 @@ (ptk/emit! store (dwpg/finalize-page (:id file) (cthf/current-page-id file))) (await (async/settle)))))) + +;; Scenario: a rollback reverts the opacity of the main's child. A rollback +;; is not a commit, so the change watch sees no component change. +;; Proves: the component's thumbnail is rendered again and uploaded, so +;; neither the tab nor the server keeps a thumbnail of the rejected data. +(t/deftest ^:async a-rollback-of-a-main-renders-and-uploads-its-thumbnail + (let [file (setup-file) + store (ths/setup-store file) + page-id (cthf/current-page-id file) + renders (atom [])] + (await + (mock/with-mocks* + {refs/workspace-data (l/derived dsh/lookup-file-data store) + st/state store + dwt.wasm/render-thumbnail (fn [_ _ frame-id & {:as opts}] + (swap! renders conj [frame-id opts]) + (noop-render-thumbnail))} + + (ptk/emit! store (dwl/watch-component-changes)) + (ptk/emit! store (dch/rebase-commits + (:id file) + {:undo [] + :remote [{:type :mod-obj :id (cthi/id :main-child) :page-id page-id + :operations [{:type :set :attr :opacity :val 0.5}]}] + :redo []})) + + (await (async/wait-for #(seq @renders) "the thumbnail to render")) + (t/is (= [[(cthi/id :main-root) {:persist? true}]] @renders)) + + (ptk/emit! store (dwpg/finalize-page (:id file) page-id)) + (await (async/settle)))))) diff --git a/frontend/test/frontend_tests/logic/drag_on_save_failure_test.cljs b/frontend/test/frontend_tests/logic/drag_on_save_failure_test.cljs new file mode 100644 index 0000000000..7ccf1c9d9c --- /dev/null +++ b/frontend/test/frontend_tests/logic/drag_on_save_failure_test.cljs @@ -0,0 +1,214 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.logic.drag-on-save-failure-test + "Tests for drags and drawings in progress when saves start failing and + the workspace locks." + (:require + [app.common.data :as d] + [app.common.geom.point :as gpt] + [app.common.geom.rect :as grc] + [app.common.test-helpers.compositions :as ctho] + [app.common.test-helpers.files :as cthf] + [app.common.test-helpers.ids-map :as cthi] + [app.common.test-helpers.shapes :as cths] + [app.common.types.path :as path] + [app.main.data.changes :as dch] + [app.main.data.persistence.lock :as plk] + [app.main.data.workspace.drawing :as dwd] + [app.main.data.workspace.drawing.curve :as-alias dwdc] + [app.main.data.workspace.transforms :as dwt] + [app.main.snap :as snap] + [app.main.streams :as ms] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.mock :as mock] + [frontend-tests.helpers.state :as ths] + [potok.v2.core :as ptk])) + +(t/use-fixtures :each {:before cthi/reset-idmap!}) + +(defn- shape-rect + [store] + (-> (ths/get-file-from-state @store) + (cths/get-shape :rect1) + :points + (grc/points->rect))) + +(defn- ^:async drag + "Drags rect1 with the gesture `start` makes, from (120, 220) to (160, 260), + then emits `stop`. Resolves to the commits made and the store." + [start stop] + (let [file (-> (cthf/sample-file :file1) + (ctho/add-rect :rect1 :x 100 :y 200 :width 50 :height 50)) + rect (cths/get-shape file :rect1) + store (ths/setup-store file {:renderer :svg}) + commits (atom 0) + sub (->> (ptk/input-stream store) + (rx/filter dch/commit?) + (rx/subs! (fn [_] (swap! commits inc))))] + (await + (mock/with-mocks* + ;; Called with seven arguments, more than a stub forwards + {snap/closest-snap-move (fn [& _] (rx/of (gpt/point 0 0))) + snap/closest-snap-point (fn [& _] (rx/of (gpt/point 0 0))) + ;; Moves a DOM node the tests lack + dwt/set-ghost-displacement (mock/stub (fn [_] (ptk/data-event ::ghost-moved)))} + (try + (rx/push! ms/mouse-position (gpt/point 120 220)) + (ptk/emit! store + #(assoc-in % [:workspace-local :selected] (d/ordered-set (:id rect))) + (start rect)) + (rx/push! ms/mouse-position (gpt/point 160 260)) + (await (async/wait-for #(seq (:workspace-modifiers @store)) "the drag shows a preview")) + (ptk/emit! store stop) + (await (async/settle)) + (finally + (rx/push! ms/mouse-position nil) + (rx/dispose! sub))))) + {:commits @commits :store store})) + +(def ^:private save-failure + "Saves fail for good: the workspace locks." + (ptk/reify ::save-failure + ptk/UpdateEvent + (update [_ state] + (assoc state :persistence {:status :error})) + + ptk/WatchEvent + (watch [_ _ _] + (rx/of (plk/sync-failure-lock))))) + +(defn- move + [rect] + (dwt/start-move (gpt/point 120 220) [(:id rect)])) + +(defn- rotate + [rect] + (dwt/start-rotate [rect])) + +;; Scenario: the user drags a shape and stops it with Escape. Proves: the +;; drag commits where it stopped (the case the lock must not follow). +(t/deftest ^:async an-interrupted-move-commits-its-position + (let [{:keys [commits store]} (await (drag move :interrupt))] + (t/is (pos? commits)) + (t/is (= 140 (:x (shape-rect store)))))) + +;; Scenario: saves fail while the user drags a shape, or rotates it. Proves: +;; the lock cancels the gesture, the shape goes back to where it was and +;; nothing is committed behind the failed save. +(defn- check-cancelled + [{:keys [commits store]}] + (t/is (some? (get-in @store [:workspace-global :persistence-lock])) "the workspace is locked") + (t/is (= 0 commits) "nothing is committed") + (t/is (empty? (:workspace-modifiers @store)) "the preview is dropped") + (t/is (nil? (get-in @store [:workspace-local :transform]))) + (t/is (= (grc/make-rect 100 200 50 50) (shape-rect store)) + "the shape stays where it was")) + +(t/deftest ^:async a-save-failure-cancels-a-move-in-progress + (check-cancelled (await (drag move save-failure)))) + +(t/deftest ^:async a-save-failure-cancels-a-rotation-in-progress + (check-cancelled (await (drag rotate save-failure)))) + +;; -- Drawing --------------------------------------------------------- + +(defn- drawn-shapes + "Shapes of the current page other than the root." + [store] + (let [page-id (:current-page-id @store) + objects (get-in (ths/get-file-from-state @store) [:data :pages-index page-id :objects])] + (remove #(= (:id %) (:parent-id %)) (vals objects)))) + +(defn- ^:async draw + "Starts drawing with `tool` on an empty page and moves the pointer with + `move` (gets the store, resolves once the drawing shows), then emits + `stop` and waits for the drawing to end. Resolves to the commits made + and the store." + [tool move stop] + (let [file (cthf/sample-file :file1) + store (ths/setup-store file {:renderer :svg}) + commits (atom 0) + sub (->> (ptk/input-stream store) + (rx/filter dch/commit?) + (rx/subs! (fn [_] (swap! commits inc))))] + (await + (mock/with-mocks* + ;; Called with seven arguments, more than a stub forwards + {snap/closest-snap-point (fn [& _] (rx/of (gpt/point 0 0)))} + (try + (rx/push! ms/mouse-position (gpt/point 100 100)) + (ptk/emit! store (dwd/handle-drawing tool)) + (await (move store)) + (ptk/emit! store stop) + ;; A box drawing ends 100 ms after its stop + (await (async/wait-for #(nil? (get-in @store [:workspace-drawing :object])) + "the drawing ends" {:max-ticks 500})) + (await (async/settle)) + (finally + (rx/push! ms/mouse-position nil) + (rx/dispose! sub))))) + {:commits @commits :store store})) + +(defn- ^:async drag-box + [store] + (rx/push! ms/mouse-position (gpt/point 160 180)) + (await (async/wait-for #(< 50 (get-in @store [:workspace-drawing :object :width] 0)) + "the box follows the pointer"))) + +(defn- ^:async drag-curve + [store] + (doseq [x [110 130 150 170]] + (rx/push! ms/mouse-position (gpt/point x (+ x 20))) + (await (async/wait-for #(some (fn [p] (= x (:x p))) + (get-in @store [:workspace-drawing :object ::dwdc/points])) + "the curve takes the point")))) + +(defn- ^:async click-path + "Stands for the pen clicks: the drawn path gets two nodes." + [store] + (await (async/wait-for #(some? (get-in @store [:workspace-drawing :object])) "the pen starts")) + (ptk/emit! store #(assoc-in % [:workspace-drawing :object :content] + (path/content [{:command :move-to :params {:x 100 :y 100}} + {:command :line-to :params {:x 180 :y 160}}])))) + +(defn- check-drawn + [{:keys [commits store]}] + (t/is (pos? commits)) + (t/is (= 1 (count (drawn-shapes store))) "the shape is added")) + +(defn- check-not-drawn + [{:keys [commits store]}] + (t/is (plk/locked? @store) "the workspace is locked") + (t/is (= 0 commits) "nothing is committed") + (t/is (empty? (drawn-shapes store)) "no shape is added") + (t/is (nil? (get-in @store [:workspace-drawing :object])) "no drawing is left on screen")) + +;; Scenario: the user draws a rectangle, a curve or a path and stops it +;; with Escape. Proves: the shape is added (the case the lock must not +;; follow). +(t/deftest ^:async an-interrupted-rectangle-is-added + (check-drawn (await (draw :rect drag-box :interrupt)))) + +(t/deftest ^:async an-interrupted-curve-is-added + (check-drawn (await (draw :curve drag-curve :interrupt)))) + +(t/deftest ^:async an-interrupted-path-is-added + (check-drawn (await (draw :path click-path :interrupt)))) + +;; Scenario: saves fail while the user draws a rectangle, a curve or a +;; path. Proves: the lock drops the drawing: no shape is added, nothing is +;; committed behind the failed save and no half-drawn shape stays. +(t/deftest ^:async a-save-failure-drops-a-rectangle-in-progress + (check-not-drawn (await (draw :rect drag-box save-failure)))) + +(t/deftest ^:async a-save-failure-drops-a-curve-in-progress + (check-not-drawn (await (draw :curve drag-curve save-failure)))) + +(t/deftest ^:async a-save-failure-drops-a-path-in-progress + (check-not-drawn (await (draw :path click-path save-failure)))) diff --git a/frontend/test/frontend_tests/logic/ignore_tree_test.cljs b/frontend/test/frontend_tests/logic/ignore_tree_test.cljs new file mode 100644 index 0000000000..f625d0009c --- /dev/null +++ b/frontend/test/frontend_tests/logic/ignore_tree_test.cljs @@ -0,0 +1,56 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.logic.ignore-tree-test + (:require + [app.common.files.helpers :as cfh] + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] + [app.common.test-helpers.compositions :as ctho] + [app.common.test-helpers.files :as cthf] + [app.common.test-helpers.ids-map :as cthi] + [app.common.types.component :as ctk] + [app.main.data.workspace.modifiers :as dwm] + [cljs.test :as t :include-macros true])) + +(t/use-fixtures :each {:before cthi/reset-idmap!}) + +(defn- setup + [] + (let [file (-> (cthf/sample-file :file1) + (ctho/add-frame :board) + (ctho/add-nested-component-with-copy :component1 :main1-root :main1-child + :component2 :main2-root :nested-head + :copy2-root + :copy2-root-params {:parent-label :board}))] + (:objects (cthf/current-page file)))) + +(defn- ignore-tree + [objects moved-ids] + (let [move (gmt/translate-matrix (gpt/point 10 20))] + (dwm/calculate-ignore-tree-wasm (zipmap moved-ids (repeat move)) objects))) + +(defn- copy-ids + [objects] + (->> (cfh/get-children-ids-with-self objects (cthi/id :copy2-root)) + (filter #(ctk/in-component-copy? (get objects %))))) + +(t/deftest moving-a-board-keeps-its-copies-untouched + (let [objects (setup) + tree (ignore-tree objects (cfh/get-children-ids-with-self objects (cthi/id :board)))] + (t/is (seq (copy-ids objects))) + (t/is (every? #(true? (get tree %)) (copy-ids objects))))) + +(t/deftest moving-a-copy-keeps-its-children-untouched + (let [objects (setup) + tree (ignore-tree objects (cfh/get-children-ids-with-self objects (cthi/id :copy2-root)))] + (t/is (every? #(true? (get tree %)) (copy-ids objects))))) + +(t/deftest moving-a-shape-inside-a-copy-touches-it + (let [objects (setup) + rect-id (last (copy-ids objects)) + tree (ignore-tree objects [rect-id])] + (t/is (false? (get tree rect-id))))) diff --git a/frontend/test/frontend_tests/logic/move_shapes_to_frame_test.cljs b/frontend/test/frontend_tests/logic/move_shapes_to_frame_test.cljs new file mode 100644 index 0000000000..7dab714ad3 --- /dev/null +++ b/frontend/test/frontend_tests/logic/move_shapes_to_frame_test.cljs @@ -0,0 +1,65 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.logic.move-shapes-to-frame-test + "Tests for the reparenting step that ends a move (`dwt/move-shapes-to-frame`)." + (:require + [app.common.test-helpers.compositions :as ctho] + [app.common.test-helpers.files :as cthf] + [app.common.test-helpers.ids-map :as cthi] + [app.common.test-helpers.shapes :as cths] + [app.common.uuid :as uuid] + [app.main.data.changes :as dch] + [app.main.data.workspace.transforms :as dwt] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.state :as ths] + [potok.v2.core :as ptk])) + +(t/use-fixtures :each {:before cthi/reset-idmap!}) + +(defn- count-commits + "Runs `event` on a store of `file` and calls `check` with the number of + commits it made and the resulting file." + [done file event check] + (let [store (ths/setup-store file) + commits (atom 0) + sub (->> (ptk/input-stream store) + (rx/filter dch/commit?) + (rx/subs! (fn [_] (swap! commits inc))))] + (ths/run-store store done [event] + (fn [state] + (rx/dispose! sub) + (check @commits (ths/get-file-from-state state)))))) + +;; Scenario: a move ends over the frame the shape already belongs to. +;; Proves: nothing changes parent, so no commit (and no save) is made. +(t/deftest moving-a-shape-within-its-frame-commits-nothing + (t/async + done + (let [file (-> (cthf/sample-file :file1) + (ctho/add-rect :rect1)) + rect (cths/get-shape file :rect1)] + (count-commits done file + (dwt/move-shapes-to-frame [(:id rect)] uuid/zero nil nil) + (fn [commits _] + (t/is (zero? commits))))))) + +;; Scenario: a move ends over another board. Proves: the shape is moved into +;; it with one commit. +(t/deftest moving-a-shape-into-a-board-commits-the-new-parent + (t/async + done + (let [file (-> (cthf/sample-file :file1) + (ctho/add-frame :frame1) + (ctho/add-rect :rect1)) + rect (cths/get-shape file :rect1) + frame (cths/get-shape file :frame1)] + (count-commits done file + (dwt/move-shapes-to-frame [(:id rect)] (:id frame) nil nil) + (fn [commits file'] + (t/is (= 1 commits)) + (t/is (= (:id frame) (:parent-id (cths/get-shape file' :rect1))))))))) diff --git a/frontend/test/frontend_tests/main_errors_test.cljs b/frontend/test/frontend_tests/main_errors_test.cljs index 32898be282..4f59e70b95 100644 --- a/frontend/test/frontend_tests/main_errors_test.cljs +++ b/frontend/test/frontend_tests/main_errors_test.cljs @@ -829,6 +829,17 @@ (t/is (= timeout (get-in state [:notification :timeout]))) (t/is (= :visible (get-in state [:notification :status])))))))) +(t/deftest flash-shows-errors-unless-asked-for-another-level + (doseq [[notify level] [[#(errors/flash :hint "Ordinary error") :error] + [#(errors/flash :hint "Undone changes" :level :warning) :warning]]] + (let [scheduled (atom []) + events (atom [])] + (with-redefs [tm/schedule (mock/stub #(swap! scheduled conj %)) + st/emit! (mock/stub (fn [& emitted] (swap! events into emitted)))] + (notify) + (doseq [callback @scheduled] (callback)) + (t/is (= level (get-in (ptk/update (first @events) {}) [:notification :level]))))))) + (t/deftest ^:async persistence-notifications-include-an-error-report-download (let [scheduled (atom []) idle-callbacks (atom []) @@ -1010,8 +1021,15 @@ (await (async/settle)) (t/is (= [::rt/nav-raw] (mapv ptk/type @events)))))))) -(t/deftest a-deleted-file-during-save-shows-the-exception-page - (t/testing "a file that no longer exists shows its page, not the save notification" +(defn- shown-notifications + "Notifications the scheduled callbacks of a flash put on screen." + [events] + (keep #(when (implements? ptk/UpdateEvent %) + (get-in (ptk/update % {}) [:notification :content])) + events)) + +(t/deftest a-not-found-save-keeps-the-workspace + (t/testing "a save answered not-found notifies instead of leaving the workspace" (let [events (atom []) scheduled (atom [])] (with-redefs [rt/assign-exception (fn [error] error) @@ -1019,9 +1037,24 @@ (swap! events into emitted))) tm/schedule (mock/stub (fn [callback] (swap! scheduled conj callback)))] - (errors/flash-persistence (ex-info "File not found" {:type :not-found})) + (errors/flash-persistence (ex-info "Save failed" {:type :persistence + :cause-type :not-found + :code :object-not-found})) (doseq [callback @scheduled] (callback)) - (t/is (= [:not-found] (mapv :type @events))))))) + (t/is (not-any? #(= :not-found (:type %)) @events) "no exception page") + (t/is (= [(i18n/tr "errors.save-failed")] (shown-notifications @events))))))) + +(t/deftest lost-edit-permission-tells-how-many-changes-were-not-saved + (let [events (atom []) + scheduled (atom [])] + (with-redefs [st/emit! (mock/stub (fn [& emitted] (swap! events into emitted))) + tm/schedule (mock/stub (fn [callback] (swap! scheduled conj callback)))] + (errors/flash-persistence (ex-info "Edit permission was lost" + {:type :persistence + :code :save-permission-denied + :unsaved-commits 3})) + (doseq [callback @scheduled] (callback)) + (t/is (= [(i18n/tr "errors.save-permission-lost" (i18n/c 3))] (shown-notifications @events)))))) (t/deftest a-restored-version-during-save-reloads-the-file (t/testing "a version restored elsewhere reloads the file instead of notifying" diff --git a/frontend/test/frontend_tests/plugins/context_shapes_test.cljs b/frontend/test/frontend_tests/plugins/context_shapes_test.cljs index 5c7da3694f..f0ac83ed4a 100644 --- a/frontend/test/frontend_tests/plugins/context_shapes_test.cljs +++ b/frontend/test/frontend_tests/plugins/context_shapes_test.cljs @@ -507,6 +507,33 @@ (t/is (= 1 @calls) "the update function ran once for the committed shape") (t/is (= 0.5 (.-opacity rect)) "the single computed result was committed"))) +(t/deftest test-writing-to-a-removed-shape-throws + (let [store (ths/setup-store (cthf/sample-file :file1 :page-label :page1) + {:renderer :svg}) + _ (set! st/state store) + _ (set! st/stream (ptk/input-stream store)) + ^js ctx (api/create-context zero-id) + ^js rect (.createRectangle ctx)] + (set! (.-name rect) "Before") + (t/is (= "Before" (.-name rect)) "a write to an existing shape works") + (.remove rect) + (t/is (thrown-with-msg? js/Error #"Shape not found" + (set! (.-name rect) "After")) + "a write to a removed shape throws, even without throwValidationErrors"))) + +(t/deftest test-resizing-or-rotating-a-removed-shape-throws + (let [store (ths/setup-store (cthf/sample-file :file1 :page-label :page1) + {:renderer :svg}) + _ (set! st/state store) + _ (set! st/stream (ptk/input-stream store)) + ^js ctx (api/create-context zero-id) + ^js rect (.createRectangle ctx)] + (.remove rect) + (t/is (thrown-with-msg? js/Error #"Shape not found" (.resize rect 10 10)) + "resizing a removed shape throws instead of doing nothing") + (t/is (thrown-with-msg? js/Error #"Shape not found" (.rotate rect 45)) + "rotating a removed shape throws instead of doing nothing"))) + (t/deftest test-wait-for-layout-update-no-pending ;; When nothing is pending the promise resolves immediately via the fast path ;; (the behavior-subject replays the empty map on subscribe). diff --git a/frontend/test/frontend_tests/plugins/creation_parent_test.cljs b/frontend/test/frontend_tests/plugins/creation_parent_test.cljs new file mode 100644 index 0000000000..6fe12ebc82 --- /dev/null +++ b/frontend/test/frontend_tests/plugins/creation_parent_test.cljs @@ -0,0 +1,158 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC Sucursal en España SL + +(ns frontend-tests.plugins.creation-parent-test + (:require + ["sax$default" :as sax] + [app.common.geom.point :as gpt] + [app.common.geom.rect :as grc] + [app.common.test-helpers.components :as cthc] + [app.common.test-helpers.files :as cthf] + [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.shapes :as cths] + [app.common.uuid :as uuid] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.workspace.svg-upload :as dwsvg] + [app.main.data.workspace.undo :as dwu] + [app.main.refs :as refs] + [app.main.repo :as rp] + [app.main.store :as st] + [app.main.streams :as ms] + [app.main.worker :as mw] + [app.plugins.api :as api] + [app.plugins.library :as library] + [app.util.dom :as dom] + [app.util.object :as obj] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.mock :as mock] + [frontend-tests.helpers.state :as ths] + [frontend-tests.tokens.helpers.state :as tohs] + [potok.v2.core :as ptk] + [tubax.core :as tubax])) + +(def ^:private plugin-id "00000000-0000-0000-0000-000000000000") + +(def ^:private svg + "") + +(t/deftest ^:async plugin-creation-uses-the-page-root + (doseq [layout [nil :flex :grid] + creation [:component :svg :svg-with-images]] + (let [file (-> (cthf/sample-file :file :page-label :page) + (cths/add-sample-shape :main :type :frame :x 1000 :y 1000) + (cths/add-sample-shape :child :parent-label :main) + (cthc/make-component :component :main) + (cths/add-sample-shape :board :type :frame :x 0 :y 0 + :width 400 :height 300 :layout layout + :layout-grid-rows [] :layout-grid-columns [] + :layout-grid-cells {})) + store (ths/setup-store file {:renderer :svg}) + uploads (atom []) + path [:files (:id file) :data :pages-index (thi/id :page) :objects]] + (await + (mock/with-mocks* + {st/state store + st/emit! (fn + ([event] (ptk/emit! store event)) + ([event & events] (apply ptk/emit! store event events))) + mw/ask! (mock/stub (fn [_] (rx/empty))) + rp/cmd! (mock/stub (fn [cmd params] + (swap! uploads conj [cmd params]) + (rx/observe-on + :async + (rx/of {:id (uuid/next) :width 10 :height 20 + :mtype "image/png" :name "image"})))) + ;; Node exposes sax's CommonJS parser on the default export. + tubax/create-parser (mock/stub (fn [_] (sax/parser true #js {})))} + (ptk/emit! store (tohs/watch-undo-stack)) + (let [^js context (api/create-context plugin-id) + ^js created (case creation + :component (.instance (library/lib-component-proxy plugin-id (:id file) (thi/id :component))) + :svg (.createShapeFromSvg context svg) + :svg-with-images + (await (.createShapeFromSvgWithImages + context + ""))) + id (obj/get created "$id")] + (await (async/wait-for #(some? (get-in @store (conj path id))) "created shape committed")) + (let [created-shape (get-in @store (conj path id))] + (t/is (= uuid/zero (:parent-id created-shape)) (str creation " / " layout)) + (t/is (= uuid/zero (:frame-id created-shape)) (str creation " / " layout)) + (t/is (not (some #{id} (get-in @store (into path [(thi/id :board) :shapes]))))) + (t/is (= 1 (count (get-in @store [:workspace-undo :items]))))) + (when (= creation :svg-with-images) + ;; Other RPCs (e.g. the delayed thumbnail cleanup) may land too. + (t/is (= 1 (count (filter #{:create-file-media-object-from-url} + (map first @uploads)))))) + (ptk/emit! store dwu/undo) + (await (async/wait-for #(nil? (get-in @store (conj path id))) "creation undone")) + (t/is (nil? (get-in @store (conj path id)))) + (t/is (empty? (get-in @store (into path [(thi/id :board) :shapes])))) + (ptk/emit! store dwu/redo) + (await (async/wait-for #(some? (get-in @store (conj path id))) "creation redone")) + (set! (.-x created) 1000) + (set! (.-y created) 1200) + (await (async/wait-for #(= [1000 1200] ((juxt :x :y) (get-in @store (conj path id)))) "position updated")) + (t/is (= [1000 1200] ((juxt :x :y) (get-in @store (conj path id))))) + (let [^js page (.-currentPage context) + ^js board (.getShapeById page (str (thi/id :board)))] + (.appendChild board created)) + (await (async/wait-for #(= (thi/id :board) (get-in @store (into path [id :parent-id]))) "explicit parent applied")) + (t/is (= (thi/id :board) (get-in @store (into path [id :parent-id])))))))))) + +(t/deftest ^:async interactive-creation-keeps-coordinate-based-parents + (doseq [[creation position parent-label] [[:component (gpt/point 50 60) :board] + [:component (gpt/point 600 700) nil] + [:svg (gpt/point 50 60) :board] + [:svg (gpt/point 600 700) nil]]] + (let [file (-> (cthf/sample-file :file :page-label :page) + (cths/add-sample-shape :main :type :frame :x 1000 :y 1000) + (cths/add-sample-shape :child :parent-label :main) + (cthc/make-component :component :main) + (cths/add-sample-shape :board :type :frame :x 0 :y 0 + :width 400 :height 300)) + store (ths/setup-store file {:renderer :svg}) + id-ref (atom nil) + svg-id (uuid/next) + parent (if parent-label (thi/id parent-label) uuid/zero) + path [:files (:id file) :data :pages-index (thi/id :page) :objects]] + (await + (mock/with-mocks* + {st/state store + st/emit! (fn + ([event] (ptk/emit! store event)) + ([event & events] (apply ptk/emit! store event events))) + mw/ask! (mock/stub (fn [_] (rx/empty))) + dom/get-element-by-class (mock/stub (constantly nil)) + refs/vbox (atom (grc/make-rect 0 0 2000 2000)) + ms/mouse-position (rx/behavior-subject position) + ms/mouse-position-alt (rx/behavior-subject false) + ms/mouse-position-shift (rx/behavior-subject false) + ms/mouse-position-mod (rx/behavior-subject false)} + (let [events {:component (dwl/instantiate-component (:id file) (thi/id :component) position + {:id-ref id-ref :origin "sidebar" + :start-move? true :initial-point position}) + :svg (dwsvg/add-svg-shapes svg-id + {:name "test.svg" :attrs {:width 24 :height 24} + :content [{:tag :rect :attrs {:width "24" :height "24"}}]} + position nil)}] + (ptk/emit! store (get events creation))) + (let [id (if (= creation :component) @id-ref svg-id)] + (await (async/wait-for #(some? (get-in @store (conj path id))) "interactive shape committed")) + (t/is (= parent (get-in @store (into path [id :parent-id])))) + (t/is (= parent (get-in @store (into path [id :frame-id])))) + (t/is (= #{id} (set (get-in @store [:workspace-local :selected])))) + (when (= creation :component) + (await (async/wait-for #(= :move (get-in @store [:workspace-local :transform])) "interactive move started")) + (t/is (= :move (get-in @store [:workspace-local :transform]))) + (rx/push! ms/mouse-position position) + (await (async/settle)) + (ptk/emit! store :interrupt) + (await (async/wait-for #(nil? (get-in @store [:workspace-local :transform])) "interactive move finished")) + (t/is (nil? (get-in @store [:workspace-local :transform]))) + (t/is (= parent (get-in @store (into path [id :parent-id]))))))))))) diff --git a/frontend/test/frontend_tests/plugins/events_test.cljs b/frontend/test/frontend_tests/plugins/events_test.cljs index b32a9914c7..765471f00d 100644 --- a/frontend/test/frontend_tests/plugins/events_test.cljs +++ b/frontend/test/frontend_tests/plugins/events_test.cljs @@ -8,9 +8,11 @@ "Unit tests for app.plugins.events, the plugin state-change listeners." (:require [app.common.uuid :as uuid] + [app.main.data.persistence :as dps] [app.main.store :as st] [app.plugins.events :as events] - [cljs.test :as t :include-macros true])) + [cljs.test :as t :include-macros true] + [potok.v2.core :as ptk])) (def ^:private plugin-id (str (uuid/next))) @@ -64,3 +66,24 @@ (remove-watch st/state ::probe-watch) (events/remove-listener key) (swap! st/state dissoc ::probe))))) + +(t/deftest contentrollback-listener-gets-every-rollback + (t/async done + (let [[file-id page-id shape-id] (repeatedly 3 uuid/next) + seen (atom []) + key (events/add-listener "contentrollback" plugin-id #(swap! seen conj (js->clj %)) nil)] + (st/emit! (ptk/data-event ::dps/rolled-back {:file-ids #{file-id} :page-ids #{page-id} :shape-ids #{shape-id}}) + (ptk/data-event ::dps/rolled-back {:file-ids #{file-id} :page-ids #{} :shape-ids #{}})) + (js/setTimeout + (fn [] + (events/remove-listener key) + (t/is (= [{"fileIds" [(str file-id)] "pageIds" [(str page-id)] "shapeIds" [(str shape-id)]} + {"fileIds" [(str file-id)] "pageIds" [] "shapeIds" []}] + @seen)) + (st/emit! (ptk/data-event ::dps/rolled-back {:file-ids #{file-id}})) + (js/setTimeout + (fn [] + (t/is (= 2 (count @seen)) "a removed listener gets nothing") + (done)) + 20)) + 20)))) diff --git a/frontend/test/frontend_tests/plugins/library_test.cljs b/frontend/test/frontend_tests/plugins/library_test.cljs index 4ee8ec0357..982a62c566 100644 --- a/frontend/test/frontend_tests/plugins/library_test.cljs +++ b/frontend/test/frontend_tests/plugins/library_test.cljs @@ -456,19 +456,27 @@ team-id (uuid/next) library-id (uuid/next) stream (rx/subject) - events (atom [])] + events (atom []) + state (atom {:current-file-id file-id :current-team-id team-id + :permissions {:can-edit true}})] (await (mock/with-mocks* - {st/state (atom {:current-file-id file-id :current-team-id team-id}) + {st/state state st/stream stream st/emit! (mock/stub (fn [& emitted] (swap! events into emitted))) rp/cmd! (shared-files-cmd [{:id library-id :name "Published"}]) r/check-permission (mock/stub (constantly true))} (let [context (library/library-subcontext plugin-id) - result (settled (.connectLibrary context (str library-id))) + promise (.connectLibrary context (str library-id)) + result (settled promise) + done? (atom false) link? #(= ::dwl/link-file-to-library (ptk/type %))] + (.then promise #(reset! done? true)) (await (async/wait-for #(some link? @events) "link event emitted")) (rx/push! stream (ptk/data-event ::dwl/link-file-to-library-finished)) + (await (async/settle)) + (t/is (false? @done?) "waits until the library data is loaded") + (swap! state assoc-in [:files library-id :data] {:id library-id}) (let [{:keys [resolved]} (await result) link-data (ev/-data (first (filter link? @events)))] (t/is (= library-id (obj/get resolved "$id"))) @@ -483,7 +491,8 @@ events (atom [])] (await (mock/with-mocks* - {st/state (atom {:current-file-id file-id :current-team-id (uuid/next)}) + {st/state (atom {:current-file-id file-id :current-team-id (uuid/next) + :permissions {:can-edit true}}) st/emit! (mock/stub (fn [& emitted] (swap! events into emitted))) rp/cmd! (shared-files-cmd [{:id (uuid/next) :name "Other published"}]) r/check-permission (mock/stub (constantly true))} @@ -497,7 +506,8 @@ events (atom [])] (await (mock/with-mocks* - {st/state (atom {:current-file-id file-id :current-team-id (uuid/next)}) + {st/state (atom {:current-file-id file-id :current-team-id (uuid/next) + :permissions {:can-edit true}}) st/emit! (mock/stub (fn [& emitted] (swap! events into emitted))) rp/cmd! (shared-files-cmd [{:id file-id :name "Current file"}]) r/check-permission (mock/stub (constantly true))} @@ -529,3 +539,51 @@ {:keys [rejected]} (await (settled (.connectLibrary context (str (uuid/next)))))] (t/is (str/includes? (str rejected) "Plugin doesn't have 'library:write' permission")) (t/is (empty? @mock/rpc-calls)))))) + +(t/deftest ^:async library-context-ignores-files-left-by-earlier-workspaces + (let [file-id (uuid/next) + linked-id (uuid/next) + visited-id (uuid/next) + other-id (uuid/next)] + (await + (mock/with-mocks* + {st/state (atom {:current-file-id file-id + :current-team-id (uuid/next) + :files {file-id {:id file-id :data {}} + linked-id {:id linked-id :data {}} + visited-id {:id visited-id}}}) + rp/cmd! (shared-files-cmd [{:id linked-id :name "Linked"} + {:id visited-id :name "Visited"} + {:id other-id :name "Other"}]) + r/check-permission (mock/stub (constantly true))} + (let [context (library/library-subcontext plugin-id)] + (t/is (= #{file-id linked-id} + (into #{} (map #(obj/get % "$id")) (.-connected context)))) + (t/is (= #{(str visited-id) (str other-id)} + (into #{} (map #(.-id %)) (await (.availableLibraries context)))))))))) + +;; Scenario: a library is linked; while the list of published libraries +;; loads, a save fails and locks the workspace. Proves: the library is not +;; linked and the promise rejects with the read-only error. +(t/deftest ^:async connect-library-is-refused-when-a-save-fails-meanwhile + (let [library-id (uuid/next) + state (atom {:current-file-id (uuid/next) + :current-team-id (uuid/next) + :permissions {:can-edit true}}) + answer (rx/subject) + events (atom [])] + (await + (mock/with-mocks* + {st/state state + st/emit! (mock/stub (fn [& emitted] (swap! events into emitted))) + rp/cmd! (mock/stub (fn [cmd params] + (swap! mock/rpc-calls conj {:cmd cmd :params params}) + (->> answer (rx/take 1) (rx/observe-on :async))))} + (let [context (library/library-subcontext plugin-id) + result (settled (.connectLibrary context (str library-id)))] + (await (async/wait-for #(seq @mock/rpc-calls) "the published libraries are requested")) + (swap! state assoc-in [:workspace-global :persistence-lock] {:read-only? false}) + (rx/push! answer [{:id library-id :name "Published"}]) + (let [{:keys [rejected]} (await result)] + (t/is (str/includes? (ex-message rejected) "cannot be saved")) + (t/is (empty? @events) "the library is not linked"))))))) diff --git a/frontend/test/frontend_tests/plugins/management_test.cljs b/frontend/test/frontend_tests/plugins/management_test.cljs new file mode 100644 index 0000000000..15d04f8f7f --- /dev/null +++ b/frontend/test/frontend_tests/plugins/management_test.cljs @@ -0,0 +1,566 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.plugins.management-test + (:require + [app.common.uuid :as uuid] + [app.main.data.dashboard :as dd] + [app.main.data.team :as dtm] + [app.main.repo :as rp] + [app.main.store :as st] + [app.plugins.events :as events] + [app.plugins.management :as management] + [app.plugins.register :as r] + [app.plugins.utils :as u] + [app.util.object :as obj] + [app.util.sse :as sse] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as a] + [frontend-tests.helpers.mock :as mock] + [potok.v2.core :as ptk])) + +(def file-id (uuid/next)) +(def page-id (uuid/next)) +(def team-id (uuid/next)) +(def project-id (uuid/next)) + +;; The zero plugin id always holds every permission. +(def plugin-id (str uuid/zero)) + +(def dashboard + {:route {:data {:name :dashboard-recent}} + :current-team-id team-id}) + +(def loading + (assoc dashboard + :route {:data {:name :workspace} + :params {:query {:file-id (str file-id) :team-id (str team-id)}}} + :current-file-id file-id + :workspace-ready file-id)) + +(def ready + (assoc loading + :current-page-id page-id + :files {file-id {:id file-id :name "Design" :project-id project-id + :data {:pages-index {page-id {:id page-id :objects {}}}}}})) + +(def team + {:id team-id :name "Team" :is-default true}) + +(def project + {:id project-id :team-id team-id :name "Drafts" :is-default true :is-pinned false :count 2}) + +(def file + {:id file-id :team-id team-id :project-id project-id :name "Design" + :is-shared false :modified-at (js/Date. "2026-01-02T03:04:05Z")}) + +(t/deftest teams-do-not-expose-deletion + (doseq [id [plugin-id r/mcp-plugin-id]] + (t/is (nil? (obj/get (management/team-proxy id team) "remove"))))) + +(defn- props + "Reads `keys` from a proxy into a map." + [proxy keys] + (into {} (map (fn [k] [k (obj/get proxy k)])) keys)) + +(defn- message + "Resolves with the rejection message of `promise`, or nil when it resolves." + [promise] + (.then promise (fn [] nil) #(.-message %))) + +(defn- cmd-stub + "Mock `rp/cmd!` recording calls in `requests` and answering with `respond`." + [requests respond] + (mock/stub + (fn + ([command] (swap! requests conj [command]) (respond command nil)) + ([command params] (swap! requests conj [command params]) (respond command params))))) + +(defn- emit-stub + "Mock `st/emit!` applying update functions to `state` and recording events." + [state events] + (mock/stub + (fn [event] + (if (fn? event) + (swap! state event) + (do (swap! events conj event) + (when (satisfies? ptk/UpdateEvent event) + (swap! state (partial ptk/update event)))))))) + +(defn- throwing + "Makes validation errors of the test plugin throw." + [state] + (assoc-in state [:plugins :flags plugin-id :throw-validation-errors] true)) + +(t/deftest workspace-readiness-requires-an-initialized-page + (t/is (= "none" (:status (management/workspace-context dashboard)))) + (t/is (= "loading" (:status (management/workspace-context loading)))) + (t/is (= "ready" (:status (management/workspace-context ready)))) + (t/is (= "none" (:status (management/workspace-context (assoc ready :route (:route dashboard))))))) + +(t/deftest ^:async open-file-waits-for-page-readiness + (let [state (atom dashboard) + seen (atom []) + done? (atom false)] + (await + (mock/with-mocks* + {st/state state + st/emit! (fn ([_] (js/setTimeout #(reset! state loading) 0)) + ([_ & _] nil))} + (let [listener (events/add-listener "workspacechange" (str uuid/zero) + #(swap! seen conj (.-status %)) nil) + result (-> (management/open-file (str file-id) nil) + (.then #(reset! done? true)))] + (try + (await (a/wait-for #(= ["loading"] @seen) "workspace loading")) + (t/is (false? @done?)) + (reset! state ready) + (await result) + (t/is (= ["loading" "ready"] @seen)) + (t/is (true? @done?)) + (finally + (events/remove-listener listener)))))))) + +(t/deftest ^:async open-file-rejects-superseded-navigation + (let [state (atom dashboard)] + (await + (mock/with-mocks* + {st/state state + st/emit! (fn ([_] (js/setTimeout #(reset! state loading) 0)) + ([_ & _] nil))} + (let [result (message (management/open-file (str file-id) nil))] + (await (a/wait-for #(= loading @state) "navigation started")) + (reset! state {:route {:data {:name :settings-profile}}}) + (t/is (= "File navigation was superseded" (await result)))))))) + +(t/deftest ^:async open-file-rejects-invalid-identifiers + (t/is (= "Expected a file UUID and a team UUID" + (await (message (management/open-file "invalid" #js {:teamId (str team-id)})))))) + +(t/deftest ^:async listing-returns-team-project-and-file-objects + (let [requests (atom []) + other-id (uuid/next)] + (await + (mock/with-mocks* + {st/state (atom dashboard) + rp/cmd! (cmd-stub requests + (fn [command _] + (rx/of (case command + :get-teams [team] + :get-projects [project {:id other-id :deleted-at (js/Date.)}] + :get-project-files [(dissoc file :team-id)]))))} + (let [context (management/create-context plugin-id) + [team*] (await (.listTeams context)) + [project*] (await (.listProjects context)) + team-project (first (await (.listProjects team*))) + [file*] (await (.listFiles project*))] + (t/is (= {"id" (str team-id) "name" "Team" "isDefault" true} + (props team* ["id" "name" "isDefault"]))) + (t/is (= {"id" (str project-id) "teamId" (str team-id) "name" "Drafts" + "isDefault" true "pinned" false "fileCount" 2} + (props project* ["id" "teamId" "name" "isDefault" "pinned" "fileCount"]))) + (t/is (= (str project-id) (obj/get team-project "id"))) + (t/is (= {"id" (str file-id) "teamId" (str team-id) "projectId" (str project-id) + "name" "Design" "shared" false "modifiedAt" "2026-01-02T03:04:05.000Z"} + (props file* ["id" "teamId" "projectId" "name" "shared" "modifiedAt"]))) + (t/is (= [[:get-teams] + [:get-projects {:team-id team-id}] + [:get-projects {:team-id team-id}] + [:get-project-files {:project-id project-id}]] + @requests))))))) + +(t/deftest ^:async get-file-reads-loaded-files-and-fetches-the-rest + (let [other-id (uuid/next)] + (doseq [[state id expected] + [[ready file-id + [[:get-project {:id project-id}]]] + [dashboard other-id + [[:get-file {:id other-id :features nil}] + [:get-project {:id project-id}]]]]] + (let [requests (atom [])] + (await + (mock/with-mocks* + {st/state (atom state) + rp/cmd! (cmd-stub requests + (fn [command _] + (rx/of (case command + :get-file (assoc file :id other-id :data {}) + :get-project project))))} + (let [file* (await (.getFile (management/create-context plugin-id) (str id)))] + (t/is (= {"id" (str id) "teamId" (str team-id) "projectId" (str project-id)} + (props file* ["id" "teamId" "projectId"]))) + (t/is (= expected @requests))))))))) + +(t/deftest ^:async listing-rejects-invalid-identifiers-and-preserves-backend-errors + (let [requests (atom [])] + (await + (mock/with-mocks* + {st/state (atom {}) + rp/cmd! (cmd-stub requests (fn [_ _] (rx/of nil)))} + (let [context (management/create-context plugin-id)] + (t/is (= "Expected a team UUID" (await (message (.listProjects context nil))))) + (t/is (= "Expected a team UUID" (await (message (.listProjects context #js {:teamId "invalid"}))))) + (t/is (= "Expected a file UUID" (await (message (.getFile context "invalid"))))) + (await (a/settle)) + (t/is (empty? @requests)))))) + (let [error (js/Error. "Access denied")] + (await + (mock/with-mocks* + {st/state (atom dashboard) + rp/cmd! (mock/stub (fn [& _] (rx/throw error)))} + (let [context (management/create-context plugin-id)] + (t/is (identical? error (await (.then (.listTeams context) (fn [] nil) identity)))) + (t/is (identical? error (await (.then (.listProjects context) (fn [] nil) identity))))))))) + +(t/deftest ^:async teams-are-created + (let [requests (atom []) + events (atom []) + state (atom dashboard) + created {:id (uuid/next) :name "Brand" :is-default false}] + (await + (mock/with-mocks* + {st/state state + st/emit! (emit-stub state events) + rp/cmd! (cmd-stub requests (fn [& _] (->> (rx/of created) (rx/observe-on :async))))} + (let [team* (await (.createTeam (management/create-context plugin-id) #js {:name " Brand "}))] + (t/is (= {"id" (str (:id created)) "name" "Brand" "isDefault" false} + (props team* ["id" "name" "isDefault"]))) + (t/is (= [[:create-team "Brand"]] + (mapv (fn [[command params]] [command (:name params)]) @requests))) + (t/is (= 1 (count (filter #(= ::dtm/fetch-teams (ptk/type %)) @events)))) + (t/is (= "Expected a name with 1 to 250 characters" + (await (message (.createTeam (management/create-context plugin-id) #js {:name ""})))))))))) + +(t/deftest ^:async setters-update-values-after-the-backend-succeeds + (let [state (atom (throwing dashboard)) + events (atom [])] + (await + (mock/with-mocks* + {st/state state + st/emit! (emit-stub state events) + rp/cmd! (mock/stub (fn [& _] (->> (rx/of {}) (rx/observe-on :async))))} + (let [team* (management/team-proxy plugin-id team) + project* (management/project-proxy plugin-id project) + file* (management/file-proxy plugin-id file)] + (obj/set! team* "name" " Brand ") + (obj/set! project* "name" "Icons") + (obj/set! project* "pinned" true) + (obj/set! file* "name" "Login") + (obj/set! file* "shared" true) + (t/is (= "Team" (obj/get team* "name"))) + (doseq [event @events] + (await (a/observe (ptk/watch event @state (rx/empty)) + :on-next #(when (fn? %) (swap! state %))))) + (t/is (= "Brand" (obj/get team* "name"))) + (t/is (= "Icons" (obj/get project* "name"))) + (t/is (true? (obj/get project* "pinned"))) + (t/is (= "Login" (obj/get file* "name"))) + (t/is (true? (obj/get file* "shared"))) + (t/is (= 5 (count @events)))))))) + +(t/deftest ^:async stale-object-setter-errors-stay-out-of-the-global-handler + (doseq [[constructor property value original] + [[#(management/team-proxy plugin-id team) "name" "New team" "Team"] + [#(management/project-proxy plugin-id project) "name" "New project" "Drafts"] + [#(management/project-proxy plugin-id project) "pinned" true false] + [#(management/file-proxy plugin-id file) "name" "New file" "Design"] + [#(management/file-proxy plugin-id file) "shared" true false]]] + (let [state (atom (throwing ready)) + events (atom []) + reports (atom []) + escaped (atom [])] + (await + (mock/with-mocks* + {st/state state + st/emit! (emit-stub state events) + rp/cmd! (mock/stub (fn [& _] + (->> (rx/throw (ex-info "http error" {:type :not-found :code :object-not-found})) + (rx/observe-on :async)))) + u/display-not-valid (mock/stub (fn [code value] (swap! reports conj [code value])))} + (let [proxy (constructor)] + (obj/set! proxy property value) + (doseq [event @events] + (await (a/observe (ptk/watch event @state (rx/empty)) + :on-next #(when (fn? %) (swap! state %)) + :on-error #(swap! escaped conj %)))) + (t/is (empty? @escaped) property) + (t/is (= original (obj/get proxy property)) property) + (t/is (= (throwing ready) @state)) + (t/is (= 1 (count @reports)) property))))))) + +(t/deftest setters-reject-invalid-values-and-missing-permissions + (let [state (atom (throwing dashboard)) + events (atom [])] + (with-redefs [st/state state + st/emit! (emit-stub state events)] + (let [team* (management/team-proxy plugin-id team) + project* (management/project-proxy plugin-id project) + file* (management/file-proxy plugin-id file)] + (doseq [[proxy key value] [[team* "name" " "] + [project* "name" (apply str (repeat 251 "a"))] + [project* "pinned" "yes"] + [file* "name" nil] + [file* "shared" 1]]] + (t/is (thrown? js/Error (obj/set! proxy key value)) key)) + (with-redefs [r/has-permission? (mock/stub (constantly false))] + (doseq [[proxy key value permission] [[team* "name" "Brand" "manage:teams"] + [project* "name" "Icons" "manage:projects"] + [project* "pinned" true "manage:projects"] + [file* "name" "Login" "content:write"] + [file* "shared" true "library:write"]]] + (t/is (thrown-with-msg? js/Error (re-pattern permission) (obj/set! proxy key value))))) + (t/is (= "Team" (obj/get team* "name"))) + (t/is (= "Design" (obj/get file* "name"))) + (t/is (empty? @events)))))) + +(t/deftest ^:async creation-returns-objects-and-updates-only-the-dashboard + (let [created-project {:id (uuid/next) :team-id team-id :name "Icons" :is-default false} + created-file (assoc file :id (uuid/next) :name "Login" :data {})] + (doseq [initial [dashboard ready]] + (let [state (atom (assoc initial :projects {project-id project})) + requests (atom [])] + (await + (mock/with-mocks* + {st/state state + st/emit! (emit-stub state (atom [])) + rp/cmd! (cmd-stub requests + (fn [command _] + (->> (rx/of (case command + :create-project created-project + :create-file created-file)) + (rx/observe-on :async))))} + (let [project* (await (.createProject (management/team-proxy plugin-id team) #js {:name " Icons "})) + parent (management/project-proxy plugin-id project) + file* (await (.createFile parent #js {:name "Login"}))] + (t/is (= {"name" "Icons" "fileCount" 0} (props project* ["name" "fileCount"]))) + (t/is (= {"name" "Login" "teamId" (str team-id)} (props file* ["name" "teamId"]))) + (t/is (= 2 (obj/get parent "fileCount"))) + (t/is (= [:create-project {:team-id team-id :name "Icons"}] (first @requests))) + (if (= initial dashboard) + (do (t/is (contains? (:projects @state) (:id created-project))) + (t/is (contains? (:files @state) (:id created-file)))) + (t/is (= (assoc ready :projects {project-id project}) @state)))))))))) + +(t/deftest ^:async file-and-project-methods-call-the-backend + (let [requests (atom []) + other-id (uuid/next) + copy (assoc file :id (uuid/next) :name "Design copy") + project-2 (assoc project :id other-id :is-default false)] + (await + (mock/with-mocks* + {st/state (atom dashboard) + st/emit! (mock/stub (fn [_] nil)) + rp/cmd! (cmd-stub requests + (fn [command _] + (rx/of (case command + :duplicate-file copy + :duplicate-project (dissoc project-2 :count :is-pinned) + :get-project-files [file file] + :get-project project-2 + nil))))} + (let [file* (management/file-proxy plugin-id file) + project* (management/project-proxy plugin-id project-2) + team* (management/team-proxy plugin-id (assoc team :id other-id)) + duplicate (await (.duplicate file* #js {:name "Design copy"}))] + (t/is (= "Design copy" (obj/get duplicate "name"))) + (t/is (= {"name" "Drafts" "fileCount" 2 "pinned" false} + (props (await (.duplicate project*)) ["name" "fileCount" "pinned"]))) + (await (.moveTo file* project*)) + (t/is (= (str other-id) (obj/get file* "projectId"))) + (await (.moveTo project* team*)) + (t/is (= (str other-id) (obj/get project* "teamId"))) + (await (.remove file*)) + (await (.remove project*)) + (t/is (= [[:duplicate-file {:file-id file-id :name "Design copy"}] + [:duplicate-project {:project-id other-id}] + [:get-project-files {:project-id other-id}] + [:get-project {:id other-id}] + [:move-files {:ids #{file-id} :project-id other-id}] + [:move-project {:project-id other-id :team-id other-id}] + [:delete-file {:id file-id}] + [:delete-project {:id other-id}]] + @requests))))))) + +(t/deftest ^:async methods-reject-invalid-input-and-missing-permissions + (let [requests (atom [])] + (await + (mock/with-mocks* + {st/state (atom dashboard) + rp/cmd! (cmd-stub requests (fn [_ _] (rx/of nil)))} + (let [team* (management/team-proxy plugin-id team) + project* (management/project-proxy plugin-id project) + file* (management/file-proxy plugin-id file)] + (t/is (= "Expected a name with 1 to 250 characters" (await (message (.createProject team* #js {:name ""}))))) + (t/is (= "Expected a name with 1 to 250 characters" (await (message (.createFile project* nil))))) + (t/is (= "Expected a name with 1 to 250 characters" (await (message (.duplicate file* #js {:name " "}))))) + (t/is (= "Expected a project" (await (message (.moveTo file* #js {:id "invalid"}))))) + (t/is (= "Expected a team" (await (message (.moveTo project* nil))))) + (with-redefs [r/has-permission? (mock/stub (constantly false))] + (doseq [[promise permission] [[(.createTeam (management/create-context plugin-id) #js {:name "Brand"}) "manage:teams"] + [(.listProjects team*) "content:read"] + [(.createProject team* #js {:name "Icons"}) "manage:projects"] + [(.createFile project* #js {:name "Login"}) "content:write"] + [(.duplicate project*) "manage:projects"] + [(.moveTo project* team*) "manage:teams"] + [(.remove project*) "manage:delete"] + [(.open file*) "content:read"] + [(.duplicate file*) "content:write"] + [(.moveTo file* project*) "content:write"] + [(.remove file*) "manage:delete"]]] + (t/is (= (str "Permission " permission " is not granted") (await (message promise)))))) + (await (a/settle)) + (t/is (empty? @requests))))))) + +(t/deftest ^:async file-move-rejects-a-project-that-now-belongs-to-another-team + (let [state (atom ready) + requests (atom []) + target (assoc project :id (uuid/next))] + (await + (mock/with-mocks* + {st/state state + st/emit! (emit-stub state (atom [])) + rp/cmd! (cmd-stub requests + (fn [_ _] + (->> (rx/of (assoc target :team-id (uuid/next))) + (rx/observe-on :async))))} + (let [file* (management/file-proxy plugin-id file)] + (t/is (= "Cannot move a file to a project in another team" + (await (message (.moveTo file* (management/project-proxy plugin-id target)))))) + (t/is (= (str project-id) (obj/get file* "projectId"))) + (t/is (= (str team-id) (obj/get file* "teamId"))) + (t/is (= ready @state)) + (t/is (= [[:get-project {:id (:id target)}]] @requests))))))) + +(t/deftest ^:async backend-rejections-include-the-type-and-code + (await + (mock/with-mocks* + {rp/cmd! (mock/stub (fn [& _] + (->> (rx/throw (ex-info "http error" {:type :validation + :code :project-already-in-team + :hint "Project is already in this team"})) + (rx/observe-on :async))))} + (t/is (= "Project is already in this team (validation/project-already-in-team)" + (await (message (.moveTo (management/project-proxy plugin-id project) + (management/team-proxy plugin-id team))))))))) + +(t/deftest ^:async file-count-stays-at-its-listing-snapshot-after-creation-and-deletion + (let [state (atom (assoc dashboard :projects {project-id project}))] + (await + (mock/with-mocks* + {st/state state + st/emit! (emit-stub state (atom [])) + rp/cmd! (mock/stub (fn [command _] + (->> (rx/of (when (= command :create-file) file)) + (rx/observe-on :async))))} + (let [project* (management/project-proxy plugin-id project) + file* (await (.createFile project* #js {:name "Design"}))] + (t/is (= 2 (obj/get project* "fileCount"))) + (t/is (= 3 (get-in @state [:projects project-id :count]))) + (await (.remove file*)) + (t/is (= 2 (obj/get project* "fileCount"))) + (t/is (= 2 (get-in @state [:projects project-id :count])))))))) + +(t/deftest ^:async mcp-can-delete-projects-and-files-without-an-extra-grant + (let [state (atom ready) + requests (atom [])] + (await + (mock/with-mocks* + {st/state state + st/emit! (emit-stub state (atom [])) + rp/cmd! (cmd-stub requests (fn [& _] (->> (rx/of nil) (rx/observe-on :async))))} + (doseq [proxy [(management/project-proxy r/mcp-plugin-id project) + (management/file-proxy r/mcp-plugin-id file)]] + (t/is (nil? (await (.remove proxy))))) + (t/is (= [[:delete-project {:id project-id}] [:delete-file {:id file-id}]] @requests)))))) + +(t/deftest ^:async deleted-objects-can-be-listed-and-restored + (let [state (atom ready) + deleted-project (assoc project :deleted-at (js/Date. "2099-01-01")) + deleted-file (assoc file :will-be-deleted-at (js/Date. "2099-01-01"))] + (await + (mock/with-mocks* + {st/state state + st/emit! (emit-stub state (atom [])) + rp/cmd! (mock/stub (fn [command _] + (->> (rx/of (case command + :get-projects [deleted-project] + :get-team-deleted-files [deleted-file] + ::sse/restore-deleted-team-files #js {:type "end" :data #{file-id}})) + (rx/observe-on :async))))} + (let [[project*] (await (.listDeletedProjects (management/create-context r/mcp-plugin-id))) + [file*] (await (.listDeletedFiles project*))] + (t/is (= (str project-id) (obj/get project* "id"))) + (t/is (= (str file-id) (obj/get file* "id"))) + (t/is (nil? (await (.restore project*)))) + (t/is (nil? (await (.restore file*))))))))) + +(t/deftest ^:async restoration-refreshes-dashboard-projects-and-trash + (doseq [constructor [management/project-proxy management/file-proxy]] + (let [restored? (atom false) + state (atom (assoc dashboard + :projects {project-id (assoc project :deleted-at (js/Date. "2099-01-01"))} + :deleted-files {file-id file}))] + (await + (mock/with-mocks* + {st/state state + st/emit! (emit-stub state (atom [])) + rp/cmd! (mock/stub (fn [command _] + (->> (rx/of (case command + :get-projects [(assoc project :count 1)] + :get-project-files [file] + :get-team-deleted-files (if @restored? [] [file]) + ::sse/restore-deleted-team-files + (do (reset! restored? true) #js {:type "end" :data #{file-id}}))) + (rx/observe-on :async))))} + (await (.restore (constructor plugin-id (if (= constructor management/project-proxy) project file)))) + (t/is (nil? (get-in @state [:projects project-id :deleted-at]))) + (t/is (= 1 (get-in @state [:projects project-id :count]))) + (t/is (= file (get-in @state [:files file-id]))) + (t/is (empty? (:deleted-files @state)))))))) + +(t/deftest ^:async restoration-waits-for-the-trash-operation-to-finish + (let [state (atom ready) + response (rx/subject) + completed? (atom false)] + (await + (mock/with-mocks* + {st/state state + rp/cmd! (mock/stub (fn [command params] + (t/is (= ::sse/restore-deleted-team-files command)) + (t/is (= {:team-id team-id :ids #{file-id}} params)) + (rx/observe-on :async response)))} + (let [promise (.then (.restore (management/file-proxy plugin-id file)) + #(reset! completed? true))] + (rx/push! response #js {:type "progress" :data {:file-id file-id}}) + (await (a/settle)) + (t/is (false? @completed?)) + (rx/push! response #js {:type "end" :data #{file-id}}) + (await promise) + (t/is (true? @completed?))))))) + +(t/deftest ^:async restoration-reports-trash-errors-without-changing-the-workspace + (doseq [response [(rx/throw (ex-info "stream exception" {:type :not-found :code :object-not-found})) + (rx/of #js {:type "end" :data #{}}) + (rx/of #js {:type "progress" :data {}})]] + (let [state (atom ready)] + (await + (mock/with-mocks* + {st/state state + rp/cmd! (mock/stub (fn [_ _] (rx/observe-on :async response)))} + (t/is (string? (await (message (.restore (management/file-proxy plugin-id file)))))) + (t/is (= ready @state))))))) + +(t/deftest ^:async projects-without-recoverable-files-cannot-be-restored + (let [state (atom ready)] + (await + (mock/with-mocks* + {st/state state + rp/cmd! (mock/stub (fn [command _] + (t/is (= :get-team-deleted-files command)) + (->> (rx/of []) (rx/observe-on :async))))} + (t/is (= "Cannot restore a project without recoverable files" + (await (message (.restore (management/project-proxy plugin-id project)))))))))) diff --git a/frontend/test/frontend_tests/plugins/read_only_test.cljs b/frontend/test/frontend_tests/plugins/read_only_test.cljs new file mode 100644 index 0000000000..81532173fd --- /dev/null +++ b/frontend/test/frontend_tests/plugins/read_only_test.cljs @@ -0,0 +1,231 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.plugins.read-only-test + "Plugin writes in a read-only workspace." + (:require + [app.common.test-helpers.files :as cthf] + [app.common.uuid :as uuid] + [app.main.data.helpers :as dsh] + [app.main.data.workspace.media :as dwm] + [app.main.repo :as rp] + [app.main.store :as st] + [app.plugins.api :as api] + [app.plugins.register :as r] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.mock :as mock] + [frontend-tests.helpers.state :as ths] + [frontend-tests.helpers.wasm :as thw])) + +;; Its validation flag is off, so only errors that always throw reach it +(def ^:private plugin-id "00000000-0000-0000-0000-000000000000") + +(defn- set-can-edit + [store can-edit?] + (swap! store assoc-in [:permissions :can-edit] can-edit?)) + +(defn- shape-count + [store] + (count (dsh/lookup-page-objects @store))) + +(defn- with-context + "Calls `f` with a store holding a sample file and a plugin context on it." + [f] + (thw/with-wasm-mocks* + (fn [] + (let [store (ths/setup-store (cthf/sample-file :file1 :page-label :page1))] + (set! st/state store) + (f store (api/create-context plugin-id)))))) + +(t/deftest writes-throw-in-a-read-only-workspace + (with-context + (fn [store ^js context] + (let [^js board (.createBoard context)] + (set-can-edit store false) + + (t/testing "a shape setter" + (t/is (thrown-with-msg? js/Error #"read-only" (set! (.-name board) "Renamed"))) + (t/is (not= "Renamed" (.-name board)))) + + (t/testing "a shape method" + (t/is (thrown-with-msg? js/Error #"read-only" (.bringToFront board)))) + + (t/testing "a context method without a permission check" + (let [before (shape-count store)] + (t/is (thrown-with-msg? js/Error #"read-only" (.createRectangle context))) + (t/is (= before (shape-count store))))) + + (t/testing "a context method with a permission check" + (t/is (thrown-with-msg? js/Error #"read-only" (.createPage (.-currentFile context))))))))) + +(t/deftest writes-work-again-once-the-workspace-is-editable + (with-context + (fn [store ^js context] + (let [^js board (.createBoard context)] + (set-can-edit store false) + (set-can-edit store true) + (set! (.-name board) "Renamed") + (t/is (= "Renamed" (.-name board))))))) + +(t/deftest the-inspect-tab-does-not-block-writes + (with-context + (fn [store ^js context] + (let [^js board (.createBoard context)] + (swap! store update :workspace-global assoc + :read-only? true + :options-mode :inspect) + (set! (.-name board) "Renamed") + (t/is (= "Renamed" (.-name board))))))) + +(t/deftest each-read-only-source-says-why + (doseq [[path value reason] [[[:permissions :can-edit] false #"cannot edit this file"] + [[:workspace-global :preview-id] (random-uuid) #"version is being previewed"] + [[:render-state :lost] true #"canvas stopped rendering"]]] + (with-context + (fn [store ^js context] + (swap! store assoc-in path value) + (t/testing (str path) + (t/is (thrown-with-msg? js/Error reason (.createBoard context)))))))) + +(t/deftest a-save-failure-lock-says-why-the-workspace-is-read-only + (with-context + (fn [store ^js context] + (swap! store update :workspace-global assoc + :read-only? true + :persistence-lock {:read-only? false}) + (t/is (thrown-with-msg? js/Error #"cannot be saved" (.createBoard context)))))) + +(t/deftest reads-and-undo-blocks-work-in-a-read-only-workspace + (with-context + (fn [store ^js context] + (let [^js board (.createBoard context)] + (set-can-edit store false) + (t/is (string? (.-name board))) + (let [block (.undoBlockBegin (.-history context))] + (t/is (some? block)) + (t/is (nil? (.undoBlockFinish (.-history context) block)))))))) + +(t/deftest read-only-keeps-permission-checks-of-reads + (with-context + (fn [store _] + (set-can-edit store false) + (t/is (true? (r/check-permission plugin-id "content:read"))) + (t/is (true? (r/has-permission? plugin-id "content:write"))) + (t/is (thrown? js/Error (r/check-permission plugin-id "library:write")))))) + +;; Writes that wait for the server before they change the file + +(defn- settled + "Resolves with `{:resolved value}` or `{:rejected cause}` once `p` settles, + or with `{:timeout true}` when it stays pending for 2 seconds." + [p] + (js/Promise.race + #js [(.then p (fn [v] {:resolved v}) (fn [e] {:rejected e})) + (js/Promise. (fn [resolve] (js/setTimeout #(resolve {:timeout true}) 2000)))])) + +(defn- lock-saves + "Locks `store` as a failing save does." + [store] + (swap! store update :workspace-global assoc + :read-only? true + :persistence-lock {:read-only? false})) + +(defn- held-rpc + "An `rp/cmd!` that records each command in `calls` and answers with the + next value pushed to `answer`." + [calls answer] + (mock/stub (fn [cmd _] + (swap! calls conj cmd) + (->> answer (rx/take 1) (rx/observe-on :async))))) + +(def ^:private media-object + {:id (uuid/next) :name "image" :width 10 :height 10 :mtype "image/png"}) + +(def ^:private svg-with-image + (str "" + "" + "")) + +(defn- parsed-svg-with-image + "What `dwm/svg->clj` gives for `svg-with-image`. The test build cannot + load the XML parser, a CommonJS module." + [[name _]] + (rx/of {:tag :svg + :name name + :attrs {:xmlns "http://www.w3.org/2000/svg" :width "10" :height "10"} + :content [{:tag :image + :attrs {:href "data:image/png;base64,iVBORw0KGgo=" :width "10" :height "10"} + :content []}]})) + +(defn- read-only-error? + [cause] + (boolean (some->> cause ex-message (re-find #"cannot be saved")))) + +;; Scenario: an SVG with an image is created; while its image uploads a +;; save fails and locks the workspace. Proves: the shape is not added and +;; the promise rejects with the read-only error. +(t/deftest ^:async an-svg-whose-upload-ends-after-a-save-failure-is-refused + (thw/setup-wasm-mocks!) + (try + (let [store (ths/setup-store (cthf/sample-file :file1 :page-label :page1)) + calls (atom []) + answer (rx/subject)] + (await + (mock/with-mocks* + {st/state store + rp/cmd! (held-rpc calls answer) + dwm/valid-svg-string? (constantly true) + dwm/svg->clj (mock/stub parsed-svg-with-image)} + (let [^js context (api/create-context plugin-id) + before (shape-count store) + result (settled (.createShapeFromSvgWithImages context svg-with-image))] + (await (async/wait-for #(= [:upload-file-media-object] @calls) "the image upload starts")) + (lock-saves store) + (rx/push! answer media-object) + (let [{:keys [rejected]} (await result)] + (t/is (read-only-error? rejected)) + (await (async/settle)) + (t/is (= before (shape-count store)) "no shape is added")))))) + (finally + (thw/teardown-wasm-mocks!)))) + +;; Uploads `upload` with the context; while the request for `cmd` runs, a +;; save fails and locks the workspace. +(defn- ^:async upload-during-a-save-failure + [upload cmd] + (let [store (ths/setup-store (cthf/sample-file :file1 :page-label :page1)) + calls (atom []) + answer (rx/subject)] + (await + (mock/with-mocks* + {st/state store + rp/cmd! (held-rpc calls answer)} + ;; Bound one by one: a nested call with a local fn would compile to + ;; an awaited IIFE, which waits for the upload itself + (let [context (api/create-context plugin-id) + result (settled (upload context))] + (await (async/wait-for #(= [cmd] @calls) "the upload starts")) + (lock-saves store) + (rx/push! answer media-object) + (let [{:keys [rejected]} (await result)] + (t/is (read-only-error? rejected) (str cmd)))))))) + +;; Scenario: an image is uploaded from data and from a URL; while each +;; upload runs a save fails and locks the workspace. Proves: the promise +;; rejects with the read-only error. The media object stays on the server. +(t/deftest ^:async media-uploaded-after-a-save-failure-is-refused + (thw/setup-wasm-mocks!) + (try + (await (upload-during-a-save-failure + #(.uploadMediaData ^js % "image" (js/Uint8Array. #js [1 2 3]) "image/png") + :upload-file-media-object)) + (await (upload-during-a-save-failure + #(.uploadMediaUrl ^js % "image" "https://example.com/image.png") + :create-file-media-object-from-url)) + (finally + (thw/teardown-wasm-mocks!)))) diff --git a/frontend/test/frontend_tests/plugins/register_test.cljs b/frontend/test/frontend_tests/plugins/register_test.cljs index 55ddd1c4a6..a4399ab6af 100644 --- a/frontend/test/frontend_tests/plugins/register_test.cljs +++ b/frontend/test/frontend_tests/plugins/register_test.cljs @@ -12,6 +12,12 @@ [cljs.test :as t :include-macros true] [frontend-tests.helpers.mock :as mock])) +(t/deftest mcp-has-default-management-permissions + (t/is (preg/check-permission preg/mcp-plugin-id "manage:delete")) + (t/is (preg/check-permission preg/mcp-plugin-id "manage:teams")) + (t/is (preg/check-permission preg/mcp-plugin-id "content:write")) + (t/is (not (preg/check-permission preg/mcp-plugin-id "unknown:permission")))) + (defn- record-cmd-mock "Mock rp/cmd! that records calls in mock/rpc-calls and answers with (response-fn cmd params)." @@ -320,3 +326,24 @@ (t/is (= [true false] @seen) "no calls after unsubscribing") (done')) done)))) + +;; --- parse-manifest --- + +(t/deftest parse-manifest-adds-allow-global-for-global-scope + (let [manifest (preg/parse-manifest + "http://localhost:4400/manifest.json" + #js {:name "Global plugin" + :code "plugin.js" + :scope "global" + :permissions #js ["content:read"]})] + (t/is (= "global" (:scope manifest))) + (t/is (= #{"content:read" "allow:global"} (:permissions manifest))))) + +(t/deftest parse-manifest-keeps-workspace-plugins-without-allow-global + (let [manifest (preg/parse-manifest + "http://localhost:4400/manifest.json" + #js {:name "Workspace plugin" + :code "plugin.js" + :permissions #js ["content:read"]})] + (t/is (nil? (:scope manifest))) + (t/is (= #{"content:read"} (:permissions manifest))))) diff --git a/frontend/test/frontend_tests/plugins/shape_bugfixes_test.cljs b/frontend/test/frontend_tests/plugins/shape_bugfixes_test.cljs index 3d05d8abc1..cdd9d44017 100644 --- a/frontend/test/frontend_tests/plugins/shape_bugfixes_test.cljs +++ b/frontend/test/frontend_tests/plugins/shape_bugfixes_test.cljs @@ -102,7 +102,8 @@ emitted (atom nil) context (api/create-context plugin-id)] (set! st/state (atom {:current-file-id file-id - :current-page-id page-id})) + :current-page-id page-id + :permissions {:can-edit true}})) (with-redefs [dw/convert-selected-to-path (mock/stub (fn [ids] (reset! emitted ids) diff --git a/frontend/test/frontend_tests/plugins/tokens_test.cljs b/frontend/test/frontend_tests/plugins/tokens_test.cljs index 5f47fb92b3..a8e5033cef 100644 --- a/frontend/test/frontend_tests/plugins/tokens_test.cljs +++ b/frontend/test/frontend_tests/plugins/tokens_test.cljs @@ -945,6 +945,71 @@ (t/is (array? result)) (t/is (= ["Inter" "Arial"] (vec result))))) +(t/deftest font-family-token-resolved-value-keeps-multi-word-families + ;; An unquoted family whose name has several words resolves to a list of word + ;; symbols, so the family name is the string form of each entry. + (let [token (ctob/make-token + {:name "font.body" + :type :font-family + :value ["Hanken Grotesk" "IBM Plex Mono"]}) + result (get-resolved-value token {(:name token) token})] + (t/is (array? result)) + (t/is (= ["Hanken Grotesk" "IBM Plex Mono"] (vec result))))) + +(t/deftest typography-token-resolved-value-keeps-multi-word-families + (let [token (ctob/make-token + {:name "type.body" + :type :typography + :value {:font-family ["Hanken Grotesk" "Arial"] + :font-size "16px"}}) + result (get-resolved-value token {(:name token) token}) + entry (aget result 0)] + (t/is (= ["Hanken Grotesk" "Arial"] (vec (aget entry "fontFamilies")))))) + +(t/deftest font-family-token-resolved-value-keeps-names-with-expression-words + ;; Words like `Red` or `Black` read as colors and `2P` as a number and a word + ;; when tokenscript parses the family name. + (let [families ["Red Hat Display" "Crimson Pro" "Archivo Black" + "Press Start 2P" "M PLUS 1p" "Crimson"] + token (ctob/make-token + {:name "font.display" + :type :font-family + :value families}) + result (get-resolved-value token {(:name token) token})] + (t/is (= families (vec result))))) + +(t/deftest font-family-token-resolved-value-mixes-references-and-names + (let [base (ctob/make-token + {:name "font.base" + :type :font-family + :value ["Red Hat Text"]}) + token (ctob/make-token + {:name "font.stack" + :type :font-family + :value ["{font.base}" "Rock 3D"]}) + result (get-resolved-value token {(:name base) base + (:name token) token})] + (t/is (= ["Red Hat Text" "Rock 3D"] (vec result))))) + +(t/deftest font-family-token-resolved-value-keeps-quoted-names + (let [token (ctob/make-token + {:name "font.quoted" + :type :font-family + :value ["\"Red Hat Mono\"" "'Black Ops One'"]}) + result (get-resolved-value token {(:name token) token})] + (t/is (= ["Red Hat Mono" "Black Ops One"] (vec result))))) + +(t/deftest typography-token-resolved-value-keeps-names-with-expression-words + (let [token (ctob/make-token + {:name "type.display" + :type :typography + :value {:font-family ["Red Hat Display" "Press Start 2P"] + :font-size "16px"}}) + result (get-resolved-value token {(:name token) token}) + entry (aget result 0)] + (t/is (= ["Red Hat Display" "Press Start 2P"] + (vec (aget entry "fontFamilies")))))) + (t/deftest token-theme-add-set-accepts-token-set-id (let [plugin-id "plugin-id" file-id (uuid/next) diff --git a/frontend/test/frontend_tests/render_wasm/serialization_test.cljs b/frontend/test/frontend_tests/render_wasm/serialization_test.cljs index c067793ab9..b204bc13b6 100644 --- a/frontend/test/frontend_tests/render_wasm/serialization_test.cljs +++ b/frontend/test/frontend_tests/render_wasm/serialization_test.cljs @@ -5,7 +5,7 @@ ;; Copyright (c) KALEIDOS SUBSIDIARY SL (ns frontend-tests.render-wasm.serialization-test - "Routing tests for `serialize-shape!` and `serialize-shapes-batch!`. + "Routing tests for `serialize-shape` and `serialize-shapes-batch`. A shared `tail-cases` table states, per shape, whether the svg-attrs/path tail must fire. The pure `needs-shape-tail?` predicate is tested against @@ -15,6 +15,7 @@ render output below the FFI line needs headed or exporter pixel runs." (:require [app.common.render-wasm.api.props :as props] + [app.common.render-wasm.api.select :as wselect] [app.common.render-wasm.api.upload :as upload] [app.common.render-wasm.serialize-shape :as serialize-shape] [app.common.render-wasm.svg-derived :as svg-derived] @@ -22,22 +23,26 @@ (defn- with-ffi-stubs* "Stubs the single-arity FFI boundary fns (`flush-shapes-batch!`, - `set-shape-svg-attrs`, `set-shape-path-content`). `set-shape-upload!` is - deliberately left real: it is a trivial one-liner over the stubbed flush, - so the single test exercises its actual delegation and default opts. A - plain fn `set!` onto a multi-arity var breaks under the `:esm` test build - (call sites dispatch via `cljs$core$IFn$_invoke$arity$N`; see `mock/stub`)." + `use-shape`, `set-shape-svg-attrs`, `set-shape-path-content`). + `set-shape-upload!` is deliberately left real: it is a trivial one-liner + over the stubbed flush, so the single test exercises its actual delegation + and default opts. A plain fn `set!` onto a multi-arity var breaks under + the `:esm` test build (call sites dispatch via + `cljs$core$IFn$_invoke$arity$N`; see `mock/stub`)." [stubs thunk] (let [orig-flush upload/flush-shapes-batch! + orig-select wselect/use-shape orig-svg-attrs props/set-shape-svg-attrs orig-path props/set-shape-path-content] (set! upload/flush-shapes-batch! (:flush stubs)) + (set! wselect/use-shape (or (:select stubs) (fn [_] nil))) (set! props/set-shape-svg-attrs (:svg-attrs stubs)) (set! props/set-shape-path-content (:path stubs)) (try (thunk) (finally (set! upload/flush-shapes-batch! orig-flush) + (set! wselect/use-shape orig-select) (set! props/set-shape-svg-attrs orig-svg-attrs) (set! props/set-shape-path-content orig-path))))) @@ -109,14 +114,14 @@ flush-calls (atom []) events (atom []) current (atom nil) - select-fn (fn [id] - (reset! current id) - (swap! events conj [:select id])) stubs {:flush (fn [s o] (swap! flush-calls conj {:shapes s :opts o}) nil) + :select (fn [id] + (reset! current id) + (swap! events conj [:select id])) :svg-attrs (fn [attrs] (swap! events conj [:svg-attrs @current attrs]) nil) :path (fn [content] (swap! events conj [:path @current content]) nil)} result (with-ffi-stubs* stubs - #(serialize-shape/serialize-shapes-batch! shapes opts select-fn)) + #(serialize-shape/serialize-shapes-batch shapes opts)) flushed (first @flush-calls)] (t/is (= 1 (count @flush-calls)) "exactly one flush") (t/is (= opts (:opts flushed)) "flush carries passed opts") @@ -135,7 +140,7 @@ :svg-attrs (fn [attrs] (swap! svg-applied conj attrs) nil) :path (fn [content] (swap! path-applied conj content) nil)}] (let [expected (svg-derived/apply-svg-derived shape) - result (with-ffi-stubs* stubs #(serialize-shape/serialize-shape! shape))] + result (with-ffi-stubs* stubs #(serialize-shape/serialize-shape shape))] (t/is (= [{:shapes [expected] :opts {:include-layout? false}}] @flush-calls) (str "single upload carries derived shape without layout for " (:type shape))) (t/is (= expected result) @@ -152,13 +157,11 @@ flush-calls (atom []) selected (atom []) stubs {:flush (fn [s o] (swap! flush-calls conj {:shapes s :opts o}) nil) + :select (fn [id] (swap! selected conj id)) :svg-attrs (fn [_] (t/is false "no svg-attrs write on empty batch") nil) :path (fn [_] (t/is false "no path write on empty batch") nil)} result (with-ffi-stubs* stubs - #(serialize-shape/serialize-shapes-batch! - [] - opts - (fn [id] (swap! selected conj id))))] + #(serialize-shape/serialize-shapes-batch [] opts))] (t/is (= [] result) "returns empty prepared") (t/is (empty? @flush-calls) "no flush on empty batch") (t/is (empty? @selected) "no select on empty batch"))) diff --git a/frontend/test/frontend_tests/render_wasm/shape_proxy_test.cljs b/frontend/test/frontend_tests/render_wasm/shape_proxy_test.cljs new file mode 100644 index 0000000000..276be80d53 --- /dev/null +++ b/frontend/test/frontend_tests/render_wasm/shape_proxy_test.cljs @@ -0,0 +1,24 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.render-wasm.shape-proxy-test + (:require + [app.common.uuid :as uuid] + [app.render-wasm.shape :as wasm.shape] + [cljs.test :as t :include-macros true])) + +(def ^:private attrs + {:id (uuid/next) :type :rect :name "Rect" :x 10 :y 20}) + +(t/deftest count-includes-id-and-type + (let [shape (wasm.shape/create-shape attrs)] + (t/is (= (count attrs) (count shape))) + (t/is (= (count shape) (count (seq shape)))))) + +(t/deftest equals-plain-map-with-same-entries + (let [shape (wasm.shape/create-shape attrs)] + (t/is (= shape attrs)) + (t/is (= attrs shape)))) diff --git a/frontend/test/frontend_tests/render_wasm/text_editor_content_range_test.cljs b/frontend/test/frontend_tests/render_wasm/text_editor_content_range_test.cljs new file mode 100644 index 0000000000..6b6445f362 --- /dev/null +++ b/frontend/test/frontend_tests/render_wasm/text_editor_content_range_test.cljs @@ -0,0 +1,72 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.render-wasm.text-editor-content-range-test + "The styled content of a char range, as copied from the editor selection." + (:require + [app.render-wasm.text-editor :as text-editor] + [cljs.test :as t :include-macros true])) + +(defn- span [text attrs] + (assoc attrs :text text)) + +(defn- paragraph [attrs & spans] + (assoc attrs :type "paragraph" :children (vec spans))) + +(defn- content [& paragraphs] + {:type "root" + :children [{:type "paragraph-set" :children (vec paragraphs)}]}) + +(def ^:private bold {:font-weight "700"}) +(def ^:private red {:fills [{:fill-color "#ff0000" :fill-opacity 1}]}) +(def ^:private centered {:text-align "center"}) +(def ^:private right {:text-align "right"}) + +(def ^:private source + (content (paragraph centered (span "Hello " {}) (span "bold" bold) (span " end" {})) + (paragraph right (span "red" red) (span " tail" {})))) + +(t/deftest single-paragraph + (t/testing "spans are cut at both ends and keep their styles" + (t/is (= (content (paragraph centered (span "lo " {}) (span "bold" bold) (span " e" {}))) + (text-editor/content-range source {:start-para 0 :start-offset 3 + :end-para 0 :end-offset 12}))))) + +(t/deftest across-paragraphs + (t/testing "every paragraph in the range keeps its attrs" + (t/is (= (content (paragraph centered (span "end" {})) + (paragraph right (span "re" red))) + (text-editor/content-range source {:start-para 0 :start-offset 11 + :end-para 1 :end-offset 2})))) + + (t/testing "a range starting at a paragraph end keeps an empty paragraph with its style" + (t/is (= (content (paragraph centered (span "" {})) + (paragraph right (span "red" red))) + (text-editor/content-range source {:start-para 0 :start-offset 14 + :end-para 1 :end-offset 3}))))) + +(t/deftest utf16-offsets + (t/testing "offsets count UTF-16 units like WASM's" + (t/is (= (content (paragraph {} (span "😀b" bold))) + (text-editor/content-range (content (paragraph {} (span "a😀b" bold))) + {:start-para 0 :start-offset 1 + :end-para 0 :end-offset 4}))))) + +(t/deftest span-ranges + (let [slice (text-editor/content-range source {:start-para 0 :start-offset 3 + :end-para 1 :end-offset 5})] + (t/testing "the first paragraph's spans start at the slice start; later ones at 0" + (t/is (= [[{:start-para 0 :start-offset 3 :end-para 0 :end-offset 6} + {:start-para 0 :start-offset 6 :end-para 0 :end-offset 10} + {:start-para 0 :start-offset 10 :end-para 0 :end-offset 14}] + [{:start-para 1 :start-offset 0 :end-para 1 :end-offset 3} + {:start-para 1 :start-offset 3 :end-para 1 :end-offset 5}]] + (text-editor/span-ranges slice {:para 0 :offset 3}))))) + + (t/testing "offsets count UTF-16 units like WASM's" + (t/is (= [[{:start-para 2 :start-offset 1 :end-para 2 :end-offset 4}]] + (text-editor/span-ranges (content (paragraph {} (span "😀b" bold))) + {:para 2 :offset 1}))))) diff --git a/frontend/test/frontend_tests/render_wasm/text_paste_test.cljs b/frontend/test/frontend_tests/render_wasm/text_paste_test.cljs new file mode 100644 index 0000000000..a52c1f5e2e --- /dev/null +++ b/frontend/test/frontend_tests/render_wasm/text_paste_test.cljs @@ -0,0 +1,289 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.render-wasm.text-paste-test + "Restyling pasted text. Test content already holds the pasted text in the + style of the span it went into, as after `text-editor-sync-content`." + (:require + [app.render-wasm.text-paste :as text-paste] + [cljs.test :as t :include-macros true])) + +(def ^:private regular + {:font-id "sourcesanspro" :font-variant-id "regular" + :font-weight "400" :font-style "normal"}) + +(defn- span + ([text] (span text {})) + ([text attrs] (merge regular attrs {:text text}))) + +(defn- content [& paragraphs] + {:type "root" + :children [{:type "paragraph-set" + :children (mapv (fn [spans] {:type "paragraph" :children spans}) + paragraphs)}]}) + +(defn- spans-of [content] + (mapv :children (-> content :children first :children))) + +(defn- fragment-paragraph [& runs] + {:attrs {} :children (vec runs)}) + +(defn- run + ([text] (run text {})) + ([text attrs] {:text text :attrs attrs})) + +(def ^:private bold-variant + {:font-weight "700" :font-style "normal" :font-variant-id "bold"}) + +(t/deftest fragment->text + (t/testing "paragraphs are joined by newlines" + (t/is (= "ab\n\nc" + (text-paste/fragment->text + [(fragment-paragraph (run "a") (run "b")) + (fragment-paragraph) + (fragment-paragraph (run "c"))]))))) + +(t/deftest bold-run-resolves-to-a-font-variant + (t/testing "a bold run inside a paragraph gets the bold variant of the span's font" + (let [pasted (content [(span "Hello big world")]) + fragment [(fragment-paragraph (run "big " {:font-weight "700"}))] + result (text-paste/apply-fragment-styles pasted fragment {:para 0 :offset 6})] + (t/is (= [[(span "Hello ") (span "big " bold-variant) (span "world")]] + (spans-of result)))))) + +(t/deftest italic-run-keeps-the-span-weight + (t/testing "italic over a bold span picks the bold italic variant" + (let [pasted (content [(span "x" bold-variant)]) + fragment [(fragment-paragraph (run "x" {:font-style "italic"}))] + result (text-paste/apply-fragment-styles pasted fragment {:para 0 :offset 0})] + (t/is (= [[(span "x" {:font-weight "700" :font-style "italic" + :font-variant-id "bolditalic"})]] + (spans-of result)))))) + +(t/deftest html-emphasis-replaces-the-caret-emphasis + (let [no-emphasis {:font-weight "400" :font-style "normal" + :text-decoration "none" :text-transform "none"}] + (t/testing "plain source text pasted into bold, underlined text comes out plain" + (let [pasted (content [(span "aXb" (assoc bold-variant :text-decoration "underline"))]) + fragment [(fragment-paragraph (run "X" no-emphasis))] + result (text-paste/apply-fragment-styles pasted fragment {:para 0 :offset 1})] + (t/is (= [[(span "a" (assoc bold-variant :text-decoration "underline")) + (span "X" {:text-decoration "none" :text-transform "none"}) + (span "b" (assoc bold-variant :text-decoration "underline"))]] + (spans-of result))))) + + (t/testing "not bold leaves a light weight as it is" + (let [light {:font-weight "300" :font-style "normal" :font-variant-id "300"} + pasted (content [(span "X" light)]) + fragment [(fragment-paragraph (run "X" no-emphasis))] + result (text-paste/apply-fragment-styles pasted fragment {:para 0 :offset 0})] + (t/is (= [[(span "X" (assoc light :text-decoration "none" :text-transform "none"))]] + (spans-of result))))))) + +(t/deftest unknown-font-keeps-its-weight + (t/testing "a font we have no data for cannot resolve a variant, so it stays" + (let [unknown (span "x" {:font-id "missing-font"}) + fragment [(fragment-paragraph (run "x" {:font-weight "700"}))] + result (text-paste/apply-fragment-styles (content [unknown]) fragment {:para 0 :offset 0})] + (t/is (= [[unknown]] (spans-of result)))))) + +(t/deftest typography-link + (let [linked {:typography-ref-id "typo-id" :typography-ref-file "file-id"}] + (t/testing "a span changed by the paste leaves its typography" + (let [fragment [(fragment-paragraph (run "x" {:text-decoration "underline"}))] + result (text-paste/apply-fragment-styles + (content [(span "x" linked)]) fragment {:para 0 :offset 0})] + (t/is (= [[(span "x" {:text-decoration "underline"})]] + (spans-of result))))) + + (t/testing "a span the paste leaves as it was keeps its typography" + (let [already (span "x" (merge bold-variant linked)) + fragment [(fragment-paragraph (run "x" {:font-weight "700"}))] + result (text-paste/apply-fragment-styles (content [already]) fragment {:para 0 :offset 0})] + (t/is (= [[already]] (spans-of result))))))) + +(t/deftest ranges-across-paragraphs + (t/testing "runs after the first paragraph start at offset 0 of their paragraph" + (let [pasted (content [(span "abX")] [(span "Ycd")]) + fragment [(fragment-paragraph (run "X" {:font-weight "700"})) + (fragment-paragraph (run "Y" {:font-weight "700"}))] + result (text-paste/apply-fragment-styles pasted fragment {:para 0 :offset 2})] + (t/is (= [[(span "ab") (span "X" bold-variant)] + [(span "Y" bold-variant) (span "cd")]] + (spans-of result)))))) + +(t/deftest ranges-count-utf16-units + (t/testing "an emoji in the fragment moves later runs by its two UTF-16 units" + (let [pasted (content [(span "😀b")]) + fragment [(fragment-paragraph (run "😀") (run "b" {:font-weight "700"}))] + result (text-paste/apply-fragment-styles pasted fragment {:para 0 :offset 0})] + (t/is (= [[(span "😀") (span "b" bold-variant)]] + (spans-of result))))) + + (t/testing "a paste after an emoji starts at the UTF-16 offset WASM reports" + (let [pasted (content [(span "😀Xb")]) + fragment [(fragment-paragraph (run "X" {:font-weight "700"}))] + result (text-paste/apply-fragment-styles pasted fragment {:para 0 :offset 2})] + (t/is (= [[(span "😀") (span "X" bold-variant) (span "b")]] + (spans-of result)))))) + +(t/deftest fragment->content + (t/testing "runs become spans over the base style, and empty paragraphs keep one empty span" + (let [result (text-paste/fragment->content + [(fragment-paragraph (run "Hello ") (run "bold" {:font-weight "700"})) + (fragment-paragraph)] + regular)] + (t/is (= [[(span "Hello ") (span "bold" bold-variant)] + [(span "")]] + (spans-of result))))) + + (t/testing "paragraphs carry the base style" + (let [result (text-paste/fragment->content [(fragment-paragraph (run "x"))] regular)] + (t/is (= (assoc regular :type "paragraph") + (dissoc (-> result :children first :children first) :children)))))) + +;; --- Text copied in Penpot + +(def ^:private brand + {:font-id "gfont-roboto" :font-family "Roboto" :font-variant-id "700italic" + :font-weight "700" :font-style "italic" + :fills [{:fill-color "#ff0000" :fill-opacity 1}]}) + +(t/deftest content->fragment + (t/testing "paragraphs and spans keep their whole style, and empty spans are dropped" + (t/is (= [{:attrs {:text-align "center"} + :children [{:text "x" :attrs brand}]} + {:attrs {:text-align "right"} + :children []}] + (text-paste/content->fragment + {:type "root" + :children [{:type "paragraph-set" + :children [{:type "paragraph" :text-align "center" + :children [(assoc brand :text "x") (assoc brand :text "")]} + {:type "paragraph" :text-align "right" + :children [(assoc brand :text "")]}]}]}))))) + +(t/deftest penpot-styles-replace-the-caret-style + (t/testing "pasted runs take their whole style, dropping the target's typography" + (let [pasted (content [(span "AxyB" {:typography-ref-id "t" :typography-ref-file "f"})]) + fragment [(fragment-paragraph (run "x" brand) (run "y" regular))] + result (text-paste/apply-content-styles pasted fragment {:para 0 :offset 1})] + (t/is (= [[(span "A" {:typography-ref-id "t" :typography-ref-file "f"}) + (assoc brand :text "x") + (span "y") + (span "B" {:typography-ref-id "t" :typography-ref-file "f"})]] + (spans-of result))))) + + (t/testing "the first paragraph keeps its style; later ones bring theirs" + (let [pasted {:type "root" + :children [{:type "paragraph-set" + :children [{:type "paragraph" :text-align "left" :children [(span "Ax")]} + {:type "paragraph" :text-align "left" :children [(span "yB")]}]}]} + fragment [{:attrs {:text-align "center"} :children [(run "x" brand)]} + {:attrs {:text-align "right"} :children [(run "y" brand)]}] + result (text-paste/apply-content-styles pasted fragment {:para 0 :offset 1})] + (t/is (= ["left" "right"] + (map :text-align (-> result :children first :children)))) + (t/is (= [[(span "A") (assoc brand :text "x")] + [(assoc brand :text "y") (span "B")]] + (spans-of result)))))) + +;; --- Gradients + +(defn- gradient-fill [gradient] + {:fills [{:fill-color-gradient (merge {:type :linear :width 1 :stops []} gradient)}]}) + +(def ^:private source-box {:width 200 :height 40}) + +(def ^:private target-box {:width 100 :height 80}) + +(def ^:private source-geometry + {:origins [[{:x 50 :y 0 :box source-box}]]}) + +(def ^:private target-geometry + {:origins [[{:x 10 :y 40 :box target-box}]]}) + +(defn- pasted-gradient [content] + (-> (spans-of content) first second :fills first :fill-color-gradient)) + +(t/deftest gradients-keep-their-offset-from-the-span + (let [linear (gradient-fill {:start-x 0.25 :start-y 0 :end-x 0.75 :end-y 1}) + pasted (content [(span "HELLO" linear)]) + target (content [(span "abc") (span "HELLO" linear)]) + result (text-paste/move-span-fills target pasted {:para 0 :offset 3} + source-geometry target-geometry)] + (t/testing "points move into the target box at the same pixel offsets" + (t/is (= {:start-x 0.1 :start-y 0.5 :end-x 1.1 :end-y 1} + (select-keys (pasted-gradient result) [:start-x :start-y :end-x :end-y])))) + (t/testing "a linear width stays as it was" + (t/is (= 1 (:width (pasted-gradient result))))) + (t/testing "text before the paste keeps its fills" + (t/is (= (span "abc") (-> (spans-of result) first first)))))) + +(t/deftest radial-width-follows-the-aspect-ratio + (let [radial (gradient-fill {:type :radial :start-x 0.5 :start-y 0.5 :end-x 0.5 :end-y 1}) + pasted (content [(span "HELLO" radial)]) + target (content [(span "abc") (span "HELLO" radial)]) + result (text-paste/move-span-fills target pasted {:para 0 :offset 3} + source-geometry target-geometry)] + ;; 200x40 is 5:1 and 100x80 is 5:4, so the width grows 4 times. + (t/is (= 4 (:width (pasted-gradient result)))))) + +(t/deftest spans-without-geometry-are-left-alone + (let [red {:fills [{:fill-color "#ff0000" :fill-opacity 1}]} + linear (gradient-fill {:start-x 0 :start-y 0 :end-x 1 :end-y 1}) + pasted (content [(span "red" red) (span "grad" linear)]) + target (content [(span "red" red) (span "grad" linear)])] + (t/testing "a span with no origin keeps its gradient" + (t/is (= target (text-paste/move-span-fills + target pasted {:para 0 :offset 0} + {:origins [[nil nil]]} + {:origins [[nil nil]]})))) + (t/testing "an empty box leaves the gradients as they were" + (t/is (= target (text-paste/move-span-fills + target pasted {:para 0 :offset 0} + {:origins [[nil {:x 0 :y 0 :box source-box}]]} + {:origins [[nil {:x 0 :y 0 :box {:width 0 :height 80}}]]})))))) + +(t/deftest each-span-moves-from-its-own-box + ;; Copied text shapes are joined into one paste, each span keeping its box. + (let [linear (gradient-fill {:start-x 0.5 :start-y 0 :end-x 0.5 :end-y 1}) + pasted (content [(span "a" linear)] [(span "b" linear)]) + source {:origins [[{:x 0 :y 0 :box {:width 100 :height 20}}] + [{:x 0 :y 0 :box {:width 200 :height 40}}]]} + target {:origins [[{:x 0 :y 0 :box {:width 400 :height 80}}] + [{:x 0 :y 40 :box {:width 400 :height 80}}]]} + result (text-paste/move-span-fills pasted pasted {:para 0 :offset 0} source target) + points #(select-keys (-> % :fills first :fill-color-gradient) + [:start-x :start-y :end-x :end-y])] + (t/is (= [{:start-x 0.125 :start-y 0 :end-x 0.125 :end-y 0.25} + {:start-x 0.25 :start-y 0.5 :end-x 0.25 :end-y 1}] + (map (comp points first) (spans-of result)))))) + +;; --- Images + +(defn- image-fill [image] + {:fills [{:fill-image (merge {:id #uuid "00000000-0000-0000-0000-000000000042" + :width 400 :height 200 :mtype "image/png"} + image)}]}) + +(defn- pasted-image-transform [content] + (-> (spans-of content) first second :fills first :fill-image :transform)) + +(t/deftest images-keep-their-offset-from-the-span + (let [move (fn [image] + (let [fill (image-fill image)] + (pasted-image-transform + (text-paste/move-span-fills (content [(span "abc") (span "HELLO" fill)]) + (content [(span "HELLO" fill)]) + {:para 0 :offset 3} + source-geometry target-geometry))))] + (t/testing "an image over the whole source box gets a transform over the same pixels" + (t/is (= {:x -0.4 :y 0.5 :width 2 :height 0.5} + (move {})))) + (t/testing "an image with a transform keeps its pixel rect" + (t/is (= {:x 0.1 :y 0.5 :width 1 :height 0.5} + (move {:transform {:x 0.25 :y 0 :width 0.5 :height 1}})))))) diff --git a/frontend/test/frontend_tests/render_wasm/webgl_test.cljs b/frontend/test/frontend_tests/render_wasm/webgl_test.cljs new file mode 100644 index 0000000000..beb60abce9 --- /dev/null +++ b/frontend/test/frontend_tests/render_wasm/webgl_test.cljs @@ -0,0 +1,105 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.render-wasm.webgl-test + "Unit tests for `app.render-wasm.api.webgl` context init and release." + (:require + [app.render-wasm.api.webgl :as webgl] + [cljs.test :as t :include-macros true])) + +(defn- fake-env + "Builds `{canvas context module calls}` fakes. + - `calls` records `:register`, `:delete`, `:clean` and `:lose` events. + - Options: + - `:throw-in` makes that module fn throw (`:_init`, `:_set_browser`, ...), + - `:register-throws?` fails Emscripten registration (no handle exists), + - `:nil-context?` makes `getContext` return nil (nothing is acquired)." + [{:keys [throw-in register-throws? nil-context?]}] + (let [calls (atom []) + lose (fn [] (swap! calls conj [:lose]) nil) + context #js {:getExtension (fn [name] + (when (= name "WEBGL_lose_context") + #js {:loseContext lose}))} + gl #js {:registerContext (fn [_ _] + (swap! calls conj [:register]) + (when register-throws? + (throw (js/Error. "register failed"))) + 7) + :makeContextCurrent (fn [_] nil) + :deleteContext (fn [handle] + (swap! calls conj [:delete handle]) + nil)} + thrower (fn [name] + (fn [& _] + (when (= name throw-in) + (throw (js/Error. (str "fake " (pr-str name) " failure")))) + nil)) + module #js {:GL gl + :_init (thrower :_init) + :_set_render_options (thrower :_set_render_options) + :_set_browser (thrower :_set_browser) + :_clean_up (fn [& _] (swap! calls conj [:clean]) nil)} + canvas #js {:getContext (fn [_ _] (when-not nil-context? context))}] + {:calls calls :canvas canvas :context context :module module})) + +(defn- init-opts + [module] + {:module module + :context-id "webgl2" + :css-width 100 + :css-height 100 + :dpr 1 + :flags 0 + :browser 0 + :params {}}) + +(defn- init-failure + "Runs `init-context` against `env`, returning the thrown cause." + [{:keys [canvas module]}] + (try + (webgl/init-context canvas (init-opts module)) + nil + (catch :default cause + cause))) + +(defn- effect-count + [calls effect] + (count (filter #(= effect (first %)) calls))) + +(t/deftest init-failure-releases-the-acquired-context + (let [{:keys [canvas module calls]} (fake-env {:throw-in :_set_browser}) + failure (init-failure {:canvas canvas :module module})] + (t/is (some? failure) "the original failure propagates") + (t/is (= "_set_browser" (:fn (ex-data failure))) + "the cause keeps the failing fn") + (t/is (= 1 (effect-count @calls :clean)) "renderer state is released") + (t/is (some #{[:delete 7]} @calls) "the registered handle is deleted") + (t/is (some #{[:lose]} @calls) "the browser context is released"))) + +(t/deftest init-success-returns-context-and-handle + (let [{:keys [canvas context module calls]} (fake-env {}) + result (webgl/init-context canvas (init-opts module))] + (t/is (= 7 (:handle result))) + (t/is (identical? context (:context result))) + (t/is (zero? (effect-count @calls :clean)) "no release on success") + (t/is (zero? (effect-count @calls :delete)) "no release on success") + (t/is (zero? (effect-count @calls :lose)) "no release on success"))) + +(t/deftest nil-context-returns-nil-without-releasing + (let [{:keys [canvas module calls]} (fake-env {:nil-context? true})] + (t/is (nil? (webgl/init-context canvas (init-opts module)))) + (t/is (empty? @calls) "nothing acquired, nothing released"))) + +(t/deftest registration-failure-releases-browser-context-only + (let [{:keys [canvas module calls]} (fake-env {:register-throws? true}) + failure (init-failure {:canvas canvas :module module})] + (t/is (some? failure) "the original failure propagates") + (t/is (zero? (effect-count @calls :clean)) + "no renderer state exists without registration") + (t/is (zero? (effect-count @calls :delete)) + "no handle exists to delete") + (t/is (= 1 (effect-count @calls :lose)) + "the browser context is still released"))) diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index c4b052c69d..a9bf962423 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -16,10 +16,16 @@ [frontend-tests.data.exports-assets-test] [frontend-tests.data.exports-files-test] [frontend-tests.data.fix-deleted-fonts-test] + [frontend-tests.data.imports-test] + [frontend-tests.data.jobs-test] [frontend-tests.data.modal-test] [frontend-tests.data.nitrate-test] + [frontend-tests.data.persistence-commits-test] + [frontend-tests.data.persistence-lock-test] [frontend-tests.data.persistence-retry-test] + [frontend-tests.data.persistence-rollback-test] [frontend-tests.data.persistence-test] + [frontend-tests.data.pointer-map-test] [frontend-tests.data.profile-test] [frontend-tests.data.repo-test] [frontend-tests.data.store-test] @@ -29,15 +35,19 @@ [frontend-tests.data.workspace-colors-test] [frontend-tests.data.workspace-comments-test] [frontend-tests.data.workspace-context-menu-test] + [frontend-tests.data.workspace-focus-test] [frontend-tests.data.workspace-interactions-test] + [frontend-tests.data.workspace-libraries-sync-test] [frontend-tests.data.workspace-mcp-test] [frontend-tests.data.workspace-media-test] + [frontend-tests.data.workspace-notifications-test] [frontend-tests.data.workspace-pages-test] [frontend-tests.data.workspace-path-edition-test] [frontend-tests.data.workspace-reflow-test] [frontend-tests.data.workspace-shortcuts-test] [frontend-tests.data.workspace-texts-test] [frontend-tests.data.workspace-thumbnails-test] + [frontend-tests.data.workspace-undo-test] [frontend-tests.data.workspace-variant-rename-test] [frontend-tests.data.workspace-variants-test] [frontend-tests.data.workspace-versions-test] @@ -46,13 +56,17 @@ [frontend-tests.errors-test] [frontend-tests.fonts-test] [frontend-tests.helpers-shapes-test] + [frontend-tests.logic.bool-move-test] [frontend-tests.logic.comp-remove-swap-slots-test] [frontend-tests.logic.comp-watcher-test] [frontend-tests.logic.components-and-tokens] [frontend-tests.logic.copy-paste-typography-test] [frontend-tests.logic.copying-and-duplicating-test] + [frontend-tests.logic.drag-on-save-failure-test] [frontend-tests.logic.frame-guides-test] [frontend-tests.logic.groups-test] + [frontend-tests.logic.ignore-tree-test] + [frontend-tests.logic.move-shapes-to-frame-test] [frontend-tests.logic.multi-variant-switch-test] [frontend-tests.logic.nudge-selected-shapes-test] [frontend-tests.logic.pasting-in-containers-test] @@ -69,6 +83,7 @@ [frontend-tests.main.refs-test] [frontend-tests.plugins.comments-test] [frontend-tests.plugins.context-shapes-test] + [frontend-tests.plugins.creation-parent-test] [frontend-tests.plugins.events-test] [frontend-tests.plugins.file-test] [frontend-tests.plugins.flex-test] @@ -77,9 +92,11 @@ [frontend-tests.plugins.interactions-test] [frontend-tests.plugins.library-test] [frontend-tests.plugins.local-storage-test] + [frontend-tests.plugins.management-test] [frontend-tests.plugins.page-active-validation-test] [frontend-tests.plugins.page-test] [frontend-tests.plugins.parser-test] + [frontend-tests.plugins.read-only-test] [frontend-tests.plugins.register-test] [frontend-tests.plugins.shape-bugfixes-test] [frontend-tests.plugins.text-test] @@ -90,8 +107,12 @@ [frontend-tests.render-dimensions-test] [frontend-tests.render-wasm.process-objects-test] [frontend-tests.render-wasm.serialization-test] + [frontend-tests.render-wasm.shape-proxy-test] [frontend-tests.render-wasm.text-editor-apply-styles-test] [frontend-tests.render-wasm.text-editor-caret-color-test] + [frontend-tests.render-wasm.text-editor-content-range-test] + [frontend-tests.render-wasm.text-paste-test] + [frontend-tests.render-wasm.webgl-test] [frontend-tests.router-test] [frontend-tests.svg-fills-test] [frontend-tests.svg-filters-test] @@ -104,9 +125,16 @@ [frontend-tests.tokens.logic.token-remapping-test] [frontend-tests.tokens.logic.tokens-source-test] [frontend-tests.tokens.logic.tokens-status-test] + [frontend-tests.tokens.referenced-token-value-test] + [frontend-tests.tokens.shadow-form-test] [frontend-tests.tokens.style-dictionary-test] [frontend-tests.tokens.token-errors-test] + [frontend-tests.tokens.token-parsing-test] [frontend-tests.tokens.token-path-test] + [frontend-tests.tokens.token-pill-test] + [frontend-tests.tokens.token-tree-test] + [frontend-tests.tokens.tokenscript-test] + [frontend-tests.tokens.typography-form-test] [frontend-tests.tokens.workspace-tokens-remap-test] [frontend-tests.ui.check-updates-test] [frontend-tests.ui.color-token-position-test] @@ -114,14 +142,18 @@ [frontend-tests.ui.comment-input-ime-test] [frontend-tests.ui.comments-clustering-test] [frontend-tests.ui.comments-position-modifier-test] + [frontend-tests.ui.dashboard-import-linking-test] [frontend-tests.ui.drawarea-test] [frontend-tests.ui.ds-controls-numeric-input-test] [frontend-tests.ui.error-boundary-test] + [frontend-tests.ui.file-restored-modal-test] [frontend-tests.ui.gradient-handlers-test] [frontend-tests.ui.history-test] [frontend-tests.ui.inspect-geometry-test] [frontend-tests.ui.inspect-stroke-tokens-test] + [frontend-tests.ui.jobs-progress-test] [frontend-tests.ui.layout-container-multiple-test] + [frontend-tests.ui.mcp-authorize-test] [frontend-tests.ui.measurements-test] [frontend-tests.ui.measures-menu-props-test] [frontend-tests.ui.organization-team-switch-test] @@ -131,8 +163,11 @@ [frontend-tests.ui.shape-filters-test] [frontend-tests.ui.shortcuts-labels-test] [frontend-tests.ui.sidebar-scroll-test] + [frontend-tests.ui.sortable-auto-scroll-test] [frontend-tests.ui.static-test] [frontend-tests.ui.stroke-menu-test] + [frontend-tests.ui.text-attrs-multiple-test] + [frontend-tests.ui.viewport-top-bar-test] [frontend-tests.ui.workspace-libraries-test] [frontend-tests.util-clipboard-test] [frontend-tests.util-http-test] @@ -140,11 +175,17 @@ [frontend-tests.util-queue-test] [frontend-tests.util-range-tree-test] [frontend-tests.util-simple-math-test] + [frontend-tests.util-text-clipboard-test] [frontend-tests.util-text-editor-test] + [frontend-tests.util-theme-test] [frontend-tests.util-webapi-test] [frontend-tests.util-zip-test] + [frontend-tests.util.code-gen-markup-svg-wasm-test] [frontend-tests.util.dom.dnd-test] [frontend-tests.worker-snap-test] + [frontend-tests.worker.exports-test] + [frontend-tests.worker.index-test] + [frontend-tests.worker.trim-test] [goog.object :as gobj])) (enable-console-print!) @@ -170,28 +211,41 @@ 'frontend-tests.data.fix-deleted-fonts-test 'frontend-tests.data.modal-test 'frontend-tests.data.nitrate-test + 'frontend-tests.data.persistence-commits-test + 'frontend-tests.data.persistence-lock-test 'frontend-tests.data.persistence-retry-test + 'frontend-tests.data.persistence-rollback-test 'frontend-tests.data.persistence-test + 'frontend-tests.data.pointer-map-test 'frontend-tests.data.profile-test 'frontend-tests.data.repo-test 'frontend-tests.data.store-test 'frontend-tests.data.exports-assets-test 'frontend-tests.data.exports-files-test + 'frontend-tests.data.imports-test + 'frontend-tests.data.jobs-test + 'frontend-tests.worker.exports-test + 'frontend-tests.worker.index-test + 'frontend-tests.worker.trim-test 'frontend-tests.data.svg-upload-test 'frontend-tests.data.uploads-test 'frontend-tests.data.viewer-test 'frontend-tests.data.workspace-colors-test 'frontend-tests.data.workspace-comments-test 'frontend-tests.data.workspace-context-menu-test + 'frontend-tests.data.workspace-focus-test 'frontend-tests.data.workspace-interactions-test + 'frontend-tests.data.workspace-libraries-sync-test 'frontend-tests.data.workspace-mcp-test 'frontend-tests.data.workspace-media-test + 'frontend-tests.data.workspace-notifications-test 'frontend-tests.data.workspace-pages-test 'frontend-tests.data.workspace-path-edition-test 'frontend-tests.data.workspace-reflow-test 'frontend-tests.data.workspace-shortcuts-test 'frontend-tests.data.workspace-texts-test 'frontend-tests.data.workspace-thumbnails-test + 'frontend-tests.data.workspace-undo-test 'frontend-tests.data.workspace-variant-rename-test 'frontend-tests.data.workspace-variants-test 'frontend-tests.data.workspace-versions-test @@ -200,13 +254,17 @@ 'frontend-tests.errors-test 'frontend-tests.fonts-test 'frontend-tests.helpers-shapes-test + 'frontend-tests.logic.bool-move-test 'frontend-tests.logic.comp-remove-swap-slots-test 'frontend-tests.logic.comp-watcher-test 'frontend-tests.logic.components-and-tokens 'frontend-tests.logic.copy-paste-typography-test 'frontend-tests.logic.copying-and-duplicating-test + 'frontend-tests.logic.drag-on-save-failure-test 'frontend-tests.logic.frame-guides-test 'frontend-tests.logic.groups-test + 'frontend-tests.logic.ignore-tree-test + 'frontend-tests.logic.move-shapes-to-frame-test 'frontend-tests.logic.multi-variant-switch-test 'frontend-tests.logic.nudge-selected-shapes-test 'frontend-tests.logic.path-actions-test @@ -223,7 +281,9 @@ 'frontend-tests.logic.wasm-pixel-snap-test 'frontend-tests.plugins.comments-test 'frontend-tests.plugins.context-shapes-test + 'frontend-tests.plugins.creation-parent-test 'frontend-tests.plugins.events-test + 'frontend-tests.plugins.management-test 'frontend-tests.plugins.file-test 'frontend-tests.plugins.flex-test 'frontend-tests.plugins.format-test @@ -234,6 +294,7 @@ 'frontend-tests.plugins.page-active-validation-test 'frontend-tests.plugins.page-test 'frontend-tests.plugins.parser-test + 'frontend-tests.plugins.read-only-test 'frontend-tests.plugins.register-test 'frontend-tests.plugins.shape-bugfixes-test 'frontend-tests.plugins.text-test @@ -243,8 +304,12 @@ 'frontend-tests.plugins.value-objects-test 'frontend-tests.render-wasm.process-objects-test 'frontend-tests.render-wasm.serialization-test + 'frontend-tests.render-wasm.shape-proxy-test 'frontend-tests.render-wasm.text-editor-apply-styles-test 'frontend-tests.render-wasm.text-editor-caret-color-test + 'frontend-tests.render-wasm.text-editor-content-range-test + 'frontend-tests.render-wasm.text-paste-test + 'frontend-tests.render-wasm.webgl-test 'frontend-tests.browser-history-test 'frontend-tests.router-test 'frontend-tests.svg-fills-test @@ -255,9 +320,16 @@ 'frontend-tests.tokens.logic.token-actions-test 'frontend-tests.tokens.logic.token-data-test 'frontend-tests.tokens.logic.token-remapping-test + 'frontend-tests.tokens.referenced-token-value-test + 'frontend-tests.tokens.shadow-form-test 'frontend-tests.tokens.style-dictionary-test 'frontend-tests.tokens.token-errors-test + 'frontend-tests.tokens.token-parsing-test 'frontend-tests.tokens.token-path-test + 'frontend-tests.tokens.token-pill-test + 'frontend-tests.tokens.token-tree-test + 'frontend-tests.tokens.tokenscript-test + 'frontend-tests.tokens.typography-form-test 'frontend-tests.tokens.logic.tokens-source-test 'frontend-tests.tokens.logic.tokens-status-test 'frontend-tests.tokens.workspace-tokens-remap-test @@ -267,17 +339,21 @@ 'frontend-tests.ui.comment-input-ime-test 'frontend-tests.ui.comments-clustering-test 'frontend-tests.ui.comments-position-modifier-test + 'frontend-tests.ui.dashboard-import-linking-test 'frontend-tests.ui.drawarea-test 'frontend-tests.ui.ds-controls-numeric-input-test 'frontend-tests.ui.error-boundary-test + 'frontend-tests.ui.file-restored-modal-test 'frontend-tests.ui.gradient-handlers-test 'frontend-tests.ui.history-test 'frontend-tests.ui.inspect-geometry-test 'frontend-tests.ui.inspect-stroke-tokens-test + 'frontend-tests.ui.jobs-progress-test 'frontend-tests.ui.layout-container-multiple-test 'frontend-tests.ui.measurements-test 'frontend-tests.ui.measures-menu-props-test 'frontend-tests.ui.organization-team-switch-test + 'frontend-tests.ui.mcp-authorize-test 'frontend-tests.ui.routes-test 'frontend-tests.render-dimensions-test 'frontend-tests.text-editor-paste-guard-test @@ -286,8 +362,11 @@ 'frontend-tests.ui.shape-filters-test 'frontend-tests.ui.shortcuts-labels-test 'frontend-tests.ui.sidebar-scroll-test + 'frontend-tests.ui.sortable-auto-scroll-test 'frontend-tests.ui.static-test 'frontend-tests.ui.stroke-menu-test + 'frontend-tests.ui.text-attrs-multiple-test + 'frontend-tests.ui.viewport-top-bar-test 'frontend-tests.ui.workspace-libraries-test 'frontend-tests.util-clipboard-test 'frontend-tests.util-http-test @@ -295,8 +374,11 @@ 'frontend-tests.util-queue-test 'frontend-tests.util-range-tree-test 'frontend-tests.util-simple-math-test + 'frontend-tests.util-text-clipboard-test 'frontend-tests.util-text-editor-test + 'frontend-tests.util-theme-test 'frontend-tests.util-webapi-test + 'frontend-tests.util.code-gen-markup-svg-wasm-test 'frontend-tests.util.dom.dnd-test 'frontend-tests.util-zip-test 'frontend-tests.worker-snap-test]) diff --git a/frontend/test/frontend_tests/svg_fills_test.cljs b/frontend/test/frontend_tests/svg_fills_test.cljs index cecb4ad3b8..cbeb787e17 100644 --- a/frontend/test/frontend_tests/svg_fills_test.cljs +++ b/frontend/test/frontend_tests/svg_fills_test.cljs @@ -7,16 +7,18 @@ (ns frontend-tests.svg-fills-test (:require [app.common.render-wasm.svg-derived :as svg-derived] + [app.main.ui.shapes.attrs :as attrs] + [app.util.object :as obj] [cljs.test :refer [deftest is testing]])) (def sample-shape {:selrect {:x 100 :y 200 :width 200 :height 100} - :svg-attrs {:fillOpacity "0.5" + :svg-attrs {:fill-opacity "0.5" :style {:fill "url(#grad)"}} :svg-defs {"grad" {:tag :radialGradient :attrs {:id "grad" - :gradientUnits "userSpaceOnUse" + :gradient-units "userSpaceOnUse" :cx "150" :cy "250" :r "50"} @@ -42,17 +44,45 @@ (deftest skips-when-no-svg-fill (is (nil? (svg-derived/svg-fill->fills {:svg-attrs {:fill "none"}})))) +;; Stored (post-0034) spelling: migration `0034` turned stop `:style` +;; strings into kebab maps via `attrs->kebab-props`. The WASM fill +;; derivation must read both spellings, or gradients lose their stops. +(def stored-shape + {:selrect {:x 100 :y 200 :width 200 :height 100} + :svg-attrs {:fill-opacity "0.5" + :style {:fill "url(#grad)"}} + :svg-defs {"grad" + {:tag :radialGradient + :attrs {:id "grad" + :gradient-units "userSpaceOnUse" + :cx "150" + :cy "250" + :r "50"} + :content [{:tag :stop + :attrs {:offset "0" + :style {:stop-color "#ff0000" :stop-opacity "1"}}} + {:tag :stop + :attrs {:offset "1" + :style {:stop-color "#00ff00" :stop-opacity "0"}}}]}}}) + +(deftest builds-gradient-fill-from-stored-map-styles + (let [fills (svg-derived/svg-fill->fills stored-shape) + gradient (get-in (first fills) [:fill-color-gradient])] + (is (= 1 (count fills))) + (is (= "#ff0000" (get-in gradient [:stops 0 :color]))) + (is (= "#00ff00" (get-in gradient [:stops 1 :color]))))) + (def elliptical-shape {:selrect {:x 0 :y 0 :width 200 :height 100} :svg-attrs {:style {:fill "url(#grad-ellipse)"}} :svg-defs {"grad-ellipse" {:tag :radialGradient :attrs {:id "grad-ellipse" - :gradientUnits "userSpaceOnUse" + :gradient-units "userSpaceOnUse" :cx "50" :cy "50" :r "50" - :gradientTransform "matrix(2 0 0 1 0 0)"} + :gradient-transform "matrix(2 0 0 1 0 0)"} :content [{:tag :stop :attrs {:offset "0" :style "stop-color:#000000;stop-opacity:1"}} @@ -94,4 +124,20 @@ (is (= 1 (count fills))) (is (= "#fabada" (:fill-color (first fills)))))) +;; The classic renderer funnel: stored kebab keys reach React as +;; camelCase props, so a shape renders identical props before and after +;; the kebab migration. Extra attrs keep their source spelling so React +;; passes them to the DOM silently (no unknown-prop warning). +(deftest svg-attrs-render-as-camel-props + (let [props (attrs/get-svg-props {:svg-attrs {:fill-rule "evenodd" + :stroke-width "2" + :stroke-style "dotted" + :style {:fill-opacity "0.5"}}} + "r1")] + (is (= "evenodd" (obj/get props "fillRule"))) + (is (= "2" (obj/get props "strokeWidth"))) + (is (= "dotted" (obj/get props "stroke-style"))) + (is (nil? (obj/get props "strokeStyle")) "no camel duplicate for extras") + (is (= "0.5" (obj/get (obj/get props "style") "fillOpacity"))))) + diff --git a/frontend/test/frontend_tests/svg_filters_test.cljs b/frontend/test/frontend_tests/svg_filters_test.cljs index b1eb4be721..f20d3a219e 100644 --- a/frontend/test/frontend_tests/svg_filters_test.cljs +++ b/frontend/test/frontend_tests/svg_filters_test.cljs @@ -14,7 +14,7 @@ :svg-defs {"simple-filter" {:tag :filter :content [{:tag :feOffset :attrs {:dx "2" :dy "3"}} - {:tag :feGaussianBlur :attrs {:stdDeviation "4"}}]}}}) + {:tag :feGaussianBlur :attrs {:std-deviation "4"}}]}}}) (defn- filter-shape [content] @@ -46,7 +46,7 @@ (deftest derives-layer-blur-from-plain-blur-filter (let [shape (svg-derived/apply-svg-filters - (filter-shape [{:tag :feGaussianBlur :attrs {:stdDeviation "4"}}]))] + (filter-shape [{:tag :feGaussianBlur :attrs {:std-deviation "4"}}]))] (is (= :layer-blur (get-in shape [:blur :type]))) (is (= 4.0 (get-in shape [:blur :value]))) (is (nil? (:shadow shape))))) @@ -55,7 +55,7 @@ (is (= [{:color "#ff0000" :opacity 0.3}] (map :color (derived-shadow [{:tag :feOffset :attrs {:in "SourceAlpha" :dx "12" :dy "12"}} - {:tag :feGaussianBlur :attrs {:stdDeviation "4"}} + {:tag :feGaussianBlur :attrs {:std-deviation "4"}} {:tag :feColorMatrix :attrs {:type "matrix" :values "0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0"}}]))))) @@ -70,7 +70,7 @@ :type "matrix" :values "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"}} {:tag :feOffset :attrs {:dy "4"}} - {:tag :feGaussianBlur :attrs {:stdDeviation "2"}} + {:tag :feGaussianBlur :attrs {:std-deviation "2"}} {:tag :feColorMatrix :attrs {:type "matrix" :values "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"}}]))))) @@ -79,12 +79,12 @@ (is (= [{:color "#00ff00" :opacity 0.5}] (map :color (derived-shadow [{:tag :feOffset :attrs {:dx "2" :dy "2"}} - {:tag :feGaussianBlur :attrs {:stdDeviation "2"}} + {:tag :feGaussianBlur :attrs {:std-deviation "2"}} {:tag :feFlood :attrs {:flood-color "#00ff00" :flood-opacity "0.5"}}]))))) (deftest derives-shadow-from-fe-drop-shadow (let [content [{:tag :feDropShadow - :attrs {:dx "12" :dy "8" :stdDeviation "4" + :attrs {:dx "12" :dy "8" :std-deviation "4" :flood-color "#000" :flood-opacity "0.3"}}] shape (svg-derived/apply-svg-filters (filter-shape content))] (is (nil? (:blur shape))) diff --git a/frontend/test/frontend_tests/tokens/referenced_token_value_test.cljs b/frontend/test/frontend_tests/tokens/referenced_token_value_test.cljs new file mode 100644 index 0000000000..d31928b01c --- /dev/null +++ b/frontend/test/frontend_tests/tokens/referenced_token_value_test.cljs @@ -0,0 +1,55 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.tokens.referenced-token-value-test + (:require + [app.main.ui.workspace.tokens.management.forms.controls.utils :as csu] + [cljs.test :as t :include-macros true])) + +(def ^:private typography-value + {:font-family ["Inter"] + :font-size "16" + :font-weight "600"}) + +(def ^:private shadow-value + [{:offset-x "4" :offset-y "4" :blur "8" :spread "0" :color "#000000" :inset false} + {:offset-x "0" :offset-y "1" :blur "2" :spread "0" :color "#00000033" :inset true}]) + +(def ^:private tokens + {"base" {:name "base" :type :typography :value typography-value} + "alias" {:name "alias" :type :typography :value "{base}"} + "loop-a" {:name "loop-a" :type :typography :value "{loop-b}"} + "loop-b" {:name "loop-b" :type :typography :value "{loop-a}"} + "font-size" {:name "font-size" :type :font-size :value "16"} + "shadow-base" {:name "shadow-base" :type :shadow :value shadow-value} + "shadow-alias" {:name "shadow-alias" :type :shadow :value "{shadow-base}"}}) + +(t/deftest referenced-typography-value + (t/testing "returns the value of the referenced typography token" + (t/is (= typography-value (csu/referenced-token-value tokens "{base}" :typography))) + (t/is (= typography-value (csu/referenced-token-value tokens " {base} " :typography)))) + + (t/testing "follows chained references" + (t/is (= typography-value (csu/referenced-token-value tokens "{alias}" :typography)))) + + (t/testing "returns nil when the reference cannot be detached" + (t/is (nil? (csu/referenced-token-value tokens "{missing}" :typography))) + (t/is (nil? (csu/referenced-token-value tokens "{font-size}" :typography))) + (t/is (nil? (csu/referenced-token-value tokens "{loop-a}" :typography))) + (t/is (nil? (csu/referenced-token-value tokens "{base} {alias}" :typography))) + (t/is (nil? (csu/referenced-token-value tokens "base" :typography))) + (t/is (nil? (csu/referenced-token-value tokens nil :typography))))) + +(t/deftest referenced-shadow-value + (t/testing "returns the value of the referenced shadow token" + (t/is (= shadow-value (csu/referenced-token-value tokens "{shadow-base}" :shadow)))) + + (t/testing "follows chained references" + (t/is (= shadow-value (csu/referenced-token-value tokens "{shadow-alias}" :shadow)))) + + (t/testing "returns nil for a token of another type" + (t/is (nil? (csu/referenced-token-value tokens "{base}" :shadow))) + (t/is (nil? (csu/referenced-token-value tokens "{shadow-base}" :typography))))) diff --git a/frontend/test/frontend_tests/tokens/shadow_form_test.cljs b/frontend/test/frontend_tests/tokens/shadow_form_test.cljs new file mode 100644 index 0000000000..f940620095 --- /dev/null +++ b/frontend/test/frontend_tests/tokens/shadow_form_test.cljs @@ -0,0 +1,105 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.tokens.shadow-form-test + (:require + [app.common.schema :as sm] + [app.common.schema.messages :as csm] + [app.common.types.tokens-lib :as ctob] + [app.main.data.workspace.tokens.errors :as wte] + [app.main.ui.workspace.tokens.management.forms.shadow :as wtsh] + [app.util.i18n :refer [tr]] + [cljs.test :as t :include-macros true])) + +(def ^:private make-schema @#'wtsh/make-schema) +(def ^:private form-value @#'wtsh/make-default-value) +(def ^:private check-self-reference @#'wtsh/check-shadow-token-self-reference) + +(def ^:private shadow + (assoc @#'wtsh/default-token-shadow :color "#000000")) + +(def ^:private token + (ctob/make-token :name "shadow.md" :type :shadow :value [shadow])) + +(defn- schema + [tab] + (make-schema (ctob/get-token-path token) + (ctob/tokens-tree {(:name token) token}) + tab)) + +(defn- form-data + [value] + {:name (:name token) :description "" :value value}) + +(defn- shadows + [& shadows] + (form-data {:reference nil :shadow (vec shadows)})) + +(defn- errors + [tab data] + (csm/collect-schema-errors (schema tab) [] {:data data})) + +(defn- valid? + [tab data] + (sm/validate (schema tab) data)) + +(t/deftest composite-tab + (t/testing "a complete shadow is valid" + (t/is (valid? :composite (shadows shadow)))) + + (t/testing "blur can't be negative" + (t/is (= (tr "workspace.tokens.shadow-token-blur-value-error") + (get-in (errors :composite (shadows (assoc shadow :blur "-2"))) + [:value :shadow 0 :blur :message])))) + + (t/testing "spread can be negative" + (t/is (valid? :composite (shadows (assoc shadow :spread "-2"))))) + + (t/testing "blur and spread can stay blank" + (t/is (valid? :composite (shadows (assoc shadow :blur "" :spread ""))))) + + (t/testing "every shadow needs its offsets and color" + (t/is (not (valid? :composite (shadows shadow (assoc shadow :color ""))))) + (t/is (not (valid? :composite (shadows (assoc shadow :offset-x ""))))) + (t/is (contains? (errors :composite (shadows (dissoc shadow :color))) :value)))) + +(t/deftest reference-tab + (t/testing "a reference is required" + (t/is (not (valid? :reference (form-data {:reference "" :shadow []})))) + (t/is (valid? :reference (form-data {:reference "{shadow.sm}" :shadow []})))) + + (t/testing "the token can't reference itself" + (t/is (= (tr "errors.tokens.self-reference") + (get-in (errors :reference (form-data {:reference "{shadow.md}" :shadow []})) + [:value :reference :message]))))) + +(t/deftest composite-self-reference + (t/testing "the error names the shadow and field that reference the token" + (t/is (= (assoc (wte/get-error-code :error.token/direct-self-reference) + :shadow-key :color + :shadow-index 1) + (check-self-reference + {:name "shadow.md" + :value [shadow (assoc shadow :color "{shadow.md}")]})))) + + (t/testing "references to other tokens pass" + (t/is (nil? (check-self-reference + {:name "shadow.md" + :value [(assoc shadow :color "{color.black}")]}))))) + +(t/deftest initial-form-value + (t/testing "a reference opens on the reference tab" + (t/is (= {:reference "{shadow.sm}" :shadow []} + (form-value "{shadow.sm}")))) + + (t/testing "blank or missing blur and spread show as 0" + (t/is (= {:reference nil :shadow [(assoc shadow :blur "0" :spread "0")]} + (form-value [(assoc shadow :blur "" :spread " ")]) + (form-value [(dissoc shadow :blur :spread)])))) + + (t/testing "a new token starts with one default shadow" + (t/is (= {:reference nil :shadow [@#'wtsh/default-token-shadow]} + (form-value nil))))) diff --git a/frontend/test/frontend_tests/tokens/token_parsing_test.cljs b/frontend/test/frontend_tests/tokens/token_parsing_test.cljs new file mode 100644 index 0000000000..b37e8fe663 --- /dev/null +++ b/frontend/test/frontend_tests/tokens/token_parsing_test.cljs @@ -0,0 +1,46 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.tokens.token-parsing-test + (:require + [app.main.ui.workspace.tokens.management.forms.controls.token-parsing :as tp] + [cljs.test :as t :include-macros true])) + +(t/deftest extract-partial-token + (t/testing "an open reference at the cursor" + (t/is (= {:start 0 :end 4 :partial "col"} + (tp/extract-partial-token "{col" 4)))) + + (t/testing "the last open reference when an earlier one is closed" + (t/is (= {:start 14 :end 17 :partial "sp"} + (tp/extract-partial-token "{color.red} + {sp" 17)))) + + (t/testing "the reference ends at its closing brace after the cursor" + (t/is (= {:start 0 :end 5 :partial "co"} + (tp/extract-partial-token "{colo}" 3)))) + + (t/testing "nothing after a closed reference" + (t/is (nil? (tp/extract-partial-token "{color} " 8)))) + + (t/testing "nothing without a reference" + (t/is (nil? (tp/extract-partial-token "12" 2))) + (t/is (nil? (tp/extract-partial-token "" 0))))) + +(t/deftest token-at-cursor + (t/testing "the token name when the cursor is inside a reference" + (t/is (= "color.red" (tp/token-at-cursor "{color.red}" 3))) + (t/is (= "color.red" (tp/token-at-cursor "{color.red} + {spacing.md}" 1))) + (t/is (= "spacing.md" (tp/token-at-cursor "{color.red} + {spacing.md}" 18)))) + + (t/testing "nothing for a reference that isn't closed" + (t/is (nil? (tp/token-at-cursor "{color" 3)))) + + (t/testing "nothing for an empty reference or one with spaces" + (t/is (nil? (tp/token-at-cursor "{}" 1))) + (t/is (nil? (tp/token-at-cursor "{color red}" 3)))) + + (t/testing "nothing before the first reference" + (t/is (nil? (tp/token-at-cursor "1 + {color}" 1))))) diff --git a/frontend/test/frontend_tests/tokens/token_pill_test.cljs b/frontend/test/frontend_tests/tokens/token_pill_test.cljs new file mode 100644 index 0000000000..9e98a2c84e --- /dev/null +++ b/frontend/test/frontend_tests/tokens/token_pill_test.cljs @@ -0,0 +1,228 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.tokens.token-pill-test + (:require + [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.shapes :as ths] + [app.common.types.token :as cto] + [app.common.types.tokens-lib :as ctob] + [app.config :as cf] + [app.main.ui.workspace.tokens.management.token-pill :as wtp] + [app.util.i18n :refer [tr]] + [cljs.test :as t :include-macros true])) + +(t/use-fixtures :each thi/test-fixture) + +(def ^:private pill-state @#'wtp/pill-state) +(def ^:private tooltip-info @#'wtp/tooltip-info) + +(def ^:private radius + (ctob/make-token :name "radius.md" :type :border-radius :value "8")) + +(def ^:private spacing + (ctob/make-token :name "spacing.md" :type :spacing :value "16")) + +(def ^:private width + (ctob/make-token :name "width.md" :type :dimensions :value "100")) + +(def ^:private active-theme-tokens + {"radius.md" (assoc radius :resolved-value 8) + "spacing.md" (assoc spacing :resolved-value 16) + "width.md" (assoc width :resolved-value 100)}) + +(defn- with-token + [shape token attributes] + (cto/apply-token-to-shape {:shape shape :token token :attributes attributes})) + +(defn- state + [token selected-shapes & {:keys [tokens is-selected-inside-layout] + :or {tokens active-theme-tokens}}] + (pill-state token tokens selected-shapes is-selected-inside-layout)) + +(defn- with-flags + [flags f] + (with-redefs [cf/flags flags] + (f))) + +(def ^:private no-tokenscript-flags (disj cf/flags :tokenscript)) +(def ^:private tokenscript-flags (conj cf/flags :tokenscript)) + +(t/deftest applied-status + (t/testing "without a selection the token counts as fully applied" + (t/is (= {:is-applied false + :is-half-applied false + :is-disabled false + :status-icon-id "token-status-full"} + (select-keys (state radius []) + [:is-applied :is-half-applied :is-disabled :status-icon-id])))) + + (t/testing "applied to every attribute of every selected shape" + (let [result (state radius [(with-token (ths/sample-shape :rect1) radius cto/border-radius-keys) + (with-token (ths/sample-shape :rect2) radius cto/border-radius-keys)])] + (t/is (true? (:is-applied result))) + (t/is (false? (:is-half-applied result))) + (t/is (= "token-status-full" (:status-icon-id result))))) + + (t/testing "applied to only some of the attributes" + (let [result (state radius [(with-token (ths/sample-shape :rect1) radius #{:r1})])] + (t/is (true? (:is-applied result))) + (t/is (true? (:is-half-applied result))) + (t/is (= "token-status-partial" (:status-icon-id result))))) + + (t/testing "applied to only some of the selected shapes" + (let [result (state radius [(with-token (ths/sample-shape :rect1) radius cto/border-radius-keys) + (ths/sample-shape :rect2)])] + (t/is (true? (:is-half-applied result))) + (t/is (= "token-status-partial" (:status-icon-id result))))) + + (t/testing "another token on the same attributes does not count" + (let [other (ctob/make-token :name "radius.lg" :type :border-radius :value "16") + result (state radius [(with-token (ths/sample-shape :rect1) other #{:r1})])] + (t/is (false? (:is-applied result))) + (t/is (= "token-status-non-applied" (:status-icon-id result))))) + + (t/testing "color tokens count all their attributes, not only the fill" + (let [color (ctob/make-token :name "brand" :type :color :value "#ff0000")] + (t/is (true? (:is-half-applied + (state color [(with-token (ths/sample-shape :rect1) color #{:fill})] + :tokens {"brand" color})))) + (t/is (false? (:is-half-applied + (state color [(with-token (ths/sample-shape :rect1) color #{:fill :stroke-color})] + :tokens {"brand" color}))))))) + +(t/deftest disabled-status + (t/testing "a token that applies to none of the selected shapes is disabled" + (t/is (true? (:is-disabled (state radius [(ths/sample-shape :circle1 :type :circle)])))) + (t/is (true? (:is-disabled (state spacing [(ths/sample-shape :rect1)]))))) + + (t/testing "one shape the token applies to is enough" + (t/is (false? (:is-disabled (state radius [(ths/sample-shape :circle1 :type :circle) + (ths/sample-shape :rect1)]))))) + + (t/testing "spacing applies to boards with a layout" + (t/is (false? (:is-disabled (state spacing [(ths/sample-shape :board1 :type :frame :layout :flex)]))))) + + (t/testing "spacing applies as margin to shapes inside a layout" + (t/is (false? (:is-disabled (state spacing [(ths/sample-shape :rect1)] + :is-selected-inside-layout true))))) + + (t/testing "an applied token is never disabled" + (t/is (false? (:is-disabled (state radius [(with-token (ths/sample-shape :circle1 :type :circle) + radius cto/border-radius-keys)])))))) + +(t/deftest error-status + (t/testing "a valid token has no errors" + (let [result (state radius [])] + (t/is (false? (:has-errors result))) + (t/is (nil? (:error-message result))))) + + (t/testing "a reference to a token outside the active sets" + (with-flags no-tokenscript-flags + #(let [token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.missing}") + result (state token [])] + (t/is (true? (:has-missing-reference result))) + (t/is (false? (:has-invalid-value result))) + (t/is (true? (:has-errors result))) + (t/is (nil? (:error-message result))))) + + (with-flags no-tokenscript-flags + #(let [token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.md}")] + (t/is (false? (:has-errors (state token [])))))) + + (with-flags tokenscript-flags + #(let [error {:error/code :error.style-dictionary/missing-reference + :message "Missing reference"} + token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.missing}") + result (state token [] :tokens {"radius.ref" (assoc token :errors [error])})] + (t/is (true? (:has-missing-reference result))) + (t/is (true? (:has-invalid-value result))) + (t/is (= "Missing reference" (:error-message result)))))) + + (t/testing "an invalid value only shows its own message with tokenscript" + (let [error {:error/code :error.style-dictionary/invalid-token-value + :message "Invalid value"} + tokens {"radius.md" (assoc radius :errors [error])}] + (with-flags no-tokenscript-flags + #(let [result (state radius [] :tokens tokens)] + (t/is (true? (:has-invalid-value result))) + (t/is (true? (:has-errors result))) + (t/is (nil? (:error-message result))))) + (with-flags tokenscript-flags + #(t/is (= "Invalid value" (:error-message (state radius [] :tokens tokens))))))) + + (t/testing "a name collision shows its message" + (with-flags no-tokenscript-flags + #(let [error {:error/code :error.token/name-collision + :error/value "radius.md" + :error/fn (fn [value] (str "Collides: " value))} + result (state radius [] :tokens {"radius.md" (assoc radius :errors [error])})] + (t/is (true? (:has-errors result))) + (t/is (= "Collides: radius.md" (:error-message result))))))) + +(defn- tooltip + [token shapes & {:keys [tokens is-viewer] :or {tokens active-theme-tokens}}] + (tooltip-info token (get tokens (:name token)) (first shapes) + (pill-state token tokens shapes false) + is-viewer)) + +(t/deftest tooltip-messages + (t/testing "errors replace the token details" + (with-flags no-tokenscript-flags + #(let [token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.missing}")] + (t/is (= {:message (tr "workspace.tokens.ref-not-valid" "radius.ref")} + (tooltip token []))))) + + (with-flags no-tokenscript-flags + #(let [error {:error/code :error.style-dictionary/invalid-token-value}] + (t/is (= {:message (tr "workspace.tokens.value-not-valid")} + (tooltip radius [] :tokens {"radius.md" (assoc radius :errors [error])})))))) + + (t/testing "a resolved error message replaces the generic one" + (let [error {:error/code :error.token/name-collision + :error/value "radius.md" + :error/fn (fn [value] (str "Collides: " value))} + tokens {"radius.md" (assoc radius :errors [error])}] + (t/is (= {:message "Collides: radius.md"} + (tooltip radius [] :tokens tokens))))) + + (t/testing "a valid token shows its value and resolved value" + (t/is (= {:name "radius.md" + :value "8" + :resolved-value "8" + :has-more-options false + :applied-to nil} + (tooltip radius [])))) + + (t/testing "the resolved value comes from the active themes" + (let [token (ctob/make-token :name "radius.ref" :type :border-radius :value "{radius.md}")] + (t/is (= "8" (:resolved-value + (tooltip token [] :tokens (assoc active-theme-tokens + "radius.ref" (assoc token :resolved-value 8))))))))) + +(t/deftest tooltip-applied-to + (t/testing "editors don't see where the token is applied" + (t/is (nil? (:applied-to (tooltip radius [(with-token (ths/sample-shape :rect1) radius cto/border-radius-keys)]))))) + + (t/testing "viewers see the attributes of the first selected shape" + (t/is (= (str "- Border Radius: " (tr "labels.all")) + (:applied-to (tooltip radius [(with-token (ths/sample-shape :rect1) radius cto/border-radius-keys)] + :is-viewer true)))) + + (t/is (= "- Border Radius: Top Left" + (:applied-to (tooltip radius [(with-token (ths/sample-shape :rect1) radius #{:r1})] + :is-viewer true))))) + + (t/testing "dimension tokens list their attributes by category" + (t/is (= "- Sizing: Width." + (:applied-to (tooltip width [(with-token (ths/sample-shape :rect1) width #{:width})] + :is-viewer true))))) + + (t/testing "nothing is listed when the token isn't applied to the first shape" + (t/is (nil? (:applied-to (tooltip radius [(ths/sample-shape :rect1) + (with-token (ths/sample-shape :rect2) + radius cto/border-radius-keys)] + :is-viewer true)))))) diff --git a/frontend/test/frontend_tests/tokens/token_tree_test.cljs b/frontend/test/frontend_tests/tokens/token_tree_test.cljs new file mode 100644 index 0000000000..2fa83904a1 --- /dev/null +++ b/frontend/test/frontend_tests/tokens/token_tree_test.cljs @@ -0,0 +1,37 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.tokens.token-tree-test + (:require + [app.common.path-names :as cpn] + [app.common.types.tokens-lib :as ctob] + [app.main.ui.workspace.tokens.management.token-tree :as wtt] + [cljs.test :as t :include-macros true])) + +(def ^:private sort-nodes @#'wtt/sort-nodes) + +(defn- tree + [& names] + (cpn/build-tree-root + (map #(ctob/make-token :name % :type :spacing :value "4") names) + ctob/token-separator)) + +(defn- sorted-names + [nodes] + (map :name (sort-nodes nodes))) + +(t/deftest sort-nodes-test + (t/testing "tokens come before groups" + (t/is (= ["zeta" "alpha" "beta"] + (sorted-names (tree "alpha.sm" "zeta" "beta.md"))))) + + (t/testing "numbers in names sort by value" + (t/is (= ["size2" "size10" "group2" "group10"] + (sorted-names (tree "size10" "group10.sm" "size2" "group2.sm"))))) + + (t/testing "a token that also has tokens below it sorts with the groups" + (t/is (= ["md" "space"] + (sorted-names (tree "space.sm" "md" "space")))))) diff --git a/frontend/test/frontend_tests/tokens/tokenscript_test.cljs b/frontend/test/frontend_tests/tokens/tokenscript_test.cljs new file mode 100644 index 0000000000..22ee6bf712 --- /dev/null +++ b/frontend/test/frontend_tests/tokens/tokenscript_test.cljs @@ -0,0 +1,193 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.tokens.tokenscript-test + (:require + [app.common.test-helpers.ids-map :as cthi] + [app.common.types.tokens-lib :as ctob] + [app.main.data.tokenscript :as ts] + [cljs.test :as t :include-macros true])) + +(defn- tokens-map + "Tokens map of a library with one set holding `tokens`." + [& tokens] + (let [lib (-> (ctob/make-tokens-lib) + (ctob/add-set (ctob/make-token-set :id (cthi/new-id! :core-set) :name "core")))] + (-> (reduce (fn [lib token] (ctob/add-token lib (cthi/id :core-set) (ctob/make-token token))) lib tokens) + (ctob/get-all-tokens-map)))) + +(t/deftest resolve-tokens-test + (t/testing "resolves references and converts units to pixels" + (let [resolved (ts/resolve-tokens + (tokens-map {:name "borderRadius.sm" :value "12px" :type :border-radius} + {:name "borderRadius.md-with-dashes" :value "{borderRadius.sm} * 2" :type :border-radius} + {:name "borderRadius.rem" :value "2rem" :type :border-radius} + {:name "opacity.half" :value "50%" :type :opacity} + {:name "borderRadius.large" :value "123456789012345" :type :border-radius} + {:name "borderRadius.largePx" :value "123456789012345px" :type :border-radius} + {:name "borderRadius.largeFn" :value "{borderRadius.sm} * 200000000" :type :border-radius}))] + (t/is (= 12 (get-in resolved ["borderRadius.sm" :resolved-value]))) + (t/is (= 24 (get-in resolved ["borderRadius.md-with-dashes" :resolved-value]))) + (t/is (= 32 (get-in resolved ["borderRadius.rem" :resolved-value]))) + (t/is (= 0.5 (get-in resolved ["opacity.half" :resolved-value]))) + (doseq [name ["borderRadius.large" "borderRadius.largePx" "borderRadius.largeFn"]] + (t/is (nil? (get-in resolved [name :resolved-value]))) + (t/is (= :error.token/number-too-large (get-in resolved [name :errors 0 :error/code]))))))) + +(t/deftest resolve-tokens-colors-test + (let [resolved (ts/resolve-tokens + (tokens-map {:name "color.red" :value "#ff0000" :type :color} + {:name "color.alias" :value "{color.red}" :type :color} + {:name "color.alpha" :value "#ff000080" :type :color} + {:name "color.named" :value "red" :type :color} + {:name "color.light" :value "lighten({color.red}, 0.2)" :type :color}))] + (t/is (= "#ff0000" (get-in resolved ["color.alias" :resolved-value]))) + (t/is (= "#ff000080" (get-in resolved ["color.alpha" :resolved-value])) "alpha is kept as hex8") + (t/is (= "#FF0000" (get-in resolved ["color.named" :resolved-value])) "css names resolve") + (t/is (string? (get-in resolved ["color.light" :resolved-value])) "color functions resolve to hex"))) + +(t/deftest resolve-tokens-errors-test + (let [resolved (ts/resolve-tokens + (tokens-map {:name "a" :value "{missing}" :type :border-radius} + {:name "b" :value "{a} + 1" :type :border-radius} + {:name "x" :value "{y}" :type :border-radius} + {:name "y" :value "{x}" :type :border-radius} + {:name "z" :value "{x} + 1" :type :border-radius} + {:name "bad" :value "1 ´ 2" :type :border-radius}))] + (t/is (= :error.style-dictionary/missing-reference (get-in resolved ["a" :errors 0 :error/code]))) + (t/is (= ["missing"] (get-in resolved ["a" :errors 0 :error/value]))) + (t/is (= :error.style-dictionary/missing-reference (get-in resolved ["b" :errors 0 :error/code])) + "dependents report the missing token") + (t/is (= ["missing"] (get-in resolved ["b" :errors 0 :error/value]))) + (t/is (= :error.token/circular-reference (get-in resolved ["x" :errors 0 :error/code]))) + (t/is (= :error.token/circular-reference (get-in resolved ["y" :errors 0 :error/code]))) + (t/is (= :error.token/circular-reference (get-in resolved ["z" :errors 0 :error/code])) + "dependents of a cycle report the cycle") + (t/is (= :error.style-dictionary/invalid-token-value (get-in resolved ["bad" :errors 0 :error/code]))))) + +(t/deftest resolve-tokens-error-codes-test + (let [resolved (ts/resolve-tokens + (tokens-map {:name "zero" :value "1 / 0" :type :number} + {:name "mod-zero" :value "mod(4, 0)" :type :number} + {:name "units" :value "1px * 2em" :type :sizing} + {:name "fn" :value "foo(1)" :type :number} + {:name "ln" :value "ln(0)" :type :number} + {:name "ref" :value "{zero} + 1" :type :number})) + error (fn [name] (select-keys (get-in resolved [name :errors 0]) [:error/code :error/value]))] + (t/is (= {:error/code :error.token/division-by-zero :error/value nil} (error "zero"))) + (t/is (= {:error/code :error.token/division-by-zero :error/value nil} (error "mod-zero"))) + (t/is (= {:error/code :error.token/incompatible-units :error/value ["px" "em"]} (error "units"))) + (t/is (= {:error/code :error.token/unknown-function :error/value "foo"} (error "fn"))) + (t/is (= {:error/code :error.token/function-argument-out-of-range :error/value "ln"} (error "ln"))) + (t/is (= {:error/code :error.style-dictionary/invalid-token-value :error/value "{zero} + 1"} (error "ref")) + "a token failing through its reference is an invalid value"))) + +;; A token cannot contain tokens: next to a token "a", the tokens "a.b" +;; and "a.b.c" get a name collision error and `{a.b}` is missing (#9584). +(t/deftest resolve-tokens-name-collision-test + (let [resolved (ts/resolve-tokens + (tokens-map {:name "a" :value "8px" :type :border-radius} + {:name "a.b" :value "12px" :type :border-radius} + {:name "a.b.c" :value "1px" :type :border-radius} + {:name "c" :value "{a} + {a.b}" :type :border-radius})) + error (fn [name] (select-keys (get-in resolved [name :errors 0]) [:error/code :error/value]))] + (t/is (= 8 (get-in resolved ["a" :resolved-value]))) + (t/is (nil? (get-in resolved ["a" :errors]))) + (t/is (nil? (get-in resolved ["a.b" :resolved-value]))) + (t/is (= {:error/code :error.token/name-collision :error/value "a.b"} (error "a.b"))) + (t/is (= {:error/code :error.token/name-collision :error/value "a.b.c"} (error "a.b.c"))) + (t/is (= {:error/code :error.style-dictionary/missing-reference :error/value ["a.b"]} (error "c")))) + (t/testing "a new token below an existing one collides" + (let [tokens (tokens-map {:name "rad1" :value "8px" :type :border-radius}) + resolved (ts/resolve-token tokens {:name "rad1.bad" :value "10" :type :border-radius})] + (t/is (= :error.token/name-collision (get-in resolved [:errors 0 :error/code])))))) + +(t/deftest resolve-tokens-composites-test + (t/testing "typography and shadow tokens resolve field by field" + (let [resolved (ts/resolve-tokens + (tokens-map {:name "size" :value "16" :type :font-size} + {:name "typo" :type :typography + :value {:font-family ["Roboto" "Arial"] + :font-size "{size} * 2" + :font-weight "700 Italic" + :line-height "48px"}} + {:name "shadow" :type :shadow + :value [{:offset-x "2" :offset-y "{size}" :blur "4" :spread "0" + :color "#000" :inset false}]}))] + (t/is (= {:font-family ["Roboto" "Arial"] :font-size 32 :font-weight "700 Italic" :line-height 1.5} + (get-in resolved ["typo" :resolved-value])) + "line heights in pixels become a ratio of the font size") + (t/is (= [{:offset-x 2 :offset-y 16 :blur 4 :spread 0 :color "#000" :inset false}] + (get-in resolved ["shadow" :resolved-value]))))) + (t/testing "font families referencing a font family token are flattened" + (let [resolved (ts/resolve-tokens + (tokens-map {:name "families" :value ["Arial" "sans-serif"] :type :font-family} + {:name "single" :value "Roboto" :type :font-family} + {:name "typo" :value {:font-family ["{families}"]} :type :typography}))] + (t/is (= ["Arial" "sans-serif"] (get-in resolved ["families" :resolved-value]))) + (t/is (= ["Roboto"] (get-in resolved ["single" :resolved-value]))) + (t/is (= ["Arial" "sans-serif"] (get-in resolved ["typo" :resolved-value :font-family]))))) + (t/testing "a typography token with an array value is invalid" + (let [resolved (ts/resolve-tokens (tokens-map {:name "typography.bad" :value ["Roboto"] :type :typography}))] + (t/is (nil? (get-in resolved ["typography.bad" :resolved-value]))) + (t/is (= :error.style-dictionary/invalid-token-value-typography + (get-in resolved ["typography.bad" :errors 0 :error/code])))))) + +(t/deftest resolve-tokens-value-validation-test + (let [resolved (ts/resolve-tokens + (tokens-map {:name "opacity.bad" :value "2" :type :opacity} + {:name "stroke.bad" :value "-1" :type :stroke-width} + {:name "case.ok" :value "uppercase" :type :text-case} + {:name "case.bad" :value "wat" :type :text-case} + {:name "weight.ok" :value "bold" :type :font-weight} + {:name "weight.bad" :value "heavyish" :type :font-weight}))] + (t/is (= :error.style-dictionary/invalid-token-value-opacity (get-in resolved ["opacity.bad" :errors 0 :error/code]))) + (t/is (= :error.style-dictionary/invalid-token-value-stroke-width (get-in resolved ["stroke.bad" :errors 0 :error/code]))) + (t/is (= "uppercase" (get-in resolved ["case.ok" :resolved-value]))) + (t/is (= :error.style-dictionary/invalid-token-value-text-case (get-in resolved ["case.bad" :errors 0 :error/code]))) + (t/is (= "bold" (get-in resolved ["weight.ok" :resolved-value]))) + (t/is (= :error.style-dictionary/invalid-token-value-font-weight (get-in resolved ["weight.bad" :errors 0 :error/code]))))) + +;; The interpreter validates the kind of value of each token type: `1foo` +;; and `1 2` are lists, not numbers. +(t/deftest resolve-tokens-type-validation-test + (let [resolved (ts/resolve-tokens + (tokens-map {:name "size.glued" :value "1foo" :type :dimensions} + {:name "size.list" :value "1 2" :type :sizing} + {:name "size.ok" :value "1rem" :type :sizing} + {:name "number.glued" :value "1foo" :type :number} + {:name "number.unit" :value "10px" :type :number} + {:name "number.ok" :value "1.5" :type :number} + {:name "opacity.percent" :value "150%" :type :opacity} + {:name "typography.bad" :value {:font-size "1foo" :line-height "1.5"} :type :typography} + {:name "shadow.bad" :value "8" :type :shadow})) + code (fn [name] (get-in resolved [name :errors 0 :error/code]))] + (doseq [name ["size.glued" "size.list" "number.glued"]] + (t/is (nil? (get-in resolved [name :resolved-value]))) + (t/is (= :error.style-dictionary/invalid-token-value (code name)))) + (t/is (= "1foo" (get-in resolved ["size.glued" :errors 0 :error/value]))) + (t/is (= :error.style-dictionary/value-with-units (code "number.unit"))) + (t/is (= :error.style-dictionary/invalid-token-value-opacity (code "opacity.percent"))) + (t/is (= :error.style-dictionary/invalid-token-value-typography (code "typography.bad"))) + (t/is (= :error.style-dictionary/invalid-token-value-shadow (code "shadow.bad"))) + (t/is (= 16 (get-in resolved ["size.ok" :resolved-value]))) + (t/is (= 1.5 (get-in resolved ["number.ok" :resolved-value]))))) + +;; A token with a `nil` value (e.g. a composite typography token saved via +;; the workspace form with no fields filled in) is tagged with an empty +;; input error and does not affect the other tokens. +(t/deftest resolve-tokens-nil-value-test + (let [resolved (ts/resolve-tokens + (tokens-map {:name "typography.empty" :value nil :type :typography} + {:name "borderRadius.sm" :value "12px" :type :border-radius}))] + (t/is (nil? (get-in resolved ["typography.empty" :resolved-value]))) + (t/is (= :error.token/empty-input (get-in resolved ["typography.empty" :errors 0 :error/code]))) + (t/is (= 12 (get-in resolved ["borderRadius.sm" :resolved-value]))))) + +(t/deftest resolve-token-test + (let [tokens (tokens-map {:name "a" :value "8px" :type :border-radius}) + resolved (ts/resolve-token tokens {:name "b" :value "{a} * 2" :type :border-radius})] + (t/is (= 16 (:resolved-value resolved))))) diff --git a/frontend/test/frontend_tests/tokens/typography_form_test.cljs b/frontend/test/frontend_tests/tokens/typography_form_test.cljs new file mode 100644 index 0000000000..64cc06be21 --- /dev/null +++ b/frontend/test/frontend_tests/tokens/typography_form_test.cljs @@ -0,0 +1,99 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.tokens.typography-form-test + (:require + [app.common.schema :as sm] + [app.common.schema.messages :as csm] + [app.common.types.tokens-lib :as ctob] + [app.main.data.workspace.tokens.errors :as wte] + [app.main.ui.workspace.tokens.management.forms.typography :as wtty] + [app.util.i18n :refer [tr]] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true])) + +(def ^:private make-schema @#'wtty/make-schema) +(def ^:private form-value @#'wtty/composite-value->form-value) +(def ^:private check-self-reference @#'wtty/check-typography-token-self-reference) +(def ^:private validate-token @#'wtty/validate-typography-token) + +(def ^:private token + (ctob/make-token :name "type.body" :type :typography :value {:font-size "16"})) + +(defn- schema + [tab] + (make-schema (ctob/get-token-path token) + (ctob/tokens-tree {(:name token) token}) + tab)) + +(defn- form-data + [value] + {:name (:name token) :description "" :value value}) + +(defn- errors + [tab data] + (csm/collect-schema-errors (schema tab) [] {:data data})) + +(defn- valid? + [tab data] + (sm/validate (schema tab) data)) + +(t/deftest composite-tab + (t/testing "one field is enough" + (t/is (valid? :composite (form-data {:font-size "16"})))) + + (t/testing "at least one field must be set" + (t/is (not (valid? :composite (form-data {})))) + (t/is (not (valid? :composite (form-data {:font-size "" :font-weight ""}))))) + + (t/testing "a line height needs a font size" + (t/is (= (tr "errors.tokens.composite-line-height-needs-font-size") + (get-in (errors :composite (form-data {:line-height "1.5"})) + [:value :line-height :message]))) + (t/is (valid? :composite (form-data {:line-height "1.5" :font-size "16"}))))) + +(t/deftest reference-tab + (t/testing "a reference is required" + (t/is (not (valid? :reference (form-data {:reference ""})))) + (t/is (valid? :reference (form-data {:reference "{type.heading}"})))) + + (t/testing "the token can't reference itself" + (t/is (= (tr "errors.tokens.self-reference") + (get-in (errors :reference (form-data {:reference "{type.body}"})) + [:value :reference :message]))))) + +(t/deftest composite-self-reference + (let [self-reference (wte/get-error-code :error.token/direct-self-reference)] + (t/testing "the error names the field that references the token" + (t/is (= (assoc self-reference :typography-key :font-size) + (check-self-reference {:name "type.body" :value {:font-size "{type.body}"}})))) + + (t/testing "each font family is checked" + (t/is (= (assoc self-reference :typography-key :font-family) + (check-self-reference {:name "type.body" :value {:font-family ["Inter" "{type.body}"]}})))) + + (t/testing "references to other tokens pass" + (t/is (nil? (check-self-reference {:name "type.body" :value {:font-size "{font.size.md}"}})))))) + +(t/deftest submit-without-fields + (t/async + done + (->> (validate-token {:token-name "type.body" :token-value nil :tokens {}}) + (rx/subs! (fn [_] + (t/is false "a token without fields must be rejected, not saved") + (done)) + (fn [error] + (t/is (= {:errors [(wte/get-error-code :error.token/empty-input)]} error)) + (done)))))) + +(t/deftest initial-form-value + (t/testing "font families show as one comma separated field" + (t/is (= {:font-family "Inter, Arial" :font-size "16"} + (form-value {:font-family ["Inter" "Arial"] :font-size "16"})))) + + (t/testing "only the typography fields are kept" + (t/is (= {:line-height "1.5"} + (form-value {:line-height "1.5" :paragraph-spacing "4"}))))) diff --git a/frontend/test/frontend_tests/ui/dashboard_import_linking_test.cljs b/frontend/test/frontend_tests/ui/dashboard_import_linking_test.cljs new file mode 100644 index 0000000000..84d8585719 --- /dev/null +++ b/frontend/test/frontend_tests/ui/dashboard_import_linking_test.cljs @@ -0,0 +1,128 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.ui.dashboard-import-linking-test + "The links the import wizard creates for a file of the package whose + library the auto-link could not resolve. The resolution map comes from the + job result, so the type of its keys is not something the wizard can rely + on: the file id of a link must come from the entry and never from the key + of the map." + (:require + [app.common.uuid :as uuid] + [app.main.ui.dashboard.import :as import] + [cljs.test :as t :include-macros true])) + +(def ^:private pending-library-links + @#'import/pending-library-links) + +(defn- pending-entry + "One library of the package the user has to resolve, with a candidate." + [library-id] + {:id library-id + :name "Tokens" + :candidates [{:id (uuid/next) + :name "Tokens" + :project-id (uuid/next) + :project-name "Design system"}]}) + +(defn- file-resolution + "One file of a resolution, with a single library pending resolution." + [file-id library-id] + {:id file-id + :name "card.penpot" + :done [] + :pending [(pending-entry library-id)]}) + +(t/deftest the-file-id-of-a-link-comes-from-the-entry-and-not-from-the-key + (let [file-id (uuid/next) + old-lib (uuid/next) + new-lib (uuid/next) + ;; the shape a result without a declared schema has: the key of the + ;; map is a keyword and the entry carries the same id as text + resolution {(keyword (str file-id)) (file-resolution (str file-id) (str old-lib))} + selection {(str file-id) {(str old-lib) (str new-lib)}}] + + (t/is (= [{:file-id (str file-id) :library-id (str new-lib)}] + (pending-library-links resolution selection))))) + +(t/deftest the-links-are-the-same-with-a-typed-resolution + (let [file-id (uuid/next) + old-lib (uuid/next) + new-lib (uuid/next) + resolution {file-id (file-resolution file-id old-lib)} + selection {file-id {old-lib new-lib}}] + + (t/is (= [{:file-id file-id :library-id new-lib}] + (pending-library-links resolution selection))))) + +(t/deftest a-file-with-several-pending-libraries-creates-one-link-each + (let [file-id (uuid/next) + lib-a (uuid/next) + lib-b (uuid/next) + new-a (uuid/next) + new-b (uuid/next) + resolution {file-id {:id file-id + :name "card.penpot" + :done [] + :pending [(pending-entry lib-a) + (pending-entry lib-b)]}} + selection {file-id {lib-a new-a + lib-b new-b}}] + + (t/testing "both entries the user resolved become a link" + (t/is (= #{{:file-id file-id :library-id new-a} + {:file-id file-id :library-id new-b}} + (set (pending-library-links resolution selection))))))) + +(t/deftest a-partial-selection-creates-only-the-chosen-link + (let [file-id (uuid/next) + lib-a (uuid/next) + lib-b (uuid/next) + new-a (uuid/next) + resolution {file-id {:id file-id + :name "card.penpot" + :done [] + :pending [(pending-entry lib-a) + (pending-entry lib-b)]}} + selection {file-id {lib-a new-a}}] + + (t/testing "the entry with no candidate chosen emits nothing" + (t/is (= [{:file-id file-id :library-id new-a}] + (pending-library-links resolution selection)))))) + +(t/deftest a-done-entry-creates-no-link + (let [file-id (uuid/next) + library (uuid/next)] + + (t/testing "a file whose libraries were auto-linked has nothing to emit" + (t/is (= [] (pending-library-links + {file-id {:id file-id + :name "card.penpot" + :done [{:id library + :name "Tokens" + :linked-to library}] + :pending []}} + {file-id {library (uuid/next)}})))))) + +(t/deftest pending-entries-without-a-selection-create-no-link + (let [file-id (uuid/next) + old-lib (uuid/next)] + + (t/testing "an entry the user skipped creates nothing" + (t/is (= [] (pending-library-links + {file-id (file-resolution file-id old-lib)} + {file-id {}})))) + + (t/testing "a file with nothing pending creates nothing" + (t/is (= [] (pending-library-links + {file-id {:id file-id + :name "card.penpot" + :done [] + :pending []}} + {file-id {old-lib (uuid/next)}})))) + + (t/testing "an empty resolution creates nothing" + (t/is (= [] (pending-library-links {} {})))))) diff --git a/frontend/test/frontend_tests/ui/file_restored_modal_test.cljs b/frontend/test/frontend_tests/ui/file_restored_modal_test.cljs new file mode 100644 index 0000000000..169baa4573 --- /dev/null +++ b/frontend/test/frontend_tests/ui/file_restored_modal_test.cljs @@ -0,0 +1,34 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.ui.file-restored-modal-test + (:require + [app.common.time :as ct] + [app.main.ui.workspace.file-restored-modal :as frm] + [app.util.i18n :as i18n] + [cljs.test :as t :include-macros true])) + +(defn- message + "The translation key and arguments of the restore message." + [fullname label created-at] + (with-redefs [i18n/tr (fn ([key] [key]) ([key & args] (into [key] args)))] + (#'frm/restored-message fullname label created-at))) + +(t/deftest the-message-names-the-version-by-its-label-or-its-date + (let [created-at (ct/now) + date (ct/format-inst created-at "PPp")] + (t/is (= ["workspace.file-restored.message-label" "Ada" "Hand-off v2"] + (message "Ada" "Hand-off v2" created-at))) + (t/is (= ["workspace.file-restored.message-label-anonymous" "Hand-off v2"] + (message nil "Hand-off v2" created-at))) + (t/is (= ["workspace.file-restored.message-date" "Ada" date] + (message "Ada" nil created-at))) + (t/is (= ["workspace.file-restored.message-date-anonymous" date] + (message nil nil created-at))) + (t/is (= ["workspace.file-restored.message" "Ada"] + (message "Ada" nil nil))) + (t/is (= ["workspace.file-restored.message-anonymous"] + (message nil nil nil))))) diff --git a/frontend/test/frontend_tests/ui/inspect_geometry_test.cljs b/frontend/test/frontend_tests/ui/inspect_geometry_test.cljs index 3b9c5879d2..5fc4285da9 100644 --- a/frontend/test/frontend_tests/ui/inspect_geometry_test.cljs +++ b/frontend/test/frontend_tests/ui/inspect_geometry_test.cljs @@ -112,3 +112,17 @@ (let [markup (render-panel panel shapes)] (t/is (str/includes? markup "Border width")) (t/is (str/includes? markup "3px"))))))) + +;; Scenario: the selected shape points to a parent missing from the page, as +;; in a damaged file or while a rejected save is on screen. Proves: the +;; geometry panels and the code view show the size and leave out the +;; position, which needs the parent, instead of crashing the workspace. +(t/deftest geometry-without-a-parent-leaves-out-the-position + (let [orphan (assoc (shape :rect per-side-stroke) :parent-id (uuid/next) :frame-id (uuid/next))] + (doseq [panel [geometry/geometry-panel* styles-geometry/geometry-panel*]] + (let [markup (render-panel panel [orphan])] + (t/is (str/includes? markup "120px")) + (t/is (not (str/includes? markup "Left"))))) + (let [code (css/get-shape-properties-css {(:id orphan) orphan} orphan css/shape-css-properties)] + (t/is (str/includes? code "width: 120px;")) + (t/is (not (str/includes? code "left:")))))) diff --git a/frontend/test/frontend_tests/ui/jobs_progress_test.cljs b/frontend/test/frontend_tests/ui/jobs_progress_test.cljs new file mode 100644 index 0000000000..55937e561e --- /dev/null +++ b/frontend/test/frontend_tests/ui/jobs_progress_test.cljs @@ -0,0 +1,54 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.ui.jobs-progress-test + "How a job milestone is written: the stage and its counter." + (:require + [app.main.ui.jobs.progress :as jp] + [app.util.i18n :as i18n] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.mock :as mock])) + +(def ^:private stub-tr + (mock/stub (fn [k] (str "TR:" k)))) + +(t/deftest a-counter-milestone-writes-its-stage-and-counter + (with-redefs [i18n/tr stub-tr] + (t/is (= "TR:jobs.progress.stage.pages 3/8" + (jp/milestone-text {:stage :pages :counters {:pages {:current 3 :total 8}}}))) + (t/testing "a counter with no total writes the bare current" + (t/is (= "TR:jobs.progress.stage.pages 3" + (jp/milestone-text {:stage :pages :counters {:pages {:current 3}}})))))) + +(t/deftest the-upload-milestone-writes-the-percentage-of-the-file + (with-redefs [i18n/tr stub-tr] + (t/testing "the chunks sent become a whole percentage" + (t/is (= "TR:jobs.progress.stage.upload (33%)" + (jp/milestone-text {:stage :upload :counters {:upload {:current 1 :total 3}}}))) + (t/is (= "TR:jobs.progress.stage.upload (100%)" + (jp/milestone-text {:stage :upload :counters {:upload {:current 9 :total 9}}})))) + (t/testing "an upload with no total keeps the bare label" + (t/is (= "TR:jobs.progress.stage.upload" + (jp/milestone-text {:stage :upload :counters {:upload {:current 1}}})))))) + +(t/deftest the-milestone-in-course-is-the-one-of-the-files-that-report-it + (with-redefs [i18n/tr stub-tr] + (t/testing "the files of a container share their milestone, so one is enough" + (t/is (= "TR:jobs.progress.stage.pages 4/5" + (jp/current-milestone-text + [{:status :import-queued} + {:progress {:stage :pages :counters {:pages {:current 4 :total 5}}}} + {:progress {:stage :pages :counters {:pages {:current 4 :total 5}}}}])))) + + (t/testing "a file with nothing to report says nothing" + (t/is (nil? (jp/current-milestone-text [{:status :import-ready} + {:status :import-queued}]))) + (t/is (nil? (jp/current-milestone-text [])))))) + +(t/deftest a-milestone-without-a-stage-writes-nothing + (with-redefs [i18n/tr stub-tr] + (t/is (nil? (jp/milestone-text nil))) + (t/is (nil? (jp/milestone-text {}))))) diff --git a/frontend/test/frontend_tests/ui/layout_container_multiple_test.cljs b/frontend/test/frontend_tests/ui/layout_container_multiple_test.cljs index 10b64d95b1..77bc263fa3 100644 --- a/frontend/test/frontend_tests/ui/layout_container_multiple_test.cljs +++ b/frontend/test/frontend_tests/ui/layout_container_multiple_test.cljs @@ -44,3 +44,41 @@ (t/is (= {:p1 10 :p2 20 :p3 10 :p4 20} (:layout-padding values))) (t/is (= :multiple (:layout-padding-type values))))) + +(defn- fill-tokens + [shapes] + (let [[_ids _values tokens] (multiple/get-attrs* shapes {} :fill)] + tokens)) + +(defn- text-shape + [id applied-tokens] + (cond-> {:id id :type :text :fills []} + applied-tokens (assoc :applied-tokens applied-tokens))) + +(defn- rect-shape + [id applied-tokens] + (cond-> {:id id :type :rect :fills []} + applied-tokens (assoc :applied-tokens applied-tokens))) + +(t/deftest multiple-text-selection-same-fill-token + (t/is (= {:fill "color.primary"} + (fill-tokens [(text-shape :text-1 {:fill "color.primary"}) + (text-shape :text-2 {:fill "color.primary"})])))) + +(t/deftest multiple-text-selection-different-fill-tokens + (t/is (= {:fill :multiple} + (fill-tokens [(text-shape :text-1 {:fill "color.primary"}) + (text-shape :text-2 {:fill "color.secondary"})])))) + +(t/deftest multiple-text-selection-token-and-no-token + (t/is (= {:fill :multiple} + (fill-tokens [(text-shape :text-1 {:fill "color.primary"}) + (text-shape :text-2 nil)])))) + +(t/deftest multiple-selection-rect-and-text-fill-tokens + (t/is (= {:fill "color.primary"} + (fill-tokens [(rect-shape :rect-1 {:fill "color.primary"}) + (text-shape :text-1 {:fill "color.primary"})]))) + (t/is (= {:fill :multiple} + (fill-tokens [(rect-shape :rect-1 {:fill "color.primary"}) + (text-shape :text-1 {:fill "color.secondary"})])))) diff --git a/frontend/test/frontend_tests/ui/mcp_authorize_test.cljs b/frontend/test/frontend_tests/ui/mcp_authorize_test.cljs new file mode 100644 index 0000000000..b91e3cbef2 --- /dev/null +++ b/frontend/test/frontend_tests/ui/mcp_authorize_test.cljs @@ -0,0 +1,88 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.ui.mcp-authorize-test + (:require + [app.common.uri :as uri] + [app.main.router :as rt] + [app.main.store :as st] + [app.main.ui.mcp-authorize :as oauth] + [app.util.http :as http] + [app.util.storage :as storage] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as a] + [frontend-tests.helpers.mock :as mock])) + +(t/deftest ^:async approval-uses-form-encoding-and-preserves-response-keys + (let [sent (atom nil)] + (await + (mock/with-mocks* + {http/send! (mock/stub + (fn [request] + (reset! sent request) + (->> (rx/of {:status 200 :body #js {"redirect-uri" "https://client.example/cb?code=abc"}}) + (rx/observe-on :async))))} + (let [response (await (a/->promise + (#'oauth/oauth-request :post "decision" + {:request "opaque-handle" :csrf "a+b&c" :decision "approve"})))] + (t/is (= :post (:method @sent))) + (t/is (= "application/x-www-form-urlencoded" (get-in @sent [:headers "content-type"]))) + (t/is (= {:request "opaque-handle" :csrf "a+b&c" :decision "approve"} + (uri/query-string->map (:body @sent)))) + (t/is (= "https://client.example/cb?code=abc" (get-in response [:body :redirect-uri]))) + (t/is (nil? (get-in response [:body :access_token])))))))) + +(t/deftest ^:async approval-request-preserves-login-required-response + (await + (mock/with-mocks* + {http/send! (mock/stub + (fn [_] + (->> (rx/of {:status 401 :body #js {:error "login_required"}}) + (rx/observe-on :async))))} + (let [response (await (a/->promise (#'oauth/oauth-request :get "request" {:request "opaque-handle"})))] + (t/is (= 401 (:status response))) + (t/is (= "login_required" (get-in response [:body :error]))))))) + +(t/deftest ^:async login-keeps-the-approval-return-url-without-navigation + (let [href "https://penpot.example/?screen=mcp-authorize&request=opaque-handle" + saved (:login-redirect storage/session) + emitted (atom [])] + (try + (await + (mock/with-mocks* + {rt/get-current-href (mock/stub (fn [] href)) + st/emit! (mock/stub (fn [event] (swap! emitted conj event)))} + (let [params (#'oauth/prepare-login!)] + (await (a/settle)) + (t/is (= href (:callback-url params))) + (t/is (= href (:login-redirect storage/session))) + (t/is (empty? @emitted))))) + (finally + (binding [storage/*sync* true] + (if saved + (swap! storage/session assoc :login-redirect saved) + (swap! storage/session dissoc :login-redirect))))))) + +(t/deftest ^:async cancelling-login-clears-only-this-attempts-return-url + (let [href "https://penpot.example/?screen=mcp-authorize&request=opaque-handle" + saved (:login-redirect storage/session)] + (try + (await + (mock/with-mocks* + {rt/get-current-href (mock/stub (fn [] href))} + (#'oauth/prepare-login!) + (#'oauth/cancel-login!) + (t/is (nil? (:login-redirect storage/session))) + (binding [storage/*sync* true] + (swap! storage/session assoc :login-redirect "https://penpot.example/other")) + (#'oauth/cancel-login!) + (t/is (= "https://penpot.example/other" (:login-redirect storage/session))))) + (finally + (binding [storage/*sync* true] + (if saved + (swap! storage/session assoc :login-redirect saved) + (swap! storage/session dissoc :login-redirect))))))) diff --git a/frontend/test/frontend_tests/ui/sortable_auto_scroll_test.cljs b/frontend/test/frontend_tests/ui/sortable_auto_scroll_test.cljs new file mode 100644 index 0000000000..fb4d8348ac --- /dev/null +++ b/frontend/test/frontend_tests/ui/sortable_auto_scroll_test.cljs @@ -0,0 +1,178 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.ui.sortable-auto-scroll-test + (:require + [app.common.math :as mth] + [app.main.ui.hooks :as hooks] + [app.util.timers :as ts] + [cljs.test :as t :include-macros true])) + +(def ^:private auto-scroll-speed @#'hooks/auto-scroll-speed) +(def ^:private auto-scroll-state @#'hooks/auto-scroll-state) +(def ^:private start-auto-scroll! @#'hooks/start-auto-scroll!) +(def ^:private update-auto-scroll! @#'hooks/update-auto-scroll!) +(def ^:private end-auto-scroll! @#'hooks/end-auto-scroll!) + +(t/use-fixtures :each {:before #(reset! auto-scroll-state nil) + :after #(reset! auto-scroll-state nil)}) + +;; A 400px tall list, with the 56px edge zones at [0, 56] and [344, 400]. +(def ^:private rect {:top 0 :bottom 400}) + +(t/deftest auto-scroll-speed-test + (t/testing "does not scroll when the pointer is away from the edges" + (t/is (= 0 (auto-scroll-speed rect 200))) + (t/is (= 0 (auto-scroll-speed rect 56))) + (t/is (= 0 (auto-scroll-speed rect 344)))) + + (t/testing "does not scroll when the pointer is outside the container" + (t/is (= 0 (auto-scroll-speed rect -10))) + (t/is (= 0 (auto-scroll-speed rect 410)))) + + (t/testing "scrolls upwards near the top edge and downwards near the bottom" + (t/is (neg? (auto-scroll-speed rect 10))) + (t/is (pos? (auto-scroll-speed rect 390)))) + + (t/testing "scrolls faster the closer the pointer gets to the edge" + (t/is (< (auto-scroll-speed rect 5) (auto-scroll-speed rect 50) 0)) + (t/is (< 0 (auto-scroll-speed rect 350) (auto-scroll-speed rect 395))))) + +(t/deftest auto-scroll-speed-short-list-test + ;; In a 100px list each zone is capped at 25px, a quarter of the height. + (let [rect {:top 0 :bottom 100}] + (t/testing "the middle half of the list never scrolls" + (t/is (= 0 (auto-scroll-speed rect 25))) + (t/is (= 0 (auto-scroll-speed rect 50))) + (t/is (= 0 (auto-scroll-speed rect 75)))) + + (t/testing "the capped zones still scroll" + (t/is (neg? (auto-scroll-speed rect 10))) + (t/is (pos? (auto-scroll-speed rect 90)))))) + +(defn- scroll-container + "A fake 400px tall scroll container that records the scrolled pixels." + [scrolled] + #js {:contains (fn [_] true) + :getBoundingClientRect (fn [] #js {:top 0 :bottom 400}) + :scrollBy (fn [_ y] (swap! scrolled conj y))}) + +(defn- start-drag! + [element] + (reset! auto-scroll-state {:element element})) + +(t/deftest start-auto-scroll-without-container-test + (start-auto-scroll! #js {:parentElement nil}) + (t/is (= {:element nil} @auto-scroll-state))) + +(t/deftest start-auto-scroll-stops-previous-loop-test + ;; The previous drag ended without a dragend, so its loop is still running. + (let [cancelled (atom [])] + (with-redefs [ts/cancel-af! (fn [frame] (swap! cancelled conj frame))] + (reset! auto-scroll-state {:element #js {} :frame 9}) + (start-auto-scroll! #js {:parentElement nil}) + (t/is (= [9] @cancelled)) + (t/is (= {:element nil} @auto-scroll-state))))) + +(t/deftest update-auto-scroll-starts-one-loop-test + (let [scheduled (atom []) + element (scroll-container (atom []))] + (with-redefs [ts/raf (fn [f] (swap! scheduled conj f) (count @scheduled))] + (start-drag! element) + (update-auto-scroll! #js {} 10) + (update-auto-scroll! #js {} 5) + (update-auto-scroll! #js {} 1) + (t/is (= 1 (count @scheduled)) "dragover events reuse the running loop") + (t/is (= 1 (:frame @auto-scroll-state))) + (t/is (= (auto-scroll-speed rect 1) (:speed @auto-scroll-state)) + "the loop uses the latest pointer position")))) + +(t/deftest update-auto-scroll-away-from-edges-does-not-start-test + (let [scheduled (atom []) + element (scroll-container (atom []))] + (with-redefs [ts/raf (fn [f] (swap! scheduled conj f) 1)] + (start-drag! element) + (update-auto-scroll! #js {} 200) + (t/is (empty? @scheduled))))) + +(t/deftest auto-scroll-tick-scrolls-and-continues-test + (let [scheduled (atom []) + scrolled (atom []) + element (scroll-container scrolled)] + (with-redefs [ts/raf (fn [f] (swap! scheduled conj f) (count @scheduled))] + (start-drag! element) + (update-auto-scroll! #js {} 0) + ((last @scheduled) (js/performance.now)) + (t/is (= [(auto-scroll-speed rect 0)] @scrolled)) + (t/is (= 2 (count @scheduled)) "the next frame is scheduled") + (t/is (= 2 (:frame @auto-scroll-state)))))) + +(t/deftest auto-scroll-stops-when-speed-goes-back-to-zero-test + (let [scheduled (atom []) + cancelled (atom []) + element (scroll-container (atom []))] + (with-redefs [ts/raf (fn [f] (swap! scheduled conj f) 7) + ts/cancel-af! (fn [frame] (swap! cancelled conj frame))] + (start-drag! element) + (update-auto-scroll! #js {} 10) + (update-auto-scroll! #js {} 200) + (t/is (= [7] @cancelled) "the pending frame is cancelled") + (t/is (nil? (:frame @auto-scroll-state))) + (t/is (identical? element (:element @auto-scroll-state)) + "the container is kept for the rest of the drag") + + (update-auto-scroll! #js {} 10) + (t/is (= 2 (count @scheduled)) "it starts again near the edge")))) + +(t/deftest auto-scroll-stops-on-drag-end-test + (let [cancelled (atom []) + element (scroll-container (atom []))] + (with-redefs [ts/raf (fn [_] 3) + ts/cancel-af! (fn [frame] (swap! cancelled conj frame))] + (start-drag! element) + (update-auto-scroll! #js {} 10) + (end-auto-scroll!) + (t/is (= [3] @cancelled)) + (t/is (nil? @auto-scroll-state))))) + +(t/deftest auto-scroll-stale-guard-test + (let [scheduled (atom []) + scrolled (atom []) + element (scroll-container scrolled)] + (with-redefs [ts/raf (fn [f] (swap! scheduled conj f) 1) + ts/cancel-af! (fn [_])] + (start-drag! element) + (update-auto-scroll! #js {} 10) + (let [updated-at (:updated-at @auto-scroll-state)] + ;; No dragover for more than 500ms, as when a dragend is lost. + ((last @scheduled) (+ updated-at 501))) + (t/is (empty? @scrolled) "it does not scroll on a stale frame") + (t/is (= 1 (count @scheduled)) "no further frames are scheduled") + (t/is (nil? @auto-scroll-state))))) + +(defn- scroll-for-two-60hz-frames + "Runs the loop for the time of two 60 Hz frames, at `frames` frames per + 60 Hz frame, and returns the scrolled pixels. The first frame has no + previous one to measure from, so it counts as a full 60 Hz frame." + [frames] + (let [scheduled (atom []) + scrolled (atom []) + frame-ms (/ 1000 60 frames)] + (with-redefs [ts/raf (fn [f] (swap! scheduled conj f) 1)] + (reset! auto-scroll-state nil) + (start-drag! (scroll-container scrolled)) + (update-auto-scroll! #js {} 0) + (let [start (:updated-at @auto-scroll-state)] + (dotimes [i (inc frames)] + ((last @scheduled) (+ start (* i frame-ms)))))) + (reduce + @scrolled))) + +(t/deftest auto-scroll-speed-does-not-depend-on-refresh-rate-test + (let [at-60hz (scroll-for-two-60hz-frames 1) + at-120hz (scroll-for-two-60hz-frames 2)] + (t/is (neg? at-60hz)) + (t/is (<= (mth/abs (- at-120hz at-60hz)) 1) + "a 120 Hz screen scrolls the same distance in the same time"))) diff --git a/frontend/test/frontend_tests/ui/text_attrs_multiple_test.cljs b/frontend/test/frontend_tests/ui/text_attrs_multiple_test.cljs new file mode 100644 index 0000000000..4bf8018be6 --- /dev/null +++ b/frontend/test/frontend_tests/ui/text_attrs_multiple_test.cljs @@ -0,0 +1,40 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.ui.text-attrs-multiple-test + (:require + [app.main.ui.workspace.sidebar.options.shapes.multiple :as multiple] + [cljs.test :as t :include-macros true])) + +(defn- content + [font-family] + {:type "root" + :children [{:type "paragraph-set" + :children [{:type "paragraph" + :children [{:text "a" :font-family font-family}]}]}]}) + +(def ^:private inter (content "Inter")) + +(def ^:private objects + {:board-1 {:id :board-1 :type :frame :shapes [:inner :text-1]} + :inner {:id :inner :type :frame :shapes [:text-2]} + :board-2 {:id :board-2 :type :frame :shapes [:text-3 :rect]} + :text-1 {:id :text-1 :type :text :content inter :applied-tokens {:typography "body"}} + :text-2 {:id :text-2 :type :text :content inter :applied-tokens {:typography "body"}} + :text-3 {:id :text-3 :type :text :content (content "Roboto") :applied-tokens {:typography "body"}} + :rect {:id :rect :type :rect}}) + +(defn- text-attrs + [labels objects] + (multiple/get-attrs* (map objects labels) objects :text)) + +(t/deftest boards-read-nested-text-values + (let [[ids values tokens] (text-attrs [:board-1] objects)] + (t/is (= [:text-2 :text-1] ids)) + (t/is (= "Inter" (:font-family values))) + (t/is (= "body" (:typography tokens)))) + (let [[_ values] (text-attrs [:board-1 :board-2] objects)] + (t/is (= :multiple (:font-family values))))) diff --git a/frontend/test/frontend_tests/ui/viewport_top_bar_test.cljs b/frontend/test/frontend_tests/ui/viewport_top_bar_test.cljs new file mode 100644 index 0000000000..b753122243 --- /dev/null +++ b/frontend/test/frontend_tests/ui/viewport_top_bar_test.cljs @@ -0,0 +1,58 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.ui.viewport-top-bar-test + (:require + ["react-dom/server" :as rds] + [app.main.refs :as refs] + [app.main.ui.workspace.viewport.top-bar :as top-bar] + [app.util.i18n :as i18n] + [cljs.test :as t :include-macros true] + [cuerdas.core :as str] + [frontend-tests.helpers.mock :as mock] + [okulary.core :as l] + [rumext.v2 :as mf])) + +(defn- render-bar + [state] + (let [state (l/atom state)] + (with-redefs [refs/persistence (l/derived :persistence state) + refs/persistence-lock (l/derived :lock state) + refs/render-context-lost? (l/derived :context-lost state) + i18n/tr (mock/stub (fn [key] key))] + (rds/renderToStaticMarkup (mf/element top-bar/view-only-bar* #js {}))))) + +(t/deftest a-terminal-save-failure-replaces-the-view-mode-message-and-exit + (let [markup (render-bar {:lock {:read-only? false} + :persistence {:status :error :error {:type :server-error}}})] + (t/is (str/includes? markup "workspace.save-failure.stopped")) + (t/is (str/includes? markup "role=\"status\"")) + (t/is (not (str/includes? markup "workspace.top-bar.view-only"))) + (t/is (not (str/includes? markup ""))] + (t/is (= "" (svg.wasm/get-svg-markup [id]))) + (t/is (= [id 1] @called))))) + +(t/deftest get-svg-markup-multi-shapes-one-document + (let [ids [(uuid/next) (uuid/next)] + called (atom nil)] + (with-redefs [wasm.api/initialized? (constantly true) + wasm.api/render-shapes-svg + (fn [shape-ids scale] + (reset! called [shape-ids scale]) + (utf8-bytes ""))] + (t/is (= "" (svg.wasm/get-svg-markup ids))) + (t/is (= [ids 1] @called)) + (t/is (= 1 (count (re-seq #"fragment + [html] + (let [window (.-window (JSDOM. "")) + parser (new (.-DOMParser window))] + (clipboard/document->fragment (.parseFromString parser html "text/html")))) + +(defn- paragraph [& runs] + {:attrs {} :children (vec runs)}) + +;; HTML runs carry every emphasis attr, so the source decides it. +(def ^:private no-emphasis + {:font-weight "400" :font-style "normal" :text-decoration "none" :text-transform "none"}) + +(defn- run + ([text] (run text {})) + ([text attrs] {:text text :attrs (merge no-emphasis attrs)})) + +(defn- plain-run + [text] + {:text text :attrs {}}) + +(def ^:private bold {:font-weight "700"}) +(def ^:private italic {:font-style "italic"}) + +(def ^:private google-docs-html + (str "" + "" + "

" + "Hello " + "bold" + " and italic" + "


" + "

" + "Second" + "

")) + +(def ^:private word-html + (str "" + "" + "" + "

Plain bold

\n" + "

 

\n" + "

italic

" + "")) + +(def ^:private web-page-html + (str "" + "

Title

" + "

" + "Some strong and " + "a link.

" + "
    \n
  • One
  • \n
  • Two
  • \n
")) + +(t/deftest google-docs-keeps-emphasis + (t/testing "the font-weight:normal wrapper does not make the whole paste bold" + (t/is (= [(paragraph (run "Hello ") (run "bold" bold) (run " and italic" italic)) + (paragraph) + (paragraph (run "Second"))] + (html->fragment google-docs-html))))) + +(t/deftest word-keeps-emphasis + (t/testing "Word markup and its empty   paragraphs map to plain paragraphs" + (t/is (= [(paragraph (run "Plain ") (run "bold" bold)) + (paragraph) + (paragraph (run "italic" italic))] + (html->fragment word-html))))) + +(t/deftest web-page-drops-fonts-colors-links-and-list-markers + (t/testing "headings are bold, links lose their underline, list items become paragraphs" + (t/is (= [(paragraph (run "Title" bold)) + (paragraph (run "Some ") (run "strong" bold) (run " and a link.")) + (paragraph (run "One")) + (paragraph (run "Two"))] + (html->fragment web-page-html))))) + +(t/deftest font-weight-threshold + (t/testing "600 and above is bold" + (t/is (= [(paragraph (run "semi" bold))] + (html->fragment "semi")))) + + (t/testing "light weights are ignored" + (t/is (= [(paragraph (run "light"))] + (html->fragment "light"))))) + +(t/deftest decoration-and-transform + (t/testing "underline wins over line-through, as a span holds one decoration" + (t/is (= [(paragraph (run "both" {:text-decoration "underline"}))] + (html->fragment "both")))) + + (t/testing "line-through alone is kept" + (t/is (= [(paragraph (run "gone" {:text-decoration "line-through"}))] + (html->fragment "gone")))) + + (t/testing "text-transform is kept" + (t/is (= [(paragraph (run "loud" {:text-transform "uppercase"}))] + (html->fragment "loud"))))) + +(t/deftest whitespace + (t/testing "collapsible whitespace collapses and is trimmed at line ends" + (t/is (= [(paragraph (run "a b"))] + (html->fragment "

a \n b

")))) + + (t/testing "  is kept as a space" + (t/is (= [(paragraph (run "a b"))] + (html->fragment "

a  b

")))) + + (t/testing "pre keeps spaces and splits lines into paragraphs" + (t/is (= [(paragraph (run "line 1")) + (paragraph (run " line 2"))] + (html->fragment "
line 1\n  line 2
"))))) + +(t/deftest line-breaks-and-empty-paragraphs + (t/testing "br starts a new paragraph" + (t/is (= [(paragraph (run "a")) (paragraph (run "b"))] + (html->fragment "a
b")))) + + (t/testing "runs of empty lines collapse into one" + (t/is (= [(paragraph (run "a")) (paragraph) (paragraph (run "b"))] + (html->fragment "

a




b

")))) + + (t/testing "empty lines at both ends are dropped" + (t/is (= [(paragraph (run "a"))] + (html->fragment "

a



"))))) + +(t/deftest tables + (t/testing "each row is a paragraph with its cells joined by a tab" + (t/is (= [(paragraph (run "a\tb")) (paragraph (run "c\td"))] + (html->fragment + "
a b
cd
"))))) + +(t/deftest hidden-content-is-skipped + (t/testing "style, script and display:none give no text" + (t/is (= [(paragraph (run "shown"))] + (html->fragment + "

shown

hidden

")))) + + (t/testing "elements outside the allowlist are skipped with their text" + (t/is (= [(paragraph (run "kept"))] + (html->fragment + "

keptcustom

")))) + + (t/testing "HTML without text gives no fragment" + (t/is (nil? (html->fragment ""))))) + +(t/deftest plain-text + (t/testing "each line is a paragraph and CRLF counts as one break" + (t/is (= [(paragraph (plain-run "a")) (paragraph) (paragraph (plain-run "b"))] + (clipboard/text->fragment "a\r\n\r\nb")))) + + (t/testing "empty text gives no fragment" + (t/is (nil? (clipboard/text->fragment ""))))) + +(t/deftest without-overrides + (t/testing "external HTML keeps its text and paragraphs but no emphasis" + (t/is (= [(paragraph (plain-run "Hello bold and italic")) + (paragraph) + (paragraph (plain-run "Second"))] + (clipboard/without-overrides (html->fragment google-docs-html))))) + + (t/testing "empty paragraphs stay empty" + (t/is (= [(paragraph)] + (clipboard/without-overrides [(paragraph)]))))) + +;; --- Penpot payload + +(def ^:private file-id (uuid/next)) +(def ^:private team-id (uuid/next)) + +(defn- text-content [& spans] + {:type "root" + :children [{:type "paragraph-set" + :children [{:type "paragraph" :text-align "center" :children (vec spans)}]}]}) + +(def ^:private gradient-span + {:text "Ñandú 😀 \"\"" + :font-id "custom-6e2c1a5e-1c1e-4b54-9c64-2a8c5f1a4c11" + :font-family "Brand Sans" + :font-variant-id "bold" + :font-weight "700" + :font-style "italic" + :fills [{:fill-color-gradient {:type :linear :start-x 0 :start-y 0 :end-x 1 :end-y 1 :width 1 + :stops [{:color "#ff0000" :opacity 1 :offset 0} + {:color "#0000ff" :opacity 1 :offset 1}]} + :fill-opacity 1}]}) + +(t/deftest payload-round-trip + (let [content (text-content gradient-span) + hidden (clipboard/payload->html content file-id team-id)] + (t/testing "the payload decodes back to the same content, file and team" + (t/is (= {:type :copied-text :version 1 :file-id file-id :team-id team-id :content content} + (clipboard/html->payload (str "" hidden "Hello"))))) + + (t/testing "it survives the markup browsers add around it" + (let [safari (-> hidden + (.replace ">" " style=\"color: rgb(0, 0, 0); font-size: medium;\">"))] + (t/is (= content + (:content (clipboard/html->payload + (str "" safari "Hello"))))))))) + +(t/deftest payload-geometry-round-trip + (let [content (text-content gradient-span) + geometry {:origins [[{:x 12.5 :y 0.2 :box {:width 200 :height 40}}]]} + hidden (clipboard/payload->html content file-id team-id geometry)] + (t/testing "the payload keeps where the copied spans sat in their text box" + (t/is (= geometry (:geometry (clipboard/html->payload hidden))))) + (t/testing "the content stays as copied" + (t/is (= content (:content (clipboard/html->payload hidden))))))) + +(t/deftest html-without-payload + (t/testing "HTML without a payload, or with a broken one, gives nil" + (t/is (nil? (clipboard/html->payload "

Hello

"))) + (t/is (nil? (clipboard/html->payload ""))) + (t/is (nil? (clipboard/html->payload nil))))) + +(defn- tampered-html + "Payload markup carrying `payload` as is, as a plugin could write it." + [{:keys [content file-id team-id geometry]}] + (clipboard/payload->html content file-id team-id geometry)) + +(def ^:private valid-payload + {:content (text-content gradient-span) :file-id file-id :team-id team-id}) + +(t/deftest invalid-payload + (t/testing "a valid payload is read" + (t/is (some? (clipboard/html->payload (tampered-html valid-payload))))) + + (t/testing "a payload with a broken envelope is dropped" + (t/is (nil? (clipboard/html->payload (tampered-html (assoc valid-payload :file-id "file"))))) + (t/is (nil? (clipboard/html->payload (tampered-html (assoc valid-payload :team-id 42)))))) + + (t/testing "a payload whose content a text shape cannot hold is dropped" + (t/is (nil? (clipboard/html->payload + (tampered-html (assoc-in valid-payload [:content :children 0 :children] []))))) + (t/is (nil? (clipboard/html->payload + (tampered-html (assoc valid-payload :content (text-content (assoc gradient-span :text 42))))))) + (t/is (nil? (clipboard/html->payload + (tampered-html (assoc valid-payload :content + (text-content (assoc gradient-span :fills [{:fill-color "red"}])))))))) + + (t/testing "a payload with geometry that is not plain numbers is dropped" + (let [origin {:x 12.5 :y 0.2 :box {:width 200 :height 40}}] + (t/is (some? (clipboard/html->payload + (tampered-html (assoc valid-payload :geometry {:origins [[origin]]}))))) + (t/is (nil? (clipboard/html->payload + (tampered-html (assoc valid-payload :geometry {:origins [[(assoc origin :x "12")]]}))))) + (t/is (nil? (clipboard/html->payload + (tampered-html (assoc valid-payload :geometry {:origins [[(assoc origin :y ##NaN)]]}))))) + (t/is (nil? (clipboard/html->payload + (tampered-html (assoc valid-payload :geometry {:origins [[(assoc-in origin [:box :width] nil)]]}))))) + (t/is (nil? (clipboard/html->payload + (tampered-html (assoc valid-payload :geometry {:origins [[origin]] :extra 1})))))))) + +(t/deftest oversized-payload + (t/testing "HTML over the paste size limit is not decoded" + (let [hidden (clipboard/payload->html (text-content gradient-span) file-id team-id)] + (t/is (nil? (clipboard/html->payload + (str hidden (apply str (repeat clipboard/max-paste-html-length "x"))))))))) + +;; --- Clean-up + +(def ^:private image-fill + {:fill-image {:id (uuid/next) :width 10 :height 10 :mtype "image/png"} :fill-opacity 1}) + +(def ^:private red-fill + {:fill-color "#ff0000" :fill-opacity 1}) + +(defn- spans-of [content] + (-> content :children first :children first :children)) + +(t/deftest clean-content + (let [library-id (uuid/next) + typography {:typography-ref-file library-id :typography-ref-id (uuid/next)} + content (text-content (merge gradient-span typography) + {:text "img" :fills [image-fill red-fill]} + {:text "only img" :fills [image-fill]}) + same {:valid-file-ids #{library-id} :same-team? true :same-file? true}] + + (t/testing "in the same file nothing changes" + (t/is (= content (clipboard/clean-content content same)))) + + (t/testing "typographies from unreachable files are dropped" + (t/is (nil? (-> (clipboard/clean-content content (assoc same :valid-file-ids #{})) + (spans-of) + (first) + :typography-ref-id)))) + + (t/testing "custom fonts from another team become the default font, at the closest variant" + (let [span (first (spans-of (clipboard/clean-content content (assoc same :same-team? false))))] + (t/is (= {:font-id "sourcesanspro" :font-family "sourcesanspro" + :font-variant-id "bolditalic" :font-weight "700" :font-style "italic"} + (select-keys span [:font-id :font-family :font-variant-id :font-weight :font-style]))) + (t/is (nil? (:typography-ref-id span))))) + + (t/testing "builtin and google fonts stay in another team" + (let [content (text-content (assoc gradient-span :font-id "gfont-roboto" :font-family "Roboto"))] + (t/is (= content (clipboard/clean-content content (assoc same :same-team? false)))))) + + (t/testing "images from another file are dropped, leaving the default fill when nothing is left" + (t/is (= [(:fills gradient-span) [red-fill] (txt/get-default-text-fills)] + (map :fills (spans-of (clipboard/clean-content content (assoc same :same-file? false))))))))) + +(t/deftest replace-custom-fonts + (t/testing "only custom fonts become the default font" + (let [content (text-content gradient-span (assoc gradient-span :font-id "gfont-roboto" :font-family "Roboto"))] + (t/is (= ["sourcesanspro" "gfont-roboto"] + (map :font-id (spans-of (clipboard/replace-custom-fonts content)))))))) diff --git a/frontend/test/frontend_tests/util_theme_test.cljs b/frontend/test/frontend_tests/util_theme_test.cljs new file mode 100644 index 0000000000..83889bab96 --- /dev/null +++ b/frontend/test/frontend_tests/util_theme_test.cljs @@ -0,0 +1,23 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.util-theme-test + (:require + [app.util.theme :as theme] + [cljs.test :as t])) + +(t/deftest unset-theme-follows-system + (doseq [system ["light" "dark"]] + (t/is (= system (theme/resolve-theme nil system))) + (t/is (= system (theme/resolve-theme theme/default system))) + (t/is (= system (theme/resolve-theme "system" system))))) + +(t/deftest explicit-theme-overrides-system + (doseq [system ["light" "dark"]] + (t/is (= "light" (theme/resolve-theme "light" system))) + (t/is (= "dark" (theme/resolve-theme "dark" system))) + ;; Older profiles store the dark preference as "default". + (t/is (= "dark" (theme/resolve-theme "default" system))))) diff --git a/frontend/test/frontend_tests/util_zip_test.cljs b/frontend/test/frontend_tests/util_zip_test.cljs index a9fbc658f7..6621335a8d 100644 --- a/frontend/test/frontend_tests/util_zip_test.cljs +++ b/frontend/test/frontend_tests/util_zip_test.cljs @@ -7,38 +7,9 @@ (ns frontend-tests.util-zip-test (:require [app.util.zip :as-alias uz] - [app.worker.import :as worker.import] - [beicon.v2.core :as rx] - [cljs.test :as t :include-macros true] - [frontend-tests.helpers.mock :as mock] - [promesa.core :as p])) + [cljs.test :as t :include-macros true])) (t/deftest read-as-text-nil-entry-raises-typed-error (t/testing "read-as-text guards against nil entry" (t/is (thrown-with-msg? js/Error #"nil" (app.util.zip/read-as-text nil))))) - -(t/deftest read-zip-manifest-missing-throws-validation-error - (t/async - done - (t/testing "read-zip-manifest rejects ZIPs without manifest.json" - (mock/with-mocks - {app.util.zip/get-entry (mock/stub (fn [_ _] (p/resolved nil)))} - (fn [done'] - (->> (worker.import/read-zip-manifest #js {}) - (rx/subs! - (fn [_] - (t/is false "expected validation error to be thrown") - (done')) - (fn [err] - (let [data (ex-data err)] - (t/is (= :invalid-penpot-file (:code data)) - "missing manifest.json raises typed :invalid-penpot-file error") - (t/is (string? (:hint data)) - "missing manifest.json error carries a :hint") - (t/is (re-find #"manifest\.json" (:hint data)) - "missing manifest.json :hint mentions manifest.json") - (t/is (nil? (re-find #"getData" (:hint data))) - "missing manifest.json :hint does not leak the raw TypeError text") - (done')))))) - done)))) \ No newline at end of file diff --git a/frontend/test/frontend_tests/worker/exports_test.cljs b/frontend/test/frontend_tests/worker/exports_test.cljs new file mode 100644 index 0000000000..5f1a9f6953 --- /dev/null +++ b/frontend/test/frontend_tests/worker/exports_test.cljs @@ -0,0 +1,70 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.worker.exports-test + (:require + [app.common.uuid :as uuid] + [app.worker.wasm :as wwasm] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async])) + +;; Record order inside `work`: `submit` starts before any subscriber, so +;; tapping the returned bus can miss values. Plain Promise (not promesa): +;; `await` + promesa crashed the full suite in Node. +(t/deftest ^:async submit-runs-jobs-serially + (let [order (atom [])] + (await + (js/Promise. + (fn [resolve reject] + (let [timer (js/setTimeout + #(reject (js/Error. "submit queue timed out")) + 2000)] + (wwasm/submit + (fn [] + (swap! order conj :a-start) + (rx/concat + (rx/of :a-start) + (->> (rx/of :a-end) + (rx/delay 20) + (rx/tap #(swap! order conj :a-end)))))) + (wwasm/submit + (fn [] + (swap! order conj + {:b-sees-a-start (boolean (some #{:a-start} @order)) + :b-sees-a-end (boolean (some #{:a-end} @order))}) + (swap! order conj :b-end) + (->> (rx/of :b-end) + (rx/finalize + (fn [] + (js/clearTimeout timer) + (resolve nil)))))))))) + (t/is (= [:a-start :a-end + {:b-sees-a-start true :b-sees-a-end true} + :b-end] + @order) + "job B starts only after job A has finished"))) + +(t/deftest cancel-job-is-visible-to-cancelled + (let [job-id (uuid/next)] + (t/is (false? (wwasm/cancelled? job-id))) + (wwasm/cancel-job job-id) + (t/is (true? (wwasm/cancelled? job-id))) + (wwasm/clear-cancel job-id) + (t/is (false? (wwasm/cancelled? job-id))))) + +;; After a macrotask yield (as between PDF pages), `cancelled?` still holds. +(t/deftest ^:async cancel-is-honored-after-async-yield + (let [job-id (uuid/next) + steps (atom [])] + (swap! steps conj :page-1) + (wwasm/cancel-job job-id) + (await (async/settle)) + (if (wwasm/cancelled? job-id) + (swap! steps conj :stopped) + (swap! steps conj :page-2)) + (t/is (= [:page-1 :stopped] @steps)) + (wwasm/clear-cancel job-id))) diff --git a/frontend/test/frontend_tests/worker/index_test.cljs b/frontend/test/frontend_tests/worker/index_test.cljs new file mode 100644 index 0000000000..aafa1a4a05 --- /dev/null +++ b/frontend/test/frontend_tests/worker/index_test.cljs @@ -0,0 +1,160 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.worker.index-test + (:require + [app.common.files.builder :as fb] + [app.common.files.changes :as ch] + [app.common.geom.rect :as grc] + [app.common.types.shape :as cts] + [app.common.uuid :as uuid] + [app.worker.impl :as impl] + [app.worker.index :as idx] + [cljs.test :as t :include-macros true] + [clojure.set :as set])) + +(t/use-fixtures :each + {:before #(reset! idx/state {:pages-index {}})}) + +(defn- make-page + [name] + (-> (fb/create-state) + (fb/add-file {:name "Test"}) + (fb/add-page {:name name}) + (fb/add-shape {:type :rect :x 0 :y 0 :width 100 :height 100}) + (fb/get-current-page))) + +(defn- initialize + [page] + (impl/handler {:cmd :index/initialize :page page})) + +(defn- update-index + [page-id changes] + (impl/handler {:cmd :index/update :page-id page-id :changes changes})) + +(defn- indexed-ids + [state] + {:pages (set (keys (:pages-index state))) + :selection (set (keys (::idx/selection state))) + :snap (set (keys (::idx/snap state)))}) + +(defn- add-rect-change + [page-id shape-id] + (let [shape (cts/setup-shape {:id shape-id :type :rect + :x 200 :y 200 :width 50 :height 50})] + {:type :add-obj + :id shape-id + :page-id page-id + :parent-id uuid/zero + :frame-id uuid/zero + :obj shape})) + +(t/deftest finalize-drops-only-that-page + (let [page-a (make-page "Page A") + page-b (make-page "Page B")] + (initialize page-a) + (initialize page-b) + (impl/handler {:cmd :index/finalize :page-id (:id page-a)}) + (t/is (= {:pages #{(:id page-b)} + :selection #{(:id page-b)} + :snap #{(:id page-b)}} + (indexed-ids @idx/state))))) + +(t/deftest finalize-unknown-page-is-noop + (let [page (make-page "Page 1")] + (initialize page) + (let [before @idx/state] + (impl/handler {:cmd :index/finalize :page-id (uuid/next)}) + (t/is (= before @idx/state))))) + +(t/deftest update-ignores-pages-not-indexed + (let [page (make-page "Page 1") + page-id (uuid/next)] + (initialize page) + (update-index page-id [(add-rect-change page-id (uuid/next))]) + (t/is (= {:pages #{(:id page)} + :selection #{(:id page)} + :snap #{(:id page)}} + (indexed-ids @idx/state))))) + +(t/deftest update-drops-indexes-of-deleted-page + (let [page (make-page "Page 1") + page-id (:id page)] + (initialize page) + (update-index page-id [{:type :del-page :id page-id :page-id page-id}]) + (t/is (= {:pages #{} :selection #{} :snap #{}} + (indexed-ids @idx/state))))) + +(t/deftest update-indexes-new-shapes-of-indexed-page + (let [page (make-page "Page 1") + page-id (:id page) + shape-id (uuid/next)] + (initialize page) + (update-index page-id [(add-rect-change page-id shape-id)]) + (t/is (contains? (get-in @idx/state [:pages-index page-id :objects]) shape-id)) + (t/is (contains? (set (impl/handler {:cmd :index/query-selection + :page-id page-id + :rect (grc/make-rect 190 190 70 70)})) + shape-id)))) + +(defn- get-stored-shape + [page-id shape-id] + (get-in @idx/state [:pages-index page-id :objects shape-id])) + +(t/deftest initialize-stores-a-trimmed-page + (let [page (make-page "Page 1") + shape-id (-> page :objects (dissoc uuid/zero) keys first)] + (initialize page) + (t/is (some? (get-stored-shape (:id page) shape-id))) + (t/is (nil? (:name (get-stored-shape (:id page) shape-id)))))) + +(t/deftest update-stores-trimmed-fills + (let [page (make-page "Page 1") + page-id (:id page) + shape-id (-> page :objects (dissoc uuid/zero) keys first)] + (initialize page) + (update-index page-id [{:type :mod-obj :id shape-id :page-id page-id + :operations [{:type :set :attr :fills + :val [{:fill-color "#ff0000" :fill-opacity 1}]}]}]) + (t/is (= [{}] (:fills (get-stored-shape page-id shape-id)))))) + +(t/deftest update-renaming-a-shape-keeps-the-indexes + (let [page (make-page "Page 1") + page-id (:id page) + shape-id (-> page :objects (dissoc uuid/zero) keys first)] + (initialize page) + (let [before @idx/state] + (update-index page-id [{:type :mod-obj :id shape-id :page-id page-id + :operations [{:type :set :attr :name :val "renamed"}]}]) + (t/is (identical? (::idx/selection before) (::idx/selection @idx/state))) + (t/is (identical? (::idx/snap before) (::idx/snap @idx/state)))))) + +(t/deftest update-skips-changes-the-indexes-do-not-read + (let [page (make-page "Page 1") + page-id (:id page) + shape-id (-> page :objects (dissoc uuid/zero) keys first)] + (initialize page) + (let [before (get-in @idx/state [:pages-index page-id])] + (update-index page-id [{:type :set-plugin-data :object-type :shape + :object-id shape-id :page-id page-id + :namespace "ns" :key "k" :value "v"} + {:type :mod-page :id page-id :page-id page-id :name "Renamed"}]) + (t/is (= before (get-in @idx/state [:pages-index page-id])))))) + +(t/deftest every-change-type-is-replayed-or-skipped + (let [known (set/union idx/index-change-types idx/skipped-change-types)] + (t/is (= #{} (set/difference (set (keys (methods ch/process-change))) known)) + "Add the new change type to one set; replaying it may need attributes in `app.worker.trim`") + (t/is (= #{} (set/intersection idx/index-change-types idx/skipped-change-types))))) + +(t/deftest update-skips-unknown-change-types + (let [page (make-page "Page 1") + page-id (:id page) + shape-id (-> page :objects (dissoc uuid/zero) keys first)] + (initialize page) + (let [before (get-in @idx/state [:pages-index page-id])] + (update-index page-id [{:type :unknown-change :id shape-id :page-id page-id}]) + (t/is (= before (get-in @idx/state [:pages-index page-id])))))) diff --git a/frontend/test/frontend_tests/worker/trim_test.cljs b/frontend/test/frontend_tests/worker/trim_test.cljs new file mode 100644 index 0000000000..2d6bdb7a64 --- /dev/null +++ b/frontend/test/frontend_tests/worker/trim_test.cljs @@ -0,0 +1,316 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS SUBSIDIARY SL + +(ns frontend-tests.worker.trim-test + (:require + [app.common.files.changes :as ch] + [app.common.geom.point :as gpt] + [app.common.geom.rect :as grc] + [app.common.geom.shapes :as gsh] + [app.common.test-helpers.compositions :as tho] + [app.common.test-helpers.files :as thf] + [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.shapes :as ths] + [app.common.types.objects-map :as omap] + [app.common.types.path :as path] + [app.common.types.text :as txt] + [app.common.uuid :as uuid] + [app.worker.selection :as selection] + [app.worker.snap :as snap] + [app.worker.trim :as trim] + [cljs.test :as t :include-macros true] + [clojure.set :as set])) + +(t/use-fixtures :each thi/test-fixture) + +(defn- register-bool + "Computes the content of `:bool` from its children." + [file] + (update file :data ch/process-changes + [{:type :reg-objects :page-id (thf/current-page-id file) :shapes [(thi/id :bool)]}])) + +(defn- make-file + "A component main and copy, a board with a styled group, a text, and a + bool of a rounded rect and a path." + [] + (-> (thf/sample-file :file) + (tho/add-simple-component-with-copy :component :main-root :main-child :copy-root + :copy-root-params {:x 200}) + (tho/add-frame :board :x 400 :y 0 :width 300 :height 300) + (tho/add-group :group :parent-label :board) + (tho/add-rect :rect :parent-label :group :x 420 :y 20 :width 50 :height 50 + :fills (ths/sample-fills-color :fill-color "#ff0000") + :strokes [{:stroke-width 4 :stroke-alignment :outer + :stroke-color "#000000" :stroke-opacity 1}]) + (tho/add-text :text "Hello") + (ths/add-sample-shape :bool :type :bool :bool-type :union + :x 0 :y 400 :width 100 :height 100) + (tho/add-rect :rounded :parent-label :bool :x 0 :y 400 :width 60 :height 60 + :r1 10 :r2 10 :r3 10 :r4 10) + (ths/add-sample-shape :path :parent-label :bool :type :path + :content (path/content [{:command :move-to :params {:x 30 :y 430}} + {:command :line-to :params {:x 100 :y 430}} + {:command :line-to :params {:x 100 :y 500}} + {:command :close-path :params {}}])) + (register-bool))) + +(defn- get-page-objects + [data page-id] + (get-in data [:pages-index page-id :objects])) + +(defn- replay-full + [data page-id changes] + (-> (ch/process-changes data changes false) + (get-page-objects page-id) + (trim/trim-objects))) + +(deftype ReadRecorder [inner dropped-keys reads-ref] + ;; A trimmed shape that notes each read of an attribute the trim dropped + ;; or reduced; writing an attribute makes later reads of it safe. + ILookup + (-lookup [this k] + (-lookup this k nil)) + (-lookup [_ k not-found] + (when (contains? dropped-keys k) + (swap! reads-ref conj k)) + (get inner k not-found)) + + IAssociative + (-contains-key? [_ k] + (when (contains? dropped-keys k) + (swap! reads-ref conj k)) + (contains? inner k)) + (-assoc [_ k v] + (ReadRecorder. (assoc inner k v) (disj dropped-keys k) reads-ref)) + + IMap + (-dissoc [_ k] + (ReadRecorder. (dissoc inner k) (disj dropped-keys k) reads-ref)) + + ICollection + (-conj [this entry] + (if (vector? entry) + (-assoc this (nth entry 0) (nth entry 1)) + (reduce-kv -assoc this entry))) + + IEmptyableCollection + (-empty [_] + (empty inner)) + + ICounted + (-count [_] + (count inner)) + + ISeqable + (-seq [_] + (seq inner)) + + IKVReduce + (-kv-reduce [_ f init] + (reduce-kv f init inner)) + + IMeta + (-meta [_] + (meta inner)) + + IWithMeta + (-with-meta [_ m] + (ReadRecorder. (with-meta inner m) dropped-keys reads-ref)) + + IEquiv + (-equiv [_ other] + (= inner (if (instance? ReadRecorder other) (.-inner ^js other) other))) + + IHash + (-hash [_] + (hash inner)) + + IFn + (-invoke [this k] + (-lookup this k nil)) + (-invoke [this k not-found] + (-lookup this k not-found))) + +(defn- record-reads + "Wraps each trimmed shape so reads of attributes that differ from the + full shape are noted in `reads-ref`." + [trimmed-objects full-objects reads-ref] + (reduce-kv (fn [objects id shape] + (let [full (get full-objects id) + dropped (into #{} + (filter #(and (some? (get full %)) + (not= (get full %) (get shape %)))) + (keys full))] + (assoc objects id (ReadRecorder. shape dropped reads-ref)))) + {} + trimmed-objects)) + +(defn- unwrap-objects + [objects] + (update-vals objects #(if (instance? ReadRecorder %) (.-inner ^js %) %))) + +(defn- replay-trimmed + "What the worker does: replay on the trimmed page, then trim the + candidate objects again. Reads of trimmed attributes go to `reads-ref`." + [data page-id changes reads-ref] + (let [page (get-in data [:pages-index page-id]) + trimmed (trim/trim-page page) + old-objects (:objects trimmed) + recorded (assoc trimmed :objects (record-reads old-objects (:objects page) reads-ref)) + new-objects (-> (ch/process-changes {:pages-index {page-id recorded}} changes false) + (get-page-objects page-id) + (unwrap-objects)) + ids (ch/get-candidate-object-ids old-objects new-objects page-id changes)] + (trim/retrim-objects old-objects new-objects ids))) + +(defn- set-attrs-change + [page-id id attrs] + {:type :mod-obj :id id :page-id page-id + :operations (mapv (fn [[attr val]] {:type :set :attr attr :val val}) attrs)}) + +(defn- move-change + [data page-id id delta] + (let [shape (get (get-page-objects data page-id) id)] + (set-attrs-change page-id id (-> (gsh/move shape delta) + (select-keys [:x :y :selrect :points]))))) + +(defn- get-scenarios + [data page-id] + (let [objects (get-page-objects data page-id) + copy-child (-> objects (get (thi/id :copy-root)) :shapes first) + text (get objects (thi/id :text))] + {"add a shape into a board" + [{:type :add-obj :id (uuid/next) :page-id page-id + :parent-id (thi/id :board) :frame-id (thi/id :board) + :obj (ths/sample-shape nil :type :rect :name "new" :x 450 :y 50 :width 10 :height 10 + :frame-id (thi/id :board) :parent-id (thi/id :board))}] + + "rename and recolor a shape" + [(set-attrs-change page-id (thi/id :rect) + {:name "renamed" + :fills (ths/sample-fills-color :fill-color "#00ff00")})] + + "edit a copied child (touched)" + [(set-attrs-change page-id copy-child + {:fills (ths/sample-fills-color :fill-color "#00ff00")})] + + "change a text" + [(set-attrs-change page-id (thi/id :text) + {:content (txt/change-text (:content text) "Bye")})] + + "delete a group" + [{:type :del-obj :id (thi/id :group) :page-id page-id}] + + "move a group into another board" + [{:type :mov-objects :page-id page-id + :parent-id (thi/id :main-root) :shapes [(thi/id :group)]}] + + "move a shape into a copy (refused)" + [{:type :mov-objects :page-id page-id + :parent-id (thi/id :copy-root) :shapes [(thi/id :text)]}] + + "move a child out of a copy (refused)" + [{:type :mov-objects :page-id page-id + :parent-id uuid/zero :shapes [copy-child]}] + + "move a copy into its main (nesting loop, refused)" + [{:type :mov-objects :page-id page-id + :parent-id (thi/id :main-root) :shapes [(thi/id :copy-root)]}] + + "reorder the children of a copy (refused)" + [{:type :reorder-children :page-id page-id + :parent-id (thi/id :copy-root) :shapes (reverse (get-in objects [(thi/id :copy-root) :shapes]))}] + + "move a rect inside a group, then register the group" + [(move-change data page-id (thi/id :rect) (gpt/point 30 0)) + {:type :reg-objects :page-id page-id :shapes [(thi/id :rect)]}] + + "move a rounded rect inside a bool, then register the bool" + [(move-change data page-id (thi/id :rounded) (gpt/point 0 -40)) + {:type :reg-objects :page-id page-id :shapes [(thi/id :rounded)]}] + + "fix the children of a board" + [{:type :fix-obj :id (thi/id :board) :page-id page-id}]})) + +(t/deftest replay-on-trimmed-page-matches-full-replay + (let [file (make-file) + data (:data file) + page-id (thf/current-page-id file)] + (doseq [[label changes] (get-scenarios data page-id)] + (t/testing label + (t/is (= (replay-full data page-id changes) + (replay-trimmed data page-id changes (atom #{})))))))) + +;; The touched pass looks for the component root, which only edits the +;; file's components; the worker has none. +(def ^:private touched-pass-reads #{:component-root :main-instance}) + +(defn- get-set-attrs + "Attributes the `:mod-obj` changes set; `set-shape-attr` reads their old + value only to update `:touched`, which the trim drops." + [changes] + (into #{} + (comp (filter #(= :mod-obj (:type %))) + (mapcat :operations) + (keep :attr)) + changes)) + +(t/deftest replay-on-trimmed-page-reads-only-kept-attributes + (let [file (make-file) + data (:data file) + page-id (thf/current-page-id file)] + (doseq [[label changes] (get-scenarios data page-id)] + (t/testing label + (let [reads (atom #{})] + (replay-trimmed data page-id changes reads) + (t/is (= #{} (set/difference @reads touched-pass-reads (get-set-attrs changes))))))))) + +(t/deftest trim-keeps-only-what-the-worker-reads + (let [file (make-file) + page (thf/current-page file) + objects (trim/trim-objects (:objects page))] + (t/testing "fills only tell whether there are any" + (t/is (= [{}] (get-in objects [(thi/id :rect) :fills])))) + (t/testing "strokes keep width and alignment" + (t/is (= [{:stroke-width 4 :stroke-alignment :outer}] + (get-in objects [(thi/id :rect) :strokes])))) + (t/testing "names and text content are dropped" + (t/is (nil? (get-in objects [(thi/id :rect) :name]))) + (t/is (nil? (get-in objects [(thi/id :text) :content])))) + (t/testing "path content is kept" + (t/is (some? (get-in objects [(thi/id :path) :content])))))) + +(t/deftest trim-reads-objects-maps + (let [file (make-file) + objects (:objects (thf/current-page file))] + (t/is (= (trim/trim-objects objects) + (trim/trim-objects (omap/wrap objects)))))) + +(t/deftest indexes-answer-the-same-on-a-trimmed-page + (let [file (make-file) + page (thf/current-page file) + page-id (:id page) + full {:selection (selection/add-page {} page) + :snap (snap/add-page {} page)} + trimmed (let [page (trim/trim-page page)] + {:selection (selection/add-page {} page) + :snap (snap/add-page {} page)}) + rects [(grc/make-rect 0 0 1000 1000) + (grc/make-rect 440 40 1 1) + (grc/make-rect 420 20 1 1) + (grc/make-rect 90 420 1 1) + (grc/make-rect 10 480 1 1)]] + (doseq [rect rects + opts [{} {:full-frame? true} {:include-frames? true :using-selrect? true}]] + (t/testing (str "selection " rect " " opts) + (let [query (merge {:page-id page-id :rect rect} opts)] + (t/is (= (selection/query (:selection full) query) + (selection/query (:selection trimmed) query)))))) + (doseq [frame-id [uuid/zero (thi/id :board)] + axis [:x :y]] + (t/testing (str "snap " frame-id " " axis) + (t/is (= (snap/query (:snap full) page-id frame-id axis [0 1000]) + (snap/query (:snap trimmed) page-id frame-id axis [0 1000]))))))) diff --git a/frontend/test/frontend_tests/worker_snap_test.cljs b/frontend/test/frontend_tests/worker_snap_test.cljs index c6b1f38a41..2043bebe5c 100644 --- a/frontend/test/frontend_tests/worker_snap_test.cljs +++ b/frontend/test/frontend_tests/worker_snap_test.cljs @@ -11,10 +11,16 @@ [app.common.uuid :as uuid] [app.worker.snap :as snap] [cljs.pprint :refer [pprint]] - [cljs.test :as t :include-macros true])) + [cljs.test :as t :include-macros true] + [clojure.set :as set])) (def uuid-counter 1) +(defn- get-object-ids + [old-page new-page] + (set/union (set (keys (:objects old-page))) + (set (keys (:objects new-page))))) + (defn get-mocked-uuid [] (let [counter (atom 0)] @@ -207,7 +213,7 @@ (fb/delete-shape shape-id)) new-page (fb/get-current-page state) - data (snap/update-page data page new-page) + data (snap/update-page data page new-page (get-object-ids page new-page)) result-x (snap/query data (:id page) uuid/zero :x [0 100]) result-y (snap/query data (:id page) uuid/zero :y [0 100])] @@ -237,7 +243,7 @@ state (fb/delete-shape state shape-id) new-page (fb/get-current-page state) - data (snap/update-page data page new-page) + data (snap/update-page data page new-page (get-object-ids page new-page)) result-x (snap/query data (:id page) uuid/zero :x [0 100]) result-y (snap/query data (:id page) uuid/zero :y [0 100])] @@ -269,7 +275,7 @@ state (fb/delete-shape state shape-id) new-page (fb/get-current-page state) - data (snap/update-page data page new-page) + data (snap/update-page data page new-page (get-object-ids page new-page)) result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) result-frame-x (snap/query data (:id page) frame-id :x [0 100])] @@ -297,7 +303,7 @@ new-page (-> (fb/delete-guide state guide-id) (fb/get-current-page)) - data (snap/update-page data page new-page) + data (snap/update-page data page new-page (get-object-ids page new-page)) result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) result-zero-y (snap/query data (:id page) uuid/zero :y [0 100]) @@ -330,7 +336,7 @@ new-page (-> (fb/delete-guide file guide-id) (fb/get-current-page)) - data (snap/update-page data page new-page) + data (snap/update-page data page new-page (get-object-ids page new-page)) result-zero-x (snap/query data (:id page) uuid/zero :x [0 100]) result-zero-y (snap/query data (:id page) uuid/zero :y [0 100]) @@ -370,7 +376,7 @@ new-page (fb/get-current-page state) - data (snap/update-page data page new-page) + data (snap/update-page data page new-page (get-object-ids page new-page)) result-zero-x-1 (snap/query data (:id page) uuid/zero :x [0 100]) result-frame-x-1 (snap/query data (:id page) frame-id :x [0 100]) @@ -408,7 +414,7 @@ new-page (fb/get-current-page state) ;; FIXME: update - data (snap/update-page data page new-page) + data (snap/update-page data page new-page (get-object-ids page new-page)) result-zero-x-1 (snap/query data (:id page) uuid/zero :x [0 100]) result-zero-x-2 (snap/query data (:id page) uuid/zero :x [200 300])] @@ -437,7 +443,7 @@ new-page (-> (fb/update-guide state (assoc guide :position 150)) (fb/get-current-page)) - data (snap/update-page data page new-page) + data (snap/update-page data page new-page (get-object-ids page new-page)) result-zero-x-1 (snap/query data (:id page) uuid/zero :x [0 100]) result-zero-y-1 (snap/query data (:id page) uuid/zero :y [0 100]) diff --git a/frontend/text-editor/package.json b/frontend/text-editor/package.json index 45fc013e71..34e6dcb63f 100644 --- a/frontend/text-editor/package.json +++ b/frontend/text-editor/package.json @@ -28,5 +28,5 @@ "vite": "^8.2.0", "vitest": "^5.0.2" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/frontend/translations/af.po b/frontend/translations/af.po index 23e462f76f..7083eb6697 100644 --- a/frontend/translations/af.po +++ b/frontend/translations/af.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2026.10.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Reeds 'n rekening?" @@ -29,7 +29,7 @@ msgstr "" "Dit is 'n DEMO-diens, MOENIE vir werklike werk gebruik nie, die projekte " "sal periodiek uitgevee word." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Wagwoord vergeet?" @@ -37,27 +37,27 @@ msgstr "Wagwoord vergeet?" msgid "auth.fullname" msgstr "Volle naam" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Meld hier aan" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -79,7 +79,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "Wagwoordherwinningskakel na jou inkassie gestuur." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Het suksesvol by die span aangesluit" @@ -111,11 +111,11 @@ msgstr "Wagwoord vergeet?" msgid "auth.recovery-submit" msgstr "Verander jou wagwoord" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Nog nie 'n rekening nie?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Skep 'n rekening" @@ -123,111 +123,111 @@ msgstr "Skep 'n rekening" msgid "auth.register-title" msgstr "Skep 'n rekening" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Diensbepalings" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publiseer" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Alle Penpot-gebruikers" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Is jy seker jy wil hierdie skakel verwyder? As jy dit doen, is dit nie meer " "vir enigiemand beskikbaar nie" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(huidige)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Vernietig skakel" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Kry skakel" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Skakel suksesvol gekopieer" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Bestuur toestemmings" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "bladsy gedeel" msgstr[1] "%s bladsye gedeel" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Kan kommentaar lewer" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Kan kode inspekteer" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Enigiemand met skakel sal toegang hê" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Bladsye gedeel" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Deelbare skakel sal hier verskyn" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Slegs spanlede" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Deel prototipes" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Kies Alles" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Depubliseer" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Spanbestuur" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "Penpot is bedoel vir spanne. Nooi lede om saam te werk aan projekte en lêers" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Span saam!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Voeg by as Gedeelde Biblioteek" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Verander e-pos" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopieer)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Skep 'n nuwe span" @@ -235,11 +235,11 @@ msgstr "Skep 'n nuwe span" msgid "dashboard.default-team-name" msgstr "Jou Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Verwyder span" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Laai Penpot-lêer (.penpot) af" @@ -255,11 +255,11 @@ msgstr "Dupliseer %s lêers" msgid "dashboard.export-binary-multi" msgstr "Laai %s Penpot lêers (.penpot) af" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Voer borde as PDF uit" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Voer as PDF uit" diff --git a/frontend/translations/ar.po b/frontend/translations/ar.po index 0b07c80ca7..7eb87b17ce 100644 --- a/frontend/translations/ar.po +++ b/frontend/translations/ar.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: Amine Gdoura \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Arabic " "\n" "Language: ar\n" @@ -10,9 +10,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "هل لديك حساب؟" @@ -32,7 +32,7 @@ msgstr "أنشئ حساب تجريبي" msgid "auth.demo-warning" msgstr "هذه خدمة تجريبية، لا تستخدمها للعمل الحقيقي، ستُمسح المشاريع بشكل دوري." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "أنسيت كلمة السر؟" @@ -40,35 +40,35 @@ msgstr "أنسيت كلمة السر؟" msgid "auth.fullname" msgstr "الاسم الكامل" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "لِج إلى حسابي" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "لِج هنا." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot هو أداة تصميم مجانية ومفتوحة المصدر للتعاون بين التصميم والبرمجة" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "Github" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "جوجل" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -88,7 +88,7 @@ msgstr "الملف التعريف غير موثّق، يُرجى توثيق ال msgid "auth.notifications.recovery-token-sent" msgstr "أُرسل رابط استعادة كلمة السر إلى بريدك الوارد." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "انضممت إلى الفريق بنجاح" @@ -120,11 +120,11 @@ msgstr "أنسيت كلمة السر؟" msgid "auth.recovery-submit" msgstr "غيّر كلمة سرك" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "لا تملك حساب بعد؟" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "أنشئ حساب" @@ -132,43 +132,43 @@ msgstr "أنشئ حساب" msgid "auth.register-title" msgstr "أنشئ حساب" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "شروط الخدمة" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "انشر" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "جميع مستخدمي Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "أمتأكد أنك تريد إزالة هذا الرابط؟ إذا فعلت ذلك، فلن يكون متاحًا لأي شخص" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(الحالي)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "أتلف الرابط" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "احصل على الرابط" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "نُسخ الرابط بنجاح" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "أدر الأذونات" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "لم يتم مشاركة الصفحة" @@ -178,67 +178,67 @@ msgstr[3] "مشاركة صفحات" msgstr[4] "مشاركة صفحة" msgstr[5] "مشاركة صفحة" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "يمكن التعليق" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "يمكن فحص الرمز" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "أي شخص لديه الرابط سيكون لديه حق الوصول" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "الصفحات المشتركة" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "سيظهر الرابط القابل للمشاركة هنا" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "أعضاء الفريق فقط" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "شارك النماذج" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "حدّد الكل" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "ألغِ النشر" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "إدارة الفريق" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "Penpot صُمّم للفِرق. أدعُ زملاءك للعمل سوياَ على المشاريع والملفات" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "إعمل فريق!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "أضف كمكتبة مشتركة" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "غيّر البريد الإلكتروني" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(نسخ)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "أنشئ فريق جديد" @@ -246,11 +246,11 @@ msgstr "أنشئ فريق جديد" msgid "dashboard.default-team-name" msgstr "Penpot الخاص بك" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "حذف الفريق" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "تنزيل ملف Penpot (.penpot)" @@ -270,53 +270,53 @@ msgstr "أنشئ ملف جديد" msgid "dashboard.export-binary-multi" msgstr "تنزيل ملفات ٪s Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "صدّر اللوحات ك PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "صدّر ك PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s من %s عناصر محدّدة" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "صدّر" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "يمكنك إضافة إعدادات التصدير إلى العناصر من خصائص التصميم (أسفل الشريط " "الجانبي الأيمن)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "معلومات عن كيفية إعداد التصديرات في Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "لا يوجد عناصر بإعدادت التصدير." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "صدّر التحديد" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "خط مفقود" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "أهمِل الكل" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "ستظهر الخطوط المخصّصة التي ترفعها هنا." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "لم يتم إضافة أي خط" @@ -326,7 +326,7 @@ msgstr[3] "عدد قليل من الخطوط المضافة" msgstr[4] "تمت إضافة العديد من الخطوط" msgstr[5] "" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -335,7 +335,7 @@ msgstr "" "عائلة الخطوط على أنها **عائلة خط واحدة **. يمكنك رفع الخطوط بالتنسيقات " "التالية: **TTF و OTF و WOFF** (ستحتاج إلى واحد فقط)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -343,11 +343,11 @@ msgstr "" "اكتشف المزيد في قسم حقوق المحتوى في [شروط خدمة Penpot](%s). قد ترغب أيضًا " "في القراءة عن [ترخيص الخطوط](https://www.typography.com/faq).." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "ارفع الكل" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -357,71 +357,43 @@ msgstr "" "باستخدام [Transfonter](https://transfonter.org/) لإنشاء خطوط الويب وإصلاح " "الأخطاء. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "استورد ملفات Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "عذراً! لم نتمكن من استيراد هذا الملف" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgstr "%s ملف تم ادراجهم بنجاح." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "احتوت بعض الملفات على كائنات غير صالحة أُزيلت." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "معالجة الألوان" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "معالجة المكوّنات" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "معالجة الوسائط" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "معالجة الصفحة: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "معالجة الخطوط" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "يرفع البيانات للخادم (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "يرفع الملف: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "ادعُ الناس" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "غادر الفريق" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "المكتبات والقوالب" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "استكشف المزيد منهم وتعلم كيف تساهم" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "حدثت مشكلة أثناء استيراد القالب. لم يتم استيراد القالب." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "المكتبات" @@ -429,7 +401,7 @@ msgstr "المكتبات" msgid "dashboard.loading-files" msgstr "جارِ تحميل ملفاتك …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "جارِ تحميل خطوطك …" @@ -445,35 +417,35 @@ msgstr "انقل %s ملف إلى" msgid "dashboard.move-to-other-team" msgstr "انقل إلى فريق آخر" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ ملف جديد" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "ملف جديد" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ مشروع جديد" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "مشروع جديد" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "لم يتم العثور على مطابقات ل \"%s\"" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "ستظهر المشاريع المثبتة هنا" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "حُدّث عنوان بريدك الإلكتروني بنجاح" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "تحققَ من عنوان بريدك الإلكتروني بنجاح" @@ -481,7 +453,7 @@ msgstr "تحققَ من عنوان بريدك الإلكتروني بنجاح" msgid "dashboard.notifications.password-saved" msgstr "حُفظت كلمة السر بنجاح!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s الأعضاء" @@ -489,7 +461,7 @@ msgstr "%s الأعضاء" msgid "dashboard.open-in-new-tab" msgstr "افتح الملف في لسان جديد" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "الخيارات" @@ -497,39 +469,39 @@ msgstr "الخيارات" msgid "dashboard.password-change" msgstr "غيّر كلمة السر" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "ثبت/ألغِ التثبيت" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "المشاريع" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "أتريد إزالة حسابك؟" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "احفظ الإعدادات" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "ابحث…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "البحث عن \"%s\"…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "حدّد لغة واجهة المستخدم" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "حدّد نَسقا" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "أظهر كل الملفات" @@ -549,7 +521,7 @@ msgstr "تم تكرار ملفك بنجاح" msgid "dashboard.success-duplicate-project" msgstr "كُرّر مشروعك بنجاح" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "نُقل ملفك بنجاح" @@ -561,63 +533,63 @@ msgstr "نُقلت الملفات بنجاح" msgid "dashboard.success-move-project" msgstr "نُقل مشروعك بنجاح" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "معلومات الفريق" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "أعضاء الفريق" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "مشاريع الفريق" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "نسق واجهة المستخدم" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "نتائج البحث" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "اكتب لإظهار نتائج البحث" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "ألغِ نشر المكتبة" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "حدّث الإعدادات" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "نشط" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "عندما يتم تشغيل هذا الخطاف، سيتم تسليم تفاصيل الحدث" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "نوع المحتوى" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "أنشئ webhook" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "حسابك" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "البريد الالكتروني" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "اسمك" @@ -633,7 +605,7 @@ msgstr "انتباه" msgid "ds.component-subtitle" msgstr "مكوّنات لتحدّثها:" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "ألغِ" @@ -649,27 +621,27 @@ msgstr "هل أنت متأكد؟" msgid "errors.auth-provider-not-configured" msgstr "موفّر الاستيثاق غير مضبط." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "يبدوا أنك غير مُستوثق أو أن الجلسة انتهت." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "تعذر تحميل الخط %s" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "تعذر تحميل الخطوط %s" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "لا يمكن للمتصفح إجراء هذه العملية" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "البريد الإلكتروني مستخدم بالفعل" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "تحققَ من صحة البريد الإلكتروني بالفعل." @@ -677,25 +649,25 @@ msgstr "تحققَ من صحة البريد الإلكتروني بالفعل." msgid "errors.email-as-password" msgstr "لا يمكن لكلمة السر أن تكون بريدك الإلكتروني" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "يحتوي البريد الإلكتروني «%s» على العديد من تقارير الارتداد الدائم." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "بُلّغ عن البريد الإلكتروني «%s» كبريد عشوائي أو مرتد بشكل دائم." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "يبدو أنك تفتح ملفًا فُعّل الميزة \"%s\" فيه ولكن الواجهة الأمامية لـ penpot " "لا تدعمه أو عُطّل." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "الميزة '%s' غير مدعومة." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "حدث خطأ ما." @@ -707,7 +679,7 @@ msgstr "لون غير صالح" msgid "errors.invalid-email" msgstr "أدخل بريدًا إلكترونيًا صالحًا من فضلك" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "يجب أن يتطابق البريد الإلكتروني للتأكيد" @@ -739,7 +711,7 @@ msgstr "يبدو أن محتويات الصورة لا تتطابق مع امت msgid "errors.media-type-not-allowed" msgstr "يبدو أن هذه ليست صورة صالحة." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "يحتوي الملف الشخصي الذي تدعوه على رسائل بريد إلكتروني مكتومة (تقارير البريد " @@ -757,7 +729,7 @@ msgstr "يجب ألا تقل كلمة السر عن 8 أحرف" msgid "errors.profile-blocked" msgstr "هذا الملف الشخصي محظور" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "يحتوي ملفك الشخصي على رسائل بريد إلكتروني مكتومة (تقارير البريد المزعجة أو " @@ -767,15 +739,15 @@ msgstr "" msgid "errors.registration-disabled" msgstr "التسجيل مُعطّل حاليًا." -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "أعضاء غير كافيين لمغادرة الفريق، ربما تريد حذفه." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "العضو الذي تحاول تعيينه غير موجود." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "لا يمكن للمالك مغادرة الفريق ، يجب إعادة تعيين دور المالك." @@ -783,27 +755,27 @@ msgstr "لا يمكن للمالك مغادرة الفريق ، يجب إعاد msgid "errors.unexpected-error" msgstr "حدث خطأ غير متوقع." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "رمز غير معروف" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "خطأ في الاتصال ، عنوان إلكتروني لا يمكن الوصول إليه" -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "آخر تسليم لم يكن ناجحًا." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "نفذ الوقت" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "خطأ غير متوقع في التحقق" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "حالة غير متوقعة٪ s" @@ -819,11 +791,11 @@ msgstr "كلمة السر القديمة غير صحيحة" msgid "feedback.description" msgstr "وصف" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "يسعدنا وجودك هنا. إذا كنت بحاجة إلى مساعدة، يرجى البحث أولا قبل النشر." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "مجتمع Penpot" @@ -837,11 +809,11 @@ msgstr "" "يرجى وصف سبب بريدك الإلكتروني ، وتحديد ما إذا كانت مشكلة أم فكرة أم شك. " "سيرد أحد أعضاء فريقنا في أسرع وقت ممكن." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "هنا للمساعدة في استفساراتك التقنية." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "رد عنوان تويتر" @@ -877,39 +849,27 @@ msgstr "ظلل" msgid "inspect.attributes.stroke" msgstr "لون الحدّ" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "مركز" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "داخل" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "خارج" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "صياغة الحروف" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "عائلة الخط" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "حجم الخط" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "نوع الخط" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "تباعد الحروف" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "ارتفاع الخط" @@ -921,11 +881,11 @@ msgstr "لا شئ" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "مسطر" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "حالة العنوان" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "أحرف صغيرة" @@ -933,7 +893,7 @@ msgstr "أحرف صغيرة" msgid "inspect.attributes.typography.text-transform.none" msgstr "لا شئ" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "الأحرف الكبيرة" @@ -989,23 +949,23 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "نص" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "الاختصارات" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "إقبل" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "إضافة خط مخصص" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "مشرف" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "الكل" @@ -1013,25 +973,25 @@ msgstr "الكل" msgid "labels.and" msgstr "و" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "العودة" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "يبدو أنك بحاجة إلى الانتظار قليلا وإعادة المحاولة. نحن نقوم بصيانة صغيرة " "لخوادمنا." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "مدخل خاطأ" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "ألغِ" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "غلق" @@ -1039,11 +999,11 @@ msgstr "غلق" msgid "labels.code" msgstr "شفرة" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "تعليقات" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "مجتمع" @@ -1051,31 +1011,31 @@ msgstr "مجتمع" msgid "labels.confirm-password" msgstr "أكّد كلمة السر" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "استمر" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "يمكنك المتابعة مع حساب Penpot" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "أنشئ" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "أنشئ فريق جديد" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "أدخل اسم الفريق الجديد" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "لوحة التحكم" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "حذف" @@ -1087,7 +1047,7 @@ msgstr "حذف التعليق" msgid "labels.delete-comment-thread" msgstr "حذف موضوع" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "حذف الدعوة" @@ -1095,23 +1055,23 @@ msgstr "حذف الدعوة" msgid "labels.delete-multi-files" msgstr "حذف %s ملفات" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "المسودات" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "تعديل" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "محرر" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "منتهي الصلاحية" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "استخراج" @@ -1123,35 +1083,35 @@ msgstr "تعطيل الملاحظات" msgid "labels.feedback-sent" msgstr "تم إرسال الملاحظات" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "عائلة الخط" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "الأنماط" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "الخطوط" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "مستودع Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "إعطاء ملاحظات" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "الرجوع للخلف" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "مركز المساعدة" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "إخفاء التعليقات التي تم حلها" @@ -1159,39 +1119,39 @@ msgstr "إخفاء التعليقات التي تم حلها" msgid "labels.info" msgstr "معلومات" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "الخطوط المتوفرة" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "خطأ داخلي" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "الدعوات" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "اللغة" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "المكتبات والقوالب" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "تسجيل الدخول أو الاشتراك" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "تسجيل خروج" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "عضو" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "الأعضاء" @@ -1199,19 +1159,19 @@ msgstr "الأعضاء" msgid "labels.new-password" msgstr "كلمة سر جديدة" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "ليس لديك أي إشعارات تعليق معلقة" -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "لا توجد دعوات." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "عفواً!" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "0 ملف" @@ -1221,7 +1181,7 @@ msgstr[3] "قليل" msgstr[4] "كثير" msgstr[5] "غير ذلك" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "0" @@ -1231,7 +1191,7 @@ msgstr[3] "بعض" msgstr[4] "العديد" msgstr[5] "غير ذلك" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "0" @@ -1245,131 +1205,131 @@ msgstr[5] "غير ذلك" msgid "labels.old-password" msgstr "كلمة السر القديمة" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "مالك" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "كلمة السر" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "قيد الانتظار" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "الملف الشخصي" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "المشاريع" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "ملاحظات الإصدار" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "إزالة" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "إزالة العضو" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "اعاده تسميه" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "إعادة تسمية الفريق" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "إعادة إرسال الدعوة" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "أعد المحاولة" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "دور" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "حفظ" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "البحث عن الخط" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "ارسل" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "ارسال…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "نحن في صيانة مبرمجة لأنظمتنا." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "الخدمة غير متوفرة" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "إعدادات" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "المكتبات المشتركة" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "إظهار كافة التعليقات" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "قائمة التعليقات" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "إظهار تعليقاتك فقط" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "الحالة" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "الدروس" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "تحديث" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "تحديث الفريق" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "رفع" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "تحميل الخطوط المخصصة" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "جارٍ الرفع …" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "مشاهد" @@ -1377,11 +1337,11 @@ msgstr "مشاهد" msgid "labels.write-new-comment" msgstr "كتابة تعليق جديد" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(أنت)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "حسابك" @@ -1403,43 +1363,43 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "إضافة “%s” كمكتبة مشتركة" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "دفعة كبيرة" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "تحقق من البريد الإلكتروني الجديد" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "سنرسل لك رسالة إلى بريدك الإلكتروني الحالي “%s” للتحقق من هويتك." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "بريد إلكتروني جديد" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "تغيير البريد الإلكتروني" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "تغيير بريدك الإلكتروني" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "ألغِ واحتفظ بحسابي" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "نعم، احذف حسابي" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "بحذف حسابك، ستفقد جميع مشاريعك وأرشيفاتك الحالية." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "هل انت متأكد انك تريد حذف حسابك؟" @@ -1481,27 +1441,27 @@ msgstr "هل تريد بالتأكيد حذف %s من الملفات؟" msgid "modals.delete-file-multi-confirm.title" msgstr "حذف %s الملفات" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "هل أنت متأكد أنك تريد حذف نمط هذا الخط؟ لن يُحمل إذا استُخدم في ملف." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "حذف نمط الخط" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "هل أنت متأكد أنك تريد حذف هذا الخط؟ لن يُحمل إذا استُخدم في ملف." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "حذف الخط" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "هل أنت متأكد أنك تريد حذف هذه الصفحة؟" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "احذف الصفحة" @@ -1531,99 +1491,99 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "حذف الفريق" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "احذف العضو" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "هل أنت متأكد أنك تريد حذف هذا العضو من الفريق؟" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "احذف عضو الفريق" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "أرسال دعوة" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "رسائل البريد الإلكتروني، مفصولة بفواصل" -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "ادعُ الأعضاء إلى الفريق" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "نظرًا لأنك العضو الوحيد في هذا الفريق ، فسيتم حذف الفريق مع مشاريعه وملفاته." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "هل أنت متأكد أنك تريد مغادرة فريق %s؟" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "لا يمكنك مغادرة الفريق إذا لم يكن هناك عضو آخر للترقية إلى المالك. قد ترغب " "في حذف الفريق." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "أنت %s المالك." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "رقِ وغادر" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "حدّد عضوًا للترقية" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "قبل مغادرتك" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "غادر الفريق" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "هل أنت متأكد أنك تريد مغادرة هذا الفريق؟" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "مغادرة الفريق" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "كمية الدفع" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "انقل الملكية" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "إذا نقلت الملكية، فستتغير دورك إلى المسؤول، وستفقد بعض الأذونات المتعلقة " "بهذا الفريق. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "هل أنت متأكد أنك تريد ترقية هذا المستخدم إلى مالك؟" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "مالك الفريق الجديد" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "دفعة صغيرة" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgstr "إلغاء النشر" @@ -1645,19 +1605,19 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "حدّث المكوّن في المكتبة المشتركة" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "أُرسلت الدعوة بنجاح" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "لا يمكنك حذف ملف تعريفك، أعد تعيين فرقك قبل المتابعة." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "حُفظ ملف التعريف بنجاح!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "أُرسل رسالة التحقق إلى %s. راجع بريدك الالكتروني!" @@ -1669,11 +1629,11 @@ msgstr "مرحبًا بك في Penpot!" msgid "onboarding.choice.team-up.create-team-desc" msgstr "بعد تسمية فريقك، ستتمكن من دعوة الأشخاص للانضمام." -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "ادعُ أعضاء" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "دعوة مع الدور:" @@ -1719,79 +1679,79 @@ msgstr "ابدأ التصميم" msgid "profile.recovery.go-to-login" msgstr "اذهب إلى الولوج" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "مُختلط" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "تعليقات" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "محاذاة لأسفل" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "محاذاة المركز أفقيًا" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "محاذاة اليسار" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "محاذاة اليمين" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "‌محاذاة الأعلى" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "محاذاة المركز عموديًا" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "أنشئ لوحة من التحديد" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "فرق منطقي" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "استبعاد منطقي" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "تقاطع منطقي" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "الاتحاد المنطقي" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "أرسل إلى الخلف" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "إرسال إلى الوراء" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "ثابر للأمام" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "أحضر إلى الأمام" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "مسح التراجع" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "نسخ" @@ -1799,35 +1759,35 @@ msgstr "نسخ" msgid "shortcuts.create-new-project" msgstr "أضف جديد" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "قطع" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "تصغير" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "حذف" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "حذف العقدة" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "فصل المكوّن" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "منحنى" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "الشكل البيضاوي" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "لوحة" @@ -1835,39 +1795,39 @@ msgstr "لوحة" msgid "shortcuts.draw-nodes" msgstr "أرسم المسار" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "المسار" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "مستطيل" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "نص" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "كرر" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "ألغِ" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "تصدير الأشكال" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "تكبير لتناسب الجميع" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "قلب أفقيًا" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "قلب عموديًا" @@ -1883,63 +1843,63 @@ msgstr "إذهب إلى المكتبات المشتركة" msgid "shortcuts.go-to-search" msgstr "بحث" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "مجموعة" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "وزع أفقيًا" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "إظهار / إخفاء واجهة المستخدم" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "تكبير" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "إدراج صورة" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "ربط العقد" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "إصنع زاوية" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "إصنع منحنى" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "قناع" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "دمج العقد" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "تحرك" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "تحرك بسرعة لأسفل" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "تحرك يسارا بسرعة" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "تحرك يميناً بسرعة" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "تحرك للأعلى بسرعة" @@ -1947,173 +1907,173 @@ msgstr "تحرك للأعلى بسرعة" msgid "shortcuts.move-nodes" msgstr "نقل العقدة" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "تحرك لأسفل" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "تحرك يساراً" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "تحرك يميناً" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "تحرك للأعلى" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "اللوحة التالية" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "لا يوجد إختصارات" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "ضبط التعتيم على 100٪" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "اضبط التعتيم على 10٪" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "اضبط التعتيم على 20٪" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "اضبط التعتيم على 30٪" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "اضبط التعتيم على 40٪" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "اضبط التعتيم على 50٪" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "اضبط التعتيم على 60٪" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "اضبط التعتيم على 70٪" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "اضبط التعتيم على 80٪" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "اضبط التعتيم على 90٪" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "أداة انتقاء اللون" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "اذهب إلى لوحة المعلومات" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "الأساسيات" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "لوحة القيادة" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "مشاهد" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "مساحة العمل" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "محاذاة" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "عام" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "القائمة الرئيسية" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "تعديل الطبقات" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "التنقل" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "التنقل" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "التنقل" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "اللوحات" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "مسارات" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "الأشكال" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "أدوات" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "تكبير" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "تكبير" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "تبديل الطبقات" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "أضف\\أزل ثني التخطيط" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "قفل\\فتح" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "قفل النسب" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "تبديل لوحة النص" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "أظهر\\أخف" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "تبديل أسلوب التكبير" @@ -2121,67 +2081,67 @@ msgstr "تبديل أسلوب التكبير" msgid "shortcuts.underline" msgstr "الخط التحتي" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "تراجع" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "فك التجميع" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "ألغِ القناع" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "النشر عموديا" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "تنقيص عدسة التكبير" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "زيادة عدسة التكبير" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "كبر المحدد" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "مزودي الخط - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "الخطوط - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "المشاريع - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "البحث - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "المكاتب المشتركة - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - صمّم حرية للفرق" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "أرسل انطباعك - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "إعدادات - Penpot" @@ -2189,27 +2149,27 @@ msgstr "إعدادات - Penpot" msgid "title.settings.password" msgstr "كلمة السر - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "ملف التعريف - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "الدعوات - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "الأعضاء - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "إعدادات - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "خطافات الويب - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - وضع العرض - Penpot" @@ -2217,103 +2177,103 @@ msgstr "%s - وضع العرض - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "لم يُعثر على لوحات في الصفحة." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "لم يُعثر على لوحة." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "التعليقات (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "لا تطهر التفاعلات" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "تكبير الشاشة" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "التفحص (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "التفاعلات" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "التفاعلات (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "نسخ الرابط" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "أطهر التفاعلات بالنقر" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "أطهر التفاعلات بالنقر" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "خريطة الموقع" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "محاذاة المركز الأفقي" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "محاذاة المركز الأفقي (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "توزيع المسافات الأفقية (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "محاذاة للشمال (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "محاذاة لليمين (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "محاذاة للأسفل (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "محاذاة للمركز العمودي (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "توزيع التباعد الرئسي (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "محاذاة لأعلى (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "أضف مكتبة" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "كل الأصول" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "الألوان" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "عناصر" @@ -2321,15 +2281,15 @@ msgstr "عناصر" msgid "workspace.assets.create-group" msgstr "أنشئ مجموعة" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "ستُسمى عناصرك تلقائيًا ك \"اسم مجموعة / اسم عنصر\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "امسح" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "انسخ" @@ -2337,27 +2297,27 @@ msgstr "انسخ" msgid "workspace.assets.duplicate-main" msgstr "انسخ الأصل" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "عدّل" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "مجموعة" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "اسم المجموعة" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "مكتبة محلية" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "أدر المكتبة" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "لا يوجد أصول" @@ -2365,7 +2325,7 @@ msgstr "لا يوجد أصول" msgid "workspace.assets.open-library" msgstr "افتح ملف المكتبة" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "أعد التسمية" @@ -2373,43 +2333,43 @@ msgstr "أعد التسمية" msgid "workspace.assets.rename-group" msgstr "أعد تسمية المجموعة" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "ابحث عن أصول" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "مكتبة مشتركة" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "الطباعة" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "الحجم" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "اسلوب الخط" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "اذهب إلى تحرير نوع ملف المكتبة" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "تباعد الحروف" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "طول الخط" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "أسلوب خط النص" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "تحويل النص" @@ -2417,23 +2377,23 @@ msgstr "تحويل النص" msgid "workspace.assets.ungroup" msgstr "فك التجميع" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "أنشئ منطقة" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "أنشئ لوحة" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "وضع التركيز" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "تعطيل التركيز" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "فعّل التركيز" @@ -2445,151 +2405,151 @@ msgstr "الانحدار الخطي" msgid "workspace.gradients.radial" msgstr "الانحدار الشعاعي" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "إبطال المحاذاة الدينماكية" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "عطّل المقياس التناسبي" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "عطّل المحاذاة إلى الخطوط الإرشادية" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "عطّل المحاذاة للبكسل" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "فعِّل المحاذاة الدينماكية" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "فعِّل المقياس النسبي" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "محاذاة إلى الخطوط الإرشادية" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "فعِّل المحاذاة للبكسل" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "أخفِ أسماء اللوحة" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "أخفِ لون اللوحة" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "أخفِ تشبيك البكسل" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "أخفِ المسطرات" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "أخفِ لوحة الخطوط" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "عدّل" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "الملف" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "المساعدة و المعلومة" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "التفضيلات" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "العرض" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "أعد" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "حدّد الكل" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "أظهر أسماء اللوحة" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "أظهر لون اللوحة" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "أظهر شبكة البكسل" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "أظهر المسطرات" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "أظهر لوخة أسلوب الخط" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "ارجع" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "صفّر" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "خطأ في حفظ الملف" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "حُفظ" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "الحفظ" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "وضع العرض (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "تكبير" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "ملء - تغيير الحجم لملء" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "احتواء - تصغير للاحتواء" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "التكبير لتناسب الكل" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "ملء الشاشة" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "تكبير" @@ -2597,19 +2557,19 @@ msgstr "تكبير" msgid "workspace.layout-grid.editor.title" msgstr "تحرير التشبيك" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "لا يوجد أنماط اللون في مكتبتك" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "لا يوجد أنماط الكتابة في مكتبتك" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "مكتبة الملفات" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "الألوان المؤخرة" @@ -2621,55 +2581,55 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "حفظ أسلوب اللون" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "مكتبة الملف" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "المكتبات في هذا الملف" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "المكتبات" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "تحديثات المكتبة" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "لا يوجد مكتبات مشتركة تحتاج إلى تحديث" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "لا يوجد ما يطابق هذا “%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "البحث في المكتبات المشتركة" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "مكتبات مشتركة" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "اتصل بمكتبة" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "افصل المكتبة" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "التحديث" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "رؤية جميع التغييرات" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "التحديثات" @@ -2689,35 +2649,35 @@ msgstr "تطميس المحدد" msgid "workspace.options.canvas-background" msgstr "خلفية اللوحة القماشية" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "محتوى المقطع" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "العنصر" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "حاشية" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "النسخ" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "أنشئ تعليق توضيحي" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "تحرير حاشية" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "أصل" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "لا توجد أصول في هذه المكتبة بعد" @@ -2761,11 +2721,11 @@ msgstr "أعلى" msgid "workspace.options.constraints.topbottom" msgstr "أعلى و أسفل" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "تصميم" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "تصدير" @@ -2773,23 +2733,23 @@ msgstr "تصدير" msgid "workspace.options.export-multiple" msgstr "تحديد المصدر" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "لاحقة" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "تم التصدير" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "جاري التصدير…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Export failed" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "التصدير بطيء بشكل غير متوقع" @@ -2813,67 +2773,67 @@ msgstr "جاري التخطيط" msgid "workspace.options.grid.auto" msgstr "تلقائي" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "أعمدة" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "لون" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "مزراب" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "طول" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "هامش" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "تعيين كافتراضي" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "أسفل" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "وسط" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "شمال" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "يمين" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "تمديد" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "أعلى" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "استعمل الإفتراضي" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "عرض" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "صفوف" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "مربع" @@ -2885,11 +2845,11 @@ msgstr "ملأ المجموعة" msgid "workspace.options.group-stroke" msgstr "ضرب المجموعة" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "طول" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspectفحص" @@ -3101,99 +3061,99 @@ msgstr "حين الضغط" msgid "workspace.options.interactions" msgstr "التفاعلات" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "اللون" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "احتراق اللون" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "انقاص كثافة اللون" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "أغمق" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "الفارق" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "الإستبعاد" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "ضوء الثابت" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "مسحة" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "تفتيح" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "لمعان" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "تضاعف" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "عادي" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "تراكب" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "التشبع" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "شاشة" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "ضوء خافت" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "أقصى ارتفاع" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "أقصى عرض" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "الحد الأدنو للارتفاع" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "الحد الأدنى للعرض" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "المزيد من الألوان" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "المزيد من ألوان المكتبة" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "العتامة" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "النموذج المبدئي" @@ -3217,19 +3177,19 @@ msgstr "أعلى الشمال" msgid "workspace.options.radius-top-right" msgstr "أعلى اليمين" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "مؤخر" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "أعد المحاولة" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "دوران" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "البخث عن نوع الخط" @@ -3237,7 +3197,7 @@ msgstr "البخث عن نوع الخط" msgid "workspace.options.select-a-shape" msgstr "اختر الشكل أو اللوحة لجر الإتصال الى لوحة أو شكل آخر" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "الألوان المختارة" @@ -3293,47 +3253,47 @@ msgstr "ظلال الإختيار" msgid "workspace.options.show-fill-on-export" msgstr "أظهر في المصدر" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "أظهر في وضع العرض" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "مقاس" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "الإعدادات المسبقة للحجم" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "الدائرة" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "الماس" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "سهم" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "لا شيء" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "دائري" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "مربع" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "مستطيل" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "المثلث" @@ -3341,35 +3301,35 @@ msgstr "المثلث" msgid "workspace.options.stroke-color" msgstr "لون الضرب" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "عرض الضرب" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "مركز" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "متقطع" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "منقط" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "داخل" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "مختلط" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "خارج" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "صلب" @@ -3405,11 +3365,11 @@ msgstr "العرض التلقائي" msgid "workspace.options.text-options.grow-fixed" msgstr "مثبت" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "تباعد الحروف" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "ارتفاع الخط" @@ -3453,84 +3413,84 @@ msgstr "تسطير (%s)" msgid "workspace.options.use-play-button" msgstr "استعمل زر التشغيل أعلاه لتشغيل منظر النموذج المبدئي." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "عرض" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X محور" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y محور" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "جر العقدة (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "صل العقد (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "الى الزاوية (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "الى المنحنى (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "دمج العقد (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "نقل العقد (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "فصل العقد (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "كسر العقد (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "افتح" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "عُدّل الملحق منذ آخر مرة فتحته. ويريد الآن أيضًا الوصول إلى:" -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "يُرجى ملاحظة أن هذا المكون الإضافي أُنشئ بواسطة طرف خارجي، لذا تأكد من " "الوثوق به قبل منحه حق الوصول. خصوصية بياناتك وأمنها مهمان بالنسبة لنا. إذا " "كانت لديك أي مخاوف، يُرجى التواصل مع الدعم." -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "ليس اﻵن" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "أتريد إلقاء نظرة؟ سيتم فتحه في مسودة جديدة لفريقك الحالي. (وإلا، يمكنك " "دائمًا العثور عليه في الملحقات المثبتة لأي ملف.)" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "إضافة تخطيط الثني" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "إضافة تخطيط التشبيك" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "أنشئ تنويعة" @@ -3542,7 +3502,7 @@ msgstr "أرسل الى الخلف" msgid "workspace.shape.menu.backward" msgstr "أرسل الى الخلف" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "انسخ" @@ -3550,27 +3510,27 @@ msgstr "انسخ" msgid "workspace.shape.menu.create-annotation" msgstr "أنشئ تعليق توضيحي" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "تحديد إلى اللوحة" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "أنشئ مكوّن" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "أنشى مكوّنات جديدة" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "قطع" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "حذف" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "حذف المخطط" @@ -3582,7 +3542,7 @@ msgstr "فصل المثال" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "فصل الامثلة" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "الفارق" @@ -3590,15 +3550,15 @@ msgstr "الفارق" msgid "workspace.shape.menu.duplicate" msgstr "ينسخ" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "تحرير" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "استبعاد" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "تسطيح" @@ -3610,7 +3570,7 @@ msgstr "Flip horizontal" msgid "workspace.shape.menu.flip-vertical" msgstr "التوجيه الرئسي" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "بداية المخطط" @@ -3622,39 +3582,39 @@ msgstr "النقل الى الأمام" msgid "workspace.shape.menu.front" msgstr "النقل الى الأمام" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "مجموعة" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "إخفاء" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "أظهر أو إخف UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "التفاعلات" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "إغلاق" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "قناع" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "معجون" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "طريق" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "إزالة تخطيط المنحنى" @@ -3670,11 +3630,11 @@ msgstr "استعادة العنصر الرئيسي" msgid "workspace.shape.menu.select-layer" msgstr "اختيار الطبقة" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "العرض" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "العرض في لوحة الاصول" @@ -3690,19 +3650,19 @@ msgstr "أطهر الصورة المصغرة" msgid "workspace.shape.menu.thumbnail-set" msgstr "تعيين كصورة مصغرة" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "فك التجميع" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "اتحاد" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "الغاء القفل" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "كشف القناع" @@ -3718,43 +3678,43 @@ msgstr "انهيار الشريط الجانبي" msgid "workspace.sidebar.expand" msgstr "توسيع الشريط الجانبي" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "التاريخ" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "الطبقات" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "العناصر" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "لوحات" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "مجموعات" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "صور" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "قناعات" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "بسومات" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "نصوص" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "السمات المستوردةSVG" @@ -3762,7 +3722,7 @@ msgstr "السمات المستوردةSVG" msgid "workspace.sidebar.sitemap" msgstr "صفحات" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "خريطة الموقع" @@ -3770,47 +3730,47 @@ msgstr "خريطة الموقع" msgid "workspace.toolbar.assets" msgstr "أصول" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "لوحة اللون (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "تعليقات (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "منحنى (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "الشكل البيضاوي (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "لوحة (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "صورة (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "تحريك (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "مسار (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "مستطيل (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "نص (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "الكتابات (%s)" @@ -3818,7 +3778,7 @@ msgstr "الكتابات (%s)" msgid "workspace.top-bar.read-only.done" msgstr "تم" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "لا يوجد تغييرات في التاريخ الى الآن" @@ -3958,19 +3918,19 @@ msgstr "أصل الكتابة" msgid "workspace.undo.entry.unknown" msgstr "انتهت العملية %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "رفض" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "مزيد من المعلومات" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "يوجد تحديثات في المكتبة المشتركة" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "تحديث" diff --git a/frontend/translations/bn.po b/frontend/translations/bn.po index 23859aedb6..2966ee6dde 100644 --- a/frontend/translations/bn.po +++ b/frontend/translations/bn.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.6-dev\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "অ্যাকাউন্ট আছে?" @@ -29,7 +29,7 @@ msgstr "" "এটি একটি ডেমো সার্ভিস। প্রয়োজনীয় কোনো কাজে ব্যবহার করবেন না। কিছু সময় পর " "প্রজেক্টগুলো মুছে ফেলা হবে।" -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "পাসওয়ার্ড ভুলে গেছেন?" @@ -37,27 +37,27 @@ msgstr "পাসওয়ার্ড ভুলে গেছেন?" msgid "auth.fullname" msgstr "পুরো নাম" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "এখানে লগিন করুন" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "গিটহাব" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "গিটল্যাব" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "গুগল" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "ওপেনআইডি" diff --git a/frontend/translations/ca.po b/frontend/translations/ca.po index 005dc3b908..f6e6d01277 100644 --- a/frontend/translations/ca.po +++ b/frontend/translations/ca.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:17+0000\n" -"Last-Translator: Aryiu \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Catalan " "\n" "Language: ca\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Ja teniu un compte?" @@ -33,7 +33,7 @@ msgstr "" "Aquest és un servei de PROVA. NO L'UTILITZEU en treballs reals, ja que els " "projectes s'eliminaran periòdicament." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Heu oblidat la contrasenya?" @@ -41,37 +41,37 @@ msgstr "Heu oblidat la contrasenya?" msgid "auth.fullname" msgstr "Nom complet" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Entreu al vostre compte" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Inicieu la sessió aquí." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "El Penpot és l'eina de disseny lliure i de codi obert per a la " "col·laboració entre el disseny i el codi" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -79,7 +79,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Escriviu una contrasenya nova" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "Ara formeu part de %s" @@ -95,7 +95,7 @@ msgstr "El perfil encara no s'ha verificat, feu-ho abans de continuar." msgid "auth.notifications.recovery-token-sent" msgstr "S'ha enviat un enllaç de recuperació de contrasenya al vostre correu." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Us heu unit a l'equip correctament" @@ -127,7 +127,7 @@ msgstr "Heu oblidat la contrasenya?" msgid "auth.recovery-submit" msgstr "Canvieu la contrasenya" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "No teniu un compte?" @@ -139,7 +139,7 @@ msgstr "Digueu-nos com us hem de dir al tauler i als correus electrònics." msgid "auth.register-account-title" msgstr "El vostre nom" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Creeu un compte" @@ -154,7 +154,7 @@ msgstr "" "Estic d'acord amb les [condicions del servei](%s) i la [política de " "privadesa](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Condicions del servei" @@ -176,93 +176,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "No s'ha trobat ningú amb @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publica" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Tots els usuaris de Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Segur que voleu eliminar l'enllaç? Si ho feu, ja no estarà disponible per a " "ningú" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(actual)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Destrueix l'enllaç" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Obtén l'enllaç" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "S'ha copiat l'enllaç correctament" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Gestió de permisos" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 pàgina compartida" msgstr[1] "%s pàgines compartides" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Pot comentar" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Pot inspeccionar el codi" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Qualsevol persona amb l'enllaç hi tindrà accés" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Pàgines compartides" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "L'enllaç per a compartir apareixerà aquí" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Només membres de l'equip" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Compartiu prototips" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Selecciona-ho tot" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Despublica" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Gestió de l'equip" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "El Penpot està pensat per a equips. Convida a membres i treballeu plegats " "en projectes i fitxers" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Fes equip!" @@ -278,33 +278,33 @@ msgstr "Restaura el fitxer" msgid "dashboard.add-file" msgstr "Afegeix un fitxer" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Afegeix a la biblioteca compartida" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Canvia el correu" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "MOU L'EQUIP" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Mou a:" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "" "Els projectes i els fitxers continuaran disponibles per als membres de " "l'equip. L'equip adoptarà la configuració de la nova organització." -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Selecciona una organització" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "Canvia l'organització de l'equip" @@ -364,23 +364,23 @@ msgstr "Mostra el registre de canvis complet" msgid "dashboard.check-updates.view-release-notes" msgstr "Mostra les notes de la versió" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Buida la paperera" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(còpia)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Crea una organització" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Crea un equip nou" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Fixa aquesta versió" @@ -388,7 +388,7 @@ msgstr "Fixa aquesta versió" msgid "dashboard.default-team-name" msgstr "El meu Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Esteu segur que voleu suprimir definitivament tots els projectes i fitxers " @@ -400,44 +400,44 @@ msgstr "" "Esteu segur que voleu suprimir definitivament %s? Aquesta acció és " "irreversible." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "S'han eliminat %s fitxers correctament." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Suprimeix definitivament" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Elimina el projecte" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Esteu segur que voleu suprimir definitivament el projecte %s? El suprimireu " "definitivament amb tots els fitxers que conté. Aquesta acció és " "irreversible." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "S'ha eliminat %s correctament." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Elimina l'equip" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "" "La vostra paperera és buida. Els fitxers i projectes eliminats apareixeran " "aquí." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Se suprimirà %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Baixa el fitxer Penpot (.penpot)" @@ -449,19 +449,19 @@ msgstr "Duplica" msgid "dashboard.duplicate-multi" msgstr "Duplica %s fitxers" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Quan un membre del projecte creï un esborrany, apareixerà aquí." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Encara no hi ha esborranys." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Quan un membre del projecte creï un fitxer, apareixerà aquí." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Encara no hi ha fitxers." @@ -509,23 +509,23 @@ msgstr "Importa un fitxer .penpot" msgid "dashboard.empty-project.start" msgstr "Comença a crear coses increïbles" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "S'ha produït un error en suprimir els fitxers." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "S'ha produït un error en suprimir el projecte %s." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "S'ha produït un error en restaurar el fitxer %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "S'ha produït un error en restaurar els fitxers." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "S'ha produït un error en restaurar el projecte %s i els seus fitxers." @@ -533,37 +533,37 @@ msgstr "S'ha produït un error en restaurar el projecte %s i els seus fitxers." msgid "dashboard.export-binary-multi" msgstr "Baixa %s fitxers Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exporta els taulers a PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exporta a PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s de %s elements seleccionats" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exporta" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Podeu afegir una configuració d'exportació a elements des de les propietats " "del disseny (a sota de la barra de la dreta)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informació sobre com establir exportacions a Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "No hi ha elements amb configuració d'exportació." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Selecció d'exportació" @@ -587,35 +587,35 @@ msgstr "Vista de quadrícula" msgid "dashboard.files-layout.list" msgstr "Vista de llista" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "S'ha eliminat la tipografia" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Descarta-ho tot" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Encara no teniu cap tipografia personalitzada instal·lada." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Encara no hi ha tipografies personalitzades." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Quan un membre del projecte pengi una tipografia personalitzada, apareixerà " "aquí." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "S'ha afegit 1 tipografia" msgstr[1] "S'han afegit %s tipografies" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -625,7 +625,7 @@ msgstr "" "sola família tipogràfica**. Podeu pujar tipografies en aquests formats: " "**TTF, OTF i WOFF** (només en cal un)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -635,11 +635,11 @@ msgstr "" "Penpot](%s). També podeu llegir sobre les [llicències de les " "tipografies](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Puja-ho tot" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -650,23 +650,23 @@ msgstr "" "utilitzar el [Transfonter](https://transfonter.org/) per generar " "tipografies web i corregir errors. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Ves a la consola d'administració" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "Darrera modificació %s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importa fitxers de Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Vaja! No s'ha pogut importar aquest fitxer" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "S'ha vinculat automàticament 1 biblioteca pel nom." @@ -676,41 +676,41 @@ msgstr[1] "S'han vinculat automàticament %s biblioteques pel nom." msgid "dashboard.import.bad-url" msgstr "La importació ha fallat. L'URL de la plantilla és incorrecte" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "Confirma els vincles de biblioteca" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "No hem pogut verificar aquest fitxer." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Aquest fitxer sembla malmès." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "No s'han importat tots els fitxers" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Els fitxers següents tenen errors:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Els fitxers amb errors no es penjaran." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "S'ha produït un error en processar aquest fitxer." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "S'ha importat 1 fitxer correctament." msgstr[1] "S'han importat %s fitxers correctament." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Alguns fitxers contenen objectes no vàlids que s'han eliminat." @@ -718,79 +718,51 @@ msgstr "Alguns fitxers contenen objectes no vàlids que s'han eliminat." msgid "dashboard.import.no-perms" msgstr "No teniu permís per importar en aquest equip" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "S'estan carregant els colors" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "S'estan carregant els components" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "S'està carregant la multimèdia" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "S'està carregant la pàgina: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "S'estan processant les tipografies" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "S'estan penjant les dades al servidor (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "S'està pujant el fitxer: %s" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "" "Algunes biblioteques no s'han pogut vincular automàticament. Seleccioneu la " "biblioteca correcta per a cadascuna:" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "Reviseu els vincles de biblioteca abans de confirmar:" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "Vinculades" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "Cap biblioteca seleccionada" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Esteu a punt de suprimir les invitacions de:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Esteu a punt de reenviar les invitacions de:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Suprimeix les invitacions" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Reenvia les invitacions" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "No teniu permís per convidar persones a unir-se a aquest equip ni per " "editar o suprimir invitacions." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Convida a l'equip" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "No teniu permís per convidar persones a aquest equip" @@ -802,31 +774,31 @@ msgstr "Abandona l'organització" msgid "dashboard.leave-organization.toast" msgstr "Ja no formeu part de l'organització %s." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Abandona l'equip" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Biblioteques i plantilles" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Aquí teniu algunes biblioteques i plantilles que podeu afegir al vostre " "projecte" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Explora'n més i coneix com contribuir-hi" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "" "Hi ha hagut un problema en importar la plantilla. La plantilla no s'ha " "importat." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Biblioteques compartides" @@ -834,7 +806,7 @@ msgstr "Biblioteques compartides" msgid "dashboard.loading-files" msgstr "S'estan carregant els fitxers…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "s'estan carregant les tipografies…" @@ -854,63 +826,63 @@ msgstr "Mou %s fitxers a" msgid "dashboard.move-to-other-team" msgstr "Mou a un altre equip" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Fitxer nou" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Fitxer nou" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Crea un projecte" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Crea un projecte" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "No s'ha trobat cap coincidència amb “%s“" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "No teniu permís per afegir equips a cap de les vostres organitzacions." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "No teniu permís per crear equips dins de l'organització %s. Si necessiteu " "més informació, contacteu amb la persona propietària de l'organització." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "No teniu permís per suprimir equips que formen part de l'organització %s. " "Si necessiteu més informació, contacteu amb la persona propietària de " "l'organització." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "No teniu permís per moure equips que formen part de l'organització %s. Si " "necessiteu més informació, contacteu amb la persona propietària de " "l'organització." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Els projectes fixats apareixeran aquí" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notificacions" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "S'ha actualitzat l'adreça de correu" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "S'ha verificat l'adreça de correu" @@ -922,11 +894,11 @@ msgstr "S'ha actualitzat la configuració de les notificacions" msgid "dashboard.notifications.password-saved" msgstr "La contrasenya s'ha desat correctament!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Mostra les notificacions" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s membres" @@ -934,23 +906,23 @@ msgstr "%s membres" msgid "dashboard.open-in-new-tab" msgstr "Obre el fitxer en una pestanya nova" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opcions" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Ordena per rol" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Ordena per estat" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "S'ha suprimit l'organització %s." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "Altres equips" @@ -974,11 +946,11 @@ msgstr "Ara sou propietaris d'aquest equip." msgid "dashboard.permissions-change.viewer" msgstr "Ara sou lectors d'aquest equip." -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "Projectes personals" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Fixa/Deixa de fixar" @@ -994,27 +966,27 @@ msgstr "No es pot analitzar el manifest de l'extensió" msgid "dashboard.plugins.try-plugin" msgstr "Prova l'extensió: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "S'estan suprimint els fitxers…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "S'estan restaurant els fitxers…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "Supressió inesperadament lenta" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "Restauració inesperadament lenta" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projectes" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Voleu eliminar el vostre compte?" @@ -1022,116 +994,116 @@ msgstr "Voleu eliminar el vostre compte?" msgid "dashboard.removed-from-team" msgstr "Ja no formeu part de l'equip “%s“." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "" "Esteu a punt de restaurar tots els vostres projectes i fitxers. Això pot " "trigar una estona." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Restaura tots els projectes i fitxers" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Restaura-ho tot" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "S'han restaurat %s fitxers correctament." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Restaura el projecte" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Esteu a punt de restaurar el projecte %s i tots els fitxers que conté." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Restaura el projecte" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "S'ha restaurat %s correctament." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Desa la configuració" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Cerca…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "S'està cercant “%s“…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Organitzacions" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "Equips" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "AFEGEIX A UNA ORGANITZACIÓ" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "Trieu una organització:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" només permet que els membres de l'organització s'uneixin als seus " "equips. Les invitacions pendents a persones de fora de l'organització es " "revocaran." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Les invitacions pendents a usuaris externs se cancel·laran." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Aquí trobareu les organitzacions de les quals formeu part on teniu permís " "per moure l'equip." -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "" "Aquí trobareu les organitzacions de les quals formeu part on teniu permís " "per crear o afegir equips." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "Selecciona una organització" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "Afegeix l'equip a una organització" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Selecciona la llengua de la interfície" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Selecciona un tema" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Dark (per defecte)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Light" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Tema del sistema" @@ -1195,11 +1167,11 @@ msgstr "Actualitza la configuració" msgid "dashboard.settings.notifications.title" msgstr "Notificacions" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Mostra tots els fitxers" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Historial de versions" @@ -1223,7 +1195,7 @@ msgstr[1] "S'han duplicat els fitxers" msgid "dashboard.success-duplicate-project" msgstr "S'ha eliminat el projecte" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "S'ha mogut el fitxer" @@ -1235,182 +1207,182 @@ msgstr "S'han mogut els fitxers" msgid "dashboard.success-move-project" msgstr "S'ha mogut el projecte" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s ara forma part de %s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Informació de l'equip" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Membres de l'equip" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s ja no forma part de %s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Organització de l'equip" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Afegeix a una organització" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "No teniu permís per afegir aquest equip a cap de les vostres organitzacions." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Canvia l'organització de l'equip" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Només es pot convidar persones de la vostra organització." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "Aquest equip no forma part de cap organització" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Elimina l'equip de l'organització" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Projectes de l'equip" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Afegeix al vostre projecte" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema de la interfície" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Resultats de la cerca" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Els fitxers eliminats romandran a la paperera durant" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr "%s dies. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "Després d'això, se suprimiran definitivament." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Si canvieu d'opinió, podeu restaurar-los o suprimir-los definitivament des " "del menú de cada fitxer." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Escriviu per cercar resultats" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Despublica la biblioteca" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Actualitza la configuració" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "Ja no sou membre de l'organització %s" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Beta" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "El renderatge WebGL pot millorar el rendiment, però és en fase beta i " "potser menys estable. Poden aparèixer diferències visuals entre el llenç, " "les exportacions (SVG/PDF) i el mode de vista." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Desactivat" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Activat" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Doneu-nos la vostra opinió" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Estat" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "Renderatge WebGL" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "És actiu" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Quan s'activi aquest webhook, es lliuraran els detalls de l'esdeveniment" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Només podeu suprimir o modificar els webhooks que hàgiu creat." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Tipus de contingut" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Crea un webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "S'ha creat el webhook correctament." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Els webhooks són una manera senzilla de permetre que altres llocs web i " "aplicacions rebin avisos quan passen determinats esdeveniments al Penpot. " "Enviarem una petició POST a cadascuna de les URL que indiqueu." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Premeu el botó \"Crea un webhook\" per afegir-ne un." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Encara no s'ha creat cap webhook." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "El meu compte" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Correu electrònic" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Nom" @@ -1426,11 +1398,11 @@ msgstr "Atenció" msgid "ds.component-subtitle" msgstr "Components a actualitzar:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Permet" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Cancel·la" @@ -1450,7 +1422,7 @@ msgstr "No hi ha tokens aplicables als conjunts ni als temes actius." msgid "ds.inputs.numeric-input.no-matches" msgstr "No s'ha trobat cap coincidència." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Obre la llista de tokens" @@ -1466,15 +1438,15 @@ msgstr "Proveïdor d'autenticació no permès per a aquest perfil" msgid "errors.auth-provider-not-configured" msgstr "L'autenticació del proveïdor no està configurada." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Sembla que no esteu autenticat o que la sessió ha caducat." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "No s'ha pogut carregar el tipus de lletra %s" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "No s'han pogut carregar els tipus de lletra %s" @@ -1486,57 +1458,57 @@ msgstr "No es pot carregar el fitxer multimèdia." msgid "errors.character-limit-exceeded" msgstr "S'ha superat el límit de caràcters" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "No es pot afegir la biblioteca: això crearia una dependència circular" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "L'API del porta-retalls no està disponible. Serviu el Penpot amb HTTPS per " "a habilitar l'accés al porta-retalls" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "El vostre navegador no pot fer aquesta operació" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "Accés al porta-retalls denegat. Permeteu l'accés al porta-retalls al vostre " "navegador per a enganxar contingut" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "S'ha produït un error amb el comentari" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Ho sentim! Aquest és un fitxer antic que utilitza un tipus d'element del " "Penpot obsolet i no el podeu obrir." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "perquè us puguem ajudar." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Tot i que el Penpot ja no admet aquest tipus d'elements, podeu" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "contactar amb nosaltres" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "No es pot arrossegar un conjunt pare a un camí fill propi." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Aquest correu ja està en ús" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Aquest correu ja està validat." @@ -1548,29 +1520,29 @@ msgstr "No podeu fer servir l'adreça de correu com a contrasenya" msgid "errors.email-does-not-match-invitation" msgstr "El correu electrònic no coincideix amb la invitació." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domini no permès" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "El correu «%s» té molts informes de retorn permanents." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "El correu «%s» s'ha marcat com a brossa o rebot permanent." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Sembla que esteu obrint un fitxer que té la funcionalitat «%s» habilitada, " "però la versió actual del Penpot no l'admet o la té inhabilitada." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "La funcionalitat «%s» no s'admet." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Ha de contenir com a màxim %s caràcters." @@ -1578,7 +1550,7 @@ msgstr "Ha de contenir com a màxim %s caràcters." msgid "errors.field-min-length" msgstr "Ha de contenir com a mínim 1 caràcter." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Camp buit" @@ -1586,24 +1558,24 @@ msgstr "Camp buit" msgid "errors.field-not-all-whitespace" msgstr "El nom ha de contenir algun caràcter que no sigui un espai." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Sembla que hi ha una discrepància entre les funcionalitats habilitades i " "les del fitxer que intenteu obrir. Cal aplicar les migracions de «%s» abans " "de poder obrir el fitxer." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "El nom de la família tipogràfica només pot contenir lletres, números, " "espais, guions, guions baixos i punts." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Alguna cosa ha anat malament." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "S'ha produït un problema amb el web worker." @@ -1619,7 +1591,7 @@ msgstr "Dades no vàlides" msgid "errors.invalid-email" msgstr "Escriviu un correu electrònic vàlid, si us plau" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "El correu de confirmació ha de coincidir" @@ -1656,11 +1628,11 @@ msgstr "Aquesta invitació pot estar cancel·lada o caducada." msgid "errors.ldap-disabled" msgstr "L'autenticació LDAP està inhabilitada." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Heu arribat a la quota de «%s». Contacteu amb l'assistència." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "S'ha arribat al nombre màxim (%s) de correus electrònics que es poden " @@ -1680,21 +1652,21 @@ msgstr "" msgid "errors.media-type-not-allowed" msgstr "Sembla que la imatge no és vàlida." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "El perfil que estàs convidant té els missatges de correu silenciats (per " "informes de correu brossa o de retorns alts)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migració en curs" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Només la persona creadora de la versió la pot bloquejar" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Només la persona creadora de la versió la pot desbloquejar" @@ -1708,7 +1680,7 @@ msgstr "" msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "La persona propietària de l'organització no pot abandonar-la." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "Aquesta organització no existeix" @@ -1720,7 +1692,7 @@ msgstr "La contrasenya de confirmació ha de coincidir" msgid "errors.password-too-short" msgstr "La contrasenya ha de tenir 8 caràcters com a mínim" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Dades no vàlides al porta-retalls" @@ -1728,7 +1700,7 @@ msgstr "Dades no vàlides al porta-retalls" msgid "errors.profile-blocked" msgstr "El perfil està bloquejat" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "El teu perfil té els missatges de correu silenciats (per informes de correu " @@ -1738,7 +1710,7 @@ msgstr "" msgid "errors.registration-disabled" msgstr "El registre està desactivat." -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:700 msgid "errors.save-failed" msgstr "" "Els vostres últims canvis no s'han confirmat com a desats. Pot ser que el " @@ -1746,21 +1718,21 @@ msgstr "" "pestanya: podríeu perdre canvis. Contacteu amb l'assistència per a obtenir " "ajuda." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "L'SVG no és vàlid o està mal format" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "S'ha detectat la funcionalitat incompatible «%s»" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "No hi ha suficients membres com per a abandonar l'equip, potser voleu " "eliminar-lo." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "El membre que intenteu assignar no existeix." @@ -1768,7 +1740,7 @@ msgstr "El membre que intenteu assignar no existeix." msgid "errors.team-leave.only-owner-can-delete" msgstr "Només la persona propietària d'un equip el pot suprimir." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "El propietari no pot abandonar l'equip, heu de reassignar el rol de " @@ -1782,13 +1754,13 @@ msgstr "El nom de l'equip no pot contenir cap dels caràcters següents: «.», msgid "errors.token-set-already-exists" msgstr "Ja existeix un conjunt amb el mateix nom" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "No es pot completar l'arrossegament: ja existeix un conjunt amb el mateix " "nom en aquest camí." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Ja existeix una opció de tema amb el mateix nom" @@ -1800,13 +1772,13 @@ msgstr "El tema fa referència a conjunts que no existeixen: %s" msgid "errors.tokens.circular-reference" msgstr "El token té una referència circular" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "L'interlineat depèn de la mida de la lletra. Afegiu una mida de lletra per " "a obtenir el valor resolt." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "Aquest camp no pot estar buit" @@ -1826,13 +1798,13 @@ msgstr "Error d'importació:" msgid "errors.tokens.invalid-color" msgstr "Valor de color no vàlid: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "" "Valor de token no vàlid: només podeu fer referència a un token de tipus " "font-family" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Valor de pes de lletra no vàlid: utilitzeu valors numèrics (100-950) o noms " @@ -1853,37 +1825,37 @@ msgstr "" "Els noms de token només poden contenir lletres i dígits separats per " "caràcters . i no poden començar amb el signe $." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "Tipus d'ombra no vàlid: només s'accepten «innerShadow» o «dropShadow»" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Valor de token no vàlid: només s'accepten none, Uppercase, Lowercase o " "Capitalize" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "Valor de token no vàlid: només s'accepten none, underline i strike-through" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "Valor no vàlid: ha de fer referència a un token d'ombra compost." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "Valor no vàlid: ha de fer referència a un token tipogràfic compost." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Valor de token no vàlid: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Referències de token que falten: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "" "El nom de grup de %s entra en conflicte amb un token del mateix nom en un " @@ -1897,31 +1869,31 @@ msgstr "No s'ha trobat cap token, conjunt ni tema en aquest fitxer." msgid "errors.tokens.number-too-large" msgstr "Valor de token no vàlid. El valor resolt és massa gran: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "L'opacitat ha d'estar entre 0 i 100% o entre 0 i 1 (p. ex. 50% o 0,5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "El token té una autoreferència" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "El difuminat (blur) de l'ombra ha de ser superior o igual a 0." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "L'extensió (spread) de l'ombra ha de ser superior o igual a 0." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "L'amplada del traç ha de ser superior o igual a 0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Valor no vàlid: no es permet %." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Valor no vàlid: no es permeten unitats." @@ -1929,19 +1901,19 @@ msgstr "Valor no vàlid: no es permeten unitats." msgid "errors.unexpected-error" msgstr "S'ha produït un error inesperat." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Token desconegut" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Aquesta versió ja està bloquejada" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Aquesta versió està bloquejada i la resta de persones no la poden suprimir" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "El fitxer té un número de versió incompatible" @@ -1974,39 +1946,39 @@ msgstr "Com a mínim 1 lletra majúscula" msgid "errors.weak-password.too-short" msgstr "Com a mínim 8 caràcters" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "El WebGL ha deixat de funcionar. Recarregueu la pàgina per a restablir-lo" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Ep! S'ha perdut el context del llenç" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Error de connexió, l'URL no és accessible" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "L'URL no supera la validació." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "L'últim lliurament no ha anat bé." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Error en la validació SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Temps d'espera esgotat" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Error inesperat en validar" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Estat inesperat %s" @@ -2026,15 +1998,15 @@ msgstr "Descripció" msgid "feedback.description-placeholder" msgstr "Descriviu el motiu del vostre comentari" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "Ens encanta que sigueu aquí. Si necessiteu ajuda, cerqueu abans de publicar." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Comunitat de Penpot" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Altres maneres de contactar amb nosaltres" @@ -2059,11 +2031,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Contacteu amb nosaltres" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Compte per a ajudar amb dubtes tècnics." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Compte de X d'ajuda" @@ -2083,43 +2055,39 @@ msgstr "Idea" msgid "feedback.type.issue" msgstr "Incidència" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Baixa els fitxers" - -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "Els recursos de les biblioteques vinculades no s'inclouran al fitxer." -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "Tracta els recursos com a objectes bàsics" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "" "Els fitxers amb biblioteques vinculades s'inclouran a l'exportació i " "mantindran els vincles. " -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "Exporta el fitxer + biblioteques" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "En importar, podreu vincular biblioteques existents amb noms coincidents." -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "Vincula les biblioteques coincidents en importar" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "" "El vostre fitxer s'exportarà amb els recursos de biblioteca fusionats a la " "biblioteca local." -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "Incrusta els recursos de biblioteca al fitxer" @@ -2127,7 +2095,7 @@ msgstr "Incrusta els recursos de biblioteca al fitxer" msgid "generic.error" msgstr "S'ha produït un error" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "Aplica" @@ -2175,7 +2143,7 @@ msgstr "Previsualització de la imatge d'emplenat de la forma" msgid "inspect.attributes.shadow" msgstr "Ombra" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Mida i posició" @@ -2183,43 +2151,31 @@ msgstr "Mida i posició" msgid "inspect.attributes.stroke" msgstr "Traç" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centre" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Interior" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Exterior" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografia" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Família tipogràfica" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Mida de la lletra" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Estil de la lletra" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Espaiat de la lletra" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Alçada de la línia" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "Famílies tipogràfiques mixtes" @@ -2235,11 +2191,11 @@ msgstr "Ratllat" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Subratllat" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Inicials en majúscules" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Minúscules" @@ -2251,7 +2207,7 @@ msgstr "Cap" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Sense assignar" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Majúscules" @@ -2379,23 +2335,23 @@ msgstr "Valor resolt:" msgid "inspect.tabs.styles.variants-panel" msgstr "Propietats de les variants" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "Creeu un token d'accés nou" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "Premeu el botó \"Creeu un token d'accés nou\" per a generar-ne un." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "De moment no teniu cap token." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "Tokens d'accés personals" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "Els tokens d'accés personals funcionen com una alternativa al nostre " @@ -2480,53 +2436,53 @@ msgstr "La clau MCP caducarà el %s" msgid "integrations.mcp-key.will-not-expire" msgstr "La clau MCP no té data de caducitat" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "El servidor MCP del Penpot permet que els clients MCP interactuïn " "directament amb els fitxers de disseny del Penpot." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "Com configurar els clients MCP" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "Aquesta és l'URL del servidor que haureu de configurar al vostre client MCP " "per a connectar-lo al servidor MCP del Penpot." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "Regenera la clau MCP" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "Clau MCP" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "La clau MCP és necessària per a configurar el client MCP" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Estat" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Desactivat" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Activat" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "La clau MCP utilitzada per a connectar-vos al servidor MCP ha caducat. Per " "tant, no es pot establir la connexió." -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "" "Regenereu la clau MCP i actualitzeu la configuració del vostre client amb " @@ -2536,10 +2492,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "Servidor MCP" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "Beta" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "Nom" @@ -2590,7 +2542,7 @@ msgstr "Regenera la clau MCP" msgid "integrations.regenerate-mcp-key.title.created" msgstr "S'ha regenerat la clau MCP" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "Integracions" @@ -2608,47 +2560,47 @@ msgstr "El token caducarà el %s" msgid "integrations.token.will-not-expire" msgstr "El token no té data de caducitat" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Marca-ho tot com a llegit" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Dreceres" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Quant a Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Acceptar" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Actiu" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Afegeix" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Afegeix tipografia" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "S'està afegint…" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Administració" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Tots" @@ -2656,17 +2608,17 @@ msgstr "Tots" msgid "labels.and" msgstr "i" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Enrere" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Sembla que heu d'esperar una mica i tornar a provar; estem realitzant un " "petit manteniment dels nostres servidors." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Error del servidor (Bad Gateway)" @@ -2678,23 +2630,23 @@ msgstr "Difuminat" msgid "labels.blur-effects" msgstr "Efectes de difuminat" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Cancel·la" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "Comprova si hi ha actualitzacions" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "S'està comprovant si hi ha actualitzacions…" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Tanca" @@ -2702,7 +2654,7 @@ msgstr "Tanca" msgid "labels.code" msgstr "Codi" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Replega" @@ -2714,15 +2666,15 @@ msgstr "Color" msgid "labels.comment" msgstr "Comentari" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Comentaris" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Comunitat" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Comunitat i contribucions" @@ -2734,19 +2686,19 @@ msgstr "Calculat" msgid "labels.confirm-password" msgstr "Confirmeu la contrasenya" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Contacta amb l'assistència" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Contacteu amb nosaltres" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continua" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Podeu continuar amb un compte de Penpot" @@ -2758,23 +2710,23 @@ msgstr "Copia" msgid "labels.copy-color" msgstr "Copia el color" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Copia l'enllaç" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-actualitat" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Crea" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Crea un equip nou" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Introduïu el nom de l'equip nou" @@ -2782,7 +2734,7 @@ msgstr "Introduïu el nom de l'equip nou" msgid "labels.current" msgstr "Actual" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Tauler" @@ -2790,7 +2742,7 @@ msgstr "Tauler" msgid "labels.default" msgstr "Predeterminat" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Elimina" @@ -2802,7 +2754,7 @@ msgstr "Elimina el comentari" msgid "labels.delete-comment-thread" msgstr "Elimina el fil" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Esborra invitació" @@ -2810,39 +2762,35 @@ msgstr "Esborra invitació" msgid "labels.delete-multi-files" msgstr "Elimina %s fitxers" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "Esteu segur que voleu suprimir la foto de perfil?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "Suprimeix la foto de perfil" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Suprimits" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Desenvolupament" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Descarta" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "Baixa %s" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "Baixa" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "S'està baixant el fitxer…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Esborranys" @@ -2850,19 +2798,19 @@ msgstr "Esborranys" msgid "labels.duplicate" msgstr "Duplica" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Edita" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Editor" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Buit" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Error" @@ -2870,11 +2818,11 @@ msgstr "Error" msgid "labels.exit" msgstr "Surt" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Ha caducat" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exporta" @@ -2886,7 +2834,7 @@ msgstr "Opinions desactivades" msgid "labels.feedback-sent" msgstr "S'ha enviat l'opinió" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2894,63 +2842,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Emplenament" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "Disposició flexible" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Família tipogràfica" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Estils" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Tipografies" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Repositori Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Envia opinions" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Enrere" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Disseny gràfic" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "Disposició de quadrícula" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Centre d'ajuda" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Ajuda i aprenentatge" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Amaga" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Amaga els comentaris resolts" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importa" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inactiu" @@ -2958,53 +2906,53 @@ msgstr "Inactiu" msgid "labels.info" msgstr "Informació" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Tipografies instal·lades" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "Integracions" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Alguna cosa ha fallat." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "" "Podeu tornar a provar l'operació o contacteu amb l'assistència per informar " "de l'error." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Error intern" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Invitacions" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Llengua" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Disposició" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Centre d'aprenentatge" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Biblioteques i plantilles" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "S'està carregant…" @@ -3012,7 +2960,7 @@ msgstr "S'està carregant…" msgid "labels.lock" msgstr "Bloqueja" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Inicia sessió o registra'm" @@ -3020,19 +2968,19 @@ msgstr "Inicia sessió o registra'm" msgid "labels.login" msgstr "Entra" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Tanca la sessió" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Màrqueting" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Membre" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Membres" @@ -3044,43 +2992,43 @@ msgstr "Menció" msgid "labels.new-password" msgstr "Contrasenya nova" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Següent" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "" "Aquesta instància necessita un pas de configuració que arriba amb la versió " "més recent." -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "Consulteu la nostra" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "El Penpot encara no es pot iniciar." -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "guia tècnica" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "" "El Penpot no està disponible temporalment per un problema del sistema. " "Torneu-ho a provar d'aquí a una estona." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "No teniu notificacions de comentaris pendents" -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "No hi ha invitacions." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Reuniu la vostra gent i creeu grans coses plegats." @@ -3088,27 +3036,27 @@ msgstr "Reuniu la vostra gent i creeu grans coses plegats." msgid "labels.no-matches" msgstr "Cap coincidència" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Vaja!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notificacions" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 fitxer" msgstr[1] "%s fitxers" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 tauler" msgstr[1] "%s taulers" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 projecte" @@ -3118,43 +3066,43 @@ msgstr[1] "%s projectes" msgid "labels.old-password" msgstr "Contrasenya antiga" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "Obre" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Opcions" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Altra (especifiqueu)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Altra" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Propietari" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Contrasenya" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Pendent" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Registre de canvis del Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot hub" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Projectes fixats" @@ -3162,31 +3110,31 @@ msgstr "Projectes fixats" msgid "labels.post" msgstr "Publica" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Anterior" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Disseny de producte o UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Gestió de productes" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Perfil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projectes" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Recents" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Referència" @@ -3194,27 +3142,27 @@ msgstr "Referència" msgid "labels.refresh" msgstr "Actualitza" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Notes de la versió" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Recarrega la pàgina" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Elimina" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Elimina membre" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Canvia el nom" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Canvia el nom de l’equip" @@ -3238,11 +3186,11 @@ msgstr "resposta nova" msgid "labels.reply.thread" msgstr "Respon" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Reenvia" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Reenvia invitació" @@ -3250,43 +3198,43 @@ msgstr "Reenvia invitació" msgid "labels.restore" msgstr "Restaura" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Tornar a intentar-ho" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rol" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Desa" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Cerca" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Cerca tipografia" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Seleccioneu una opció" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Envia" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "S'està enviant…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Estem de manteniment programat dels nostres sistemes." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Servei no disponible" @@ -3294,70 +3242,70 @@ msgstr "Servei no disponible" msgid "labels.sets" msgstr "Sets" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Configuració" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Ombra" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Comparteix" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Biblioteques compartides" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Mostra" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Mostra tots els comentaris" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Mostra la llista de comentaris" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Mostra només les vostres mencions" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Mostra només els meus comentaris" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Recursos" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "" "L'inici de sessió amb el proveïdor d'identitat de la vostra organització no " "s'ha completat. Pot ser que el proveïdor no estigui disponible o que el " "vostre compte encara no figuri al seu directori." -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "Torneu-ho a provar" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "No s'ha pogut iniciar la sessió a %s" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Comença" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Estat" @@ -3365,7 +3313,7 @@ msgstr "Estat" msgid "labels.stroke" msgstr "Traç" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Estudiant o professor" @@ -3377,7 +3325,7 @@ msgstr "Estils" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Intercanvia" @@ -3385,19 +3333,19 @@ msgstr "Intercanvia" msgid "labels.text" msgstr "Text" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Temes" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutorials" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Tipografia" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Error desconegut" @@ -3409,35 +3357,31 @@ msgstr "Desbloqueja" msgid "labels.unpublish-multi-files" msgstr "Despublica %s fitxers" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Actualitza" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Actualitza l'equip" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Puja" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Pugeu tipografies personalitzades" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "S'està pujant…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "S'està pujant el fitxer…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variant" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Notes de la versió %s" @@ -3445,7 +3389,7 @@ msgstr "Notes de la versió %s" msgid "labels.view-only" msgstr "Només lectura" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Visor" @@ -3459,7 +3403,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s avís" msgstr[1] "%s avisos" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -3467,11 +3411,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Escriu un comentari nou" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(jo)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "El meu compte" @@ -3619,7 +3563,7 @@ msgstr "" "Sou l'únic membre d'alguns dels equips. Aquests equips se suprimiran " "juntament amb els seus projectes i fitxers." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "ABANS D'ABANDONAR L'ORGANITZACIÓ" @@ -3627,80 +3571,80 @@ msgstr "ABANS D'ABANDONAR L'ORGANITZACIÓ" msgid "modals.before-leave-organization.warning" msgstr "Qualsevol equip del qual sigueu l'únic membre se suprimirà." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Màxima" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Repetiu el correu electrònic nou" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Us enviarem un correu electrònic a l'adreça actual \"%s\" per a verificar " "la vostra identitat." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Correu electrònic nou" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Canvia el correu" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Canvieu el vostre correu electrònic" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Crea un webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Crea un webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Payload URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Cancel·la i conserva el meu compte" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Sí, elimina el meu compte" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Si suprimiu el compte, perdreu tots els vostres projectes i arxius actuals." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "Aquesta acció és irreversible. El vostre compte, la subscripció i les " "organitzacions se suprimiran immediatament. També perdreu l'accés als " "vostres projectes i fitxers actuals." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organitzacions que se suprimiran" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s membres" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s equips" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Segur que voleu eliminar el compte?" @@ -3760,23 +3704,23 @@ msgstr "Segur que voleu eliminar %s fitxers?" msgid "modals.delete-file-multi-confirm.title" msgstr "S'estan eliminant %s fitxers" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Segur que voleu eliminar aquest estil de lletra? No es carregarà si " "s'utilitza en un fitxer." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "S'està eliminant l'estil de tipografia" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Segur que voleu eliminar aquesta tipografia? No es carregarà si s'utilitza " "en un fitxer." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "S'està eliminant la tipografia" @@ -3784,19 +3728,19 @@ msgstr "S'està eliminant la tipografia" msgid "modals.delete-organization-team-confirm.message" msgstr "Segur que voleu suprimir aquest equip que forma part de l'organització %s?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Segur que voleu eliminar la pàgina?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Elimina la pàgina" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "Segur que voleu suprimir les pàgines seleccionades?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "Elimina les pàgines" @@ -3812,31 +3756,31 @@ msgstr "Segur que voleu eliminar el projecte?" msgid "modals.delete-project-confirm.title" msgstr "Elimina el projecte" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Suprimeix el fitxer" msgstr[1] "Suprimeix els fitxers" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "No està activada en cap fitxer." msgstr[1] "No estan activades en cap fitxer." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Aquesta biblioteca està activada aquí: " msgstr[1] "Aquestes biblioteques estan activades aquí: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Esteu segur que voleu suprimir aquest fitxer?" msgstr[1] "Esteu segur que voleu suprimir aquests fitxers?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "S'està suprimint el fitxer" @@ -3856,19 +3800,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "S'està eliminant l'equip" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Elimina el membre" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Segur que voleu eliminar aquest membre de l'equip?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Elimina el membre de l'equip" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3878,23 +3822,23 @@ msgstr[1] "" "Els recursos que ja s'hagin utilitzat en aquests fitxers hi romandran (cap " "disseny no es farà malbé)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Suprimeix el webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Segur que voleu suprimir aquest webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "S'està suprimint el webhook" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Edita el webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Edita el webhook" @@ -3912,15 +3856,15 @@ msgstr "Importa els tokens" msgid "modals.import-library-tokens.title" msgstr "Voleu importar els tokens de la biblioteca?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Envia una invitació" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Correus electrònics, separats per una coma" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "Alguns membres ja són a l'equip. Convidarem la resta." @@ -3941,99 +3885,99 @@ msgstr "" "totes aquestes persones ja en són membres. Si necessiteu més informació, " "contacteu amb la persona propietària de l'organització." -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "Adreces que no rebran cap invitació" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "Cancel·la" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "Només es pot convidar membres existents de l'organització." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "Envia les invitacions" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "Algunes invitacions no es poden enviar" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "Algunes persones de la vostra llista no rebran les invitacions." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Podeu convidar membres a l'equip perquè puguin accedir a aquest fitxer i a " "tots els fitxers de l'equip." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Convida membres a l'equip" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Com que no hi ha ningú més a aquest equip, s'eliminarà l'equip amb els seus " "arxius i projectes." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Segur que vols deixar l'equip %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "No es pot abandonar l'equip si no hi ha cap altre membre capaç d'ascendir a " "propietari. És possible que vulgueu eliminar l'equip." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Sou el propietari d’aquest equip. Seleccioneu una altra persona integrant " "per a promoure-la al rol de propietat." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Ascendeix i abandona" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Seleccioneu un membre per a ascendir" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Seleccioneu un membre per a ascendir" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Abandona l'equip" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Segur que voleu deixar l'equip?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "S'està abandonant l'equip" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Sou propietari d'alguns equips de l'organització. Promoveu un altre membre " "a propietari." -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Sou l'únic membre d'alguns dels equips. Aquests equips se suprimiran " "juntament amb els seus projectes i fitxers." -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "A més, sou propietari d'alguns equips de l'organització. Promoveu un altre " @@ -4053,57 +3997,57 @@ msgstr "" msgid "modals.leave-organization-confirm.title" msgstr "Voleu abandonar l'organització %s?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Mou" msgstr[1] "Mou" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Segur que voleu moure aquesta biblioteca?" msgstr[1] "Segur que voleu moure aquestes biblioteques?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Mou la biblioteca" msgstr[1] "Mou les biblioteques" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Quantitat d'atracció" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Ascendeix" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Si transferiu la propietat, canviareu el vostre rol a admin, perdent alguns " "permisos sobre l'equip. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Actualment sou el propietari de l'equip. Segur que voleu transferir la " "propietat de l'equip a %s?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Ascendeix a propietari" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publica" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "La vostra biblioteca està buida. Segur que voleu publicar-la?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publica la biblioteca buida" @@ -4125,23 +4069,23 @@ msgstr "Segur que voleu treure l'equip «%s» de l'organització «%s»?" msgid "modals.remove-team-organization.title" msgstr "TRAIEU L'EQUIP DE L'ORGANITZACIÓ" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Mínima" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Despublica" msgstr[1] "Despublica" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Segur que voleu despublicar aquesta biblioteca?" msgstr[1] "Segur que voleu despublicar aquestes biblioteques?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Despublica la biblioteca" @@ -4231,38 +4175,38 @@ msgstr "Activa" msgid "nitrate.code-activation.title" msgstr "Activeu l'Enterprise" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "Contacteu amb vendes" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "" "Per a canviar a aquest pla, contacteu amb el nostre equip de vendes. Us " "ajudarem a actualitzar la vostra subscripció i ens assegurarem que tot " "quedi configurat correctament." -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "La vostra organització s'eliminarà." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "" "El vostre emmagatzematge total, l'historial de versions automàtiques i el " "període de recuperació de fitxers es limitaran." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "" "Els equips, els projectes i els fitxers deixaran de formar part de cap " "organització, però continuaran disponibles." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "Quan baixeu de pla:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "Voleu canviar al pla %s?" @@ -4280,15 +4224,15 @@ msgstr "Contacteu amb nosaltres per a provar l'Enterprise durant 14 dies:" msgid "nitrate.form.contact-upgrade" msgstr "Contacteu amb nosaltres per a pujar a l'Enterprise:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "Contacteu amb nosaltres per a provar el pla Enterprise durant 14 dies:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "Contacteu amb nosaltres per a pujar al pla Enterprise:" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "" "Ara mateix no se us cobrarà res. El pagament es processarà al final del " @@ -4310,7 +4254,7 @@ msgstr "" "Llibertat total del codi obert, amb la governança que la vostra " "organització necessita" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "" "Tot el que els vostres equips necessiten per a treballar de manera segura a " @@ -4320,7 +4264,7 @@ msgstr "" msgid "nitrate.form.enterprise.price" msgstr "per membre i mes" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "Proveu-ho gratis durant 14 dies" @@ -4336,7 +4280,7 @@ msgstr "Comenceu amb l'Enterprise" msgid "nitrate.form.subscribe-with-code" msgstr "Subscriviu-vos amb un codi d'activació" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "Desbloquegeu les funcions Enterprise" @@ -4348,7 +4292,7 @@ msgstr "Proveu-ho gratis durant 14 dies" msgid "nitrate.modal-success.title" msgstr "Us donem la benvinguda a l'Enterprise!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "Actiu fins al %s" @@ -4356,25 +4300,25 @@ msgstr "Actiu fins al %s" msgid "nitrate.subscription.renew-success" msgstr "La vostra subscripció a l'Enterprise s'ha renovat." -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "Cancel·leu la subscripció" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "" "Per a cancel·lar la vostra subscripció a l'Enterprise, contacteu amb " "nosaltres a:" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "Renoveu amb un codi d'activació" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Aquesta pàgina no existeix" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Error 404" @@ -4392,11 +4336,11 @@ msgstr "Us hem enviat un correu de recuperació a" msgid "not-found.login.sent-recovery-check" msgstr "Comproveu el correu i feu clic en l'enllaç per a crear una contrasenya nova." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Registreu-vos gratuïtament" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "I comenceu a utilitzar el Penpot en segons!" @@ -4404,67 +4348,67 @@ msgstr "I comenceu a utilitzar el Penpot en segons!" msgid "not-found.made-with-love" msgstr "Fet amb AMOR i codi obert" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Ja heu sol·licitat accés a aquest fitxer." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Ja heu sol·licitat accés a aquest fitxer o a altres fitxers o projectes " "d'aquest equip." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Ja heu sol·licitat accés a aquest projecte o a altres projectes o fitxers " "d'aquest equip." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Ja heu sol·licitat accés a aquest projecte." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "SOL·LICITEU L'ACCÉS" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Recordeu que, si el propietari ho permet, us convidaran a l'equip." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "La vostra sol·licitud s'ha enviat correctament!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "No teniu accés a aquest fitxer." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Aneu als projectes personals" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Si el propietari ho permet, us convidaran a l'equip." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Fitxer del Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "No teniu accés a aquest projecte." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJECTE" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Per a accedir a aquest fitxer, podeu demanar-ho al propietari de l'equip." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Per a accedir a aquest projecte, podeu demanar-ho al propietari de l'equip." @@ -4478,33 +4422,33 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Hi ha una versió nova disponible." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "Aquesta invitació ja no està disponible." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Invitació suprimida correctament" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "La invitació s'ha enviat correctament" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Enllaç d'invitació copiat" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "No podeu eliminar el vostre perfil. Reassigneu els vostres equips abans de " "continuar." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "El perfil s'ha desat correctament!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "S'ha enviat un correu electrònic de verificació a %s. Reviseu el correu!" @@ -4542,7 +4486,7 @@ msgstr "Nom de l'equip" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Creeu l'equip" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Convida membres" @@ -4552,98 +4496,98 @@ msgstr "" "Desenvolupadors, dissenyadors, gestors... tothom és benvingut. Podreu " "convidar més gent més tard." -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Convideu amb el rol:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Comencem!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Generar codi real a partir de dissenys" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Crear sistemes de disseny" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototipatge" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Dissenyar la UI/UX d'una app" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Per a què utilitzareu el Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Expliqueu-nos una mica qui sou per a ajudar-nos a fer que el Penpot " "funcioni per a vosaltres. Les vostres respostes ens ajudaran a prioritzar " "funcionalitats noves i a orientar-vos per a començar." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Ajudieu-nos a conèixer-vos" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Quina d'aquestes eines utilitzeu més?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Quina mena de feina feu?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Quina és la mida de la vostra empresa?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Per on voldríeu començar?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1.001 - 5.000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 - 500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 - 100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 - 1.000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "Només jo" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5.001+" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Educació" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Personal" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Feina" @@ -4725,11 +4669,11 @@ msgstr "Restaura la configuració predeterminada" msgid "restore-shortcuts.restore" msgstr "Restaura" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Desvincula" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Mixt" @@ -4741,35 +4685,35 @@ msgstr "Elimina el color" msgid "settings.select-this-color" msgstr "Selecciona els elements que utilitzen aquest estil" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Comentaris" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Alinea a baix" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Alinea al centre horitzontalment" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Alinea a l'esquerra" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Alinea a la dreta" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Alinea a dalt" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Alinea al centre verticalment" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Crea un tauler a partir de selecció" @@ -4777,55 +4721,55 @@ msgstr "Crea un tauler a partir de selecció" msgid "shortcuts.bold" msgstr "Commuta la negreta" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Diferència booleana" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Exclusió booleana" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Intersecció booleana" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Unió booleana" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Envia-ho al fons" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Envia-ho cap avall" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Envia-ho cap amunt" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Envia-ho a dalt" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Esborra desfer" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copia" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Copia l'enllaç" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Copia les propietats" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Crea un component o una variant" @@ -4833,63 +4777,63 @@ msgstr "Crea un component o una variant" msgid "shortcuts.create-new-project" msgstr "Crea nou" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Retalla" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Disminueix el zoom" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Elimina" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Elimina el node" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "Elimina el node i els segments" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "Desselecciona-ho tot" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Separa el component" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "Deshabilita la drecera" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "Deshabilitada" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "Drecera deshabilitada" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "Fletxa" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Corba" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "El·lipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Tauler" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "Línia" @@ -4897,65 +4841,65 @@ msgstr "Línia" msgid "shortcuts.draw-nodes" msgstr "Dibuixa el camí" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Camí" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rectangle" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Text" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplica" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "Combinació assignada a \"%s\". Si la deseu, s'eliminarà d'allà." -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "Si la deseu, aquesta drecera tornarà al valor original:" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "" "Aquesta combinació de tecles està disponible i es pot assignar sense " "conflictes." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "Personalitzeu-les a la configuració" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Cancel·la" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Exporta formes" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "Cerca" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "Cerca i reemplaça" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Amplia per encabir-ho tot" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Gira horitzontalment" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Gira verticalment" @@ -4979,27 +4923,27 @@ msgstr "Ves a les biblioteques compartides" msgid "shortcuts.go-to-search" msgstr "Cerca" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Agrupa" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribueix horitzontalment" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Mostra/Amaga la interfície" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "Importa/Exporta" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Amplia" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Insereix imatge" @@ -5007,11 +4951,11 @@ msgstr "Insereix imatge" msgid "shortcuts.italic" msgstr "Commuta la cursiva" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Uneix nodes" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "Premeu la combinació de tecles" @@ -5019,39 +4963,39 @@ msgstr "Premeu la combinació de tecles" msgid "shortcuts.line-through" msgstr "Commuta el text tatxat" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Fes cantonada" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Fes corba" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Màscara" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Fusiona nodes" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Mou" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Mou avall ràpidament" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Mou a l'esquerra ràpidament" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Mou a la dreta ràpidament" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Mou amunt ràpidament" @@ -5059,379 +5003,379 @@ msgstr "Mou amunt ràpidament" msgid "shortcuts.move-nodes" msgstr "Mou node" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Mou avall" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Mou a l'esquerra" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Mou a la dreta" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Mou amunt" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Següent tauler" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "No hi ha dreceres deshabilitades" -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "Ves a Totes per començar a personalitzar les dreceres." -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "No hi ha cap drecera que coincideixi amb la cerca" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "No s'han trobat dreceres" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Fixa l'opacitat al 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Fixa l'opacitat al 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Fixa l'opacitat al 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Fixa l'opacitat al 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Fixa l'opacitat al 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Fixa l'opacitat al 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Fixa l'opacitat al 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Fixa l'opacitat al 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Fixa l'opacitat al 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Fixa l'opacitat al 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Selector de color" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Ves als comentaris del visor" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Ves al panell de control" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Ves a la secció d'inspecció del visor" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Ves a les interaccions del visor" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Ves a les interaccions del visor" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Ves a l'espai de treball" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " o " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "Aquesta drecera s'ha reassignat a %s" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "Pàgina de dreceres" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Enganxa" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Enganxa les propietats" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "Enganxa i reemplaça" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "Personalitzades" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Gestor d'extensions" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Tauler anterior" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Refés" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "" "Després d'editar les dreceres, recarregueu les altres pestanyes obertes del " "Penpot per aplicar els canvis." -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Canvia el nom" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "Restableix els valors predeterminats" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Restableix el zoom" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Escala" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Bàsics" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Panell de control" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Visor" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Espai de treball" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Selecciona-ho tot" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Selecciona la capa següent" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Selecciona la capa pare" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Selecciona la capa anterior" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Separa els nodes" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "Mostra les mesures" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Mostra/Amaga graella de píxels" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Mostra/Amaga dreceres" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Ajusta als nodes" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Ajusta a la graella de píxels" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Comença a editar" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Alineació" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Edita" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Genèric" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menú principal" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modifica les capes" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navegació" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navegació" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navegació" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panells" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Camins" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formes" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Textos" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Eines" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Estableix miniatures" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Dreceres de teclat" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Commuta l'alineació dinàmica" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Commuta els recursos" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Commuta paleta de colors" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "Mostra o amaga els comentaris" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Activa/desactiva el mode de concentració" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Activa/desactiva la pantalla completa" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Mostra / Amaga les guies" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Mostra/Amaga l'historial" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Commuta les capes" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Afegeix o suprimeix la disposició flexible" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Afegeix o suprimeix la disposició de quadrícula" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Bloqueja la selecció" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Bloqueja les proporcions" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Mostra o amaga les regles" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Ajusta a les guies" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Ajusta a les guies de les regles" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Mostra/amaga la paleta de text" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Canvia el tema" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Mostra/amaga l'element" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Commuta l'estil de zoom" @@ -5439,31 +5383,31 @@ msgstr "Commuta l'estil de zoom" msgid "shortcuts.underline" msgstr "Commuta el subratllat" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Desfés" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Desagrupa" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Desemmascara" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribueix verticalment" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Redueix el zoom de la lupa" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Augmenta el zoom de la lupa" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Amplia a la selecció" @@ -5482,7 +5426,7 @@ msgstr "Vegeu l'Enterprise en acció" msgid "subscription.current-plan.enterprise" msgstr "Enterprise" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Enterprise" @@ -5494,7 +5438,7 @@ msgstr "Enterprise (prova)" msgid "subscription.current-plan.professional" msgstr "Professional" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "La vostra subscripció" @@ -5627,107 +5571,107 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Convidar persones amb el pla Unlimited" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "El pagament no s'ha completat. Torneu-ho a intentar quan vulgueu." -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "" "El pagament no s'ha completat. Torneu-ho a intentar. Si el problema " "continua, contacteu amb nosaltres: support@penpot.app." -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Subscripció" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "Introduïu el codi d'activació" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Afegiu un mètode de pagament per a continuar després del període de prova" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "Consola d'administració" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Tots els avantatges del pla Professional i:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Tots els avantatges del pla Unlimited i:" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "Tots els avantatges del pla Unlimited, a més de:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s editors)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise (prova)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "Versions desades automàticament de 90 dies i recuperació de fitxers" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Factura mensual fixa" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "Control avançat:" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "Seguretat Enterprise:" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Gestió multiorganització:" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "" "SAML/SSO natiu i registre complet d'auditoria (Audit Trail) per a informes " "de compliment." -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "" "Llista blanca granular de complements (plugins), inclòs el nostre servidor " "MCP segur i aïllat per a la connectivitat amb IA." -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "" "Compatibilitat amb jerarquies d'equips complexes i suborganitzacions " "independents." -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Emmagatzematge il·limitat" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Gestioneu la vostra subscripció" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Afegiu les dades de pagament" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Afegiu ara les vostres dades de pagament perquè la vostra subscripció " @@ -5735,176 +5679,176 @@ msgstr "" "continueu donant suport al nostre projecte de codi obert. De moment no se " "us farà cap càrrec." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Ometeu-ho de moment i comenceu la prova" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Ajudeu-nos a créixer i faciliteu el vostre període de prova" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Actualment teniu %s persona als vostres equips que pot editar." msgstr[1] "Actualment teniu %s persones als vostres equips que poden editar." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Atenció: si canvieu a un pla inferior, tindreu menys emmagatzematge i " "còpies de seguretat i un historial de versions més curts." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Editors" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "" "(Propietaris, administradors i editors. Els lectors no compten com a " "editors)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "No podeu fixar un nombre d'editors inferior al que teniu ara. Canvieu el " "rol (d'editor/administrador a lector) de les persones que no editen fitxers " "a la configuració de l'equip." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "" "El càrrec es farà després del període de prova. Ara mateix no cal cap " "targeta de crèdit." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/mes" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Apliqueu %s als vostres equips" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Consell: podeu augmentar ara el nombre de seients per a avançar-vos a les " "invitacions. Amb 25 o més editors en tots els equips, gaudireu d'una tarifa " "plana de 175 $ al mes." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Membre del Penpot des de: %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Més informació" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "membre de l'organització / mes" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Altres plans del Penpot" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "organització per mes" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "usuari / mes" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professional" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "Versions desades automàticament de 7 dies i recuperació de fitxers" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "Totes les funcions de disseny i prototipatge. Assistència de la comunitat." -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "Control total sobre les dades i la infraestructura." -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "Usuaris, equips i fitxers il·limitats." -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10 GB d'emmagatzematge" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Equips il·limitats. Fins a 8 editors als vostres equips." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Recomanat" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "La vostra subscripció" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Comenceu la prova gratuïta" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Subscriviu-vos" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "Esteu al pla %s des de %s." -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Podeu editar la vostra subscripció en qualsevol moment des de la " "pàgina\"Subscripció\" als detalls del compte." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Gaudiu del vostre pla!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Gràcies per triar el pla %s del Penpot!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Sou %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Proveu-ho gratis durant 14 dies" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Unlimited" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unlimited (prova)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "Versions desades automàticament de 30 dies i recuperació de fitxers" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Factura mensual limitada a 175 $" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25 GB d'emmagatzematge" @@ -5929,45 +5873,45 @@ msgstr "" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "Si voleu augmentar aquest límit, [canvieu el vostre pla|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 invitació seleccionada" msgstr[1] "%s invitacions seleccionades" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Proveïdors de tipografies - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Tipografies - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projectes - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Cerca - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Biblioteques compartides - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Llibertat de disseny per a equips" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Envia opinions - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "Integracions - Penpot" @@ -5975,7 +5919,7 @@ msgstr "Integracions - Penpot" msgid "title.settings.notifications" msgstr "Notificacions - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Configuració - Penpot" @@ -5983,27 +5927,27 @@ msgstr "Configuració - Penpot" msgid "title.settings.password" msgstr "Contrasenya - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Perfil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Invitacions - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Membres - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Configuració - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Visor - Penpot" @@ -6015,51 +5959,51 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Desvincula el token" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "No s'ha trobat cap tauler a la pàgina." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "No s'ha trobat el tauler." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Comentaris (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "No mostris les interaccions" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Pantalla completa" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspecciona (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interaccions" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interaccions (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Copia l'enllaç" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Mostra les interaccions" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Mostra les interaccions en fer clic" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Mapa del lloc" @@ -6096,67 +6040,67 @@ msgstr "Seguiu la nostra " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "guia de resolució de problemes de WebGL" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "Renderització WebGL desactivada" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "Renderització WebGL activada" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "S'ha perdut el context WebGL" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "S'ha recuperat el context WebGL" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "L'últim lliurament ha anat bé." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Alinea el centre horitzontal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribueix l'espaiat horitzontal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Alinea a l'esquerra (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Alinea a la dreta (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Alinea la part inferior (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Alinea el centre vertical (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribueix l'espaiat vertical (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Alinea la part superior (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Afegeix una biblioteca" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Tots els recursos" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Colors" @@ -6168,7 +6112,7 @@ msgstr "Afegeix un color" msgid "workspace.assets.component-group-options" msgstr "Opcions del grup de components" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Components" @@ -6176,7 +6120,7 @@ msgstr "Components" msgid "workspace.assets.components.add-component" msgstr "Afegeix un component" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s variants" @@ -6184,13 +6128,13 @@ msgstr "%s variants" msgid "workspace.assets.create-group" msgstr "Crea un grup" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Els elements s'anomenaran automàticament com a \"nom del grup / nom de " "l'element\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Elimina" @@ -6198,11 +6142,11 @@ msgstr "Elimina" msgid "workspace.assets.delete-group" msgstr "Elimina el grup" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "Elimina les pàgines" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplica" @@ -6210,39 +6154,39 @@ msgstr "Duplica" msgid "workspace.assets.duplicate-main" msgstr "Duplica el principal" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Edita" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtra" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Vista de quadrícula" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Agrupa" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nom del grup" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Vista de llista" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "llibreria local" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Gestiona la biblioteca" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "No s'han trobat recursos" @@ -6250,7 +6194,7 @@ msgstr "No s'han trobat recursos" msgid "workspace.assets.open-library" msgstr "Obre el fitxer de la biblioteca" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Canvia el nom" @@ -6258,11 +6202,11 @@ msgstr "Canvia el nom" msgid "workspace.assets.rename-group" msgstr "Canvia el nom del grup" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Cerca recursos" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Biblioteca compartida" @@ -6272,43 +6216,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 component" msgstr[1] "%s components" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Ordena" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Tipografies" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Afegeix una tipografia" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Mida" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Estil de la lletra" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Vés al fitxer de la biblioteca d'estils per a editar-lo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Espaiat de la lletra" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Alçada de la línia" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transforma el text" @@ -6316,15 +6260,15 @@ msgstr "Transforma el text" msgid "workspace.assets.ungroup" msgstr "Desagrupa" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "S'està copiant la imatge…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "Imatge copiada al porta-retalls" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "Error en copiar la imatge" @@ -6336,85 +6280,85 @@ msgstr "Selector de color" msgid "workspace.colorpicker.color-tokens" msgstr "Tokens de color" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Els components no es poden niar dins d'ells mateixos." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "No s'han pogut intercanviar algunes còpies. Els components no es poden niar " "dins d'ells mateixos." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Crea una àrea" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "Copia les columnes" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "Copia les files" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Crea un tauler" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Fusiona les cel·les" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "Enganxa" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Afegeix 1 columna a la dreta" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Afegeix 1 columna a l'esquerra" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Elimina la columna" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Elimina la columna amb el contingut" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Duplica la columna" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Afegeix 1 fila a sota" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Afegeix 1 fila a sobre" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Elimina la fila" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Elimina la fila amb el contingut" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Duplica la fila" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "Color de la guia" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "Elimina la guia" @@ -6422,15 +6366,15 @@ msgstr "Elimina la guia" msgid "workspace.debug.title" msgstr "Eines de depuració" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Mode d'enfocament" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Enfocament apagat" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Enfocament actiu" @@ -6442,243 +6386,243 @@ msgstr "Degradat lineal" msgid "workspace.gradients.radial" msgstr "Degradat radial" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Desactiva l'alineació dinàmica" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Desactiva l'escala proporcional" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "No ajustis a les guies" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "No ajustis als píxels" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "No ajustis a les guies de les regles" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "Desactiva el renderatge WebGL (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Activa l'alineació dinàmica" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Activa l'escala proporcional" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Ajusta a les guies" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Ajusta als píxels" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Ajusta a les guies de les regles" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "Activa el renderatge WebGL (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "Cerca" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "Cerca i reemplaça" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Amaga els noms dels taulers" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "Amaga els comentaris" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Amaga les guies" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Amaga la paleta de colors" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Amaga la graella de píxels" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Amaga les regles" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Amaga la paleta de tipografies" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "Bloca les guies" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "Connecta" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "Desconnecta" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "Gestiona (estat: desactivat)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "Gestiona (estat: activat)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Edita" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Fixer" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Ajuda i informació" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "Servidor MCP" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferències" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Mostra" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Refés" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Selecciona-ho tot" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Mostra els noms dels taulers" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "Mostra els comentaris" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Mostra les guies" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Mostra la paleta de colors" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Mostra graella de píxels" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Mostra les regles" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Mostra la paleta de tipografies" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Canvia al mode fosc" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Canvia al mode clar" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Canvia al tema del sistema" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Desfés" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "Desbloca les guies" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Restableix" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "S'ha produït un error en desar" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Desat" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "S'està desant" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Comparteix" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Visor (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoom" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Emplena - Amplia per a omplir" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Ajusta - Redueix per a encaixar" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Apropa per a omplir-ho tot" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Pantalla completa" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Apropa la selecció" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Edita la grid" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Surt" @@ -6686,7 +6630,7 @@ msgstr "Surt" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Padding inferior" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Mostra les opcions de padding als 4 costats" @@ -6722,37 +6666,37 @@ msgstr "Fet" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Localitza" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Localitza el layout de grid" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 color" msgstr[1] "%s colors" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "Afegeix un color a la biblioteca" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Encara no hi ha estils de color a la vostra biblioteca" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "Encara no hi ha colors recents" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Encara no hi ha estils de tipografia a la vostra biblioteca" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Biblioteca del fitxer" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Colors recents" @@ -6764,75 +6708,75 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Desa l'estil de color" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "Mostra/amaga la paleta de colors" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 component" msgstr[1] "%s components" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Connectada amb" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "O afegiu-ne algunes per a provar:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "No hi ha biblioteques compartides al vostre equip; podeu cercar" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "algunes plantilles aquí" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Biblioteca del fitxer" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 gràfic" msgstr[1] "%s gràfics" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTEQUES EN AQUEST FITXER" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTEQUES" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "ACTUALITZACIONS DE LES BIBLIOTEQUES" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "S'està carregant…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "No hi ha biblioteques compartides pendents d'actualitzar" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "No s'ha trobat cap coincidència per a “%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Cerca biblioteques compartides" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "BIBLIOTEQUES COMPARTIDES" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Connecta una biblioteca" @@ -6856,25 +6800,25 @@ msgstr "Desvincula tots els recursos" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "Desvincula tots els tokens" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 tipografia" msgstr[1] "%s tipografies" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Desconnecta la biblioteca" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Actualitza" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "mostra tots els canvis" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "ACTUALITZACIONS" @@ -6936,7 +6880,7 @@ msgstr "Mostra/amaga el difuminat" msgid "workspace.options.blur-options.toggle-more-options" msgstr "Mostra/amaga més opcions" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "Cancel·la" @@ -6944,117 +6888,117 @@ msgstr "Cancel·la" msgid "workspace.options.canvas-background" msgstr "Fons del llenç" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Trunca el contingut" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Component" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Anotació" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Còpia" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Crea una anotació" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Edita una anotació" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Principal" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Intercanvia el component" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Encara no hi ha recursos en aquesta biblioteca" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Desvinculat" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Localitza les variants en conflicte" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Aquest component té variants en conflicte. Assegureu-vos que cada variant " "tingui un conjunt únic de propietats i valors." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Localitza les variants duplicades" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Algunes variants tenen propietats i valors idèntics" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Aquestes variants tenen propietats i valors idèntics. Ajusteu els valors " "per tal que es puguin recuperar." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Aquesta variant té propietats i valors idèntics als d'una altra variant. " "Ajusteu els valors per tal que es puguin recuperar." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Algunes d'aquestes variants tenen propietats i valors idèntics. Ajusteu els " "valors per tal que es puguin recuperar." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Aquest component té variants amb noms no vàlids. Assegureu-vos que cada " "variant segueixi l'estructura correcta." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Localitza les variants no vàlides" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Algunes variants tenen noms no vàlids" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Localitza les variants no vàlides" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Aquestes variants tenen noms no vàlids." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Aquesta variant té un nom no vàlid." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Algunes d'aquestes variants tenen noms no vàlids." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[propietat]=[valor], [propietat]=[valor]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Proveu d'utilitzar l'estructura següent:" @@ -7092,7 +7036,7 @@ msgstr "Tenen el mateix nivell de jerarquia." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Els grups, els taulers i els layouts es consideren equivalents." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Com es mantenen connectades les variants" @@ -7140,11 +7084,11 @@ msgstr "Superior i inferior" msgid "workspace.options.convert-to-typography" msgstr "Crea un estil de tipografia" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Disseny" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exporta" @@ -7152,53 +7096,53 @@ msgstr "Exporta" msgid "workspace.options.export-multiple" msgstr "Exporta la selecció" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Exporta 1 element" msgstr[1] "Exporta %s elements" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Afegeix una exportació" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Elimina l'exportació" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Sufix" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "El servei d'exportació està ocupat; torneu-ho a provar més tard" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "Exportació cancel·lada" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "S'està cancel·lant…" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Exportació completa" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "S'està exportant…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Exportació fallida" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Exportació inesperadament lenta" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "S'està esperant…" @@ -7218,7 +7162,7 @@ msgstr "Elimina l'emplenat" msgid "workspace.options.fill.section" msgstr "Secció d'emplenat" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Redimensiona el tauler per a ajustar-lo al contingut" @@ -7242,67 +7186,67 @@ msgstr "S'inicia el flux" msgid "workspace.options.grid.auto" msgstr "Automàtic" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Columnes" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Color" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Espaiat" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Alçada" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Marge" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Estableix com a predeterminat" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Inferior" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centre" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Esquerra" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Dreta" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Estira" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Superior" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Usa valors predeterminats" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Ample" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Files" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Quadrat" @@ -7314,7 +7258,7 @@ msgstr "Emplenament del grup" msgid "workspace.options.group-stroke" msgstr "Traç del grup" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Afegeix una guia" @@ -7322,19 +7266,19 @@ msgstr "Afegeix una guia" msgid "workspace.options.guides.remove-guide" msgstr "Elimina la guia" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Guies" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Mostra/amaga la guia" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Altura" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspecciona" @@ -7550,115 +7494,115 @@ msgstr "Interaccions" msgid "workspace.options.interactions.add-interaction" msgstr "Afegeix una interacció" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Color" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Crema el color" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Aclareix el color" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Enfosqueix" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Resta" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Exclusió" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Llum forta" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "To" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Il·lumina" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Lluminositat" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Multiplica" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Llum normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Superposa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturació" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Pantalla" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Llum suau" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "Secció del menú de capes" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Mostra/amaga la capa" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Alçada màx." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Amplada màx." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Alçada mín." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Amplada mín." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Més colors" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Més llibreries de colors" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "Més tokens de color" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "Cap mida predefinida coincident" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacitat" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototip" @@ -7694,23 +7638,23 @@ msgstr "Secció del radi de la vora" msgid "workspace.options.radius.show-single-corners" msgstr "Mostra els radis independents" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Recent" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Torna-ho a provar" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotació" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Cerca tipografia" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "Cerca una mida predefinida" @@ -7720,11 +7664,11 @@ msgstr "" "Seleccioneu una forma, un tauler o un grup per a arrossegar una connexió a " "un altre tauler." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Colors seleccionats" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "Secció de selecció de colors" @@ -7792,55 +7736,55 @@ msgstr "Mostra/amaga l'ombra" msgid "workspace.options.show-fill-on-export" msgstr "Mostra en l'exportació" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Mostra al visor" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Mida" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Mides predefinides" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Bloca la proporció" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Desbloca la proporció" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Cercle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamant" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Fletxa" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Cap" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Redó" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Quadrat" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rectangle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triangle" @@ -7848,31 +7792,31 @@ msgstr "Triangle" msgid "workspace.options.stroke-color" msgstr "Color del traç" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "Guió" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "Buit" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Ample del traç" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "Inferior (block end)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "Esquerra (inline start)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "Dreta (inline end)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "Superior (block start)" @@ -7880,49 +7824,49 @@ msgstr "Superior (block start)" msgid "workspace.options.stroke.add-stroke" msgstr "Afegeix un color de traç" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centrat" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "A ratlles" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Puntejat" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Interior" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Mixte" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Exterior" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "Traç per costat" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "" "La funció de traç per costat només està disponible amb el renderatge nou. " "Canvieu el renderatge per a utilitzar-la." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Elimina el traç" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Sòlid" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "Mostra/amaga el traç" @@ -7958,11 +7902,11 @@ msgstr "Ample automàtic" msgid "workspace.options.text-options.grow-fixed" msgstr "Fix" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Espaiat de la lletra" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Alçada de la línia" @@ -8012,154 +7956,154 @@ msgstr "" "Utilitzeu el botó de «play» de la capçalera per a executar la vista de " "prototip." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Ample" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Dibuixa els nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "Alineat" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "Independent" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "Igual" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "Comportament del tirador" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Uneix els nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Converteix a cantó (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Converteix a corba (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Combina els nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Mou els nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Separa els nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Alinea els nodes (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Obre" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Descobreix [més extensions](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Encara no hi ha extensions instal·lades" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "El manifest de l'extensió no és correcte." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Heu de ser editors per a utilitzar aquesta extensió" -#: src/app/main/data/plugins.cljs:158 +#: src/app/main/data/plugins.cljs:160 msgid "workspace.plugins.error.unreachable" msgstr "No s'ha pogut contactar amb el connector." -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "L'extensió no existeix o l'URL no és correcte." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Instal·la" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Extensions instal·lades" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Gestor d'extensions" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Extensions" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "ACTUALITZA AQUESTA EXTENSIÓ" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "L'extensió s'ha modificat des de la darrera vegada que l'heu oberta. Ara " "també vol accedir a:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Inicia baixades de fitxers." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Desa dades al navegador." -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "Llegeix el contingut del porta-retalls." -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "Llegeix i escriu al porta-retalls." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Llegeix els vostres comentaris i respostes." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Llegeix i modifica els vostres comentaris i respon en nom vostre." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Llegeix el contingut dels fitxers als quals teniu accés." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Llegeix i modifica el contingut dels fitxers als quals teniu accés." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Tingueu en compte que un tercer ha creat aquesta extensió; assegureu-vos " @@ -8167,74 +8111,74 @@ msgstr "" "són importants per a nosaltres. Si teniu cap dubte, contacteu amb " "l'assistència." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Llegeix les vostres biblioteques i recursos." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Llegeix i modifica les vostres biblioteques i recursos." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "L'EXTENSIÓ '%s' VOL ACCEDIR A:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Llegeix la informació del perfil de l'usuari actual." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Llista d'extensions" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Elimina l'extensió" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Escriu l'URL d'una extensió" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Extensions" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "ARA NO" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Voleu fer-hi una ullada? S'obrirà en un esborrany nou de l'equip actual. " "(Si no, sempre la trobareu a les extensions instal·lades de qualsevol " "fitxer.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "L'EXTENSIÓ '%s' S'HA INSTAL·LAT PER AL VOSTRE USUARI!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "PROVA L'EXTENSIÓ" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Afegeix un flex layout" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Afegeix un grid layout" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Afegeix un layout" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Crea una variant" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Afegeix una propietat nova" @@ -8246,11 +8190,11 @@ msgstr "Envia-ho al fons" msgid "workspace.shape.menu.backward" msgstr "Envia-ho darrere" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "Elimina les guies del tauler" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Combina com a variants" @@ -8258,7 +8202,7 @@ msgstr "Combina com a variants" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Els components han d'estar a la mateixa pàgina" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Copia" @@ -8302,27 +8246,27 @@ msgstr "Copia com a text" msgid "workspace.shape.menu.create-annotation" msgstr "Crea una anotació" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Selecciona al tauler" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Crea un component" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Crea components múltiples" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Retalla" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Elimina" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Elimina l'inici del flux" @@ -8334,7 +8278,7 @@ msgstr "Desconnecta la instància" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Desenganxa les instàncies" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Diferència" @@ -8342,15 +8286,15 @@ msgstr "Diferència" msgid "workspace.shape.menu.duplicate" msgstr "Duplica" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Edita" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Exclou" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Aplana" @@ -8362,7 +8306,7 @@ msgstr "Volteja horitzontalment" msgid "workspace.shape.menu.flip-vertical" msgstr "Volteja verticalment" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Inici del flux" @@ -8374,31 +8318,31 @@ msgstr "Porta-ho endavant" msgid "workspace.shape.menu.front" msgstr "Porta-ho a primer pla" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Agrupa" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Amaga" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Mostra/Amaga la interfície" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Intersecció" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Bloca" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Màscara" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Enganxa" @@ -8406,27 +8350,27 @@ msgstr "Enganxa" msgid "workspace.shape.menu.paste-props" msgstr "Enganxa les propietats" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Camí" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Elimina el flex layout" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Elimina el grid layout" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Elimina el layout" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Elimina la propietat" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "La variant ha de tenir com a mínim una propietat" @@ -8450,11 +8394,11 @@ msgstr "Restaura la variant" msgid "workspace.shape.menu.select-layer" msgstr "Selecciona la capa" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Mostra" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Mostra al panell de recursos" @@ -8462,7 +8406,7 @@ msgstr "Mostra al panell de recursos" msgid "workspace.shape.menu.show-main" msgstr "Vés al component principal" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "Converteix el traç en camí" @@ -8474,19 +8418,19 @@ msgstr "Elimina la miniatura" msgid "workspace.shape.menu.thumbnail-set" msgstr "Estableix com a miniatura" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Desagrupa" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Unió" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Desbloca" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Desemmascara" @@ -8502,79 +8446,79 @@ msgstr "Replega la barra lateral" msgid "workspace.sidebar.expand" msgstr "Desplega la barra lateral" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Historial" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Capes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Components" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Filtra" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Taulers" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Grups" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Imatges" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Màscares" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "Cap coincidència" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "Reemplaça" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "Reemplaça-ho tot" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "Reemplaça amb…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Cerca capes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "Cerca i reemplaça" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "Contingut del text" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "Noms de les capes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Formes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Textos" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Atributs SVG importats" @@ -8586,7 +8530,7 @@ msgstr "Pàgines" msgid "workspace.sidebar.sitemap.add-page" msgstr "Afegeix una pàgina" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Mapa del lloc" @@ -8614,7 +8558,7 @@ msgstr "Afegeix un set a aquest grup" msgid "workspace.tokens.add-token" msgstr "Afegeix un token: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Aplicat a" @@ -8634,7 +8578,7 @@ msgstr "Mida de la lletra de base" msgid "workspace.tokens.base-font-size.error" msgstr "La mida de la lletra de base ha de ser un valor en píxels o sense unitat." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Color" @@ -8646,7 +8590,7 @@ msgstr "Copia el camí del token" msgid "workspace.tokens.create-new-theme" msgstr "Creeu el primer tema ara." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Creeu-ne un." @@ -8670,7 +8614,7 @@ msgstr "Duplica el token" msgid "workspace.tokens.duplicate-group" msgstr "Duplicar grup de tokens" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "còpia" @@ -8694,7 +8638,7 @@ msgstr "Edita el token de %s" msgid "workspace.tokens.enter-token-name" msgstr "Escriviu el nom del token de %s" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "No es poden aplicar tokens mentre editeu text. Seleccioneu la capa de text." @@ -8722,7 +8666,7 @@ msgstr "Previsualització:" msgid "workspace.tokens.export.single-file" msgstr "Fitxer únic" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Mida de la lletra o {alias}" @@ -8732,7 +8676,7 @@ msgstr "" "Error en definir el pes o l'estil de la lletra. Aquest estil no existeix en " "la lletra actual" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Pes de la lletra (300, Bold Italic...) o un {alias}" @@ -8770,7 +8714,7 @@ msgstr "" "En un fitxer JSON únic, les claus de primer nivell han de ser els noms dels " "sets de tokens." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Importa els tokens" @@ -8778,17 +8722,17 @@ msgstr "Importa els tokens" msgid "workspace.tokens.import-warning" msgstr "En importar tokens se sobreescriuran els tokens, sets i temes actuals." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Inactiu" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Aquest set no està actiu. Canvieu el tema o activeu aquest set per a veure " "els canvis a la vista" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Utilitza tokens individuals" @@ -8808,11 +8752,11 @@ msgstr "Tema" msgid "workspace.tokens.label.theme-placeholder" msgstr "Afegeix un tema (p. ex. Clar)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Espaiat de la lletra o {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Alçada de la línia (multiplicador, px, %) o {alias}" @@ -8828,11 +8772,11 @@ msgstr "Mida màx." msgid "workspace.tokens.min-size" msgstr "Mida mín." -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Referència no trobada" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Feu clic dret per a veure les opcions" @@ -8848,7 +8792,7 @@ msgstr "Cap tema actiu" msgid "workspace.tokens.no-permisions-set" msgstr "Heu de ser editors per a activar o desactivar sets" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Heu de ser editors per a utilitzar temes" @@ -8860,7 +8804,7 @@ msgstr "Encara no hi ha cap set definit. Creeu-ne un primer." msgid "workspace.tokens.no-sets-yet" msgstr "Encara no hi ha cap set." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "No hi ha cap tema." @@ -8876,7 +8820,7 @@ msgstr "No ho remapegis" msgid "workspace.tokens.num-active-sets" msgstr "%s sets actius" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Valor original: %s" @@ -8888,15 +8832,15 @@ msgstr "Emplenats" msgid "workspace.tokens.radius" msgstr "Radi" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "La referència de {%s} no és vàlida o no és a cap set actiu." -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Escriviu un àlies de tipografia de token" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Escriviu un àlies d'ombra de token" @@ -8934,7 +8878,7 @@ msgstr "Canviar nom del grup de tokens" msgid "workspace.tokens.rename-group-name-hint" msgstr "Els teus tokens es renomenaran automàticament a %s.(sufix).(token)" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Valor resolt: %s" @@ -8972,27 +8916,27 @@ msgstr "" msgid "workspace.tokens.settings" msgstr "Configuració dels tokens" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Afegeix una ombra" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Difuminat" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Interior" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Elimina l'ombra" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Difusió" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "El valor de difuminat no pot ser negatiu" @@ -9000,11 +8944,11 @@ msgstr "El valor de difuminat no pot ser negatiu" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "El valor de difusió no pot ser negatiu" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -9012,11 +8956,11 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "Mida" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize o {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through o {alias}" @@ -9034,31 +8978,31 @@ msgstr "Llista de temes" msgid "workspace.tokens.token-description" msgstr "Descripció" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Selecciona la família tipogràfica" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Família tipogràfica" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Família tipogràfica o llista de lletres separades per comes (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nom" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "Ja existeix un token al camí: %s o en un prefix d'aquest." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "El nom ha de tenir com a mínim 1 caràcter" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" "no és un nom de token vàlid.\n" @@ -9069,11 +9013,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Valor" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Escriviu un valor o un àlies amb {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENS - %s" @@ -9092,15 +9036,15 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "El tipus '%s' no està disponible (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Utilitza una referència" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "El valor no és vàlid" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "Fletxa (%s)" @@ -9108,79 +9052,75 @@ msgstr "Fletxa (%s)" msgid "workspace.toolbar.assets" msgstr "Recursos" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Paleta de colors (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Comentaris (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Corba (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "El·lipse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Tauler (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Imatge (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:413 +#: src/app/main/ui/workspace/top_toolbar.cljs:362 msgid "workspace.toolbar.label" msgstr "Barra d'eines" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "Línia (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "Connecta aquí" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP connectat" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Mou (%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "Barra de paletes" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Camí (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Extensions (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rectangle (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Text (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Tipografies (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Mostra/amaga la barra d'eines" @@ -9201,11 +9141,11 @@ msgstr "El renderatge no està disponible. Actualitzeu per a restaurar l'edició msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "Actualitza" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Encara no hi ha canvis a l'historial" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "per %s" @@ -9345,19 +9285,19 @@ msgstr "recurs tipogràfic" msgid "workspace.undo.entry.unknown" msgstr "Operació sobre %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Descarta" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Més informació" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Hi ha actualitzacions a les biblioteques compartides" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Actualitza" diff --git a/frontend/translations/cs.po b/frontend/translations/cs.po index 5760d638f1..70f84a12b6 100644 --- a/frontend/translations/cs.po +++ b/frontend/translations/cs.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: \"Amerey.eu\" \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Czech " "\n" "Language: cs\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Už máte účet?" @@ -29,7 +29,7 @@ msgstr "" "Toto je DEMO služba, NEPOUŽÍVEJTE ji pro skutečnou práci, projekty budou " "pravidelně mazány." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Zapomněli jste heslo?" @@ -37,37 +37,37 @@ msgstr "Zapomněli jste heslo?" msgid "auth.fullname" msgstr "Jméno a příjmení" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Přihlásit se do mého účtu" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Přihlaste se zde." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot je bezplatný open-source návrhářský nástroj pro spolupráci v oblasti " "designu a kódu" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -87,7 +87,7 @@ msgstr "Profil není ověřen, před pokračováním prosím ověřte profil." msgid "auth.notifications.recovery-token-sent" msgstr "Odkaz na obnovení hesla byl zaslán na váš e-mail." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Připojení k týmu proběhlo úspěšně" @@ -119,7 +119,7 @@ msgstr "Zapomněli jste heslo?" msgid "auth.recovery-submit" msgstr "Změnit heslo" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Ještě nemáte účet?" @@ -131,7 +131,7 @@ msgstr "Řekněte nám, jak bychom vám měli říkat." msgid "auth.register-account-title" msgstr "Vaše jméno" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Vytvořit účet" @@ -144,7 +144,7 @@ msgstr "Vytvořit účet" msgid "auth.terms-and-privacy-agreement" msgstr "Souhlasím se [smluvními podmínkami](%s) a [zásadami ochrany soukromí](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Všeobecné podmínky poskytování služeb" @@ -156,94 +156,94 @@ msgstr "Pracovní email" msgid "comments.mentions.not-found" msgstr "Pro @%s nebyli nalezeni žádní lidé" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Zveřejnit" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Všichni uživatelé Penpotu" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Opravdu chcete tento odkaz odstranit? Pokud to uděláte, nebude už pro " "nikoho dostupný" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(aktuální)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Zničit odkaz" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Získat odkaz" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Odkaz byl úspěšně zkopírován" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Spravovat oprávnění" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 sdílená stránka" msgstr[1] "%s sdílené stránky" msgstr[2] "%s sdílených stránek" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Může komentovat" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Může prohlížet kód" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Kdokoliv s odkazem bude mít přístup" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Sdílené stránky" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Odkaz ke sdílení se zobrazí zde" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Pouze členové týmu" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Sdílet prototypy" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Vybrat vše" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Zrušit zveřejnění" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Správa týmu" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot je určený pro týmy. Pozvěte členy ke spolupráci na projektech a " "souborech" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Vytvořte tým!" @@ -251,23 +251,23 @@ msgstr "Vytvořte tým!" msgid "dashboard.add-file" msgstr "Přidat soubor" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Přidat jako sdílenou knihovnu" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Změnit e-mail" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(zkopírovat)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Vytvořit nový tým" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Připnout tuto verzi" @@ -275,11 +275,11 @@ msgstr "Připnout tuto verzi" msgid "dashboard.default-team-name" msgstr "Váš Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Smazat tým" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Stáhnout soubor Penpot (.penpot)" @@ -291,19 +291,19 @@ msgstr "Duplikovat" msgid "dashboard.duplicate-multi" msgstr "Duplikovat %s soubory" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Jakmile člen projektu vytvoří koncept, zobrazí se zde." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Zatím žádné koncepty." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Jakmile člen projektu vytvoří soubor, zobrazí se zde." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Zatím žádné soubory." @@ -326,68 +326,68 @@ msgstr "Zatím žádné knihovny." msgid "dashboard.export-binary-multi" msgstr "Stáhnout soubory %s Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exportovat tabule do PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exportovat do PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s z %s prvků vybráno" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exportovat" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Nastavení exportu můžete přidat k prvkům ve vlastnostech návrhu (v dolní " "části pravého postranního panelu)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informace o nastavování exportů v Penpotu." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Neexistují žádné prvky s nastavením exportu." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Výběr exportu" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Písmo odstraněno" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Odmítnout vše" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Stále nemáte nainstalované žádné vlastní písma." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Zatím žádná vlastní písma." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "Jakmile člen projektu nahraje vlastní písmo, zobrazí se zde." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "Přidáno 1 písmo" msgstr[1] "%s písma přidány" msgstr[2] "%s písem přidáno" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -397,7 +397,7 @@ msgstr "" "Můžete nahrávat písma v následujících formátech: **TTF, OTF a WOFF** (bude " "potřeba pouze jeden)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -406,11 +406,11 @@ msgstr "" "[smluvních podmínek společnosti Penpot](%s). Můžete si také přečíst o " "[licencování písem](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Nahrát vše" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -421,11 +421,11 @@ msgstr "" "[Transfonter](https://transfonter.org/) ke generování webových písem a " "opravě chyb. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importovat Penpot soubory" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Ups! Tento soubor se nepodařilo importovat" @@ -433,14 +433,14 @@ msgstr "Ups! Tento soubor se nepodařilo importovat" msgid "dashboard.import.bad-url" msgstr "Import se nezdařil. Adresa URL šablony je nesprávná" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 soubor byl úspěšně importován." msgstr[1] "%s soubory byly úspěšně importovány." msgstr[2] "%s souborů bylo úspěšně importováno." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Některé soubory obsahovaly neplatné objekty, které byly odstraněny." @@ -448,55 +448,27 @@ msgstr "Některé soubory obsahovaly neplatné objekty, které byly odstraněny. msgid "dashboard.import.no-perms" msgstr "Nemáte oprávnění k importu do tohoto týmu" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Zpracovávání barev" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Zpracovávání komponentů" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Zpracovávání médií" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Zpracovává se stránka: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Zpracovávání typografií" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Nahrávání dat na server (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Nahrávání souboru: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Pozvat do týmu" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Odejít z týmu" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Knihovny a šablony" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Prozkoumejte je víc a zjistěte, jak přispět" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Při importu šablony došlo k problému. Šablona nebyla importována." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Knihovny" @@ -504,7 +476,7 @@ msgstr "Knihovny" msgid "dashboard.loading-files" msgstr "načítání vašich souborů …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "načítání vašich písem …" @@ -520,39 +492,39 @@ msgstr "Přesunout soubory %s do" msgid "dashboard.move-to-other-team" msgstr "Přesunout do jiného týmu" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nový soubor" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Nový soubor" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nový projekt" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nový projekt" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Nebyly nalezeny žádné shody pro “%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Připnuté projekty se zobrazí zde" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Oznámení" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Vaše e-mailová adresa byla úspěšně aktualizována" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Vaše e-mailová adresa byla úspěšně ověřena" @@ -564,11 +536,11 @@ msgstr "Nastavení oznámení bylo aktualizováno" msgid "dashboard.notifications.password-saved" msgstr "Heslo bylo úspěšně uloženo!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Zobrazit oznámení" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "Počet členů: %s" @@ -576,7 +548,7 @@ msgstr "Počet členů: %s" msgid "dashboard.open-in-new-tab" msgstr "Otevřít soubor na nové kartě" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Možnosti" @@ -600,7 +572,7 @@ msgstr "Nyní jste vlastníkem tohoto týmu." msgid "dashboard.permissions-change.viewer" msgstr "Nyní jste v tomto týmu divákem." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Připnout/Odepnout" @@ -616,11 +588,11 @@ msgstr "Manifest pluginu nelze analyzovat" msgid "dashboard.plugins.try-plugin" msgstr "Zkuste plugin: " -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projekty" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Chcete odstranit svůj účet?" @@ -628,23 +600,23 @@ msgstr "Chcete odstranit svůj účet?" msgid "dashboard.removed-from-team" msgstr "Již nejste součástí týmu „%s“." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Uložit nastavení" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Vyhledat…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Hledá se “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Vybrat jazyk uživatelského rozhraní" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Vyberte téma" @@ -708,11 +680,11 @@ msgstr "Aktualizovat nastavení" msgid "dashboard.settings.notifications.title" msgstr "Oznámení" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Zobrazit všechny soubory" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Historie verze" @@ -738,7 +710,7 @@ msgstr[2] "Vaše soubory byly úspěšně duplikovány" msgid "dashboard.success-duplicate-project" msgstr "Váš projekt byl úspěšně duplikován" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Váš soubor byl úspěšně přesunut" @@ -750,86 +722,86 @@ msgstr "Vaše soubory byly úspěšně přesunuty" msgid "dashboard.success-move-project" msgstr "Váš projekt byl úspěšně přesunut" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Informace o týmu" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Členové týmu" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Projekty týmu" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Téma UI" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Výsledky vyhledávání" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Zadejte výraz pro hledání" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Zrušit zveřejnění knihovny" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Aktualizovat nastavení" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Je aktivní" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Když je tento webhook spuštěn, budou doručeny podrobnosti o události" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Smazat nebo upravit můžete pouze vámi vytvořené webhooky." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Typ obsahu" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Vytvořit webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook byl úspěšně vytvořen." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhooky jsou jednoduchým způsobem, jak umožnit jiným webům a aplikacím, " "aby byly upozorňovány na určité události v Penpotu. Na každou z vámi " "poskytnutých adres URL odešleme požadavek POST." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Chcete-li webhook přidat, stiskněte tlačítko „Přidat webhook“." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Dosud nebyly vytvořeny žádné webhooky." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Váš účet" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-mail" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Vaše jméno" @@ -845,11 +817,11 @@ msgstr "Pozor" msgid "ds.component-subtitle" msgstr "Komponenty k aktualizaci:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Povolit" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Zrušit" @@ -869,15 +841,15 @@ msgstr "Poskytovatel autorizace není pro tento profil povolen" msgid "errors.auth-provider-not-configured" msgstr "Zprostředkovatel ověřování není nakonfigurován." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Zdá se, že nejste ověřeni nebo platnost relace vypršela." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Písmo %s se nepodařilo načíst" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Písma %s se nepodařilo načíst" @@ -885,15 +857,15 @@ msgstr "Písma %s se nepodařilo načíst" msgid "errors.cannot-upload" msgstr "Nelze nahrát soubor médií." -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Váš prohlížeč tuto operaci nedokáže provést" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Již použitá e-mailová adresa" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-mail byl již ověřen." @@ -901,25 +873,25 @@ msgstr "E-mail byl již ověřen." msgid "errors.email-as-password" msgstr "Jako heslo nelze použít váš e-mail" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Doména není povolena" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "E-mail «%s» má mnoho trvalých zpráv o nedoručitelnosti." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "E-mail «%s» byl nahlášen jako spam nebo byl trvale nedostupný." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Vypadá to, že otevíráte soubor, který má povolenou funkci '%s', ale " "aktuální verze penpotu ji nepodporuje nebo je deaktivovaná." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Funkce '%s' není podporována." @@ -931,14 +903,14 @@ msgstr "Musí obsahovat alespoň 1 znak." msgid "errors.field-not-all-whitespace" msgstr "Název musí obsahovat jiný znak než mezeru." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Zdá se, že existuje nesoulad mezi povolenými funkcemi a funkcemi souboru, " "který se pokoušíte otevřít. Před otevřením souboru je třeba provést migraci " "pro '%s'." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Něco se pokazilo." @@ -950,7 +922,7 @@ msgstr "Chybná barva" msgid "errors.invalid-email" msgstr "Zadejte prosím platný email" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Potvrzovací e-mail se musí shodovat" @@ -970,11 +942,11 @@ msgstr "Tato pozvánka byla pravděpodobně zrušena nebo vypršela její platno msgid "errors.ldap-disabled" msgstr "Ověřování LDAP je vypnuto." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Dosáhli jste '%s' kvóty. Kontaktujte podporu." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Bylo dosaženo maximálního (%s) počtu e-mailů, které lze pozvat v jedné " @@ -992,13 +964,13 @@ msgstr "Zdá se, že obsah obrázku neodpovídá příponě souboru." msgid "errors.media-type-not-allowed" msgstr "Zdá se, že toto není platný obrázek." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Profil, který zvete, má ztlumené e-maily (zprávy o spamu nebo vysoké " "nedoručitelnosti)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Probíhá migrace" @@ -1010,7 +982,7 @@ msgstr "Potvrzovací heslo se musí shodovat" msgid "errors.password-too-short" msgstr "Heslo by mělo mít nejméně 8 znaků" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Neplatná data ve schránce" @@ -1018,7 +990,7 @@ msgstr "Neplatná data ve schránce" msgid "errors.profile-blocked" msgstr "Profil je zablokován" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "Váš profil má ztlumené e-maily (zprávy o spamu nebo vysoká nedoručitelnost)." @@ -1026,21 +998,21 @@ msgstr "Váš profil má ztlumené e-maily (zprávy o spamu nebo vysoká nedoru msgid "errors.registration-disabled" msgstr "Registrace je momentálně zakázána." -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Zjištěna nekompatibilní funkce '%s'" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Nedostatečný počet členů pro opuštění týmu, pravděpodobně jste chtěli tým " "smazat." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Člen, kterého se pokoušíte přiřadit, neexistuje." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Vlastník nemůže opustit tým, musíte přeřadit roli vlastníka." @@ -1048,39 +1020,39 @@ msgstr "Vlastník nemůže opustit tým, musíte přeřadit roli vlastníka." msgid "errors.unexpected-error" msgstr "Došlo k neočekávané chybě." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Neznámý token" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Soubor má nekompatibilní číslo verze" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Chyba připojení, adresa URL není dostupná" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "Adresa URL neprošla ověřením." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Poslední dodávka nebyla úspěšná." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Chyba při ověřování SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Timeout" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Při ověřování došlo k neočekávané chybě" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Neočekávaný stav %s" @@ -1096,13 +1068,13 @@ msgstr "Staré heslo je nesprávné" msgid "feedback.description" msgstr "Popisek" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Jsme rádi, že jste tady. Pokud potřebujete pomoc, zkuste nejdříve hledání " "na fóru." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot komunita" @@ -1116,11 +1088,11 @@ msgstr "" "Popište důvod svého e-mailu a uveďte, zda se jedná o problém, nápad nebo " "připomínku. Člen našeho týmu vám co nejdříve odpoví." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Zde vám pomůžeme s vašimi technickými dotazy." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Účet podpory na X" @@ -1152,7 +1124,7 @@ msgstr "Stáhnout zdrojový obrázek" msgid "inspect.attributes.shadow" msgstr "Stín" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Velikost a pozice" @@ -1160,39 +1132,27 @@ msgstr "Velikost a pozice" msgid "inspect.attributes.stroke" msgstr "Obrys" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Střed" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Uvnitř" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Venku" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Typografie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Rodina písem" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Velikost písma" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Styl písma" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Mezery mezi písmeny" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Výška řádku" @@ -1204,11 +1164,11 @@ msgstr "Žádná" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Podtržení" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "První písmena velká" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Malá písmena" @@ -1220,7 +1180,7 @@ msgstr "Žádná" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Nenastaveno" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Velká písmena" @@ -1284,39 +1244,39 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Text" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Zkratky" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Přijmout" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Aktivní" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Přidat" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Přidat vlastní písmo" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Přidávání..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Správce" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Vše" @@ -1324,29 +1284,29 @@ msgstr "Vše" msgid "labels.and" msgstr "a" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Zpět" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Vypadá to, že musíte chvíli počkat a zkusit to znovu; provádíme drobnou " "údržbu našich serverů." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Špatná brána" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Zrušit" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Zavřít" @@ -1354,7 +1314,7 @@ msgstr "Zavřít" msgid "labels.code" msgstr "Kód" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Sbalit" @@ -1362,11 +1322,11 @@ msgstr "Sbalit" msgid "labels.comment" msgstr "Komentář" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Komentáře" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Komunita" @@ -1374,35 +1334,35 @@ msgstr "Komunita" msgid "labels.confirm-password" msgstr "Potvrďte heslo" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Pokračovat" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Můžete pokračovat s účtem Penpot" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Kopírovat odkaz" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Vytvořit" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Vytvořit nový tým" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Zadejte název nového týmu" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Menu" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Smazat" @@ -1414,7 +1374,7 @@ msgstr "Smazat komentář" msgid "labels.delete-comment-thread" msgstr "Smazat vlákno" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Smazat pozvánku" @@ -1422,31 +1382,31 @@ msgstr "Smazat pozvánku" msgid "labels.delete-multi-files" msgstr "Smazat soubory %s" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Vývojář" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Zahodit" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Pracovní verze" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Upravit" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Editor" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Platnost vypršela" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exportovat" @@ -1458,51 +1418,51 @@ msgstr "Zpětná vazba zakázána" msgid "labels.feedback-sent" msgstr "Zpětná vazba byla odeslána" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Rodina písem" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Styly" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Písma" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Úložiště Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Poslat zpětnou vazbu" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Jít zpět" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Grafický design" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Centrum nápovědy" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Skrýt vyřešené komentáře" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Import" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Neaktivní" @@ -1510,35 +1470,35 @@ msgstr "Neaktivní" msgid "labels.info" msgstr "Informace" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Nainstalovaná písma" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Interní chyba" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Pozvánky" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Jazyk" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Knihovny a šablony" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Načítání…" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Přihlásit se ne registrovat" @@ -1546,19 +1506,19 @@ msgstr "Přihlásit se ne registrovat" msgid "labels.login" msgstr "Přihlášení" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Odhlásit se" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Člen" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Členové" @@ -1566,41 +1526,41 @@ msgstr "Členové" msgid "labels.new-password" msgstr "Nové heslo" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Další" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Vše je aktuální! Zde se zobrazí upozornění na nové komentáře." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Nejsou žádné pozvánky." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Ups!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Oznámení" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 soubor" msgstr[1] "%s soubory" msgstr[2] "%s souborů" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 tabule" msgstr[1] "%s tabule" msgstr[2] "%s tabulí" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 projekt" @@ -1611,27 +1571,27 @@ msgstr[2] "%s projektů" msgid "labels.old-password" msgstr "Staré heslo" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Možnosti" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Jiné (upřesněte)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Ostatní" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Majitel" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Heslo" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Nevyřízený" @@ -1639,43 +1599,43 @@ msgstr "Nevyřízený" msgid "labels.post" msgstr "Příspěvek" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Předchozí" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Produktový nebo UX design" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Produktový nebo projektový manažer" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projekty" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Poznámky k verzi" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Odstranit" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Odebrat člena" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Přejmenovat" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Přejmenovat tým" @@ -1699,7 +1659,7 @@ msgstr "nová odpověď" msgid "labels.reply.thread" msgstr "Odpověď" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Znovu poslat pozvánku" @@ -1707,43 +1667,43 @@ msgstr "Znovu poslat pozvánku" msgid "labels.restore" msgstr "Obnovit" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Zkusit znovu" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Role" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Uložit" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Hledat" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Hledat písmo" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Vyberte možnost" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Poslat" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Odesílání…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Provádíme plánovanou údržbu našich systémů." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Služba je nedostupná" @@ -1751,55 +1711,55 @@ msgstr "Služba je nedostupná" msgid "labels.sets" msgstr "Sady" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Nastavení" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Sdílet" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Knihovny" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Zobrazit všechny komentáře" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Zobrazit seznam komentářů" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Zobrazit pouze vaše zmínky" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Zobrazit pouze vaše komentáře" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Start" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Student nebo učitel" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Motivy" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutoriály" @@ -1807,23 +1767,23 @@ msgstr "Tutoriály" msgid "labels.unpublish-multi-files" msgstr "Zrušit publikování %s souborů" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Aktualizovat" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Aktualizovat tým" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Nahrát" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Nahrát vlastní písma" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Nahrávání…" @@ -1831,11 +1791,11 @@ msgstr "Nahrávání…" msgid "labels.view-only" msgstr "Jen pro čtení" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Prohlížeč" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -1843,11 +1803,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Napsat nový komentář" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(vy)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Váš účet" @@ -1900,61 +1860,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Přidat „%s“ jako Sdílenou knihovnu" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Velké posunutí" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Ověřit nový e-mail" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Zašleme vám e-mail na váš aktuální e-mail „%s“, abychom ověřili vaši " "identitu." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Nový e-mail" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Změnit e-mail" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Změňte svůj e-mail" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Vytvořit webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Vytvořit webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Adresa URL datové části" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Zrušit a ponechat si můj účet" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Ano, odstranit můj účet" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Odebráním účtu ztratíte všechny své aktuální projekty a archivy." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Opravdu chcete smazat svůj účet?" @@ -2004,29 +1964,29 @@ msgstr "Opravdu chcete smazat soubory %s?" msgid "modals.delete-file-multi-confirm.title" msgstr "Mazání %s souborů" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Opravdu chcete smazat tento styl písma? Pokud je použit v souboru, nenačte " "se." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Odstranění stylu písma" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "Opravdu chcete smazat toto písmo? Pokud je použito v souboru, nenačte se." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Mazání písma" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Opravdu chcete smazat tuto stránku?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Smazat stránku" @@ -2042,35 +2002,35 @@ msgstr "Opravdu chcete smazat tento projekt?" msgid "modals.delete-project-confirm.title" msgstr "Smazat projekt" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Smazat soubor" msgstr[1] "Smazat soubory" msgstr[2] "Smazat soubory" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Není aktivován v žádném souboru." msgstr[1] "Nejsou aktivovány v žádném souboru." msgstr[2] "Nejsou aktivovány v žádném souboru." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Tato knihovna je aktivována zde: " msgstr[1] "Tyto knihovny jsou aktivovány zde: " msgstr[2] "Tyto knihovny jsou aktivovány zde: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Opravdu chcete smazat tento soubor?" msgstr[1] "Opravdu chcete smazat tyto soubory?" msgstr[2] "Opravdu chcete smazat tyto soubory?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Mazání souboru" @@ -2091,19 +2051,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Mazání týmu" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Smazat člena" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Opravdu chcete tohoto člena smazat z týmu?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Smazat člena týmu" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -2116,172 +2076,172 @@ msgstr[2] "" "Aktiva, která již byla v těchto souborech použita, tam zůstanou (nebude " "porušen žádný návrh)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Smazat webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Opravdu chcete tento webhook smazat?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Mazání webhooku" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Upravit webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Upravit webhook" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Poslat pozvánku" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-maily oddělené čárkou" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Některé e-maily jsou od současných členů týmu. Jejich pozvánky nebudou " "odeslány." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Můžete pozvat členy do týmu, aby měli přístup k tomuto souboru a všem " "souborům týmu." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Pozvat členy do týmu" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Protože jste jediným členem tohoto týmu, tým bude smazán spolu s jeho " "projekty a soubory." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Opravdu chcete opustit tým %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Nemůžete opustit tým, pokud není žádný jiný člen, kterého by bylo možné " "povýšit na vlastníka. Možná budete chtít tým smazat." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Jste vlastníkem tohoto týmu. Než odejdete, vyberte jiného člena, kterého " "chcete povýšit na vlastníka." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Povýšit a odejít" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Vyberte člena, kterého chcete povýšit" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Než odejdete" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Opustit tým" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Opravdu chcete opustit tento tým?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Opuštění týmu" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Přesunout" msgstr[1] "Přesunout" msgstr[2] "Přesunout" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Opravdu chcete tuto knihovnu přesunout?" msgstr[1] "Opravdu chcete přesunout tyto knihovny?" msgstr[2] "Opravdu chcete přesunout tyto knihovny?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Přesunout knihovnu" msgstr[1] "Přesunout knihovny" msgstr[2] "Přesunout knihovny" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Posunout o" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Převést vlastnictví" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Pokud převedete vlastnictví, změníte svou roli na správce a ztratíte " "některá oprávnění k tomuto týmu. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Jste současným vlastníkem tohoto týmu. Opravdu chcete udělat %s novým " "vlastníkem týmu?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Nový majitel týmu" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publikovat" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Vaše knihovna je prázdná. Opravdu to chcete publikovat?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publikovat prázdnou knihovnu" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Malé posunutí" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Zrušit publikování" msgstr[1] "Zrušit publikování" msgstr[2] "Zrušit publikování" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Opravdu chcete zrušit publikování této knihovny?" msgstr[1] "Opravdu chcete zrušit publikování těchto knihoven?" msgstr[2] "Opravdu chcete zrušit publikování těchto knihoven?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Zrušit publikování knihovny" @@ -2306,11 +2266,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Aktualizovat komponentu ve sdílené knihovně" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Tato stránka neexistuje" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Chyba 404" @@ -2328,11 +2288,11 @@ msgstr "Odeslali jsme obnovovací e-mail na adresu" msgid "not-found.login.sent-recovery-check" msgstr "Zkontrolujte svůj e-mail a kliknutím na odkaz vytvořte nové heslo." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Zaregistrujte se zdarma" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "A začněte používat Penpot během několika sekund!" @@ -2340,67 +2300,67 @@ msgstr "A začněte používat Penpot během několika sekund!" msgid "not-found.made-with-love" msgstr "Vytvořeno s LÁSKOU a Open Source" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "O přístup k tomuto souboru jste již požádali." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Již jste požádali o přístup k tomuto souboru nebo jiným souborům nebo " "projektům tohoto týmu." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Již jste požádali o přístup k tomuto projektu nebo jiným projektům nebo " "souborům tohoto týmu." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Již jste požádali o přístup k tomuto projektu." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "POŽÁDAT O PŘÍSTUP" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Pamatujte, že pokud to vlastník dovolí, budete pozváni do týmu." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Vaše žádost byla správně odeslána!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "K tomuto souboru nemáte přístup." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Přejděte na svůj Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Pokud to vlastník dovolí, budete pozváni do týmu." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Soubor Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "K tomuto projektu nemáte přístup." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJEKT" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Chcete-li získat přístup k tomuto souboru, můžete požádat vlastníka týmu." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Chcete-li získat přístup k tomuto projektu, můžete požádat vlastníka týmu." @@ -2412,23 +2372,23 @@ msgstr "Přestávka na údržbu: do 5 minut budeme mimo provoz na krátkou údr msgid "notifications.by-code.upgrade-version" msgstr "K dispozici je nová verze." -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Pozvánka byla úspěšně odeslána" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Odkaz na pozvánku zkopírován" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "Nemůžete smazat svůj profil. Než budete pokračovat, znovu přiřaďte své týmy." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil byl úspěšně uložen!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Ověřovací e-mail byl odeslán na adresu %s. Zkontrolujte si e-mail!" @@ -2456,78 +2416,78 @@ msgstr "Po pojmenování svého týmu budete moci pozvat lidi, aby se přidali." msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Vytvořte tým" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Pozvat členy" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Pozvat s rolí:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Pusťme se do toho!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Vytváření reálného kódu z návrhů" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Vytváření designových systémů" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototypování" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Design UI/UX aplikace" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "K čemu budete Penpot používat?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Řekněte nám něco o sobě, abyste nám pomohli nastavit Penpot pro vás. Vaše " "odpovědi nám pomohou stanovit priority nových funkcí a nasměrovat vás " "správným směrem, abyste mohli začít." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Pomozte nám se s vámi seznámit" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Který z těchto nástrojů používáte nejčastěji?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Jakou práci děláte?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Řekněte nám o své práci" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Kde byste chtěli začít?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Vzdělání" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Osobní" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Pracovní" @@ -2577,11 +2537,11 @@ msgstr "Začněte navrhovat" msgid "profile.recovery.go-to-login" msgstr "Přejít na přihlášení" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Odpojit" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Smíšený" @@ -2593,35 +2553,35 @@ msgstr "Odstranit barvu" msgid "settings.select-this-color" msgstr "Vyberte položky pomocí tohoto stylu" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Komentáře" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Zarovnat dolů" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Zarovnat vodorovně na střed" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Zarovnat vlevo" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Zarovnat vpravo" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Zarovnat nahoru" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Zarovnat na střed svisle" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Vytvořit tabuli z výběru" @@ -2629,47 +2589,47 @@ msgstr "Vytvořit tabuli z výběru" msgid "shortcuts.bold" msgstr "Přepnout tučné písmo" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Rozdíl" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Vyloučení" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Průnik" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Sloučení" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Přesunout dozadu" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Přesunout dozadu" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Přesunout dopředu" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Posunout dopředu" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Vymazat zpět" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Kopírovat" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Zkopírovat odkaz do schránky" @@ -2677,35 +2637,35 @@ msgstr "Zkopírovat odkaz do schránky" msgid "shortcuts.create-new-project" msgstr "Vytvořit nový" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Vyjmout" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Oddálit" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Smazat" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Smazat uzel" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Odpojit komponentu" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Křivka" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipsa" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Tabule" @@ -2713,39 +2673,39 @@ msgstr "Tabule" msgid "shortcuts.draw-nodes" msgstr "Nakreslit cestu" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Cesta" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Obdélník" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Text" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplikovat" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Zrušit" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Exportovat tvary" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Přiblížit, aby se vešlo vše" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Překlopit vodorovně" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Překlopit svisle" @@ -2769,23 +2729,23 @@ msgstr "Přejít na sdílené knihovny" msgid "shortcuts.go-to-search" msgstr "Hledat" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Skupina" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Rozložit vodorovně" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Zobrazit/skrýt uživatelské rozhraní" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Přiblížit" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Vložit obrázek" @@ -2793,7 +2753,7 @@ msgstr "Vložit obrázek" msgid "shortcuts.italic" msgstr "Přepnout kurzívu" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Propojit uzly" @@ -2801,39 +2761,39 @@ msgstr "Propojit uzly" msgid "shortcuts.line-through" msgstr "Přepnout řádek" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Udělat roh" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Udělat křivku" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Maska" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Sloučit uzly" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Posunout" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Posunout dolů rychle" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Posunout doleva rychle" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Posunout doprava rychle" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Posunout nahoru rychle" @@ -2841,321 +2801,321 @@ msgstr "Posunout nahoru rychle" msgid "shortcuts.move-nodes" msgstr "Posunout uzel" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Posunout dolů" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Posunout vlevo" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Posunout vpravo" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Posunout nahoru" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Další tabule" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Nebyly nalezeny žádné zkratky" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Nastavit průhlednost na 100 %" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Nastavit průhlednost na 10 %" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Nastavit průhlednost na 20 %" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Nastavit průhlednost na 30 %" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Nastavit průhlednost na 40 %" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Nastavit průhlednost na 50 %" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Nastavit průhlednost na 60 %" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Nastavit průhlednost na 70 %" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Nastavit průhlednost na 80 %" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Nastavit průhlednost na 90 %" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Výběr barvy" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Přejít do sekce komentářů" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Přejít na nástěnku" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Přejděte do sekce inspektor" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Přejít do části interakce" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Přejít do části interakce" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Přejít na pracovní plochu" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " nebo " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Vložit" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Předchozí tabule" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Znovu" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Přejmenovat" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Obnovit přiblížení" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Měřítko" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Základy" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Nástěnka" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Prohlížeč" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Pracovní plocha" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Vybrat vše" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Vybrat další vrstvu" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Vybrat nadřazenou vrstvu" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Vybrat předchozí vrstvu" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Rozdělit uzly" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Zobrazit/skrýt mřížku pixelů" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Zobrazit/skrýt zkratky" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Přichytit k uzlům" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Přichytit k mřížce pixelů" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Začít upravovat" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Zarovnání" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Obecný" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Hlavní menu" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Upravit vrstvy" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigace" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigace" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigace" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panely" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Cesty" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Tvary" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Texty" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Nástroje" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Přiblížení" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Přiblížení" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Nastavit náhledy" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Klávesové zkratky" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Přepnout dynamické zarovnání" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Přepnout podklady" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Přepnout paletu barev" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Přepnout režim soustředění" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Přepnout zobrazení na celou obrazovku" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Zobrazit / Skrýt vodící linky" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Přepnout historii" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Přepínání vrstev" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Přidat/odebrat flexibilní rozložení" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Přidat/odebrat rozložení mřížky" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Uzamknout vybrané" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Uzamknout proporce" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Zobrazit / Skrýt pravítka" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Přichytit na vodící linky" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Přichytit k vodítkům pravítek" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Přepnout paletu textu" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Změnit motiv" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Přepnout viditelnost" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Přepnout styl přiblížení" @@ -3163,63 +3123,63 @@ msgstr "Přepnout styl přiblížení" msgid "shortcuts.underline" msgstr "Přepnout podtržení" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Zpět" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Zrušit seskupení" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Zrušit masku" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Rozmístit vertikálně" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Zmenšení zoomu" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Zvětšení zoomu" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Přiblížit vybrané" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Poskytovatelé písem - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Písma - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projekty - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Hledat - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Sdílené knihovny - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot – Svoboda designu pro týmy" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Poskytněte zpětnou vazbu – Penpot" @@ -3227,7 +3187,7 @@ msgstr "Poskytněte zpětnou vazbu – Penpot" msgid "title.settings.notifications" msgstr "Upozornění – Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Nastavení - Penpot" @@ -3235,27 +3195,27 @@ msgstr "Nastavení - Penpot" msgid "title.settings.password" msgstr "Heslo - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Pozvánky – %s – Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Členové - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Nastavení - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Režim zobrazení - Penpot" @@ -3263,99 +3223,99 @@ msgstr "%s - Režim zobrazení - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Na stránce nebyly nalezeny žádné tabule." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Tabule nenalezena." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Komentáře (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Nezobrazovat interakce" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Celá obrazovka" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Zkontrolovat (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interakce" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interakce (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Kopírovat odkaz" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Zobrazit interakce" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Zobrazit interakce po kliknutí" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Mapa stránek" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Poslední doručení proběhlo úspěšně." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Zarovnat vodorovně na střed (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Rozmístit vodorovné mezery (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Zarovnat doleva (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Zarovnat doprava (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Zarovnat dolů (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Zarovnat svisle na střed (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Rozmístit vertikální mezery (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Zarovnat nahoru (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Přidat knihovnu" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Všechny podklady" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Barvy" @@ -3363,7 +3323,7 @@ msgstr "Barvy" msgid "workspace.assets.colors.add-color" msgstr "Přidat barvu" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Komponenty" @@ -3375,17 +3335,17 @@ msgstr "Přidat komponentu" msgid "workspace.assets.create-group" msgstr "Vytvořit skupinu" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Vaše položky budou automaticky pojmenovány jako „název skupiny / název " "položky“" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Delete" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplikovat" @@ -3393,35 +3353,35 @@ msgstr "Duplikovat" msgid "workspace.assets.duplicate-main" msgstr "Duplikovat hlavní" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Upravit" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtr" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Zobrazení mřížky" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Skupina" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Název skupiny" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Zobrazení seznamu" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "místní knihovna" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Nebyly nalezeny žádné podklady" @@ -3429,7 +3389,7 @@ msgstr "Nebyly nalezeny žádné podklady" msgid "workspace.assets.open-library" msgstr "Otevřete soubor knihovny" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Přejmenovat" @@ -3437,11 +3397,11 @@ msgstr "Přejmenovat" msgid "workspace.assets.rename-group" msgstr "Přejmenovat skupinu" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Hledat v podkladech" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Sdílená knihovna" @@ -3452,43 +3412,43 @@ msgstr[0] "1 komponenta" msgstr[1] "%s komponenty" msgstr[2] "%s komponent" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Seřadit" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Typografie" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Přidat typografii" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Velikost" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Styl písma" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Chcete-li upravit, přejděte do souboru knihovny stylů" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Mezery mezi písmeny" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Výška řádku" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformace textu" @@ -3496,67 +3456,67 @@ msgstr "Transformace textu" msgid "workspace.assets.ungroup" msgstr "Zrušit seskupení" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Vytvořit oblast" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Vytvořit tabuli" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Sloučit buňky" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Přidat 1 sloupec vpravo" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Přidat 1 sloupec vlevo" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Smazat sloupec" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Smazat sloupec a tvary" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Duplikovat sloupec" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Přidat 1 řádek níže" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Přidat 1 řádek výše" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Smazat řádek" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Smazat řádek a tvary" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Duplikovat řádek" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Režim soustředění" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Vypnout režim soustředění" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Zapnout režim soustředění" @@ -3568,191 +3528,191 @@ msgstr "Lineární přechod" msgid "workspace.gradients.radial" msgstr "Radiální přechod" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Zakázat dynamické zarovnání" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Zakázat proporcionální měřítko" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Zakázat přichycení k vodicím lištám" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Zakázat přichycení k pixelu" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Zakázat přichycení na vodící linky pravítek" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Povolit dynamické zarovnání" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Povolit proporcionální měřítko" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Přichytit k vodicím lištám" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Povolit přichycení k pixelu" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Přichytit k vodítkům pravítek" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Skrýt názvy tabulí" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Skrýt vodící linky" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Skrýt paletu barev" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Skrýt mřížku pixelů" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Skrýt pravítka" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Skrýt paletu písem" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Upravit" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Soubor" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Nápověda a informace" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Předvolby" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Zobrazit" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Znovu" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Vybrat vše" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Zobrazit názvy tabulí" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Zobrazit vodící linky" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Zobrazit paletu barev" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Zobrazit mřížku pixelů" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Zobrazit pravítka" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Zobrazit paletu písem" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Přepnout na tmavý motiv" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Přepnout na světlý motiv" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Zpět" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Resetovat" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Chyba při ukládání" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Uloženo" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Ukládání" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Sdílet" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Režim zobrazení (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Přiblížení" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Zvětšit na celou obrazovku" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Zmenšit na celou obrazovku" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Přiblížit, aby se vše vešlo" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Celá obrazovka" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Přiblížit vybrané" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Upravit mřížku" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Odejít" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Zobrazit možnosti mezer uvnitř všech stran" @@ -3768,23 +3728,23 @@ msgstr "Hotovo" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Lokalizovat" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Vyhledat rozložení mřížky" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Ve vaší knihovně zatím nejsou žádné barevné styly" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Ve vaší knihovně zatím nejsou žádné typografické styly" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Knihovna souborů" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Nedávné barvy" @@ -3796,71 +3756,71 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Uložit styl barev" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Nebo přidejte některé z těchto:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "Ve vašem týmu nejsou žádné sdílené knihovny, které můžete hledat" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "nějaké šablony zde" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Knihovna souborů" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "KNIHOVNY V TOMTO SOUBORU" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "KNIHOVNY" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "AKTUALIZACE KNIHOVNY" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Načítání…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Neexistují žádné sdílené knihovny, které by potřebovaly aktualizaci" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Nebyly nalezeny žádné shody pro „%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Prohledat sdílené knihovny" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "SDÍLENÉ KNIHOVNY" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Připojit knihovnu" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Odpojit knihovnu" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Aktualizace" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "zobrazit všechny změny" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "AKTUALIZACE" @@ -3892,39 +3852,39 @@ msgstr "Přepnout rozostření" msgid "workspace.options.canvas-background" msgstr "Pozadí plátna" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Oříznout obsah" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Komponenta" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Anotace" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Kopírovat" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Vytvořte anotaci" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Upravit anotaci" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Hlavní" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Vyměnit komponentu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "V této knihovně zatím nejsou žádné položky" @@ -3968,11 +3928,11 @@ msgstr "Nahoře" msgid "workspace.options.constraints.topbottom" msgstr "Nahoře a dole" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exportovat" @@ -3980,38 +3940,38 @@ msgstr "Exportovat" msgid "workspace.options.export-multiple" msgstr "Exportovat výběr" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Exportovat 1 prvek" msgstr[1] "Exportovat %s prvky" msgstr[2] "Exportovat %s prvků" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Přidat export" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Odebrat export" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Přípona" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Export byl dokončen" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Exportování…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Export se nezdařil" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Export je nečekaně pomalý" @@ -4027,7 +3987,7 @@ msgstr "Přidat barvu výplně" msgid "workspace.options.fill.remove-fill" msgstr "Odebrat barvu výplně" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Změňte velikost panelu podle obsahu" @@ -4051,67 +4011,67 @@ msgstr "Začátky" msgid "workspace.options.grid.auto" msgstr "Automaticky" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Sloupce" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Barva" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Mezera" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Výška" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Mezera vně" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Nastavit jako výchozí" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Dole" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Střed" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Vlevo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Vpravo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Roztáhnout" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Nahoře" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Použít výchozí" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Šířka" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Řádky" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Čtverec" @@ -4123,15 +4083,15 @@ msgstr "Výplň skupiny" msgid "workspace.options.group-stroke" msgstr "Ohraničení skupiny" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Vodící linky" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Výška" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspektor" @@ -4347,103 +4307,103 @@ msgstr "Interakce" msgid "workspace.options.interactions.add-interaction" msgstr "Přidat interakci" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Color" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Color burn" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Color dodge" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Darken" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Difference" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Exclusion" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Hard light" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Hue" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Lighten" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminosity" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Multiply" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Overlay" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturation" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Screen" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Soft light" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Přepnout viditelnost vrstvy" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Maximální výška" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Maximální šířka" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Minimální výška" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Minimální šířka" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Více barev" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Více barev knihovny" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Průhlednost" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototyp" @@ -4467,19 +4427,19 @@ msgstr "Nahoře vlevo" msgid "workspace.options.radius-top-right" msgstr "Nahoře vpravo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Nedávné" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Opakovat" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Otočit" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Hledat písmo" @@ -4487,7 +4447,7 @@ msgstr "Hledat písmo" msgid "workspace.options.select-a-shape" msgstr "Vyberte tvar, tabuli nebo skupinu a přetáhněte připojení k jiné tabuli." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Vybrané barvy" @@ -4555,47 +4515,47 @@ msgstr "Přepnout stín" msgid "workspace.options.show-fill-on-export" msgstr "Zobrazit v exportech" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Zobrazit v režimu zobrazení" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Velikost" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Předvolby velikosti" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Kruh" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamant" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Šipka" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Žádné" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Kulaté" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Čtvercové" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Obdélník" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Trojúhelník" @@ -4603,7 +4563,7 @@ msgstr "Trojúhelník" msgid "workspace.options.stroke-color" msgstr "Barva obrysu" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Šířka obrysu" @@ -4611,35 +4571,35 @@ msgstr "Šířka obrysu" msgid "workspace.options.stroke.add-stroke" msgstr "Přidat barvu obrysu" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Uprostřed" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Přerušovaný" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Tečkovaný" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Uvnitř" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Smíšený" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Vně" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Odebrat obrys" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Plný" @@ -4675,11 +4635,11 @@ msgstr "Automatická šířka" msgid "workspace.options.text-options.grow-fixed" msgstr "Pevná" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Mezery mezi písmeny" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Výška řádku" @@ -4723,186 +4683,186 @@ msgstr "Podtrhnout (%s)" msgid "workspace.options.use-play-button" msgstr "Pomocí tlačítka přehrávání v záhlaví spusťte zobrazení prototypu." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Šířka" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Kreslit uzly (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Připojit uzly (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Do rohu (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Do křivky (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Sloučit uzly (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Přesunout uzly (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Oddělit uzly (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Přichytit uzly (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Otevřít" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Objevte [další pluginy](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Zatím nejsou nainstalovány žádné pluginy" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Manifest pluginu je nesprávný." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Abyste mohli tento plugin používat, musíte být editor" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Plugin neexistuje nebo adresa URL není správná." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Instalovat" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Nainstalované pluginy" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Správce pluginů" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Pluginy" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "AKTUALIZOVAT TENTO PLUGIN" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "Plugin byl změněn od posledního otevření. Nyní chce také získat přístup k:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Zahájit stahování souborů." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Přečtěte si své komentáře a odpovědi." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Přečtěte si a upravte své komentáře a odpovídejte svým jménem." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Číst obsah souborů, ke kterým mají uživatelé přístup." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Číst a upravovat obsah souborů, ke kterým mají uživatelé přístup." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Tento plugin je vytvořen třetí stranou, takže se před udělením přístupu " "ujistěte, že mu důvěřujete. Ochrana a bezpečnost vašich dat jsou pro nás " "důležité. Máte-li jakékoli obavy, kontaktujte podporu." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Číst vaše knihovny a položky." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Číst a upravovat vaše knihovny a položky." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "PLUGIN '%s' POŽADUJE PŘÍSTUP K:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Číst informace o profilu aktuálního uživatele." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Seznam pluginů" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Odebrat plugin" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Napište adresu URL pluginu" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Pluginy" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "TEĎ NE" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Chcete se podívat? Otevře se v novém konceptu pro váš aktuální tým. (Pokud " "ne, můžete jej vždy najít v nainstalovaných zásuvných modulech libovolného " "souboru.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "PLUGIN '%s' JE NAINSTALOVÁN PRO VAŠEHO UŽIVATELE!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "VYZKOUŠET PLUGIN" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Přidat flexibilní rozložení" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Přidat rozvržení mřížky" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Přidat rozvržení" @@ -4914,7 +4874,7 @@ msgstr "Poslat na konec" msgid "workspace.shape.menu.backward" msgstr "Poslat dozadu" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Kopírovat" @@ -4946,27 +4906,27 @@ msgstr "Kopírovat jako text" msgid "workspace.shape.menu.create-annotation" msgstr "Vytvořit anotaci" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Výběr na tabuli" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Vytvořit komponentu" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Vytvořte více komponent" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Vyjmout" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Smazat" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Smazat počáteční bod" @@ -4978,7 +4938,7 @@ msgstr "Odpojit instanci" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Odpojit instance" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Rozdíl" @@ -4986,15 +4946,15 @@ msgstr "Rozdíl" msgid "workspace.shape.menu.duplicate" msgstr "Duplikovat" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Upravit" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Vyloučit" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Vyrovnat" @@ -5006,7 +4966,7 @@ msgstr "Překlopit vodorovně" msgid "workspace.shape.menu.flip-vertical" msgstr "Překlopit svisle" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "začátek" @@ -5018,31 +4978,31 @@ msgstr "Posunout dopředu" msgid "workspace.shape.menu.front" msgstr "Posunout na začátek" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Skupina" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Skrýt" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Zobrazit/skrýt uživatelské rozhraní" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Průnik" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Zamknout" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Maska" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Vložit" @@ -5050,19 +5010,19 @@ msgstr "Vložit" msgid "workspace.shape.menu.paste-props" msgstr "Vlastnosti vložit" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Cesta" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Odstranit flexibilní rozložení" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Odebrat rozložení mřížky" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Odebrat rozložení" @@ -5082,11 +5042,11 @@ msgstr "Obnovit hlavní komponentu" msgid "workspace.shape.menu.select-layer" msgstr "Vybrat vrstvu" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Zobrazit" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Zobrazit v panelu položek" @@ -5102,19 +5062,19 @@ msgstr "Odebrat miniaturu" msgid "workspace.shape.menu.thumbnail-set" msgstr "Nastavit jako miniaturu" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Zrušit seskupení" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Sloučení" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Odemknout" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Zrušit masku" @@ -5130,47 +5090,47 @@ msgstr "Sbalit postranní panel" msgid "workspace.sidebar.expand" msgstr "Rozbalit postranní panel" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Historie" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Vrstvy" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Komponenty" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Tabule" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Skupiny" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Obrázky" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Masky" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Prohledat vrstvy" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Tvary" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Texty" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Importované atributy SVG" @@ -5182,7 +5142,7 @@ msgstr "Stránky" msgid "workspace.sidebar.sitemap.add-page" msgstr "Přidat stránku" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Mapa stránek" @@ -5190,7 +5150,7 @@ msgstr "Mapa stránek" msgid "workspace.tokens.active-themes" msgstr "%s aktivních motivů" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Aplikováno na" @@ -5202,7 +5162,7 @@ msgstr "Zpět na seznam motivů" msgid "workspace.tokens.create-new-theme" msgstr "Vytvořte si svůj první motiv hned teď." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Vytvořte si." @@ -5254,7 +5214,7 @@ msgstr "Žádný motiv není aktivní" msgid "workspace.tokens.no-permisions-set" msgstr "K aktivaci/deaktivaci sad musíte být editor" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Abyste mohli používat témata, musíte být editor" @@ -5266,7 +5226,7 @@ msgstr "Zatím nejsou definovány žádné sady. Nejprve si jednu vytvořte." msgid "workspace.tokens.no-sets-yet" msgstr "Zatím nejsou žádné sady." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Nejsou zde žádné motivy." @@ -5274,11 +5234,11 @@ msgstr "Nejsou zde žádné motivy." msgid "workspace.tokens.no-themes-currently" msgstr "Momentálně nemáte žádné motivy." -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Původní hodnota: %s" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Vyřešená hodnota: %s" @@ -5300,11 +5260,11 @@ msgstr "" msgid "workspace.tokens.token-description" msgstr "Popis" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Jméno" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " není platný název tokenu.\n" @@ -5315,7 +5275,7 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Hodnota" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENY - %s" @@ -5323,7 +5283,7 @@ msgstr "TOKENY - %s" msgid "workspace.tokens.tools" msgstr "Nástroje" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Hodnota není platná" @@ -5331,55 +5291,55 @@ msgstr "Hodnota není platná" msgid "workspace.toolbar.assets" msgstr "Položky" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Paleta barev (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Komentáře (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Křivka (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elipsa (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Tabule (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Obrázek (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Přesunout (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Cesta (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Pluginy (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Obdélník (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Text (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Typografie (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Přepnout panel nástrojů" @@ -5392,7 +5352,7 @@ msgstr "Hotovo" msgid "workspace.top-bar.view-only" msgstr "**Kontrola kódu** (Pouze zobrazení)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Zatím nedošlo k žádným změnám v historii" @@ -5532,19 +5492,19 @@ msgstr "položka typografie" msgid "workspace.undo.entry.unknown" msgstr "Operace nad %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Zrušit" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Více informací" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Ve sdílených knihovnách jsou aktualizace" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Aktualizace" diff --git a/frontend/translations/da.po b/frontend/translations/da.po index 21387692b9..49c8e2aaf6 100644 --- a/frontend/translations/da.po +++ b/frontend/translations/da.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2026.10.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Har du allerede en konto?" @@ -29,7 +29,7 @@ msgstr "" "Det her er en DEMO service, BRUG IKKE for rigtigt arbejde, projekterne vil " "blive slettet periodevis." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Glemt adgangskode?" @@ -37,27 +37,27 @@ msgstr "Glemt adgangskode?" msgid "auth.fullname" msgstr "Fulde Navn" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Log på her" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "Github" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "Gitlab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -77,7 +77,7 @@ msgstr "Profilen er ikke bekræftet, venligt verificer profilen før du går vid msgid "auth.notifications.recovery-token-sent" msgstr "Gendannelseslink for adgangskoden er sendt til din indbakke." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Tilsluttet teamet med succes" @@ -105,11 +105,11 @@ msgstr "Glemt adgangskode?" msgid "auth.recovery-submit" msgstr "Skift din adgangskode" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Ingen konto?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Opret en konto" @@ -117,19 +117,19 @@ msgstr "Opret en konto" msgid "auth.register-title" msgstr "Opret en konto" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Tilføj som Delt Bibliotek" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Skift email" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopi)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Opret nyt team" @@ -137,7 +137,7 @@ msgstr "Opret nyt team" msgid "dashboard.default-team-name" msgstr "Dit Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Slet team" @@ -149,7 +149,7 @@ msgstr "Dublikér" msgid "dashboard.duplicate-multi" msgstr "Dublikér %s filer" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -159,7 +159,7 @@ msgstr "" "som en **enkelt skrifttypefamilie**. Du kan uploade skrifttyper med " "følgende formater: **TTF, OTF og WOFF** (kun én er nødvendig)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -167,15 +167,15 @@ msgstr "" "Penpot. Find ud af mere i sektionen om indholdsrettigheder i [Penpot's " "Terms of Service] (%s). Du kan også læse om [font licensing](2)." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Invitér til team" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Forlad team" -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Delte Biblioteker" @@ -195,35 +195,35 @@ msgstr "Flyt %s filer til" msgid "dashboard.move-to-other-team" msgstr "Flyt til andet team" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Ny Fil" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Ny Fil" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nyt projekt" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nyt Projekt" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Intet match fundet for “%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Fastgjorte projekter bliver vist her" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Din email-adresse er blevet opdateret med succes" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Din email-adresse er blevet bekræftet med succes" @@ -231,7 +231,7 @@ msgstr "Din email-adresse er blevet bekræftet med succes" msgid "dashboard.notifications.password-saved" msgstr "Adgangskode gemt med succes!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s medlemmer" @@ -243,35 +243,35 @@ msgstr "Åben fil i en ny fane" msgid "dashboard.password-change" msgstr "Skift adgangskode" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Fastgør/Løsne" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projekter" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Vil du slette din konto?" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Søg…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Søger efter “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Vælg UI sprog" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Vælg tema" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Vis alle filer" @@ -291,7 +291,7 @@ msgstr "Din fil er blevet dublikeret med succes" msgid "dashboard.success-duplicate-project" msgstr "Dit projekt er blevet dublikeret med succes" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Din fil er blevet flyttet med succes" @@ -303,47 +303,47 @@ msgstr "Dine filer er blevet flyttet med succes" msgid "dashboard.success-move-project" msgstr "Dit projekt er blevet flyttet med succes" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Team info" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Medlemmer" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Team projekter" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "UI tema" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Søgeresultater" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Skriv for at søge i resultater" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Opdater indstillinger" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Din konto" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Email" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Dit navn" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Fortryd" @@ -355,7 +355,7 @@ msgstr "Ok" msgid "ds.confirm-title" msgstr "Er du sikker?" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Din browser kan ikke gøre denne operation" @@ -363,52 +363,52 @@ msgstr "Din browser kan ikke gøre denne operation" msgid "errors.invalid-recovery-token" msgstr "Genopretningspoletten er ugyldig." -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Skrifttypefamilie" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Skrifttyper" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Gå tilbage!" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Installeret skrifttyper" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Søg efter skrifttype" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Upload" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Upload brugerdefinerede skrifttyper" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Uploader..." -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Er du sikker på, at du vil slette denne skrifttype? Den vil ikke indlæse, " "hvis den bliver brugt i en fil." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Sletter skrifttype" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Skrifttype Udbydere - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Skrifttyper - %s - Penpot" diff --git a/frontend/translations/de.po b/frontend/translations/de.po index b04b506455..334a2786b9 100644 --- a/frontend/translations/de.po +++ b/frontend/translations/de.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:13+0000\n" -"Last-Translator: Anonymous \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: German " "\n" "Language: de\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Sie haben bereits ein Konto?" @@ -33,7 +33,7 @@ msgstr "" "Das ist eine DEMO-VERSION, verwenden Sie es NICHT zum Arbeiten, die " "Projekte werden regelmäßig gelöscht." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Passwort vergessen?" @@ -41,37 +41,37 @@ msgstr "Passwort vergessen?" msgid "auth.fullname" msgstr "Vollständiger Name" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "In mein Konto einloggen" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Hier anmelden." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot ist das kostenlose Open-Source-Design-Tool für die Zusammenarbeit " "von Design und Code" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -79,6 +79,10 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Geben Sie ein neues Passwort ein" +#: src/app/main/ui/auth/verify_token.cljs:54 +msgid "auth.notifications.organization-invitation-accepted" +msgstr "Sie gehören jetzt zu %s" + #: src/app/main/ui/auth/recovery.cljs:52 msgid "auth.notifications.password-changed-successfully" msgstr "Passwort erfolgreich geändert" @@ -93,7 +97,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "Der Link zur Wiederherstellung des Passworts wurde an Ihre E-Mail gesendet." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Sie sind dem Team beigetreten" @@ -125,7 +129,7 @@ msgstr "Passwort vergessen?" msgid "auth.recovery-submit" msgstr "Passwort ändern" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Noch kein Konto?" @@ -139,7 +143,7 @@ msgstr "" msgid "auth.register-account-title" msgstr "Ihr Name" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Konto erstellen" @@ -154,7 +158,7 @@ msgstr "" "Ich stimme den [Nutzungsbedingungen](%s) und der " "[Datenschutzrichtlinie](%s) zu." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Nutzungsbedingungen" @@ -176,93 +180,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Es wurden keine Personen für @%s gefunden" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Veröffentlichen" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Alle Penpot-Benutzer" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Sind Sie sicher, dass Sie diesen Link löschen möchten? Wenn Sie das tun, " "wird er für niemanden mehr verfügbar sein" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(aktuell)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Link entfernen" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Link erstellen" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Link wurde erfolgreich kopiert" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Verwalten von Berechtigungen" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 Seite freigegeben" msgstr[1] "%s Seiten freigegeben" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Können kommentieren" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Können den Code ansehen" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Jeder mit dem Link kann auf die Datei zugreifen" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Geteilte Seiten" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Link zum Teilen wird hier erscheinen" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Nur Teammitglieder" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Prototypen teilen" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Alle auswählen" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Veröffentlichung aufheben" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Teammanagement" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot ist für Teams gedacht. Um gemeinsam an Projekten und Dateien zu " "arbeiten, laden Sie Mitglieder ein" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Teamwork!" @@ -278,26 +282,42 @@ msgstr "Datei wiederherstellen" msgid "dashboard.add-file" msgstr "Datei hinzufügen" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Hinzufügen als gemeinsam genutzte Bibliothek" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "E-Mail-Adresse ändern" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:614 +msgid "dashboard.change-organization-modal.accept" +msgstr "TEAM VERSCHIEBEN" + +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Verschieben nach:" -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:611 +msgid "dashboard.change-organization-modal.description" +msgstr "" +"Projekte und Dateien bleiben für die Teammitglieder verfügbar. Das Team " +"übernimmt die Einstellungen der neuen Organisation." + +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Wählen Sie eine Organisation aus" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "Organisation des Teams ändern" +#: src/app/main/ui/dashboard/check_updates.cljs:334 +msgid "dashboard.check-updates.available-message" +msgstr "" +"Neuere Versionen bringen Leistungsverbesserungen, neue Funktionen und " +"Korrekturen, damit Ihre Installation aktuell bleibt." + #: src/app/main/ui/dashboard/check_updates.cljs:309 msgid "dashboard.check-updates.available-title" msgstr "EINE NEUE PENPOT VERSION IST VERFÜGBAR" @@ -318,31 +338,49 @@ msgstr "Letzte Version" msgid "dashboard.check-updates.try-again" msgstr "Versuchen Sie es wieder" +#: src/app/main/ui/dashboard/check_updates.cljs:230 +msgid "dashboard.check-updates.unable-hint" +msgstr "" +"Überprüfen Sie Ihr Netzwerk oder die Instanzkonfiguration und versuchen Sie " +"es erneut." + #: src/app/main/ui/dashboard/check_updates.cljs:226 msgid "dashboard.check-updates.unable-message" msgstr "Diese Installation konnte den Penpot Releases Service nicht erreichen." +#: src/app/main/ui/dashboard/check_updates.cljs:265 +msgid "dashboard.check-updates.uptodate-message" +msgstr "Sie verwenden die neueste stabile Version:" + +#: src/app/main/ui/dashboard/check_updates.cljs:254 +msgid "dashboard.check-updates.uptodate-title" +msgstr "PENPOT IST AUF DEM NEUESTEN STAND" + #: src/app/main/ui/dashboard/check_updates.cljs:371 msgid "dashboard.check-updates.view-changelog" msgstr "Vollständiges Changelog anzeigen" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/check_updates.cljs:374 +msgid "dashboard.check-updates.view-release-notes" +msgstr "Versionshinweise anzeigen" + +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Papierkorb leeren" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(Kopie)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Neue Organisation erstellen" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Neues Team erstellen" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Diese Version anheften" @@ -350,7 +388,7 @@ msgstr "Diese Version anheften" msgid "dashboard.default-team-name" msgstr "Ihr Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Wollen Sie wirklich alle Projekte und Dateien unwiderruflich löschen? Diese " @@ -362,43 +400,43 @@ msgstr "" "Wollen Sie wirklich %s unwiderruflich löschen? Diese Aktion kann nicht " "rückgängig gemacht werden." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s Dateien wurden gelöscht." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Unwiderruflich löschen" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Projekt löschen" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Wollen Sie wirklich das Projekt %s löschen? Diese Aktion kann nicht " "rückgängig gemacht werden." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s wurde erfolgreich gelöscht." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Team löschen" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "" "Ihr Papierkorb ist leer. Gelöschte Dateien und Projekte werden hier " "angezeigt." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Wird gelöscht am %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Penpot-Datei herunterladen (.penpot)" @@ -410,19 +448,19 @@ msgstr "Duplizieren" msgid "dashboard.duplicate-multi" msgstr "%s Dateien duplizieren" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Sobald ein Projektmitglied einen Entwurf erstellt, wird es hier angezeigt." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Noch keine Entwürfe vorhanden." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Sobald ein Projektmitglied eine Datei erstellt, wird es hier angezeigt." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Noch keine Dateien vorhanden." @@ -470,23 +508,23 @@ msgstr "Eine .penpot-Datei importieren" msgid "dashboard.empty-project.start" msgstr "Beginnen Sie mit dem Erschaffen erstaunlicher Dinge" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "Beim Löschen der Dateien ist ein Fehler aufgetreten." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "Beim Löschen des Projektes %s ist ein Fehler aufgetreten." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "Beim Wiederherstellen der Datei %s ist ein Fehler aufgetreten." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "Beim Wiederherstellen der Dateien ist ein Fehler aufgetreten." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "Beim Wiederherstellen des Projektes %s ist ein Fehler aufgetreten." @@ -494,37 +532,37 @@ msgstr "Beim Wiederherstellen des Projektes %s ist ein Fehler aufgetreten." msgid "dashboard.export-binary-multi" msgstr "%s Penpot-Dateien herunterladen (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Zeichenflächen als PDF exportieren" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Als PDF exportieren" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s von %s Elementen ausgewählt" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exportieren" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Sie können den Elementen über die Designeigenschaften (unten in der rechten " "Seitenleiste) Exporteinstellungen hinzufügen." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informationen zum Festlegen von Exporten bei Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Es gibt keine Elemente mit Exporteinstellungen." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Auswahl exportieren" @@ -548,37 +586,37 @@ msgstr "Rasteransicht" msgid "dashboard.files-layout.list" msgstr "Listenansicht" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Schriftart gelöscht" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Alle ablehnen" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "" "Die von Ihnen hochgeladenen benutzerdefinierten Schriftarten werden hier " "angezeigt." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Noch keine eigenen Schriftarten vorhanden." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Sobald ein Projektmitglied eine benutzerdefinierte Schriftart hochgeladen " "hat, wird sie hier angezeigt." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 Schriftart hinzugefügt" msgstr[1] "%s Schriftarten hinzugefügt" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -589,7 +627,7 @@ msgstr "" "den folgenden Formaten hochladen: **TTF, OTF und WOFF** (nur eine wird " "benötigt)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -599,11 +637,11 @@ msgstr "" "Penpot](%s). Mehr über die [Lizenzierung von Schriftarten erfahren Sie " "hier](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Alle hochladen" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -614,47 +652,69 @@ msgstr "" "empfehlen wir die Verwendung von [Transfonter](https://transfonter.org/), " "um Webfonts zu generieren und Fehler zu beheben. " -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 +msgid "dashboard.go-to-admin-console" +msgstr "Zur Admin-Konsole" + +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "Letzte Änderung %s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Dateien importieren" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Huch! Wir konnten diese Datei nicht importieren" +#: src/app/main/ui/dashboard/import.cljs:698 +msgid "dashboard.import.auto-linked-libraries" +msgid_plural "dashboard.import.auto-linked-libraries" +msgstr[0] "1 Bibliothek wurde automatisch anhand des Namens verknüpft." +msgstr[1] "%s Bibliotheken wurden automatisch anhand des Namens verknüpft." + #: src/app/main/ui/dashboard.cljs:264 msgid "dashboard.import.bad-url" msgstr "Der Import ist fehlgeschlagen. Die URL der Vorlage ist falsch" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:838 +msgid "dashboard.import.confirm-library-links" +msgstr "Bibliotheksverknüpfungen bestätigen" + +#: src/app/main/ui/dashboard/import.cljs:288 +msgid "dashboard.import.file-name-label" +msgstr "Dateiname" + +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "Wir konnten diese Datei nicht überprüfen." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Diese Datei scheint beschädigt zu sein." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Nicht alle Dateien wurden importiert" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Die folgenden Dateien sind fehlerhaft:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Dateien mit Fehlern werden nicht hochgeladen." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:728 +msgid "dashboard.import.import-error.unknown-error" +msgstr "Beim Verarbeiten dieser Datei ist ein Fehler aufgetreten." + +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgstr "%s Dateien wurden erfolgreich importiert." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Einige Dateien enthielten ungültige Objekte, die entfernt wurden." @@ -662,79 +722,95 @@ msgstr "Einige Dateien enthielten ungültige Objekte, die entfernt wurden." msgid "dashboard.import.no-perms" msgstr "Sie haben nicht die Berechtigung, in dieses Team zu importieren" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Farben werden verarbeitet" +#: src/app/main/ui/dashboard/import.cljs:431 +msgid "dashboard.import.resolve-libraries" +msgstr "" +"Einige Bibliotheken konnten nicht automatisch verknüpft werden. Wählen Sie " +"für jede die richtige Bibliothek:" -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Komponenten werden verarbeitet" +#: src/app/main/ui/dashboard/import.cljs:553 +msgid "dashboard.import.resolve-libraries-summary" +msgstr "Prüfen Sie die Bibliotheksverknüpfungen, bevor Sie bestätigen:" -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Medien werden verarbeitet" +#: src/app/main/ui/dashboard/import.cljs:509 +msgid "dashboard.import.summary.linked" +msgstr "Automatisch verknüpft" -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Seite %s wird verarbeitet" +#: src/app/main/ui/dashboard/import.cljs:525 +msgid "dashboard.import.summary.new" +msgstr "Neu" -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Textstile werden verarbeitet" +#: src/app/main/ui/dashboard/import.cljs:546 +msgid "dashboard.import.summary.no-selection" +msgstr "Keine Bibliothek ausgewählt" -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Daten werden hochgeladen (%s/%s)" +#: src/app/main/ui/dashboard/import.cljs:522 +msgid "dashboard.import.summary.original" +msgstr "Original" -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Datei %s wird hochgeladen" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Diese Einladungen werden gelöscht:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Diese Einladungen werden erneut gesendet:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Einladungen löschen" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Einladungen erneut versenden" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 +msgid "dashboard.invitations.no-permission" +msgstr "" +"Sie haben keine Berechtigung, Personen in dieses Team einzuladen oder " +"Einladungen zu bearbeiten oder zu löschen." + +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Personen einladen" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/team.cljs:136 +msgid "dashboard.invite-profile-disabled" +msgstr "Sie haben keine Berechtigung, Personen in dieses Team einzuladen" + +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:235, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:266 +msgid "dashboard.leave-organization" +msgstr "Organisation verlassen" + +#: src/app/main/data/nitrate.cljs:259 +msgid "dashboard.leave-organization.toast" +msgstr "Sie gehören nicht mehr zur Organisation %s." + +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Team verlassen" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliotheken & Vorlagen" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Hier finden Sie einige Bibliotheken und Vorlagen, die Sie zu Ihrem Projekt " "hinzufügen können" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Entdecken Sie weitere und erfahren Sie, wie Sie beitragen können" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "" "Beim Importieren der Vorlage ist ein Problem aufgetreten. Die Vorlage wurde " "nicht importiert." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Gemeinsam genutzte Bibliotheken" @@ -742,7 +818,7 @@ msgstr "Gemeinsam genutzte Bibliotheken" msgid "dashboard.loading-files" msgstr "laden Ihrer Dateien …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "laden Ihrer Schriftarten …" @@ -762,39 +838,69 @@ msgstr "%s Dateien verschieben" msgid "dashboard.move-to-other-team" msgstr "Zu anderem Team verschieben" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Neue Datei" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Neue Datei" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Neues Projekt" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Neues Projekt" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Keine Übereinstimmungen für “%s“ gefunden" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/team_form.cljs:162 +msgid "dashboard.no-organization-allows-create-team.message" +msgstr "" +"Sie haben keine Berechtigung, Teams zu einer Ihrer Organisationen " +"hinzuzufügen." + +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 +msgid "dashboard.no-organizations-yet" +msgstr "Noch keine Organisationen." + +#: src/app/main/ui/dashboard/team_form.cljs:156 +msgid "dashboard.no-permission-create-team.message" +msgstr "" +"Sie dürfen in der Organisation %s keine Teams erstellen. Wenn Sie mehr " +"Informationen benötigen, wenden Sie sich an den Eigentümer der Organisation." + +#: src/app/main/ui/dashboard/team_form.cljs:158 +msgid "dashboard.no-permission-delete-team.message" +msgstr "" +"Sie dürfen keine Teams löschen, die zur Organisation %s gehören. Wenn Sie " +"mehr Informationen benötigen, wenden Sie sich an den Eigentümer der " +"Organisation." + +#: src/app/main/ui/dashboard/team_form.cljs:164 +msgid "dashboard.no-permission-move-team.message" +msgstr "" +"Sie dürfen keine Teams verschieben, die zur Organisation %s gehören. Wenn " +"Sie mehr Informationen benötigen, wenden Sie sich an den Eigentümer der " +"Organisation." + +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Angeheftete Projekte werden hier angezeigt" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Benachrichtigungen" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Ihre E-Mail-Adresse wurde erfolgreich aktualisiert" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Ihre E-Mail-Adresse wurde erfolgreich verifiziert" @@ -806,11 +912,11 @@ msgstr "Benachrichtigungseinstellungen aktualisiert" msgid "dashboard.notifications.password-saved" msgstr "Passwort wurde erfolgreich gespeichert!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Benachrichtigungen anzeigen" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s Mitglieder" @@ -818,18 +924,26 @@ msgstr "%s Mitglieder" msgid "dashboard.open-in-new-tab" msgstr "Datei in neuem Tab öffnen" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Optionen" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Nach Rolle sortieren" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Nach Status sortieren" +#: src/app/main/data/dashboard.cljs:721 +msgid "dashboard.organization-deleted" +msgstr "Die Organisation %s wurde gelöscht." + +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 +msgid "dashboard.other-teams" +msgstr "Weitere Teams" + #: src/app/main/ui/settings/password.cljs:112, src/app/main/ui/settings/password.cljs:126 msgid "dashboard.password-change" msgstr "Passwort ändern" @@ -850,7 +964,11 @@ msgstr "Sie sind jetzt Eigentümer dieses Teams." msgid "dashboard.permissions-change.viewer" msgstr "Sie sind jetzt ein Zuschauer in diesem Team." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 +msgid "dashboard.personal-projects" +msgstr "Persönliche Projekte" + +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Anheften/Lösen" @@ -866,27 +984,27 @@ msgstr "Das Plugin-Manifest kann nicht ausgewertet werden" msgid "dashboard.plugins.try-plugin" msgstr "Plugin ausprobieren: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "Dateien werden gelöscht…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "Datei werden wiederhergestellt…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "Das Löschen dauert noch etwas" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "Das Wiederherstellen dauert noch etwas" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projekte" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Möchten Sie Ihr Konto entfernen?" @@ -894,69 +1012,116 @@ msgstr "Möchten Sie Ihr Konto entfernen?" msgid "dashboard.removed-from-team" msgstr "Sie sind nicht mehr Teil des Teams “%s“." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "" "Alle Projekte und Dateien werden wiederhergestellt. Das kann eine Weile " "dauern." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Alle Projekte und Dateien wiederherstellen" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Alles wiederherstellen" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s Dateien wurden wiederhergestellt." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Projekt wiederherstellen" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Das Projekt %s und alle enthaltenen Dateien werden wiederhergestellt." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Projekt wiederherstellen" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s wurde wiederhergestellt." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Einstellungen speichern" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Suchen…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Suche nach “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 +msgid "dashboard.section.organizations" +msgstr "Organisationen" + +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 +msgid "dashboard.section.teams" +msgstr "Teams" + +#: src/app/main/data/nitrate.cljs:529 +msgid "dashboard.select-organization-modal.accept" +msgstr "ZUR ORGANISATION HINZUFÜGEN" + +#: src/app/main/data/nitrate.cljs:527 +msgid "dashboard.select-organization-modal.choose" +msgstr "Organisation auswählen:" + +#: src/app/main/ui/dashboard/team.cljs:1042 +msgid "dashboard.select-organization-modal.external-invitations-warning" +msgstr "" +"„%s“ erlaubt nur Mitgliedern der Organisation, ihren Teams beizutreten. " +"Ausstehende Einladungen an Personen außerhalb der Organisation werden " +"widerrufen." + +#: src/app/main/ui/dashboard/team.cljs:1038 +msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" +msgstr "Ausstehende Einladungen an externe Benutzer werden storniert." + +#: src/app/main/data/nitrate.cljs:595 +msgid "dashboard.select-organization-modal.permission-info" +msgstr "" +"Hier finden Sie die Organisationen, denen Sie angehören und in die Sie das " +"Team verschieben dürfen." + +#: src/app/main/data/nitrate.cljs:519 +msgid "dashboard.select-organization-modal.permission-info-add" +msgstr "" +"Hier finden Sie die Organisationen, denen Sie angehören und in denen Sie " +"Teams erstellen oder hinzufügen dürfen." + +#: src/app/main/data/nitrate.cljs:528 +msgid "dashboard.select-organization-modal.select" +msgstr "Organisation auswählen" + +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 +msgid "dashboard.select-organization-modal.title" +msgstr "Team zu einer Organisation hinzufügen" + +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Sprache der Benutzeroberfläche auswählen" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Theme auswählen" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Dunkel (Standard)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Hell" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "System-Theme" @@ -1020,11 +1185,11 @@ msgstr "Einstellungen aktualisieren" msgid "dashboard.settings.notifications.title" msgstr "Benachrichtigungen" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Alle Dateien anzeigen" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Versionsverlauf" @@ -1048,7 +1213,7 @@ msgstr[1] "Ihre Dateien wurden erfolgreich dupliziert" msgid "dashboard.success-duplicate-project" msgstr "Ihr Projekt wurde erfolgreich dupliziert" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Ihre Datei wurde erfolgreich verschoben" @@ -1060,110 +1225,186 @@ msgstr "Ihre Dateien wurden erfolgreich verschoben" msgid "dashboard.success-move-project" msgstr "Ihr Projekt wurde erfolgreich verschoben" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/data/common.cljs:268 +#, backend +msgid "dashboard.team-belong-organization" +msgstr "Dieses Team gehört jetzt zur Organisation %s" + +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Teaminformationen" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Teammitglieder" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/data/common.cljs:269 +#, backend +msgid "dashboard.team-no-longer-belong-organization" +msgstr "Dieses Team gehört nicht mehr zur Organisation %s" + +#: src/app/main/ui/dashboard/team.cljs:1689 +msgid "dashboard.team-organization" +msgstr "Teamorganisation" + +#: src/app/main/ui/dashboard/team.cljs:1724 +msgid "dashboard.team-organization.add" +msgstr "Zu einer Organisation hinzufügen" + +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 +msgid "dashboard.team-organization.add.no-valid-organizations" +msgstr "" +"Sie haben keine Berechtigung, dieses Team zu einer Ihrer Organisationen " +"hinzuzufügen." + +#: src/app/main/ui/dashboard/team.cljs:1713 +msgid "dashboard.team-organization.change" +msgstr "Organisation des Teams ändern" + +#: src/app/main/ui/dashboard/team.cljs:946 +msgid "dashboard.team-organization.disabled-organization-tooltip" +msgstr "Es können nur Personen aus Ihrer Organisation eingeladen werden." + +#: src/app/main/ui/dashboard/team.cljs:1720 +msgid "dashboard.team-organization.none" +msgstr "Dieses Team gehört zu keiner Organisation" + +#: src/app/main/ui/dashboard/team.cljs:1716 +msgid "dashboard.team-organization.remove" +msgstr "Team aus der Organisation entfernen" + +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Teamprojekte" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Zu Ihrem Projekt hinzufügen" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "UI-Theme" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Suchergebnisse" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Gelöschte Dateien bleiben für" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s Tage im Papierkorb. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "Danach werden sie dauerhaft gelöscht." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Wenn Sie Ihre Meinung ändern, können Sie die Dateien über das Menü der " "jeweiligen Datei wiederherstellen oder dauerhaft löschen." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Zum Suchen etwas eingeben" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Veröffentlichung der Bibliothek aufheben" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Einstellungen aktualisieren" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/data/dashboard.cljs:689 +#, backend +msgid "dashboard.user-no-longer-belong-organization" +msgstr "Sie sind kein Mitglied der Organisation %s mehr" + +#: src/app/main/ui/settings/options.cljs:115 +msgid "dashboard.webgl-switch.beta" +msgstr "Beta" + +#: src/app/main/ui/settings/options.cljs:116 +msgid "dashboard.webgl-switch.description" +msgstr "" +"Die WebGL-Darstellung kann die Leistung verbessern, befindet sich aber in " +"der Beta und kann weniger stabil sein. Zwischen Arbeitsfläche, Exporten " +"(SVG/PDF) und der Ansicht können sichtbare Unterschiede auftreten." + +#: src/app/main/ui/settings/options.cljs:119 +msgid "dashboard.webgl-switch.disabled" +msgstr "Deaktiviert" + +#: src/app/main/ui/settings/options.cljs:119 +msgid "dashboard.webgl-switch.enabled" +msgstr "Aktiviert" + +#: src/app/main/ui/settings/options.cljs:122 +msgid "dashboard.webgl-switch.feedback" +msgstr "Feedback geben" + +#: src/app/main/ui/settings/options.cljs:118 +msgid "dashboard.webgl-switch.status" +msgstr "Status" + +#: src/app/main/ui/settings/options.cljs:114 +msgid "dashboard.webgl-switch.title" +msgstr "WebGL-Darstellung" + +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Aktiv" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Wenn dieser Hook ausgelöst wird, dann werden die Eventdetails übermittelt" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Sie können nur von Ihnen erstellte Webhooks löschen oder ändern." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Inhaltstyp" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Webhook erstellen" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook wurde erfolgreich erstellt." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhooks ermöglichen es, andere Websites und Anwendungen automatisch über " "bestimmte Ereignisse bei Penpot zu informieren. Dies geschieht durch das " "Versenden einer POST-Anfrage an die von Ihnen festgelegten URLs." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "" "Um einen Webhook hinzuzufügen, drücken Sie auf die Schaltfläche \"Webhook " "hinzufügen\"." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Bisher wurden noch keine Webhooks erstellt." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Ihr Konto" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-Mail" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Ihr Name" @@ -1179,11 +1420,11 @@ msgstr "Achtung" msgid "ds.component-subtitle" msgstr "Zu aktualisierende Komponenten:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Erlauben" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Abbrechen" @@ -1203,10 +1444,14 @@ msgstr "Es sind keine passenden Tokens in aktivierten Sets oder Themes vorhanden msgid "ds.inputs.numeric-input.no-matches" msgstr "Keine Treffer gefunden." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Token-Liste öffnen" +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:55, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:54, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:118, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:134 +msgid "ds.inputs.token-field.no-active-token-option" +msgstr "Das Token {%s} ist in keinem aktiven Set oder hat einen ungültigen Wert." + #: src/app/main/data/auth.cljs:344 msgid "errors.auth-provider-not-allowed" msgstr "Auth-Provider für dieses Profil nicht erlaubt" @@ -1215,15 +1460,15 @@ msgstr "Auth-Provider für dieses Profil nicht erlaubt" msgid "errors.auth-provider-not-configured" msgstr "Authentifizierungsanbieter ist nicht konfiguriert." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Anscheinend sind Sie nicht authentifiziert oder die Sitzung ist abgelaufen." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Die Schriftart %s konnte nicht geladen werden" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Die Schriftarten %s konnten nicht geladen werden" @@ -1235,43 +1480,61 @@ msgstr "Die Mediendatei kann nicht hochgeladen werden." msgid "errors.character-limit-exceeded" msgstr "Zeichenbeschränkung überschritten" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/libraries.cljs:1778 +msgid "errors.circular-library-reference" +msgstr "" +"Bibliothek kann nicht hinzugefügt werden: Das würde eine zirkuläre " +"Abhängigkeit erzeugen" + +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 +msgid "errors.clipboard-api-unavailable" +msgstr "" +"Die Zwischenablage-API ist nicht verfügbar. Stellen Sie Penpot über HTTPS " +"bereit, damit der Zugriff auf die Zwischenablage funktioniert" + +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Ihr Browser kann diese Funktion nicht ausführen" -#: src/app/main/errors.cljs:773 +#: src/app/util/clipboard.cljs:271 +msgid "errors.clipboard-permission-denied" +msgstr "" +"Zugriff auf die Zwischenablage verweigert. Erlauben Sie in Ihrem Browser " +"den Zugriff auf die Zwischenablage, um Inhalte einzufügen" + +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "Beim Kommentar ist ein Fehler aufgetreten" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Tut uns leid! Diese Datei beinhaltet veraltete Penpot-Assets und kann " "deshalb nicht geöffnet werden." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr ", damit wir Ihnen helfen können." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Penpot unterstützt diese Assets nicht mehr. Sie können uns allerdings" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "kontaktieren" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "" "Ein übergeordnetes Set kann nicht auf einem eigenen untergeordneten Pfad " "abgelegt werden." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E-Mail-Adresse wird bereits verwendet" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-Mail-Adresse wurde bereits validiert." @@ -1283,30 +1546,30 @@ msgstr "Sie können Ihre E-Mail-Adresse nicht als Passwort verwenden" msgid "errors.email-does-not-match-invitation" msgstr "Die E-Mail stimmt nicht mit der Einladung überein." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domain nicht zulässig" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Die E-Mail-Adresse «%s» hat viele permanente Unzustellbarkeitsberichte." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "Die E-Mail \"%s\" wurde als Spam oder dauerhaft abgelehnt gemeldet." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Es scheint als würden Sie eine Datei öffnen, bei der die Funktion '%s' " "aktiviert ist. Ihr aktuelle Version von Penpot unterstützt es aber nicht " "oder hat die Funktion deaktiviert." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Die Funktion '%s' wird nicht unterstützt." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Darf maximal %s Zeichen enthalten." @@ -1314,7 +1577,7 @@ msgstr "Darf maximal %s Zeichen enthalten." msgid "errors.field-min-length" msgstr "Muss mindestens 1 Zeichen enthalten." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Leeres Feld" @@ -1322,18 +1585,24 @@ msgstr "Leeres Feld" msgid "errors.field-not-all-whitespace" msgstr "Der Name muss aus etwas anderem bestehen außer Leerzeichen." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Es scheint eine Nichtübereinstimmung zwischen den aktivierten Funktionen " "und den Funktionen der Datei zu geben. Die Migrationen für '%s' müssen " "durchgeführt werden, bevor die Datei geöffnet werden kann." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/ui/dashboard/fonts.cljs:358 +msgid "errors.font-family-invalid-chars" +msgstr "" +"Der Name der Schriftfamilie darf nur Buchstaben, Ziffern, Leerzeichen, " +"Bindestriche, Unterstriche und Punkte enthalten." + +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Etwas ist schief gelaufen." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Mit dem Web Worker ist ein Fehler aufgetreten." @@ -1349,7 +1618,7 @@ msgstr "Ungültige Daten" msgid "errors.invalid-email" msgstr "Geben Sie bitte eine gültige E-Mail-Adresse ein" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Bestätigungs-E-Mail muss übereinstimmen" @@ -1361,6 +1630,19 @@ msgstr "Der Wiederherstellungscode ist ungültig." msgid "errors.invalid-text" msgstr "Ungültiger Text" +#: src/app/main/ui/static.cljs:88 +msgid "errors.invite-email-mismatch" +msgstr "" +"Diese Einladung gilt für eine andere E-Mail-Adresse. Melden Sie sich ab und " +"mit dem eingeladenen Konto an, oder bitten Sie den Teameigentümer um eine " +"neue Einladung." + +#: src/app/main/ui/static.cljs:92 +msgid "errors.invite-expired" +msgstr "" +"Diese Einladung ist abgelaufen. Bitten Sie den Teameigentümer, Ihnen eine " +"neue zu senden." + #: src/app/main/ui/static.cljs:95 msgid "errors.invite-invalid" msgstr "Einladung ist ungültig" @@ -1373,11 +1655,11 @@ msgstr "Diese Einladung wurde möglicherweise abgebrochen oder ist abgelaufen." msgid "errors.ldap-disabled" msgstr "Die LDAP-Authentifizierung ist deaktiviert." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Sie haben das Limit von '%s' erreicht. Kontaktieren Sie den Support." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Die maximale (%s) Anzahl von E-Mails, die in einer einzigen Anfrage " @@ -1397,24 +1679,38 @@ msgstr "" msgid "errors.media-type-not-allowed" msgstr "Es scheint, dass dies kein gültiges Bild ist." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "In dem von Ihnen eingeladenen Profil sind E-Mails stummgeschaltet " "(Spam-Berichte oder hohe Unzustellbarkeitsberichte)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migration wird durchgeführt" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Nur der Ersteller der Version kann sie sperren" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Nur der Ersteller der Version kann sie entsperren" +#: src/app/main/data/nitrate.cljs:209 +msgid "errors.organization-leave.no-valid-teams" +msgstr "" +"Beim Verlassen der Organisation ist ein Problem aufgetreten. Bitte " +"versuchen Sie es erneut." + +#: src/app/main/data/nitrate.cljs:210 +msgid "errors.organization-leave.organization-owner-cannot-leave" +msgstr "Der Eigentümer der Organisation kann die Organisation nicht verlassen." + +#: src/app/main/ui/auth/verify_token.cljs:98 +msgid "errors.organization-not-found" +msgstr "Diese Organisation existiert nicht" + #: src/app/main/ui/auth/recovery.cljs:25, src/app/main/ui/settings/password.cljs:73 msgid "errors.password-invalid-confirmation" msgstr "Bestätigungspasswort muss übereinstimmen" @@ -1423,7 +1719,7 @@ msgstr "Bestätigungspasswort muss übereinstimmen" msgid "errors.password-too-short" msgstr "Das Passwort sollte mindestens 8 Zeichen lang sein" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Ungültige Daten in der Zwischenablage" @@ -1431,7 +1727,7 @@ msgstr "Ungültige Daten in der Zwischenablage" msgid "errors.profile-blocked" msgstr "Das Profil ist gesperrt" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Ihr Profil hat stummgeschaltete E-Mails (Spam-Berichte oder hohe " @@ -1441,44 +1737,77 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Die Registrierung ist derzeit deaktiviert." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:700 +msgid "errors.save-failed" +msgstr "" +"Die automatische Speicherung funktioniert wegen eines Fehlers nicht. " +"Kontaktieren Sie den Support, um den Fehler zu melden, und laden Sie neu, " +"um mit Ihrer zuletzt gespeicherten Version weiterzuarbeiten." + +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG ist ungültig oder fehlerhaft" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Inkompatible Funktion '%s' erkannt" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Nicht genügend Mitglieder, um das Team zu verlassen, wahrscheinlich wollen " "Sie es löschen." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Das Mitglied, das Sie zuzuordnen möchten, existiert nicht." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:191 +msgid "errors.team-leave.only-owner-can-delete" +msgstr "Nur der Eigentümer eines Teams kann es löschen." + +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "Der Besitzer kann das Team nicht verlassen, Sie müssen die Besitzerrolle " "neu zuweisen." +#: ../common/src/app/common/types/team.cljc:27 +msgid "errors.team-name-invalid-chars" +msgstr "Der Teamname darf keines der folgenden Zeichen enthalten: '.', ':' oder '/'" + #: src/app/main/ui/workspace/tokens/sets/helpers.cljs:25, src/app/main/ui/workspace/tokens/sets/helpers.cljs:46, ../common/src/app/common/files/tokens.cljc:291 msgid "errors.token-set-already-exists" msgstr "Ein Set mit dem gleichen Namen existiert bereits" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "Das Ablegen kann nicht abgeschlossen werden, da ein Set mit demselben Namen " "bereits im Pfad existiert." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Theme-Option mit demselben Namen existiert" +#: ../common/src/app/common/files/tokens.cljc:351 +msgid "errors.token-theme-not-existing-sets" +msgstr "Das Theme verweist auf Sets, die nicht existieren: %s" + +#: src/app/main/data/workspace/tokens/errors.cljs:50, ../common/src/app/common/files/tokens.cljc:230 +msgid "errors.tokens.circular-reference" +msgstr "Das Token hat einen zirkulären Verweis" + +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 +msgid "errors.tokens.composite-line-height-needs-font-size" +msgstr "" +"Die Zeilenhöhe hängt von der Schriftgröße ab. Fügen Sie eine Schriftgröße " +"hinzu, um den aufgelösten Wert zu erhalten." + +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 +msgid "errors.tokens.empty-field" +msgstr "Dieses Feld darf nicht leer sein" + #: src/app/main/data/workspace/tokens/errors.cljs:42, ../common/src/app/common/files/tokens.cljc:34 msgid "errors.tokens.empty-input" msgstr "Der Token-Wert darf nicht leer sein" @@ -1495,7 +1824,13 @@ msgstr "Fehler beim Import:" msgid "errors.tokens.invalid-color" msgstr "Ungültiger Farbwert: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:124 +msgid "errors.tokens.invalid-font-family-token-value" +msgstr "" +"Ungültiger Token-Wert: Sie können nur auf ein Schriftfamilien-Token " +"verweisen" + +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Ungültiger Wert für die Schriftstärke: Verwenden Sie numerische Werte " @@ -1517,38 +1852,48 @@ msgstr "" "Token-Namen dürfen nur Buchstaben und Ziffern enthalten, die durch Punkte " "getrennt sind und dürfen nicht mit einem Dollarzeichen beginnen." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "" "Ungültiger Schattentyp: Es werden nur „innerShadow” oder „dropShadow” " "akzeptiert" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Ungültiger Token-Wert: Es werden nur „none“, „Uppercase“, „Lowercase“ oder " "„Capitalize“ akzeptiert" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "" "Ungültiger Token-Wert: Es werden nur „none“, „underline“ oder " "„strike-through“ akzeptiert" -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:148 +msgid "errors.tokens.invalid-token-value-shadow" +msgstr "Ungültiger Wert: Er muss auf ein zusammengesetztes Schatten-Token verweisen." + +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "" "Ungültiger Wert: Der Wert muss auf ein zusammengesetztes Typografie-Token " "verweisen." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Ungültiger Token-Wert: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Fehlende Token-Referenzen: " +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +msgid "errors.tokens.name-collision" +msgstr "" +"Der Gruppenname von %s kollidiert mit einem Token desselben Namens in einem " +"anderen aktiven Set." + #: src/app/main/data/workspace/tokens/errors.cljs:20 msgid "errors.tokens.no-token-files-found" msgstr "In dieser Datei wurden keine Tokens, Sets oder Themen gefunden." @@ -1557,29 +1902,33 @@ msgstr "In dieser Datei wurden keine Tokens, Sets oder Themen gefunden." msgid "errors.tokens.number-too-large" msgstr "Ungültiger Tokenwert. Der ermittelte Wert ist zu hoch: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "" "Die Deckkraft muss zwischen 0 und 100 % oder zwischen 0 und 1 liegen (z. B. " "50 % oder 0,5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Der Token referenziert sich selbst" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "Wert muss größer oder gleich 0 sein." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:144 +msgid "errors.tokens.shadow-spread-range" +msgstr "Die Schattenausbreitung muss größer oder gleich 0 sein." + +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "Die Rahmenbreite muss größer oder gleich 0 sein." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Ungültiger Wert: % ist nicht zulässig." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Ungültiger Wert: Einheiten sind hier nicht zulässig." @@ -1587,57 +1936,86 @@ msgstr "Ungültiger Wert: Einheiten sind hier nicht zulässig." msgid "errors.unexpected-error" msgstr "Ein unerwarteter Fehler ist aufgetreten." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Unbekannter Token" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Diese Version ist bereits gesperrt" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Diese Version ist gesperrt und kann nicht von anderen gelöscht werden" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Die Datei hat eine inkompatible Versionsnummer" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/auth/recovery.cljs:44, src/app/main/ui/auth/register.cljs:125, src/app/main/ui/settings/password.cljs:42 +msgid "errors.weak-password" +msgstr "Das Passwort erfüllt die Anforderungen nicht" + +#: src/app/main/ui/auth/recovery.cljs:37, src/app/main/ui/auth/register.cljs:119, src/app/main/ui/settings/password.cljs:35 +#, backend +msgid "errors.weak-password.insufficient-digits" +msgstr "Mindestens 1 Ziffer" + +#: src/app/main/ui/auth/recovery.cljs:38, src/app/main/ui/auth/register.cljs:120, src/app/main/ui/settings/password.cljs:36 +#, backend +msgid "errors.weak-password.insufficient-lowercase" +msgstr "Mindestens 1 Kleinbuchstabe" + +#: src/app/main/ui/auth/recovery.cljs:40, src/app/main/ui/auth/register.cljs:122, src/app/main/ui/settings/password.cljs:38 +#, backend +msgid "errors.weak-password.insufficient-special" +msgstr "Mindestens 1 Sonderzeichen" + +#: src/app/main/ui/auth/recovery.cljs:39, src/app/main/ui/auth/register.cljs:121, src/app/main/ui/settings/password.cljs:37 +#, backend +msgid "errors.weak-password.insufficient-uppercase" +msgstr "Mindestens 1 Großbuchstabe" + +#: src/app/main/ui/auth/recovery.cljs:36, src/app/main/ui/auth/register.cljs:118, src/app/main/ui/settings/password.cljs:34 +#, backend +msgid "errors.weak-password.too-short" +msgstr "Mindestens 8 Zeichen" + +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "" "WebGL funktioniert nicht mehr. Bitte laden Sie die Seite neu, um sie " "zurückzusetzen" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Ups! Der Canvas-Kontext ist verloren gegangen" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Verbindungsfehler, URL ist nicht erreichbar" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "Die URL erfüllt nicht die Validierungskriterien." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Die letzte Übertragung war nicht erfolgreich." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Fehler bei der SSL-Validierung." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Zeitüberschreitung" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Unerwarteter Fehler bei der Validierung" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Unerwarteter Status %s" @@ -1657,17 +2035,17 @@ msgstr "Beschreibung" msgid "feedback.description-placeholder" msgstr "Bitte beschreiben Sie ihr Feedback" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Wir sind froh, dass Sie hier sind. Wenn Sie Hilfe benötigen, verwenden Sie " "die Suchfunktion, bevor Sie einen Beitrag schreiben." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot-Community" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Weitere Möglichkeiten uns zu kontaktieren" @@ -1692,11 +2070,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Kontakt" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Hier helfen wir Ihnen bei technischen Fragen." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X Support-Konto" @@ -1716,14 +2094,68 @@ msgstr "Idee" msgid "feedback.type.issue" msgstr "Problem" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Dateien herunterladen" +#: src/app/main/ui/exports/files.cljs:431 +msgid "files-export-modal.options.detach-libraries.message" +msgstr "Verknüpfte Bibliotheks-Assets werden nicht in die Datei aufgenommen." + +#: src/app/main/ui/exports/files.cljs:425 +msgid "files-export-modal.options.detach-libraries.title" +msgstr "Assets als einfache Objekte behandeln" + +#: src/app/main/ui/exports/files.cljs:429 +msgid "files-export-modal.options.include-libraries.message" +msgstr "" +"Dateien mit verknüpften Bibliotheken werden in den Export aufgenommen, die " +"Verknüpfung bleibt erhalten. " + +#: src/app/main/ui/exports/files.cljs:423 +msgid "files-export-modal.options.include-libraries.title" +msgstr "Datei und Bibliotheken exportieren" + +#: src/app/main/ui/exports/files.cljs:432 +msgid "files-export-modal.options.link-later.message" +msgstr "" +"Beim Import können Sie vorhandene Bibliotheken mit passenden Namen " +"verknüpfen." + +#: src/app/main/ui/exports/files.cljs:426 +msgid "files-export-modal.options.link-later.title" +msgstr "Passende Bibliotheken beim Import verknüpfen" + +#: src/app/main/ui/exports/files.cljs:430 +msgid "files-export-modal.options.merge-libraries.message" +msgstr "" +"Ihre Datei wird exportiert, wobei die Bibliotheks-Assets in die lokale " +"Bibliothek übernommen werden." + +#: src/app/main/ui/exports/files.cljs:424 +msgid "files-export-modal.options.merge-libraries.title" +msgstr "Bibliotheks-Assets in die Datei einbetten" #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "Ein Fehler ist aufgetreten" +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 +msgid "import-shortcuts.apply" +msgstr "Übernehmen" + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:102 +msgid "import-shortcuts.diff-modal-text" +msgstr "" +"Die folgenden Tastaturkürzel werden durch die importierte Konfiguration " +"geändert." + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:98 +msgid "import-shortcuts.diff-modal-title" +msgstr "Tastaturkürzel importieren" + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:148 +msgid "import-shortcuts.no-changes" +msgstr "" +"Zwischen den aktuellen und den importierten Tastaturkürzeln wurden keine " +"Unterschiede gefunden." + #: src/app/main/ui/components/color_input.cljs:31 msgid "inspect.attributes.color" msgstr "Farbe" @@ -1756,7 +2188,7 @@ msgstr "Bildvorschau" msgid "inspect.attributes.shadow" msgstr "Schatten" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Größe und Position" @@ -1764,55 +2196,51 @@ msgstr "Größe und Position" msgid "inspect.attributes.stroke" msgstr "Rahmen" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Zentriert" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Innen" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Außen" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Typografie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Schriftart" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Schriftgröße" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Schriftschnitt" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Zeichenabstand" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Zeilenabstand" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 +msgid "inspect.attributes.typography.mixed-font-family" +msgstr "Gemischte Schriftfamilie" + #: src/app/main/ui/inspect/attributes/text.cljs:114 msgid "inspect.attributes.typography.text-decoration.none" msgstr "Keine" +#: src/app/main/ui/inspect/attributes/text.cljs:117 +msgid "inspect.attributes.typography.text-decoration.strikethrough" +msgstr "Durchgestrichen" + #: src/app/main/ui/inspect/attributes/text.cljs:115 msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Unterstrichen" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Kapitälchen" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Kleinbuchstaben" @@ -1824,7 +2252,7 @@ msgstr "Keine" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Nicht gesetzt" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Großbuchstaben" @@ -1872,6 +2300,10 @@ msgstr "Kopieren" msgid "inspect.subtitle.main" msgstr "Hauptkomponente" +#: src/app/main/ui/inspect/right_sidebar.cljs:176 +msgid "inspect.tabs-switcher-label" +msgstr "Inspektionsansicht" + #: src/app/main/ui/inspect/right_sidebar.cljs:142 msgid "inspect.tabs.code.selected.circle" msgstr "Kreis" @@ -1948,47 +2380,274 @@ msgstr "Festgelegter Wert:" msgid "inspect.tabs.styles.variants-panel" msgstr "Varianteneigenschaften" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/settings/integrations.cljs:612 +msgid "integrations.access-tokens.create" +msgstr "Neues Zugangstoken erstellen" + +#: src/app/main/ui/settings/integrations.cljs:628 +msgid "integrations.access-tokens.empty.add-one" +msgstr "" +"Drücken Sie die Schaltfläche „Neues Zugangstoken erstellen“, um eines zu " +"erzeugen." + +#: src/app/main/ui/settings/integrations.cljs:627 +msgid "integrations.access-tokens.empty.no-access-tokens" +msgstr "Sie haben noch keine Token." + +#: src/app/main/ui/settings/integrations.cljs:602 +msgid "integrations.access-tokens.personal" +msgstr "Persönliche Zugangstoken" + +#: src/app/main/ui/settings/integrations.cljs:607 +msgid "integrations.access-tokens.personal.description" +msgstr "" +"Persönliche Zugangstoken funktionieren als Alternative zur Anmeldung mit " +"Benutzername und Passwort und können einer Anwendung Zugriff auf die " +"interne Penpot-API geben" + +#: src/app/main/ui/settings/integrations.cljs:68 +msgid "integrations.copy-to-clipboard" +msgstr "In die Zwischenablage kopieren" + +#: src/app/main/ui/settings/integrations.cljs:253 +msgid "integrations.create-access-token.title" +msgstr "Zugangstoken erstellen" + +#: src/app/main/ui/settings/integrations.cljs:252 +msgid "integrations.create-access-token.title.created" +msgstr "Zugangstoken erstellt" + +#: src/app/main/ui/settings/integrations.cljs:371 +msgid "integrations.delete-token.accept" +msgstr "Token löschen" + +#: src/app/main/ui/settings/integrations.cljs:370 +msgid "integrations.delete-token.message" +msgstr "Sind Sie sicher, dass Sie dieses Token löschen möchten?" + +#: src/app/main/ui/settings/integrations.cljs:369 +msgid "integrations.delete-token.title" +msgstr "Token löschen" + +#: src/app/main/ui/settings/integrations.cljs:213 +msgid "integrations.expiration-180-days" +msgstr "180 Tage" + +#: src/app/main/ui/settings/integrations.cljs:210 +msgid "integrations.expiration-30-days" +msgstr "30 Tage" + +#: src/app/main/ui/settings/integrations.cljs:211 +msgid "integrations.expiration-60-days" +msgstr "60 Tage" + +#: src/app/main/ui/settings/integrations.cljs:212 +msgid "integrations.expiration-90-days" +msgstr "90 Tage" + +#: src/app/main/ui/settings/integrations.cljs:208 +msgid "integrations.expiration-date.label" +msgstr "Ablaufdatum" + +#: src/app/main/ui/settings/integrations.cljs:209 +msgid "integrations.expiration-never" +msgstr "Nie" + +#: src/app/main/ui/settings/integrations.cljs:393 +msgid "integrations.expired-on" +msgstr "Abgelaufen am %s" + +#: src/app/main/ui/settings/integrations.cljs:394 +msgid "integrations.expires-on" +msgstr "Läuft ab am %s" + +#: src/app/main/ui/settings/integrations.cljs:294 +msgid "integrations.generate-mcp-key.title" +msgstr "MCP-Schlüssel erzeugen" + +#: src/app/main/ui/settings/integrations.cljs:292 +msgid "integrations.generate-mcp-key.title.created" +msgstr "MCP-Schlüssel erzeugt" + +#: src/app/main/ui/settings/integrations.cljs:193 +msgid "integrations.info.mcp-server" +msgstr "" +"Der Penpot-MCP-Server ermöglicht MCP-Clients, direkt mit " +"Penpot-Designdateien zu arbeiten." + +#: src/app/main/ui/settings/integrations.cljs:119 +msgid "integrations.mcp-key.will-expire" +msgstr "Der MCP-Schlüssel läuft am %s ab" + +#: src/app/main/ui/settings/integrations.cljs:122 +msgid "integrations.mcp-key.will-not-expire" +msgstr "Der MCP-Schlüssel hat kein Ablaufdatum" + +#: src/app/main/ui/settings/integrations.cljs:494 +msgid "integrations.mcp-server.description" +msgstr "" +"Der Penpot-MCP-Server ermöglicht MCP-Clients, direkt mit " +"Penpot-Designdateien zu arbeiten." + +#: src/app/main/ui/settings/integrations.cljs:577 +msgid "integrations.mcp-server.mcp-keys.help" +msgstr "MCP-Clients einrichten" + +#: src/app/main/ui/settings/integrations.cljs:564 +msgid "integrations.mcp-server.mcp-keys.info" +msgstr "" +"Das ist die Server-URL, die Sie brauchen, um Ihren MCP-Client mit dem " +"Penpot-MCP-Server zu verbinden." + +#: src/app/main/ui/settings/integrations.cljs:544 +msgid "integrations.mcp-server.mcp-keys.regenerate" +msgstr "MCP-Schlüssel neu erzeugen" + +#: src/app/main/ui/settings/integrations.cljs:537 +msgid "integrations.mcp-server.mcp-keys.title" +msgstr "MCP-Schlüssel" + +#: src/app/main/ui/settings/integrations.cljs:545 +msgid "integrations.mcp-server.mcp-keys.tootip" +msgstr "Der MCP-Schlüssel wird für die Einrichtung des MCP-Clients benötigt" + +#: src/app/main/ui/settings/integrations.cljs:500 +msgid "integrations.mcp-server.status" +msgstr "Status" + +#: src/app/main/ui/settings/integrations.cljs:520 +msgid "integrations.mcp-server.status.disabled" +msgstr "Deaktiviert" + +#: src/app/main/ui/settings/integrations.cljs:519 +msgid "integrations.mcp-server.status.enabled" +msgstr "Aktiviert" + +#: src/app/main/ui/settings/integrations.cljs:510 +msgid "integrations.mcp-server.status.expired.0" +msgstr "" +"Der MCP-Schlüssel für die Verbindung zum MCP-Server ist abgelaufen. Die " +"Verbindung kann deshalb nicht hergestellt werden." + +#: src/app/main/ui/settings/integrations.cljs:515 +msgid "integrations.mcp-server.status.expired.1" +msgstr "" +"Erzeugen Sie den MCP-Schlüssel neu und aktualisieren Sie die " +"Client-Konfiguration mit dem neuen Schlüssel." + +#: src/app/main/ui/settings/integrations.cljs:489 +msgid "integrations.mcp-server.title" +msgstr "MCP-Server" + +#: src/app/main/ui/settings/integrations.cljs:200 +msgid "integrations.name.label" +msgstr "Name" + +#: src/app/main/ui/settings/integrations.cljs:201 +msgid "integrations.name.placeholder" +msgstr "Der Name hilft zu erkennen, wofür das Token gedacht ist" + +#: src/app/main/ui/settings/integrations.cljs:392 +msgid "integrations.no-expiration" +msgstr "Kein Ablaufdatum" + +#: src/app/main/ui/settings/integrations.cljs:478 +msgid "integrations.notification.success.copied-link" +msgstr "Link in die Zwischenablage kopiert" + +#: src/app/main/ui/settings/integrations.cljs:241, src/app/main/ui/settings/integrations.cljs:274, src/app/main/ui/settings/integrations.cljs:319 +msgid "integrations.notification.success.created" +msgstr "Token erfolgreich erstellt" + +#: src/app/main/ui/settings/integrations.cljs:87 +msgid "integrations.notification.success.mcp-key-copied" +msgstr "MCP-Schlüssel kopiert" + +#: src/app/main/ui/settings/integrations.cljs:444 +msgid "integrations.notification.success.mcp-server-disabled" +msgstr "MCP-Server deaktiviert" + +#: src/app/main/ui/settings/integrations.cljs:443 +msgid "integrations.notification.success.mcp-server-enabled" +msgstr "MCP-Server aktiviert" + +#: src/app/main/ui/settings/integrations.cljs:88 +msgid "integrations.notification.success.token-copied" +msgstr "Token kopiert" + +#: src/app/main/ui/settings/integrations.cljs:337 +msgid "integrations.regenerate-mcp-key.info" +msgstr "" +"Wenn Sie den MCP-Schlüssel neu erzeugen, wird der aktuelle sofort " +"widerrufen. Anwendungen, die ihn verwenden, funktionieren dann nicht mehr." + +#: src/app/main/ui/settings/integrations.cljs:336 +msgid "integrations.regenerate-mcp-key.title" +msgstr "MCP-Schlüssel neu erzeugen" + +#: src/app/main/ui/settings/integrations.cljs:334 +msgid "integrations.regenerate-mcp-key.title.created" +msgstr "MCP-Schlüssel neu erzeugt" + +#: src/app/main/ui/settings/integrations.cljs:651 +msgid "integrations.title" +msgstr "Integrationen" + +#: src/app/main/ui/settings/integrations.cljs:108 +msgid "integrations.token.info.non-recuperable" +msgstr "" +"Dieses eindeutige Token kann nicht wiederhergestellt werden. Wenn Sie es " +"verlieren, müssen Sie ein neues erstellen." + +#: src/app/main/ui/settings/integrations.cljs:120 +msgid "integrations.token.will-expire" +msgstr "Das Token läuft am %s ab" + +#: src/app/main/ui/settings/integrations.cljs:123 +msgid "integrations.token.will-not-expire" +msgstr "Das Token hat kein Ablaufdatum" + +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Alle als gelesen markieren" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Tastaturkürzel" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Über Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Akzeptieren" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Aktiv" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Hinzufügen" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Eigene Schriftart hinzufügen" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Hinzufügen..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Admin" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Alles" @@ -1996,17 +2655,17 @@ msgstr "Alles" msgid "labels.and" msgstr "und" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Zurück" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Sie müssen ein wenig warten und es erneut versuchen. Wir führen eine kurze " "Wartung an unseren Servern durch." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Bad Gateway" @@ -2014,15 +2673,27 @@ msgstr "Bad Gateway" msgid "labels.blur" msgstr "Weichzeichnen" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/ui/inspect/attributes/blur.cljs:31, src/app/main/ui/inspect/styles/style_box.cljs:32, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:262, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:274 +msgid "labels.blur-effects" +msgstr "Weichzeichner" + +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Abbrechen" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/ui/dashboard/sidebar.cljs:546 +msgid "labels.check-for-updates" +msgstr "Nach Updates suchen" + +#: src/app/main/ui/dashboard/sidebar.cljs:545 +msgid "labels.checking-for-updates" +msgstr "Suche nach Updates…" + +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Schließen" @@ -2030,7 +2701,7 @@ msgstr "Schließen" msgid "labels.code" msgstr "Code" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Zusammenklappen" @@ -2042,15 +2713,15 @@ msgstr "Farbe" msgid "labels.comment" msgstr "Kommentar" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Kommentare" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Community" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Community & Beiträge" @@ -2062,19 +2733,19 @@ msgstr "Berechnet" msgid "labels.confirm-password" msgstr "Passwort bestätigen" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Support kontaktieren" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Kontakt" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Weiter" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Sie können mit einem Penpot-Konto fortfahren" @@ -2086,31 +2757,35 @@ msgstr "Kopieren" msgid "labels.copy-color" msgstr "Farbe kopieren" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Link kopieren" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-heute" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Erstellen" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Neues Team erstellen" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Neuen Teamnamen eingeben" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:111, src/app/main/ui/settings/restore_shortcuts_modal.cljs:102 +msgid "labels.current" +msgstr "Aktuell" + +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Dashboard" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Löschen" @@ -2122,7 +2797,7 @@ msgstr "Kommentar löschen" msgid "labels.delete-comment-thread" msgstr "Thread löschen" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Einladung löschen" @@ -2130,23 +2805,35 @@ msgstr "Einladung löschen" msgid "labels.delete-multi-files" msgstr "%s Dateien löschen" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/settings/profile.cljs:122 +msgid "labels.delete-profile-photo.message" +msgstr "Sind Sie sicher, dass Sie Ihr Profilfoto löschen möchten?" + +#: src/app/main/ui/settings/profile.cljs:121 +msgid "labels.delete-profile-photo.title" +msgstr "Profilfoto löschen" + +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Gelöscht" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Developer" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Verwerfen" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "%s herunterladen" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:300 +msgid "labels.download-simple" +msgstr "Herunterladen" + +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Entwürfe" @@ -2154,27 +2841,31 @@ msgstr "Entwürfe" msgid "labels.duplicate" msgstr "Duplizieren" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Bearbeiten" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Editor" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Leer" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Fehler" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/data/workspace/versions.cljs:314 +msgid "labels.exit" +msgstr "Beenden" + +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Abgelaufen" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exportieren" @@ -2186,7 +2877,7 @@ msgstr "Feedback deaktiviert" msgid "labels.feedback-sent" msgstr "Feedback gesendet" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2194,55 +2885,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Fläche" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +msgid "labels.flex-layout" +msgstr "Flex-Layout" + +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Schriftart" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Stile" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Schriftarten" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Github-Repository" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Feedback geben" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Zurück" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Grafikdesign" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 +msgid "labels.grid-layout" +msgstr "Raster-Layout" + +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Hilfezentrum" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Hilfe & Lernen" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Ausblenden" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Erledigte Kommentare ausblenden" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importieren" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inaktiv" @@ -2250,47 +2949,51 @@ msgstr "Inaktiv" msgid "labels.info" msgstr "Info" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Installierte Schriftarten" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/settings/sidebar.cljs:130 +msgid "labels.integrations" +msgstr "Integrationen" + +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Etwas Schlimmes ist passiert." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "Probieren Sie es erneut oder kontaktieren Sie unseren Support." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Interner Fehler" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Einladungen" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Sprache" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Layout" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Lernzentrum" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Bibliotheken & Vorlagen" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Laden…" @@ -2298,7 +3001,7 @@ msgstr "Laden…" msgid "labels.lock" msgstr "Sperren" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Einloggen oder anmelden" @@ -2306,19 +3009,19 @@ msgstr "Einloggen oder anmelden" msgid "labels.login" msgstr "Anmelden" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Abmelden" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Mitglied" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Mitglieder" @@ -2330,41 +3033,71 @@ msgstr "Erwähnung" msgid "labels.new-password" msgstr "Neues Passwort" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Weiter" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/static.cljs:367 +msgid "labels.nitrate-not-configured.desc-message" +msgstr "Diese Instanz braucht einen Einrichtungsschritt aus der neuesten Version." + +#: src/app/main/ui/static.cljs:370 +msgid "labels.nitrate-not-configured.learn-more" +msgstr "Sehen Sie sich unsere" + +#: src/app/main/ui/static.cljs:365 +msgid "labels.nitrate-not-configured.main-message" +msgstr "Penpot kann noch nicht starten." + +#: src/app/main/ui/static.cljs:375 +msgid "labels.nitrate-not-configured.technical-guide" +msgstr "technische Anleitung" + +#: src/app/main/ui/static.cljs:355 +msgid "labels.nitrate-unavailable.main-message" +msgstr "" +"Penpot ist wegen eines Systemproblems vorübergehend nicht verfügbar. Bitte " +"versuchen Sie es in Kürze erneut." + +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "" "Sie sind auf dem neuesten Stand! Neue Benachrichtigungen zu Kommentaren " "werden hier angezeigt." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Keine ausstehenden Einladungen." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/dashboard/team.cljs:850 +msgid "labels.no-invitations-gather-people" +msgstr "Holen Sie Ihre Leute zusammen und erschaffen Sie gemeinsam Großes." + +#: src/app/main/ui/components/select.cljs:256 +msgid "labels.no-matches" +msgstr "Keine Treffer" + +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Ups!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Benachrichtigungen" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 Datei" msgstr[1] "%s Dateien" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 Zeichenfläche" msgstr[1] "%s Zeichenflächen" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 Projekt" @@ -2374,39 +3107,43 @@ msgstr[1] "%s Projekte" msgid "labels.old-password" msgstr "Altes Passwort" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 +msgid "labels.open" +msgstr "Öffnen" + +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Optionen" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Sonstiges (bitte angeben)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Sonstiges" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Eigentümer" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Passwort" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Ausstehend" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Penpot Changelog" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot Hub" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Angeheftete Projekte" @@ -2414,31 +3151,31 @@ msgstr "Angeheftete Projekte" msgid "labels.post" msgstr "Versenden" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Zurück" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Produkt- oder UX-Design" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Produkt- oder Projektmanager" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projekte" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Aktuell" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Referenz" @@ -2446,27 +3183,27 @@ msgstr "Referenz" msgid "labels.refresh" msgstr "Aktualisieren" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Versionshinweise" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Seite neu laden" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Entfernen" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Mitglied entfernen" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Umbenennen" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Team umbenennen" @@ -2490,11 +3227,11 @@ msgstr "neue Antwort" msgid "labels.reply.thread" msgstr "Antwort" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Erneut senden" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Einladung erneut senden" @@ -2502,43 +3239,43 @@ msgstr "Einladung erneut senden" msgid "labels.restore" msgstr "Wiederherstellen" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Wiederholen" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rolle" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Speichern" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Suchen" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Schriftart suchen" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Wählen Sie eine Option" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Senden" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Senden…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Wir befinden uns in der planmäßigen Wartung unserer Systeme." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Service nicht verfügbar" @@ -2546,55 +3283,59 @@ msgstr "Service nicht verfügbar" msgid "labels.sets" msgstr "Sets" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Einstellungen" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Schatten" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Teilen" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Gemeinsam genutzte Bibliotheken" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Anzeigen" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Alle Kommentare anzeigen" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Kommentarliste anzeigen" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Nur Ihre Erwähnungen anzeigen" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Nur eigene Kommentare anzeigen" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/import.cljs:808 +msgid "labels.skip" +msgstr "Überspringen" + +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Quellen" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Start" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" @@ -2602,7 +3343,7 @@ msgstr "Status" msgid "labels.stroke" msgstr "Rahmen" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Schüler oder Lehrer" @@ -2618,19 +3359,19 @@ msgstr "SVG" msgid "labels.text" msgstr "Text" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Themen" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutorials" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Typografie" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Unbekannter Fehler" @@ -2642,31 +3383,31 @@ msgstr "Entsperren" msgid "labels.unpublish-multi-files" msgstr "Veröffentlichung von %s-Dateien aufheben" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Aktualisieren" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Team aktualisieren" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Hochladen" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Eigene Schriftarten hochladen" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Lädt hoch…" -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variante" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Hinweise zu Version %s" @@ -2674,7 +3415,7 @@ msgstr "Hinweise zu Version %s" msgid "labels.view-only" msgstr "Nur ansehen" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Zuschauer" @@ -2682,7 +3423,7 @@ msgstr "Zuschauer" msgid "labels.visibility" msgstr "Sichtbarkeit" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -2690,11 +3431,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Neuen Kommentar schreiben" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(Sie)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Ihr Konto" @@ -2805,63 +3546,63 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Hinzufügen von “%s” als gemeinsam genutzte Bibliothek" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Maximal" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Neue E-Mail-Adresse verifizieren" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Wir senden Ihnen eine Nachricht an Ihre aktuelle E-Mail-Adresse “%s”, um " "Ihre Identität zu überprüfen." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Neue E-Mail-Adresse" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "E-Mail-Adresse ändern" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Ihre E-Mail-Adresse ändern" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Webhook erstellen" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Webhook erstellen" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Payload-URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Abbrechen und mein Konto behalten" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Ja, mein Konto löschen" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "Wenn Sie Ihr Konto löschen, verlieren Sie alle Ihre aktuellen Projekte und " "Archive." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Sind Sie sicher, dass Sie Ihr Konto löschen möchten?" @@ -2911,31 +3652,31 @@ msgstr "Sind Sie sicher, dass Sie %s Dateien löschen möchten?" msgid "modals.delete-file-multi-confirm.title" msgstr "Lösche %s Dateien" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Sind Sie sicher, dass Sie diesen Schriftschnitt löschen möchten? Sie wird " "nicht mehr geladen, wenn sie bereits in einer Datei verwendet wird." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Schriftschnitt löschen" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Sind Sie sicher, dass Sie diese Schriftart löschen möchten? Sie wird nicht " "mehr geladen, wenn sie bereits in einer Datei verwendet wird." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Schriftart löschen" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Sind Sie sicher, dass Sie diese Seite löschen wollen?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Seite löschen" @@ -2951,31 +3692,31 @@ msgstr "Sind Sie sicher, dass Sie dieses Projekt löschen möchten?" msgid "modals.delete-project-confirm.title" msgstr "Projekt löschen" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Datei löschen" msgstr[1] "Dateien löschen" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Es ist in keiner Datei aktiviert." msgstr[1] "Sie sind in keiner Datei aktiviert." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Diese Bibliothek ist hier aktiviert: " msgstr[1] "Diese Bibliotheken sind hier aktiviert: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Möchten Sie diese Datei wirklich löschen?" msgstr[1] "Möchten Sie diese Dateien wirklich löschen?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Datei löschen" @@ -2995,19 +3736,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Team löschen" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Mitglied löschen" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Sind Sie sicher, dass Sie dieses Mitglied aus dem Team löschen möchten?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Teammitglied löschen" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3017,165 +3758,165 @@ msgstr[1] "" "Assets, die bereits in diesen Dateien verwendet wurden, bleiben dort " "erhalten (das Design bleibt erhalten)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Webhook löschen" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Möchten Sie diesen Webhook wirklich löschen?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Webhook löschen" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Webhook bearbeiten" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Webhook bearbeiten" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Einladung senden" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-Mails, durch Komma getrennt" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Einige E-Mails stammen von aktuellen Teammitgliedern. Ihre Einladungen " "werden nicht versendet." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Sie können Mitglieder in das Team einladen, damit diese auf diese Datei und " "alle Team-Dateien zugreifen können." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Mitglieder in das Team einladen" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Da Sie das einzige Mitglied dieses Teams sind, wird das Team zusammen mit " "seinen Projekten und Dateien gelöscht." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Sind Sie sicher, dass Sie das %s-Team verlassen wollen?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Sie können das Team nicht verlassen, wenn es kein anderes Mitglied gibt, " "das Sie zum Besitzer ernennen können. Sie können das Team jedoch löschen." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "Sie sind der Eigentümer von %s." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Befördern und verlassen" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Wählen Sie ein Mitglied aus, das befördert werden soll" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Wählen Sie ein Mitglied aus, das befördert werden soll" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Team verlassen" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Sind Sie sicher, dass Sie dieses Team verlassen wollen?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Team verlassen" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Verschieben" msgstr[1] "Verschieben" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Sind Sie sicher, dass Sie diese Bibliothek verschieben wollen?" msgstr[1] "Sind Sie sicher, dass Sie diese Bibliotheken verschieben wollen?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Bibliothek verschieben" msgstr[1] "Bibliotheken verschieben" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Verschiebung" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Befördern" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Wenn Sie die Eigentümerschaft übertragen, ändern Sie Ihre Rolle in Admin " "und verlieren einige Berechtigungen für dieses Team. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "Sind Sie sicher, dass Sie diesen Benutzer zum Eigentümer befördern wollen?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Zum Eigentümer befördern" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Veröffentlichen" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "" "Ihre Bibliothek ist leer. Sind Sie sicher, dass Sie es veröffentlichen " "wollen?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Leere Bibliothek veröffentlichen" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Minimal" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Veröffentlichung aufheben" msgstr[1] "Veröffentlichung aufheben" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Möchten Sie die Veröffentlichung dieser Bibliothek wirklich aufheben?" msgstr[1] "Möchten Sie die Veröffentlichung dieser Bibliotheken wirklich aufheben?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Veröffentlichung der Bibliothek aufheben" @@ -3200,11 +3941,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Komponente aus einer geteilten Bibliothek aktualiseren" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Diese Seite existiert nicht" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404-Fehler" @@ -3224,11 +3965,11 @@ msgstr "" "Überprüfen Sie Ihre E-Mail und klicken Sie auf den Link, um ein neues " "Passwort zu erstellen." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Kostenlos anmelden" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "Und starten Sie Penpot in Sekunden!" @@ -3236,69 +3977,69 @@ msgstr "Und starten Sie Penpot in Sekunden!" msgid "not-found.made-with-love" msgstr "Mit LIEBE gemacht und Open Source" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Sie haben bereits Zugang zu dieser Datei beantragt." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Sie haben bereits Zugang zu dieser Datei oder anderen Dateien oder " "Projekten dieses Teams beantragt." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Sie haben bereits Zugang zu diesem Projekt oder anderen Projekten oder " "Dateien dieses Teams beantragt." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Sie haben bereits Zugang zu diesem Projekt beantragt." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "ZUGANG ANFORDERN" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "" "Denken Sie daran, dass Sie in das Team eingeladen werden, wenn der " "Eigentümer es erlaubt." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Ihre Anfrage wurde korrekt abgeschickt!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Sie haben keinen Zugriff auf diese Datei." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Zu deinem Penpot wechseln" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Wenn der Besitzer es zulässt, wirst du in das Team eingeladen." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot-Datei" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Sie haben keinen Zugang zu diesem Projekt." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJEKT" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Um Zugriff auf diese Datei zu erhalten, können Sie den Teambesitzer fragen." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "" "Um Zugriff auf dieses Projekt zu erhalten, können Sie den Teambesitzer " @@ -3314,29 +4055,29 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Eine neue Version ist verfügbar." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Einladung erfolgreich widerrufen" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Einladung erfolgreich gesendet" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Einladungslink kopiert" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Sie können Ihr Profil nicht löschen. Weisen Sie Ihre Teams neu zu, bevor " "Sie fortfahren." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil erfolgreich gespeichert!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Verifizierungs-E-Mail an %s gesendet. Prüfen Sie Ihren Posteingang!" @@ -3366,78 +4107,78 @@ msgstr "Nach der Bennenung Ihres Teams, können Sie andere Personen einladen." msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Team erstellen" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Mitglieder einladen" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Einladen mit der Rolle:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Los geht's!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Generierung echter Code-Entwürfe" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Erstellung von Design-Systemen" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototyping" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "UI/UX-Design einer App" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Wofür werden Sie Penpot verwenden?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Lassen Sie uns ein wenig über Sie erfahren, damit wir Penpot für Sie " "optimieren können. Ihre Antworten werden helfen uns, neue Funktionen zu " "priorisieren und Ihnen den richtigen Einstieg zu erleichtern." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Helfen Sie uns, Sie kennen zu lernen" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Welches dieser Tools verwenden Sie am meisten?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Was machen Sie beruflich?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Erzählen Sie uns von Ihrem Job" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Wo möchten Sie gerne anfangen?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Bildung" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Persönlich" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Arbeit" @@ -3487,11 +4228,11 @@ msgstr "Beginnen Sie mit dem Entwerfen" msgid "profile.recovery.go-to-login" msgstr "Zur Anmeldung" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Trennen" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Mehrere" @@ -3503,35 +4244,35 @@ msgstr "Farbe entfernen" msgid "settings.select-this-color" msgstr "Elemente mit diesem Stil auswählen" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Kommentare" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Unten ausrichten" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Horizontal zentrieren" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Linksbündig ausrichten" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Rechtsbündig ausrichten" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Oben ausrichten" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Mittig ausrichten (vertikal)" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Zeichenfläche aus Auswahl erstellen" @@ -3539,55 +4280,55 @@ msgstr "Zeichenfläche aus Auswahl erstellen" msgid "shortcuts.bold" msgstr "Umschalten auf Fettdruck" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Subtrahieren (Boolesche Operation)" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Ausschluss" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Schnittmenge" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Vereinigung" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "In den Hintergrund" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Eins nach hinten" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Eins nach vorne" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "In den Vordergrund" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Verlauf löschen" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Kopieren" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Link kopieren" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Eigenschaften kopieren" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Komponente / Variante erstellen" @@ -3595,35 +4336,35 @@ msgstr "Komponente / Variante erstellen" msgid "shortcuts.create-new-project" msgstr "Neues Projekt erstellen" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Ausschneiden" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Auszoomen" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Löschen" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Punkt löschen" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Komponente lösen" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Kurve" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Ellipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Board" @@ -3631,39 +4372,39 @@ msgstr "Board" msgid "shortcuts.draw-nodes" msgstr "Pfad zeichnen" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Pfad" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rechteck" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Text" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplizieren" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Abbrechen" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Formen exportieren" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Passend zoomen" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Horizontal spiegeln" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Vertikal spiegeln" @@ -3687,23 +4428,23 @@ msgstr "Zu gemeinsam genutzten Bibliotheken wechseln" msgid "shortcuts.go-to-search" msgstr "Suchen" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Gruppieren" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Horizontal verteilen" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Benutzeroberfläche ein-/ausblenden" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Einzoomen" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Bild einfügen" @@ -3711,7 +4452,7 @@ msgstr "Bild einfügen" msgid "shortcuts.italic" msgstr "Umschalten auf Kursivdruck" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Punkte verbinden" @@ -3719,39 +4460,39 @@ msgstr "Punkte verbinden" msgid "shortcuts.line-through" msgstr "Durchgestrichen" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Zur Ecke umwandeln" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Kurve erstellen" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Maskieren" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Punkte zusammenführen" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Verschieben" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Schnell nach unten verschieben" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Schnell nach links verschieben" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Schnell nach rechts verschieben" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Schnell nach oben verschieben" @@ -3759,329 +4500,329 @@ msgstr "Schnell nach oben verschieben" msgid "shortcuts.move-nodes" msgstr "Punkte verschieben" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Nach unten verschieben" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Nach links verschieben" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Nach rechts verschieben" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Nach oben verschieben" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Nächstes Board" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Kein Tastaturkürzel gefunden" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Deckkraft auf 100% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Deckkraft auf 10% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Deckkraft auf 20% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Deckkraft auf 30% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Deckkraft auf 40% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Deckkraft auf 50% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Deckkraft auf 60% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Deckkraft auf 70% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Deckkraft auf 80% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Deckkraft auf 90% setzen" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Farbwähler" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Zum Kommentarbereich im Ansichtsmodus" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Zum Dashboard" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Zur Spezifikation im Ansichtsmodus" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Zum Ansichtsmodus" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Zum Ansichtsmodus" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Zum Arbeitsbereich" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " oder " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Einfügen" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Eigenschaften einfügen" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Plugin-Manager" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Vorheriges Board" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Wiederholen" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Umbenennen" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Zoom zurücksetzen" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Skalieren" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Grundlagen" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Dashboard" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Ansicht" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Arbeitsbereich" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Alles auswählen" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Nächste Ebene auswählen" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Übergeordnete Ebene auswählen" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Vorherige Ebene auswählen" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Punkte trennen" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Pixelraster ein-/ausblenden" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Tastaturkürzel ein-/ausblenden" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "An den Punkten ausrichten" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Am Pixelraster ausrichten" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Mit der Bearbeitung beginnen" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Ausrichtung" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Allgemein" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Hauptmenü" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Ändern von Ebenen" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Paneele" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Pfade" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formen" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Texte" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Werkzeuge" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Miniaturansichten festlegen" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Tastaturkürzel" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Dynamische Ausrichtung umschalten" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Assets einblenden" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Farbpalette ein-/ausblenden" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Fokusmodus umschalten" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Vollbild aktivieren/deaktivieren" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Hilfslinien anzeigen / ausblenden" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Verlauf ein-/ausblenden" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Ebenen ein-/ausblenden" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Flex-Layout hinzufügen/entfernen" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Grid-Layout hinzufügen/entfernen" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Auswahl sperren" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Seitenverhältnis sperren/entsperren" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Lineale anzeigen / ausblenden" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "An Hilfslinien ausrichten" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "An den Hilfslinien des Lineals ausrichten" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Textpalette ein-/ausblenden" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Theme ändern" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Elemente ein-/ausblenden" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Zoom-Optionen umschalten" @@ -4089,31 +4830,31 @@ msgstr "Zoom-Optionen umschalten" msgid "shortcuts.underline" msgstr "Unterstrichen" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Rückgängig" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Gruppierung aufheben" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Maske entfernen" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Vertikal verteilen" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Ansicht mit Zoomwerkzeug verkleinern" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Ansicht mit Zoomwerkzeug vergrößern" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zur Auswahl zoomen" @@ -4145,142 +4886,142 @@ msgstr "Ihr Abonnement:" msgid "subscription.dashboard.team-plan" msgstr "Team-Abonnement" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Abonnement" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Alle Vorteile des unbegrenzten Abonnements und:" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90-Tage automatische Speicherung von Versionen und Dateiwiederherstellung" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Monatliche Gesamtrechnung" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Unbegrenzter Speicherplatz" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Ihr Abonnement verwalten" -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Redakteure" -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/Monat" -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Penpot-Mitglied seit %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Mehr Informationen" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "Organisation pro Monat" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professionell" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7-Tage automatische Speicherung von Versionen und Dateiwiederherstellung" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10 GB Speicherplatz" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Ihr Abonnement" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Kostenlose Testversion starten" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Abonnieren" -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Viel Spaß mit Ihrem Abonnement!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Sie sind %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Teste es kostenlos für 14 Tage" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Unbegrenzt" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unbegrenzt (Testversion)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30-Tage automatische Speicherung von Versionen und Dateiwiederherstellung" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Monatliche Rechnung mit Kostenobergrenze" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25 GB Speicherplatz" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "Eine Einladung ausgewählt" msgstr[1] "%s Einladungen ausgewählt" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Schriftenhersteller - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Schriftarten - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projekte - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Suchen - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Gemeinsam genutzte Bibliotheken - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Gestaltungsfreiheit für Teams" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Feedback geben - Penpot" @@ -4288,7 +5029,7 @@ msgstr "Feedback geben - Penpot" msgid "title.settings.notifications" msgstr "Benachrichtigungen - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Einstellungen - Penpot" @@ -4296,27 +5037,27 @@ msgstr "Einstellungen - Penpot" msgid "title.settings.password" msgstr "Passwort - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Einladungen - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Mitglieder - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Einstellungen - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Ansichtsmodus - Penpot" @@ -4328,99 +5069,99 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Token trennen" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Keine Zeichenflächen auf der Seite gefunden." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Keine Zeichenfläche gefunden." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Kommentare (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Interaktionen nicht anzeigen" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Vollbildmodus" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Spezifikation (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interaktionen" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interaktionen (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Link kopieren" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Interaktionen anzeigen" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Interaktionen beim Klicken anzeigen" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Sitemap" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Die letzte Übertragung war erfolgreich." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Zentrieren (horizontal) (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Horizontal verteilen (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Linksbündig ausrichten (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Rechtsbündig ausrichten (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Unten ausrichten (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Mittig ausrichten (vertikal) (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Vertikal verteilen (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Oben ausrichten (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Bibliothek hinzufügen" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Alle Assets" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Farben" @@ -4432,7 +5173,7 @@ msgstr "Farbe hinzufügen" msgid "workspace.assets.component-group-options" msgstr "Optionen für diese Gruppe" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Komponenten" @@ -4440,7 +5181,7 @@ msgstr "Komponenten" msgid "workspace.assets.components.add-component" msgstr "Komponente hinzufügen" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s Varianten" @@ -4448,17 +5189,17 @@ msgstr "%s Varianten" msgid "workspace.assets.create-group" msgstr "Gruppe erstellen" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Ihre Elemente werden automatisch nach diesem Schema benannt: \"Gruppenname " "/ Elementname\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Löschen" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplizieren" @@ -4466,39 +5207,39 @@ msgstr "Duplizieren" msgid "workspace.assets.duplicate-main" msgstr "Hauptkomponente duplizieren" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Bearbeiten" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtern" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Rasteransicht" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Gruppieren" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Name der Gruppe" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Listenansicht" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "lokale Bibliothek" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Bibliothek verwalten" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Keine Assets gefunden" @@ -4506,7 +5247,7 @@ msgstr "Keine Assets gefunden" msgid "workspace.assets.open-library" msgstr "Bibliotheksdatei öffnen" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Umbenennen" @@ -4514,11 +5255,11 @@ msgstr "Umbenennen" msgid "workspace.assets.rename-group" msgstr "Gruppe umbenennen" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Assets suchen" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Geteilte Bibliothek" @@ -4528,43 +5269,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 Komponente" msgstr[1] "% Komponenten" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Sortieren" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Textstile" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Typografie hinzufügen" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Größe" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Schriftschnitt" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Wechseln Sie zur Stilbibliotheksdatei, um sie zu bearbeiten" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Zeichenabstand" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Zeilenabstand" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Texttransformation" @@ -4576,65 +5317,65 @@ msgstr "Gruppierung aufheben" msgid "workspace.colorpicker.color-tokens" msgstr "Farb-Tokens" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Komponenten können nicht in sich selbst verschachtelt werden." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "Einige Elemente konnten nicht ausgetauscht werden. Komponenten können nicht " "in sich selbst verschachtelt werden." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Bereich erstellen" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Board erstellen" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Zellen zusammenführen" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "1 Spalte rechts einfügen" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "1 Spalte links einfügen" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Spalte löschen" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Spalte und Formen löschen" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Spalte duplizieren" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "1 Zeile unterhalb einfügen" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "1 Zeile oberhalb einfügen" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Zeile löschen" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Zeile und Formen löschen" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Zeile duplizieren" @@ -4642,15 +5383,15 @@ msgstr "Zeile duplizieren" msgid "workspace.debug.title" msgstr "In Arbeitsbereich bearbeiten" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Fokus-Modus" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Fokus aus" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Fokus an" @@ -4662,191 +5403,191 @@ msgstr "Linearer Farbverlauf" msgid "workspace.gradients.radial" msgstr "Radialer Farbverlauf" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Dynamische Ausrichtung deaktivieren" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Proportionale Skalierung deaktivieren" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Ausrichten an Hilfslinien deaktivieren" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Ausrichten am Pixel deaktivieren" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "An den Hilfslinien des Lineals ausrichten deaktivieren" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Dynamische Ausrichtung aktivieren" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Proportionale Skalierung aktivieren" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "An Hilfslinien ausrichten" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Ausrichten am Pixel aktivieren" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "An den Hilfslinien des Lineals ausrichten" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Namen von Zeichenflächen ausblenden" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Hilfslinien ausblenden" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Farbpalette ausblenden" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Pixelraster ausblenden" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Lineale ausblenden" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Schriftartenpalette ausblenden" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Bearbeiten" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Datei" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Hilfe und Infos" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Einstellungen" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Ansicht" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Wiederherstellen" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Alles auswählen" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Namen der Zeichenflächen anzeigen" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Hilfslinien anzeigen" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Farbpalette einblenden" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Pixelraster einblenden" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Lineale einblenden" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Schriftartenpalette anzeigen" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Zum dunklen Theme wechseln" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Zum hellen Theme wechseln" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Zum System-Theme wechseln" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Rückgängig" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Zurücksetzen" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Fehler beim Speichern" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Gespeichert" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Speichern" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Teilen" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Ansichtsmodus (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Vergrößern" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Füllen - Skalieren zum Füllen" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Anpassen - Verkleinern zum Einpassen" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Passend zoomen" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Vollbild" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zur Auswahl zoomen" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Grid bearbeiten" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Beenden" @@ -4854,7 +5595,7 @@ msgstr "Beenden" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Innenabstand unten" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "4-seitige Abstandsoptionen anzeigen" @@ -4890,29 +5631,29 @@ msgstr "Fertig" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Lokalisieren" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Grid-Layout lokalisieren" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 Farbe" msgstr[1] "%s Farben" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "In Ihrer Bibliothek sind noch keine Farbstile vorhanden" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "In Ihrer Bibliothek sind noch keine Textstile vorhanden" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Dateibibliothek" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Aktuelle Farben" @@ -4924,95 +5665,95 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Farbstil speichern" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 Komponente" msgstr[1] "%s Komponenten" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Verbunden mit" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Oder probieren Sie einige von diesen aus:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "In Ihrem Team gibt es keine gemeinsamen Bibliotheken, Sie können suchen nach" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "einige Vorlagen sind hier" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Dateibibliothek" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "%s Grafik" msgstr[1] "%s Grafiken" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTHEKEN IN DIESER DATEI" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTHEKEN" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "AKTUALISIERUNGEN DER BIBLIOTHEK" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Laden…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "" "Es gibt keine gemeinsam genutzte Bibliotheken, die aktualisiert werden " "müssen" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Keine Übereinstimmungen für “%s“ gefunden" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Suche nach gemeinsam genutzten Bibliotheken" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "GEMEINSAM GENUTZTE BIBLIOTHEKEN" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Bibliothek verknüpfen" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 Textstil" msgstr[1] "%s Textstile" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Bibliothek trennen" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Aktualisieren" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "alle Änderungen anzeigen" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "AKTUALISIERUNG" @@ -5048,111 +5789,111 @@ msgstr "Weichzeichnen ein -und aufblenden" msgid "workspace.options.canvas-background" msgstr "Hintergrundfarbe" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Inhalt beschneiden" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Komponente" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Anmerkung" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Kopie" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Eine Anmerkung erstellen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Eine Anmerkung bearbeiten" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Hauptkomponente" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Komponente austauschen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Es gibt noch keine Assets in dieser Bibliothek" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Getrennt" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Konfliktverursachende Varianten finden" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Doppelte Varianten anzeigen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Einige Varianten haben identische Eigenschaften und Werte" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Diese Varianten haben identische Eigenschaften und Werte. Geben Sie jeder " "Variante einen eindeutigen Wert." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Diese Variante hat die selben Eigenschaften order Werte wie eine andere " "Variante. Geben Sie jeder Variante einen eindeutigen Wert." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Einige dieser Varianten haben identische Eigenschaften und Werte. Geben Sie " "jeder Variante einen eindeutigen Wert." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Diese Komponente hat Varianten mit ungültigen Namen. Stellen Sie sicher, " "das jede Variante korrekt benannt ist." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Ungültige Varianten finden" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Einige Varianten haben ungültige Namen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Ungültige Varianten suchen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Diese Varianten haben ungültige Namen." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Diese Variante hat einen ungültigen Namen." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Einige dieser Varianten haben ungültige Namen." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[Eigenschaft]=[Wert], [Eigenschaft]=[Wert]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Versuchen Sie diesem Schema zu folgen:" @@ -5223,11 +5964,11 @@ msgstr "Oben" msgid "workspace.options.constraints.topbottom" msgstr "Oben & Unten" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exportieren" @@ -5235,37 +5976,37 @@ msgstr "Exportieren" msgid "workspace.options.export-multiple" msgstr "Auswahl exportieren" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Ein Element exportieren" msgstr[1] "%s Elemente exportieren" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Export hinzufügen" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Export entfernen" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Suffix" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Export abgeschlossen" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Exportiere…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Export fehlgeschlagen" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Export unerwartet langsam" @@ -5281,7 +6022,7 @@ msgstr "Füllfarbe hinzufügen" msgid "workspace.options.fill.remove-fill" msgstr "Füllung entfernen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Größe des Boards an den Inhalt anpassen" @@ -5305,67 +6046,67 @@ msgstr "Flow-Startpunkte" msgid "workspace.options.grid.auto" msgstr "Automatisch" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Spalten" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Farbe" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Zwischenräume" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Höhe" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Rand" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Als Standard festlegen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Unten" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Zentriert" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Links" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Rechts" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Gestreckt" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Oben" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Standardwerte verwenden" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Breite" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Zeile" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Quadrat" @@ -5377,7 +6118,7 @@ msgstr "Gruppe füllen" msgid "workspace.options.group-stroke" msgstr "Gruppe einrahmen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Hilfslinie hinzufügen" @@ -5385,19 +6126,19 @@ msgstr "Hilfslinie hinzufügen" msgid "workspace.options.guides.remove-guide" msgstr "Hilfslinie entfernen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Hilfslinien" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Hilfslinie ein -und ausblenden" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Höhe" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Spezifikation" @@ -5613,103 +6354,103 @@ msgstr "Interaktionen" msgid "workspace.options.interactions.add-interaction" msgstr "Interaktion hinzufügen" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Farbe" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Farbig nachbelichten" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Farbig abwedeln" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Abdunkeln" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Differenz" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Ausschluss" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Hartes Licht" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Farbton" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Aufhellen" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminanz" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Multiplizieren" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Ineinanderkopieren" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Sättigung" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Negativ multiplizieren" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Weiches Licht" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Sichtbarkeit der Ebene ein- und ausschalten" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Max.Höhe" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Max.Breite" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Min.Höhe" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Min.Breite" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Weitere Farben" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Weitere Bibliotheksfarben" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Deckkraft" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototyp" @@ -5741,19 +6482,19 @@ msgstr "Unabhängige Ecken einklappen" msgid "workspace.options.radius.show-single-corners" msgstr "Unabhängige Ecken anzeigen" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Aktuell" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Wiederholen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Drehung" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Schriftart suchen" @@ -5763,7 +6504,7 @@ msgstr "" "Wählen Sie eine Form, Zeichenfläche oder Gruppe aus, um eine Verbindung zu " "einer anderen Zeichenfläche herzustellen." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Ausgewählte Farben" @@ -5831,55 +6572,55 @@ msgstr "Schatten ein- und ausblenden" msgid "workspace.options.show-fill-on-export" msgstr "In Exporten anzeigen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Im Ansichtsmodus anzeigen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Größe" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Größenvoreinstellungen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Seitenverhältnis sperren" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Seitenverhältnis entsperren" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Kreis" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamant" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Pfeil" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Keine" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Rund" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Rechteckig" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rechteck" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Dreieck" @@ -5887,7 +6628,7 @@ msgstr "Dreieck" msgid "workspace.options.stroke-color" msgstr "Konturfarbe" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Konturbreite" @@ -5895,35 +6636,35 @@ msgstr "Konturbreite" msgid "workspace.options.stroke.add-stroke" msgstr "Strichfarbe hinzufügen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Zentriert" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Gestrichelt" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Gepunktet" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Innen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Mehrere" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Außen" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Strich entfernen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Durchgezogen" @@ -5959,11 +6700,11 @@ msgstr "Automatische Breite" msgid "workspace.options.text-options.grow-fixed" msgstr "Feste Größe" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Zeichenabstand" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Zeilenabstand" @@ -6009,197 +6750,197 @@ msgstr "" "Verwenden Sie die Wiedergabetaste in der Kopfzeile, um die Prototypansicht " "zu wechseln." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Breite" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X-Achse" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y-Achse" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Ankerpunkte zeichnen (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Ankerpunkte verbinden (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "in Ecke umwandeln (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "in Kurve umwandeln (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Ankerpunkte zusammenlegen (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Ankerpunkte verschieben (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Ankerpunkte trennen (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "An Ankerpunkten ausrichten (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Öffnen" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "[Mehr Plugins](%s) entdecken" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Noch keine Plugins installiert" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Das Plugin-Manifest ist falsch." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Sie müssen ein Redakteur sein, um dieses Plugin zu benutzen" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Das Plugin existiert nicht oder die URL ist nicht korrekt." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Installieren" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Installierte Plugins" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Plugin-Manager" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Plugins" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "DIESES PLUGIN AKTUALISIEREN" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Das Plugin wurde geändert, seit Sie es das letzte Mal geöffnet haben. Es " "möchte nun auch zugreifen:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Starten von Dateidownloads." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Daten im Browser speichern." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Ihre Kommentare und Antworten lesen." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Ihre Kommentare lesen und ändern und in Ihrem Namen antworten." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Lesen des Inhalts von Dateien, auf die Benutzer Zugriff haben." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Lesen und Ändern des Inhalts von Dateien, auf die Benutzer Zugriff haben." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "Beachten Sie, dass dieses Plugin von einer externen Quelle erstellt wurde." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Lesen Ihrer Bibliotheken und Assets." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Lesen und Ändern Ihrer Bibliotheken und Assets." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "'%s' PLUGIN BENÖTIGT ZUGRIFF AUF:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Lesen der Profil-Informationen des aktuellen Benutzers." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Liste der Plugins" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Plugin entfernen" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Eine URL des Plugins eingeben" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Plugins" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "NICHT JETZT" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Möchten Sie einen ersten Eindruck bekommen? Es wird in einem neuen Entwurf " "für Ihr aktuelles Team geöffnet. (Falls nicht, können Sie es jederzeit in " "den installierten Plugins einer beliebigen Datei finden.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "'%s' PLUGIN IST FÜR IHREN BENUTZER INSTALLIERT!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "PLUGIN TESTEN" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Flex-Layout hinzufügen" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Grid-Layout hinzufügen" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Layout hinzufügen" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Variante erstellen" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Neue Eigenschaft hinzufügen" @@ -6211,7 +6952,7 @@ msgstr "In den Hintergrund" msgid "workspace.shape.menu.backward" msgstr "Eins nach hinten" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Als Varianten kombinieren" @@ -6219,7 +6960,7 @@ msgstr "Als Varianten kombinieren" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Die Komponenten müssen sich auf derselben Seite befinden" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Kopieren" @@ -6255,27 +6996,27 @@ msgstr "Als Text kopieren" msgid "workspace.shape.menu.create-annotation" msgstr "Anmerkung erstellen" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Auswahl als Zeichenfläche" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Komponente erstellen" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Mehrere Komponenten erstellen" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Ausschneiden" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Löschen" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Startpunkt löschen" @@ -6287,7 +7028,7 @@ msgstr "Instanz trennen" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Instanzen trennen" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Subtrahieren" @@ -6295,15 +7036,15 @@ msgstr "Subtrahieren" msgid "workspace.shape.menu.duplicate" msgstr "Duplizieren" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Bearbeiten" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Ausschließen" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Pfade reduzieren" @@ -6315,7 +7056,7 @@ msgstr "Horizontal spiegeln" msgid "workspace.shape.menu.flip-vertical" msgstr "Vertikal spiegeln" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Startpunkt" @@ -6327,31 +7068,31 @@ msgstr "Eins nach vorne" msgid "workspace.shape.menu.front" msgstr "In den Vordergrund" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Gruppieren" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Ausblenden" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Benutzeroberfläche ein-/ausblenden" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Überlappen" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Sperren" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Maskieren" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Einfügen" @@ -6359,23 +7100,23 @@ msgstr "Einfügen" msgid "workspace.shape.menu.paste-props" msgstr "Eigenschaften einfügen" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Pfad" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Flex-Layout entfernen" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Grid-Layout entfernen" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Layout entfernen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Eigenschaft entfernen" @@ -6399,11 +7140,11 @@ msgstr "Variante wiederherstellen" msgid "workspace.shape.menu.select-layer" msgstr "Ebene auswählen" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Anzeigen" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Im Assets-Panel anzeigen" @@ -6419,19 +7160,19 @@ msgstr "Vorschaubild entfernen" msgid "workspace.shape.menu.thumbnail-set" msgstr "Als Vorschaubild festlegen" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Gruppierung aufheben" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Hinzufügen" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Entsperren" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Maske entfernen" @@ -6447,47 +7188,47 @@ msgstr "Seitenleiste ausblenden" msgid "workspace.sidebar.expand" msgstr "Seitenleiste einblenden" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Verlauf" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Ebenen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Komponenten" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Zeichenflächen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Gruppen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Bilder" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Masken" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Ebenen durchsuchen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Formen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Texte" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Importierte SVG-Attribute" @@ -6499,7 +7240,7 @@ msgstr "Seiten" msgid "workspace.sidebar.sitemap.add-page" msgstr "Seite hinzufügen" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Sitemap" @@ -6519,7 +7260,7 @@ msgstr "Set zu dieser Gruppe hinzufügen" msgid "workspace.tokens.add-token" msgstr "Token hinzufügen: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Angewandt auf" @@ -6535,7 +7276,7 @@ msgstr "Zurück zur Themen-Liste" msgid "workspace.tokens.base-font-size" msgstr "Standard-Schriftgröße" -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Farbe" @@ -6543,7 +7284,7 @@ msgstr "Farbe" msgid "workspace.tokens.create-new-theme" msgstr "Erstellen Sie jetzt Ihr erstes Theme." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Ein neues erstellen." @@ -6563,7 +7304,7 @@ msgstr "Theme löschen" msgid "workspace.tokens.duplicate" msgstr "Token duplizieren" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "Kopie" @@ -6611,7 +7352,7 @@ msgstr "Vorschau:" msgid "workspace.tokens.export.single-file" msgstr "Einzeln" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Schriftgröße oder {alias}" @@ -6621,7 +7362,7 @@ msgstr "" "Fehler beim Festlegen der Schriftstärke/des Schriftstils. Dieser " "Schriftstil ist in der aktuellen Schriftart nicht vorhanden" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Schriftstärke (300, Fett Kursiv) oder {alias}" @@ -6661,7 +7402,7 @@ msgstr "" "Bei einer einzelnen JSON-Datei sollten die Keys der ersten Ebene die Namen " "der Token-Sets sein." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Token importieren" @@ -6669,17 +7410,17 @@ msgstr "Token importieren" msgid "workspace.tokens.import-warning" msgstr "Beim Import werden alle bestehenden Tokens, Sets und Themes überschrieben." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Inaktiv" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Dieses Set ist nicht aktiv. Ändern Sie das Theme oder aktivieren Sie dieses " "Set, um Änderungen im Anzeigebereich sehen zu können" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Einzelne Token verwenden" @@ -6699,11 +7440,11 @@ msgstr "Theme" msgid "workspace.tokens.label.theme-placeholder" msgstr "Theme hinzufügen (z. B. „Hell“)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Zeichenabstand oder {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Zeilenabstand (Multiplikator, px, %) oder {alias}" @@ -6719,7 +7460,7 @@ msgstr "Maximale Größe" msgid "workspace.tokens.min-size" msgstr "Mindestgröße" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Rechtsklick, um Optionen anzuzeigen" @@ -6735,7 +7476,7 @@ msgstr "Kein Theme aktiviert" msgid "workspace.tokens.no-permisions-set" msgstr "Sie müssen ein Redakteur sein, um Sets zu aktivieren / deaktivieren" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Sie müssen ein Redakteur sein, um Themes zu verwenden" @@ -6747,7 +7488,7 @@ msgstr "Es sind noch keine Sets definiert. Erstellen Sie zuerst eines." msgid "workspace.tokens.no-sets-yet" msgstr "Es sind noch keine Sets vorhanden." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Es gibt keine Themes." @@ -6759,7 +7500,7 @@ msgstr "Sie haben derzeit keine Themes." msgid "workspace.tokens.num-active-sets" msgstr "%s aktivierte Sets" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Ursprünglicher Wert: %s" @@ -6771,11 +7512,11 @@ msgstr "Innenabstände" msgid "workspace.tokens.radius" msgstr "Radius" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Geben Sie einen Typografie-Alias für diesen Token ein" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Aufgelöster Wert: %s" @@ -6811,23 +7552,23 @@ msgstr "" msgid "workspace.tokens.settings" msgstr "Token-Einstellungen" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Schatten hinzufügen" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Weichzeichnen" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Innerer Schatten" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Schatten entfernen" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Streuung" @@ -6835,11 +7576,11 @@ msgstr "Streuung" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "Der Wert für die Streuung darf nicht negativ sein" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -6847,11 +7588,11 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "Größe" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize oder {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through oder {alias}" @@ -6869,27 +7610,27 @@ msgstr "Liste der Themes" msgid "workspace.tokens.token-description" msgstr "Beschreibung" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Schriftart auswählen" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Schriftart" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Schriftfamilie oder eine durch Kommas (,) getrennte Liste von Schriften" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Name" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "Der Name muss mindestens 1 Zeichen lang sein" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " sit kein gültiger Token-Name.\n" @@ -6900,11 +7641,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Wert" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Geben Sie einen Wert oder einen Alias mittels {alias} ein" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENS - %s" @@ -6923,11 +7664,11 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "„%s“ wird nicht als Datentyp unterstützt (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Referenz verwenden" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Der Wert ist nicht gültig" @@ -6935,55 +7676,55 @@ msgstr "Der Wert ist nicht gültig" msgid "workspace.toolbar.assets" msgstr "Assets" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Farbpalette (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Kommentare (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Stift (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Ellipse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Zeichenfläche (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Bild (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Verschieben (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Pfad (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Plugins (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rechteck (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Text (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Typografien (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Werkzeugleiste ein- und ausblenden" @@ -6996,7 +7737,7 @@ msgstr "Fertig" msgid "workspace.top-bar.view-only" msgstr "**Code inspizieren** (nur ansehen)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Es gibt bisher keine Änderungen im Verlauf" @@ -7136,19 +7877,19 @@ msgstr "Typografie" msgid "workspace.undo.entry.unknown" msgstr "Vorgang über %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Ignorieren" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Mehr Info" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Es gibt Updates in gemeinsam genutzten Bibliotheken" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Aktualisieren" diff --git a/frontend/translations/el.po b/frontend/translations/el.po index a2337b343b..70e36c30c9 100644 --- a/frontend/translations/el.po +++ b/frontend/translations/el.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2026.10.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Δημιουργία λογαριασμού επίδειξης" @@ -29,7 +29,7 @@ msgstr "" "Αυτή είναι μια υπηρεσία DEMO, ΜΗ ΧΡΗΣΙΜΟΠΟΙΕΙΤΕ για πραγματική εργασία, τα " "έργα θα σβήνονται περιοδικά." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Ξεχάσατε τον κωδικό;" @@ -37,23 +37,23 @@ msgstr "Ξεχάσατε τον κωδικό;" msgid "auth.fullname" msgstr "Πλήρες όνομα" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Συνδεθείτε εδώ" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" @@ -75,7 +75,7 @@ msgstr "" "Στείλαμε στο email σας έναν link για να ανακτήσουμε τον κωδικό πρόσβασής " "σας." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Μπήκατε στην ομάδα" @@ -103,11 +103,11 @@ msgstr "Ξεχάσατε τον κωδικό σας;" msgid "auth.recovery-submit" msgstr "Αλλάξτε τον κωδικό σας" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Δεν έχετε λογαριασμό;" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Δημιουργία λογαριασμού" @@ -115,19 +115,19 @@ msgstr "Δημιουργία λογαριασμού" msgid "auth.register-title" msgstr "Δημιουργία λογαριασμού" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Προσθήκη ως Κοινόχρηστη βιβλιοθήκη" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Αλλαγή email" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(Αντίγραφο)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Δημιουργία νέας ομάδας" @@ -135,7 +135,7 @@ msgstr "Δημιουργία νέας ομάδας" msgid "dashboard.default-team-name" msgstr "Το Penpot σας" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Διαγραφή ομάδας" @@ -143,15 +143,15 @@ msgstr "Διαγραφή ομάδας" msgid "dashboard.duplicate" msgstr "Αντιγραφή" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Πρόσκληση στην ομάδα" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Αφήστε την ομάδα" -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Κοινόχρηστες βιβλιοθήκες" @@ -167,27 +167,27 @@ msgstr "Μετακίνηση" msgid "dashboard.move-to-other-team" msgstr "Μετακίνηση σε άλλη ομάδα" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "Νεο αρχείο" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Νέο έργο" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Δεν βρέθηκαν αντιστοιχίσεις για το “%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Τα καρφιτσωμένα έργα θα εμφανιστούν εδώ" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Η διεύθυνση email σας ενημερώθηκε με επιτυχία" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Η διεύθυνση email σας έχει επαληθευτεί" @@ -195,7 +195,7 @@ msgstr "Η διεύθυνση email σας έχει επαληθευτεί" msgid "dashboard.notifications.password-saved" msgstr "Ο κωδικός πρόσβασης αποθηκεύτηκε!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "% s μέλη" @@ -207,35 +207,35 @@ msgstr "Άνοιγμα αρχείου σε νέα καρτέλα" msgid "dashboard.password-change" msgstr "Αλλαξε κωδικό" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Καρφίτσωμα / ξεκαρφίτσωμα" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Εργα" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Θέλετε να καταργήσετε τον λογαριασμό σας;" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Αναζήτηση…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Αναζήτηση για “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Αλλάξτε τη γλώσσα διεπαφής" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Επιλέξτε ένα θέμα" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Εμφάνιση όλων των αρχείων" @@ -255,7 +255,7 @@ msgstr "Το έργο σας έχει αναπαραχθεί με επιτυχί msgid "dashboard.success-duplicate-project" msgstr "Το έργο σας έχει αναπαραχθεί με επιτυχία" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Το έργο σας μετακινήθηκε με επιτυχία" @@ -263,47 +263,47 @@ msgstr "Το έργο σας μετακινήθηκε με επιτυχία" msgid "dashboard.success-move-project" msgstr "Το έργο σας μετακινήθηκε με επιτυχία" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Πληροφορίες ομάδας" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Μέλη ομάδας" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Ομαδικά έργα" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Οπτικό θέμα" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Αποτελέσματα αναζήτησης" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Γράψτε κάτι για αναζήτηση" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Ενημέρωση επιλογών" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Ο λογαριασμός σας" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Email" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Το όνομα σου" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "ακύρωση" @@ -315,27 +315,27 @@ msgstr "Ok" msgid "ds.confirm-title" msgstr "Είσαι σίγουρος;" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Το πρόγραμμα περιήγησής σας δεν μπορεί να εκτελέσει αυτήν τη λειτουργία" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Το email έχει ήδη χρησιμοποιηθεί" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Αυτό το email έχει ήδη επικυρωθεί." -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Το email «%s» έχει πολλές μόνιμες αναφορές αναπήδησης." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Έχει συμβεί κάτι λάθος." -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Το email επιβεβαίωσης πρέπει να ταιριάζει" @@ -361,7 +361,7 @@ msgstr "" msgid "errors.media-type-not-allowed" msgstr "Φαίνεται ότι δεν είναι έγκυρη εικόνα." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Το προφίλ που προσκαλείτε σταματά τα μηνύματα ηλεκτρονικού ταχυδρομείου " @@ -383,7 +383,7 @@ msgstr "Η εγγραφή είναι απενεργοποιημένη αυτήν msgid "errors.unexpected-error" msgstr "Προέκυψε ένα μη αναμενόμενο σφάλμα." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Άγνωστο διακριτικό" @@ -442,39 +442,27 @@ msgstr "Σκιά " msgid "inspect.attributes.stroke" msgstr "περίγραμμα" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Κέντρο" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Μέσα" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Εξω" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Τυπογραφία" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Οικογένεια γραμματοσειρών" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Μέγεθος γραμματοσειράς" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Στυλ γραμματοσειράς" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Διάστημα γραμμάτων" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Ύψος γραμμής" @@ -486,11 +474,11 @@ msgstr "Κανένα" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "υπογράμμιση" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Πρώτα κεφαλαία" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Πεζά γράμματα" @@ -498,7 +486,7 @@ msgstr "Πεζά γράμματα" msgid "inspect.attributes.typography.text-transform.none" msgstr "Κανένα" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Κεφαλαία γράμματα" @@ -542,29 +530,29 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Κείμενο" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Αποδέχομαι" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Διαχειριστής" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Ολα" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Φαίνεται ότι πρέπει να περιμένετε λίγο και να προσπαθήσετε ξανά. εκτελούμε " "μικρή συντήρηση των διακομιστών μας." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Bad Gateway" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "ακύρωση" @@ -572,7 +560,7 @@ msgstr "ακύρωση" msgid "labels.code" msgstr "Κώδικας" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Σχόλια" @@ -580,15 +568,15 @@ msgstr "Σχόλια" msgid "labels.confirm-password" msgstr "Επιβεβαίωση Κωδικού" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Δημιουργήστε μια νέα ομάδα" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "πίνακας" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Διαγραφή" @@ -600,15 +588,15 @@ msgstr "Διαγραφή σχολίου" msgid "labels.delete-comment-thread" msgstr "Διαγραφή νήματος" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Πρόχειρα" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Edit" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Editor" @@ -620,15 +608,15 @@ msgstr "Τα σχόλια απενεργοποιήθηκαν" msgid "labels.feedback-sent" msgstr "Εστάλη γνώμη" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Δώστε μας τη γνώμη σας" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Πίσω" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Απόκρυψη επιλυμένων σχολίων" @@ -636,19 +624,19 @@ msgstr "Απόκρυψη επιλυμένων σχολίων" msgid "labels.info" msgstr "Πληροφορίες" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Εσωτερικό σφάλμα" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Γλώσσα" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Αποσύνδεση" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Μέλη" @@ -656,21 +644,21 @@ msgstr "Μέλη" msgid "labels.new-password" msgstr "Νέος κωδικός πρόσβασης" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Δεν έχετε εκκρεμείς ειδοποιήσεις σχολίων" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Ωχ" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 αρχείο" msgstr[1] "%s αρχεία" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 έργο" @@ -680,87 +668,87 @@ msgstr[1] "%s έργα" msgid "labels.old-password" msgstr "Παλιός κωδικός" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Ιδιοκτήτης" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Κωδικός πρόσβασης" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Προφίλ" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Εργα" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Διαγραφή" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Μετονομασία" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Μετονομασία ομάδας " -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Ξαναδοκιμάσετε" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Ρόλος" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Αποθηκεύση" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Αποστολή ..." -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Αποστολή…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Είμαστε σε προγραμματισμένη συντήρηση των συστημάτων μας." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Η υπηρεσία δεν είναι διαθέσιμη" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Σύνθεση" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Κοινόχρηστες βιβλιοθήκες" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Εμφάνιση όλων των σχολίων" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Εμφάνιση μόνο των δικών σας σχολίων" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Ενημέρωση" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Ενημερώστε τον εξοπλισμό" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Θεατής" @@ -787,43 +775,43 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Προσθήκη “%s” ως Κοινόχρηστη βιβλιοθήκη" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "16 / 5000 Resultados de traducción Επιβεβαιώστε νέο e-mail" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Θα σας στείλουμε ένα email στο τρέχον email σας “%s” για να επαληθεύσουμε " "την ταυτότητά σας." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Νέο email" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Αλλαγή email" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Αλλάξτε το email σας" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Ακύρωση και διατήρηση του λογαριασμού μου" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Ναι, διαγράψτε τον λογαριασμό μου" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "Καταργώντας τον λογαριασμό σας, θα χάσετε όλα τα τρέχοντα έργα και τα " "αρχεία σας." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Είστε σίγουροι ότι θέλετε να διαγράψετε το λογαριασμό σας;" @@ -853,11 +841,11 @@ msgstr "Είστε σίγουροι ότι θέλετε να διαγράψετ msgid "modals.delete-file-confirm.title" msgstr "Διαγραφή αρχείου" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτή τη σελίδα;" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Διαγραφή σελίδας" @@ -887,59 +875,59 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Διαγραφή ομάδας" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Διαγραφή μέλους" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το μέλος από την ομάδα;" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Διαγραφή μέλους της ομάδας" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Αποστολή πρόσκλησης" -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "Είστε ο ιδιοκτήτης του %s" -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Προώθηση και αφήστε" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Επιλέξτε ένα μέλος για προώθηση" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Επιλέξτε ένα μέλος για προώθηση" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Αφήστε την ομάδα" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Είστε σίγουροι ότι θέλετε να αφήσετε αυτή την ομάδα;" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Αφήστε την ομάδα" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Προώθηση" -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "Είστε σίγουροι ότι θέλετε να προωθήσετε αυτό το χρήστη στον ιδιοκτήτη;" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Προώθηση σε κάτοχο" @@ -961,21 +949,21 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Ενημερώστε ένα στοιχείο σε μια κοινόχρηστη βιβλιοθήκη" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Η πρόσκληση εστάλη με επιτυχία" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Δεν μπορείτε να διαγράψετε το προφίλ σας. Επανατοποθετήστε τις ομάδες σας " "προτού συνεχίσετε." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Το προφίλ αποθηκεύτηκε με επιτυχία!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Το email επαλήθευσης εστάλη στο %s. Ελέγξτε το email σας!" @@ -983,139 +971,139 @@ msgstr "Το email επαλήθευσης εστάλη στο %s. Ελέγξτε msgid "profile.recovery.go-to-login" msgstr "Μεταβείτε στη σύνδεση" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Μικτός " -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Δεν βρέθηκαν πλαίσια στη σελίδα" -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Το πλαίσιο δεν βρέθηκε." -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Μην εμφανίζετε αλληλεπιδράσεις" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Πλήρης οθόνη" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Αντιγραφή link" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Εμφάνιση αλληλεπιδράσεων" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Εμφάνιση αλληλεπιδράσεων με click" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Sitemap" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Στοίχιση στο κέντρο (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Κατανομή οριζόντιου χώρου (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Ευθυγράμμιση προς τα αριστερά (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Ευθυγράμμιση προς τα δεξιά (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Στοίχιση κάτω (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Ευθυγραμμίστε το κάθετο κέντρο (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Κατανομή κατακόρυφων αποστάσεων (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Ευθυγραμμίστε την κορυφή (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Όλα τα περιουσιακά στοιχεία" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Χρώματα" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Συστατικά" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Διαγραφή" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Αντιγραφή" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Επεξεργασία" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Δεν βρέθηκαν στοιχεία" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Μετονομασία" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Αναζήτηση στοιχείων" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Τυπογραφίες" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Μέγεθος" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Στυλ γραμματοσειράς" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Μεταβείτε στο αρχείο βιβλιοθήκης στυλ για επεξεργασία" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Διάστημα γραμμάτων" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Υψος γραμμής" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Μετασχηματισμός κειμένου" @@ -1127,55 +1115,55 @@ msgstr "Γραμμική κλίση" msgid "workspace.gradients.radial" msgstr "Ακτινική κλίση" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Απενεργοποίηση δυναμικής ευθυγράμμισης" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Ενεργοποίηση δυναμικής ευθυγράμμισης" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Απόκρυψη παλέτας χρωμάτων" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Απόκρυψη κανόνες" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Επιλογή όλων" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Εμφάνιση παλέτας χρωμάτων" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Εμφάνιση κανόνες" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Σφάλμα κατά την αποθήκευση" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Αποθηκεύτηκε" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Aποθήκευση " -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Λειτουργία προβολής (%s)" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Βιβλιοθήκη αρχείων" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Πρόσφατα χρώματα" @@ -1183,39 +1171,39 @@ msgstr "Πρόσφατα χρώματα" msgid "workspace.libraries.colors.save-color" msgstr "Αποθηκεύστε το στυλ χρώματος" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Βιβλιοθήκη αρχείων" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "ΒΙΒΛΙΟΘΗΚΕΣ ΣΕ ΑΥΤΟ ΤΟ ΑΡΧΕΙΟ" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "ΒΙΒΛΙΟΘΗΚΕΣ" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Δεν υπάρχουν κοινόχρηστες βιβλιοθήκες που χρειάζονται ενημέρωση" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Δεν βρίσκεται «%s»" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Αναζήτηση σε κοινόχρηστες βιβλιοθήκες" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "Κοινόχρηστες βιβλιοθήκες" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Ενημέρωση" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "ΕΝΗΜΕΡΩΣΕΙΣ" @@ -1231,27 +1219,27 @@ msgstr "Θόλωμα επιλογής" msgid "workspace.options.canvas-background" msgstr "Χρώμα του φόντου" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Συστατικό" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Σχέδιο" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Εξαγωγή" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgstr "Εξαγωγή σχήματος" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Κατάληξη" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Εξαγωγή ..." @@ -1263,63 +1251,63 @@ msgstr "Γέμισμα" msgid "workspace.options.grid.auto" msgstr "Αυτόματο" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Στήλες" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "απόσταση" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Υψος" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Περιθώριο" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Ορίσετε ως προεπιλογή" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Κάτω" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Κέντρο" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Αριστερά" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Δεξιά" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Τέντωμα" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Πάνω" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "χρήση προεπιλεγμένης" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Πλάτος" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Σειρές" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "τετράγωνο" @@ -1331,71 +1319,71 @@ msgstr "Συμπλήρωση ομάδας" msgid "workspace.options.group-stroke" msgstr "Ομαδικό εγκεφαλικό" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Χρώμα" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Έγκαυμα χρώματος" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Χρώμα πιο μαλακό " -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Σκούρο" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Διαφορά" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Αποκλεισμός" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Δυνατο φως" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Απόχρωση" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Φωτίζω" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Φωτεινότητα" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Πολλαπλασιάζω" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Κανονικός" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Επικάλυμμα" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Κορεσμός" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Κόσκινο" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Απαλό φως" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Πρωτότυπο" @@ -1403,7 +1391,7 @@ msgstr "Πρωτότυπο" msgid "workspace.options.radius" msgstr "Ακτίνα κύκλου" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Περιστροφή" @@ -1457,39 +1445,39 @@ msgstr "Ομαδική σκιά" msgid "workspace.options.shadow-options.title.multiple" msgstr "Επιλογή σκιών" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Μέγεθος" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Προεπιλογές μεγέθους" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Κέντρο" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Dashed" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Διάστικτο" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Μέσα" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Μικτός" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Εξω" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Στερεός" @@ -1517,11 +1505,11 @@ msgstr "Αυτόματο πλάτος" msgid "workspace.options.text-options.grow-fixed" msgstr "Σταθερός" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Διάστημα γραμμάτων" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Υψος γραμμής" @@ -1575,19 +1563,19 @@ msgstr "Αποστολή προς τα πίσω" msgid "workspace.shape.menu.backward" msgstr "Αποστολή προς τα πίσω" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "αντίγραφο" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Δημιουργία στοιχείου" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Αποκοπή" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Διαγραφή" @@ -1599,7 +1587,7 @@ msgstr "Αποσύνδεση παρουσίας" msgid "workspace.shape.menu.duplicate" msgstr "Αντίγραφο" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Επεξεργασία" @@ -1619,23 +1607,23 @@ msgstr "Φέρτε μπροστά" msgid "workspace.shape.menu.front" msgstr "Φέρτε μπροστά" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Ομάδα" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Κρύβω" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Κλείδωμα" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Μάσκα" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Επικόλληση" @@ -1643,31 +1631,31 @@ msgstr "Επικόλληση" msgid "workspace.shape.menu.reset-overrides" msgstr "Επαναφορά παρακάμψεων" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "προβολή" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Κατάργηση ομάδας" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Ξεκλείδωμα" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Ανακάλυψη" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Ιστορικό" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "στρώσεις" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Εισαγόμενα χαρακτηριστικά SVG" @@ -1675,7 +1663,7 @@ msgstr "Εισαγόμενα χαρακτηριστικά SVG" msgid "workspace.sidebar.sitemap" msgstr "Σελίδες" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Χάρτης ιστοτόπου" @@ -1683,47 +1671,47 @@ msgstr "Χάρτης ιστοτόπου" msgid "workspace.toolbar.assets" msgstr "Στοιχεία" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Παλέτα χρωμάτων (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Σχόλια (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Στροφή (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Έλλειψη (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Artboard (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Εικόνα (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Path (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Ορθογώνιο (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Κείμενο (%s)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Δεν υπάρχουν μέχρι στιγμής αλλαγές στο ιστορικό" @@ -1863,15 +1851,15 @@ msgstr "τυπογραφικό στοιχείο" msgid "workspace.undo.entry.unknown" msgstr "Λειτουργία άνω του %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Παράβλεψη" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Υπάρχουν ενημερώσεις σε κοινόχρηστες βιβλιοθήκες" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Ενημέρωση" diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 861862dcc3..185602dbb5 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.12-dev\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Already have an account?" @@ -33,7 +33,7 @@ msgstr "" "This is a DEMO service, DO NOT USE for real work, the projects will be " "periodically wiped." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Forgot password?" @@ -41,35 +41,35 @@ msgstr "Forgot password?" msgid "auth.fullname" msgstr "Full Name" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Log into my account" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Login here." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot is the free open-source design tool for Design and Code collaboration" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -77,7 +77,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Type a new password" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "You're now part of %s" @@ -93,7 +93,7 @@ msgstr "Profile is not verified, please verify profile before continue." msgid "auth.notifications.recovery-token-sent" msgstr "Password recovery link sent to your inbox." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Joined the team successfully" @@ -125,7 +125,7 @@ msgstr "Forgot password?" msgid "auth.recovery-submit" msgstr "Change your password" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "No account yet?" @@ -137,7 +137,7 @@ msgstr "Let us know what we should call you on the dashboard and in emails." msgid "auth.register-account-title" msgstr "Your name" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Create an account" @@ -150,7 +150,7 @@ msgstr "Create an account" msgid "auth.terms-and-privacy-agreement" msgstr "I agree to the [terms of service](%s) and [privacy policy](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Terms of service" @@ -170,101 +170,101 @@ msgstr "No available color tokens. Check active sets/themes or add new tokens." msgid "comments.mentions.not-found" msgstr "No people found for @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publish" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "All Penpot users" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Are you sure you want to remove this link? If you do it, it's no longer be " "available for anyone" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(current)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Destroy link" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Get link" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Link copied successfully" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Manage permissions" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 page shared" msgstr[1] "%s pages shared" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Can comment" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Can inspect code" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Anyone with link will have access" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Pages shared" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Shareable link will appear here" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Only team members" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Share prototypes" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Select All" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Unpublish" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Team management" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot is meant for teams. Invite members to work together on projects and " "files" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Team up!" -#: src/app/main/ui/dashboard/file_menu.cljs:234 +#: src/app/main/ui/dashboard/file_menu.cljs:233 msgid "dashboard-restore-file-confirmation.description" msgstr "You're going to restore %s." -#: src/app/main/ui/dashboard/file_menu.cljs:233 +#: src/app/main/ui/dashboard/file_menu.cljs:232 msgid "dashboard-restore-file-confirmation.title" msgstr "Restore file" @@ -272,33 +272,33 @@ msgstr "Restore file" msgid "dashboard.add-file" msgstr "Add file" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:323, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Add as Shared Library" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Change email" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "MOVE TEAM" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Move to:" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "" "Projects and files will remain available to team members. The team will get " "the configuration from the new organization." -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Select an organization" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "Change team's organization" @@ -356,23 +356,27 @@ msgstr "View full changelog" msgid "dashboard.check-updates.view-release-notes" msgstr "View release notes" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/sidebar.cljs:228 +msgid "dashboard.clear-search" +msgstr "Clear search" + +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Clear trash" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:318, src/app/main/data/dashboard.cljs:568, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copy)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Create new organization" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Create new team" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Pin this version" @@ -380,76 +384,86 @@ msgstr "Pin this version" msgid "dashboard.default-team-name" msgstr "Personal Projects" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Are you sure you want to delete forever all your deleted projects and " "files? This is a non reversible action." -#: src/app/main/ui/dashboard/file_menu.cljs:247 +#: src/app/main/ui/dashboard/file_menu.cljs:246 msgid "dashboard.delete-file-forever-confirmation.description" msgstr "Are you sure you want to delete forever %s? This is a non reversible action." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:842 msgid "dashboard.delete-files-success-notification" msgstr "%s files have been successfully deleted." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:245, src/app/main/ui/dashboard/file_menu.cljs:247 msgid "dashboard.delete-forever-confirmation.title" msgstr "Delete forever" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Delete project" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Are you sure you want to delete forever %s project? You're going to delete " "it forever an all of the files contained in it. This is a non reversible " "action." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:841, src/app/main/data/dashboard.cljs:875 msgid "dashboard.delete-success-notification" msgstr "%s has been successfully deleted." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Delete team" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "Your trash is empty. Deleted files and projects will appear here." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Will be deleted %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Download Penpot file (.penpot)" -#: src/app/main/ui/dashboard/file_menu.cljs:302, src/app/main/ui/dashboard/project_menu.cljs:94 +#: src/app/main/ui/dashboard/file_menu.cljs:301, src/app/main/ui/dashboard/project_menu.cljs:94 msgid "dashboard.duplicate" msgstr "Duplicate" -#: src/app/main/ui/dashboard/file_menu.cljs:269 +#: src/app/main/ui/dashboard/file_menu.cljs:268 msgid "dashboard.duplicate-multi" msgstr "Duplicate %s files" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/data/dashboard.cljs:549 +msgid "dashboard.duplicate-unsaved-changes" +msgid_plural "dashboard.duplicate-unsaved-changes" +msgstr[0] "" +"Your file has been duplicated, but the copy doesn't include your latest " +"changes because they aren't saved yet." +msgstr[1] "" +"Your files have been duplicated, but the copies don't include your latest " +"changes because they aren't saved yet." + +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Once a project member creates a draft, it will be displayed here." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "No drafts yet." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Once a project member creates a file, it will be displayed here." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "No files yet." @@ -497,71 +511,71 @@ msgstr "Import a .penpot file" msgid "dashboard.empty-project.start" msgstr "Start building amazing stuff" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:845 msgid "dashboard.errors.error-on-delete-files" msgstr "There was an error while deleting the files." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:878 msgid "dashboard.errors.error-on-delete-project" msgstr "There was an error while deleting the project %s." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:973, src/app/main/ui/dashboard/file_menu.cljs:226 msgid "dashboard.errors.error-on-restore-file" msgstr "There was an error while restoring the file %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restore-files" msgstr "There was an error while restoring the files." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:1006 msgid "dashboard.errors.error-on-restoring-project" msgstr "There was an error while restoring the project %s and its files." -#: src/app/main/ui/dashboard/file_menu.cljs:279 +#: src/app/main/ui/dashboard/file_menu.cljs:278 msgid "dashboard.export-binary-multi" msgstr "Download %s Penpot files (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Export boards as PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Export as PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s of %s elements selected" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Export" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "You can add export settings to elements from the design properties (at the " "bottom of the right sidebar)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Info how to set exports at Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "There are no elements with export settings." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Export selection" -#: src/app/main/ui/dashboard/file_menu.cljs:263 +#: src/app/main/ui/dashboard/file_menu.cljs:262 msgid "dashboard.file-menu.delete-files-permanently-option" msgid_plural "dashboard.file-menu.delete-files-permanently-option" msgstr[0] "Delete file" msgstr[1] "Delete files" -#: src/app/main/ui/dashboard/file_menu.cljs:261 +#: src/app/main/ui/dashboard/file_menu.cljs:260 msgid "dashboard.file-menu.restore-files-option" msgid_plural "dashboard.file-menu.restore-files-option" msgstr[0] "Restore file" @@ -575,33 +589,37 @@ msgstr "Grid view" msgid "dashboard.files-layout.list" msgstr "List view" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/dashboard/fonts.cljs:442, src/app/main/ui/dashboard/fonts.cljs:443 +msgid "dashboard.fonts.delete-variant" +msgstr "Delete font style" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Missing font" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Dismiss all" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Custom fonts you upload will appear here." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "No custom fonts yet." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "Once a project member uploads a custom font, it will be displayed here." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 font added" msgstr[1] "%s fonts added" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -611,7 +629,7 @@ msgstr "" "upload fonts with the following formats: **TTF, OTF and WOFF** (only one " "will be needed)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -620,11 +638,11 @@ msgstr "" "You also might want to read about [font " "licensing](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Upload all" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -635,23 +653,23 @@ msgstr "" "using [Transfonter](https://transfonter.org/) to generate webfonts and fix " "errors. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Go to Admin Console" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "Last modified %s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Import Penpot files" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Oops! We couldn't import this file" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "1 library was automatically linked by name." @@ -661,53 +679,77 @@ msgstr[1] "%s libraries were automatically linked by name." msgid "dashboard.import.bad-url" msgstr "Import failed. The template URL is incorrect" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:625 +msgid "dashboard.import.check.message" +msgstr "Making sure each file can be imported. This only takes a moment." + +#: src/app/main/ui/dashboard/import.cljs:624 +msgid "dashboard.import.check.title" +msgstr "Checking your files..." + +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "Confirm library links" -#: src/app/main/ui/dashboard/import.cljs:709 +#: src/app/main/ui/dashboard/import.cljs:814 msgid "dashboard.import.connect-selected-libraries" msgstr "Connect selected libraries" -#: src/app/main/ui/dashboard/import.cljs:296 +#: src/app/main/ui/dashboard/import.cljs:288 msgid "dashboard.import.file-name-label" msgstr "File name" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "We couldn't verify this file." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "This file appears to be damaged." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Not all files have been imported" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "The following files have errors:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Files with errors will not be uploaded." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "Something went wrong while processing this file." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 file has been imported successfully." msgstr[1] "%s files have been imported successfully." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Some files containted invalid objects that have been removed." -#: src/app/main/ui/dashboard/import.cljs:710 +#: src/app/main/ui/dashboard/import.cljs:600 +msgid "dashboard.import.importing.done" +msgid_plural "dashboard.import.importing.done" +msgstr[0] "1 done" +msgstr[1] "%s done" + +#: src/app/main/ui/dashboard/import.cljs:637 +msgid "dashboard.import.importing.message" +msgstr "Rebuilding pages, components and assets in your project." + +#: src/app/main/ui/dashboard/import.cljs:598 +msgid "dashboard.import.importing.title" +msgid_plural "dashboard.import.importing.title" +msgstr[0] "Importing 1 file" +msgstr[1] "Importing %s files" + +#: src/app/main/ui/dashboard/import.cljs:815 msgid "dashboard.import.next-file" msgstr "Next file" @@ -715,99 +757,119 @@ msgstr "Next file" msgid "dashboard.import.no-perms" msgstr "You don’t have permission to import to this team" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Processing colors" +#: src/app/main/ui/dashboard/import.cljs:634 +msgid "dashboard.import.queued.message" +msgstr "Your files are uploaded. The import starts as soon as a slot is free." -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Processing components" +#: src/app/main/ui/dashboard/import.cljs:633 +msgid "dashboard.import.queued.title" +msgstr "Waiting to start..." -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Processing media" +#: src/app/main/ui/dashboard/import.cljs:628 +msgid "dashboard.import.ready.message" +msgstr "Your files can be imported. Continue to upload them." -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Processing page: %s" +#: src/app/main/ui/dashboard/import.cljs:627 +msgid "dashboard.import.ready.title" +msgstr "Files ready" -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Processing typographies" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Uploading data to server (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Uploading file: %s" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "" "Some libraries couldn't be linked automatically. Select the correct library " "for each:" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "Review the library links before confirming:" -#: src/app/main/ui/dashboard/import.cljs:459 +#: src/app/main/ui/dashboard/import.cljs:447 msgid "dashboard.import.resolve-libraries.connect-to" msgstr "connect to" -#: src/app/main/ui/dashboard/import.cljs:453 +#: src/app/main/ui/dashboard/import.cljs:441 msgid "dashboard.import.resolve-libraries.original-library" msgstr "original library" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:331 +msgid "dashboard.import.status.ready" +msgstr "Ready" + +#: src/app/main/ui/dashboard/import.cljs:568 +msgid "dashboard.import.steps.check" +msgstr "Check" + +#: src/app/main/ui/dashboard/import.cljs:570 +msgid "dashboard.import.steps.import" +msgstr "Import" + +#: src/app/main/ui/dashboard/import.cljs:573 +msgid "dashboard.import.steps.label" +msgstr "Import steps" + +#: src/app/main/ui/dashboard/import.cljs:569 +msgid "dashboard.import.steps.upload" +msgstr "Upload" + +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "auto-linked" -#: src/app/main/ui/dashboard/import.cljs:529 +#: src/app/main/ui/dashboard/import.cljs:517 msgid "dashboard.import.summary.manually-linked" msgstr "linked manually" -#: src/app/main/ui/dashboard/import.cljs:537 +#: src/app/main/ui/dashboard/import.cljs:525 msgid "dashboard.import.summary.new" msgstr "New" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "No library selected" -#: src/app/main/ui/dashboard/import.cljs:534 +#: src/app/main/ui/dashboard/import.cljs:522 msgid "dashboard.import.summary.original" msgstr "Original" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/import.cljs:1130 +msgid "dashboard.import.title" +msgstr "Import file" + +#: src/app/main/ui/dashboard/import.cljs:631 +msgid "dashboard.import.upload.message" +msgstr "Sending your files. Keep this dialog open until the upload ends." + +#: src/app/main/ui/dashboard/import.cljs:630 +msgid "dashboard.import.upload.title" +msgstr "Uploading your files..." + +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "You're going to delete the invitations to:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "You're going to resend the invitations to:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Delete invitations" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Resend invitations" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "You don’t have permission to invite people to join this team or to edit or " "delete invitations." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Invite people" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "You don't have permission to invite people to this team" @@ -819,27 +881,27 @@ msgstr "Leave org" msgid "dashboard.leave-organization.toast" msgstr "You're no longer part of the %s organization." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Leave team" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Libraries & Templates" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "Here you have some Libraries and templates you can add to your project" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Explore more of them and know how to contribute" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "There was a problem importing the template. The template wasn't imported." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Libraries" @@ -847,7 +909,7 @@ msgstr "Libraries" msgid "dashboard.loading-files" msgstr "loading your files …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "loading your fonts …" @@ -855,11 +917,11 @@ msgstr "loading your fonts …" msgid "dashboard.mark-all-as-read.success" msgstr "Marked all notifications as read" -#: src/app/main/ui/dashboard/file_menu.cljs:308, src/app/main/ui/dashboard/project_menu.cljs:101 +#: src/app/main/ui/dashboard/file_menu.cljs:307, src/app/main/ui/dashboard/project_menu.cljs:101 msgid "dashboard.move-to" msgstr "Move to" -#: src/app/main/ui/dashboard/file_menu.cljs:272 +#: src/app/main/ui/dashboard/file_menu.cljs:271 msgid "dashboard.move-to-multi" msgstr "Move %s files to" @@ -867,65 +929,65 @@ msgstr "Move %s files to" msgid "dashboard.move-to-other-team" msgstr "Move to other team" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ New File" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:525, src/app/main/data/dashboard.cljs:668 msgid "dashboard.new-file-prefix" msgstr "New File" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ New project" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:270, src/app/main/data/dashboard.cljs:671 msgid "dashboard.new-project-prefix" msgstr "New Project" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "No matches found for “%s“" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "You don't have permission to add teams to any of your organizations." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:185 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 msgid "dashboard.no-organizations-yet" msgstr "No organizations yet." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "You are not allowed to create teams within %s organization. If you need " "more information, contact the organization's owner." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "You are not allowed to delete teams that are part of %s organization. If " "you need more information, contact the organization's owner." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "You are not allowed to move teams that are part of %s organization. If you " "need more information, contact the organization's owner." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Pinned projects will appear here" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notifications" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Your email address has been updated successfully" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Your email address has been verified successfully" @@ -937,35 +999,35 @@ msgstr "Notification settings updated" msgid "dashboard.notifications.password-saved" msgstr "Password saved successfully!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "View notifications" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s members" -#: src/app/main/ui/dashboard/file_menu.cljs:294 +#: src/app/main/ui/dashboard/file_menu.cljs:293 msgid "dashboard.open-in-new-tab" msgstr "Open file in a new tab" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Options" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Order by role" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Order by status" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:753 msgid "dashboard.organization-deleted" msgstr "The %s organization has been deleted." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "Other teams" @@ -989,11 +1051,11 @@ msgstr "You are now owner on this team." msgid "dashboard.permissions-change.viewer" msgstr "You are now a viewer on this team." -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "Personal Projects" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Pin/Unpin" @@ -1009,27 +1071,27 @@ msgstr "Cannot parse the plugin manifest" msgid "dashboard.plugins.try-plugin" msgstr "Try plugin: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:786 msgid "dashboard.progress-notification.deleting-files" msgstr "Deleting files…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:907 msgid "dashboard.progress-notification.restoring-files" msgstr "Restoring files…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:787 msgid "dashboard.progress-notification.slow-delete" msgstr "Deletion unexpectedly slow" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:908 msgid "dashboard.progress-notification.slow-restore" msgstr "Restore unexpectedly slow" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projects" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Want to remove your account?" @@ -1037,113 +1099,113 @@ msgstr "Want to remove your account?" msgid "dashboard.removed-from-team" msgstr "You are not part of the team “%s“ anymore." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "You're going to restore all your projects and files. This may take a while." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Restore all projects and files" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Restore All" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:967 msgid "dashboard.restore-files-success-notification" msgstr "%s files have been successfully restored." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Restore project" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "You're going to restore %s project and all the files contained in it." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Restore Project" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:939, src/app/main/data/dashboard.cljs:966, src/app/main/data/dashboard.cljs:1003, src/app/main/ui/dashboard/file_menu.cljs:223 msgid "dashboard.restore-success-notification" msgstr "%s has been successfully restored." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Save settings" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Search…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Searching for “%s“…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Organizations" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "Teams" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "ADD TO ORGANIZATION" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "Choose an organization:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" only allows organization members to join its teams. Any pending " "invitations to people outside the organization will be revoked." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Pending invitations to external users will be canceled." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Here you'll find the organizations you are part of where you are allowed to " "move the team." -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "" "Here you'll find the organizations you are part of where you are allowed to " "create or add teams." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "Select an organization" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "Add team to an organization" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Select UI language" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Select theme" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Dark (default)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Light" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "System theme" @@ -1207,15 +1269,15 @@ msgstr "Update settings" msgid "dashboard.settings.notifications.title" msgstr "Notifications" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Show all files" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Version history" -#: src/app/main/ui/dashboard/file_menu.cljs:129 +#: src/app/main/ui/dashboard/file_menu.cljs:128 msgid "dashboard.success-delete-file" msgid_plural "dashboard.success-delete-file" msgstr[0] "Your file has been deleted successfully" @@ -1225,7 +1287,7 @@ msgstr[1] "Your files have been deleted successfully" msgid "dashboard.success-delete-project" msgstr "Your project has been deleted successfully" -#: src/app/main/ui/dashboard/file_menu.cljs:124 +#: src/app/main/data/dashboard.cljs:553 msgid "dashboard.success-duplicate-file" msgid_plural "dashboard.success-delete-file" msgstr[0] "Your file has been duplicated successfully" @@ -1235,11 +1297,11 @@ msgstr[1] "Your files have been duplicated successfully" msgid "dashboard.success-duplicate-project" msgstr "Your project has been duplicated successfully" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:161, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Your file has been moved successfully" -#: src/app/main/ui/dashboard/file_menu.cljs:161 +#: src/app/main/ui/dashboard/file_menu.cljs:160 msgid "dashboard.success-move-files" msgstr "Your files have been moved successfully" @@ -1247,182 +1309,182 @@ msgstr "Your files have been moved successfully" msgid "dashboard.success-move-project" msgstr "Your project has been moved successfully" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s now belongs to %s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Team info" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Team members" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s is no longer part of %s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Team organization" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Add to an organization" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "You don't have permission to add this team to any of your organizations." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Change team organization" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Only people within your organization can be invited." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "This team is not part of any organization" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Remove team from organization" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Team projects" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Add to your project" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "UI theme" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Search results" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Deleted files will remain in the trash for" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s days. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "After that, they will be permanently deleted." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "If you change your mind, you can restore them or delete them permanently " "from each file's menu." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Type to search results" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:321, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Unpublish Library" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Update settings" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:721 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "You are no longer a member of the organization %s" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Beta" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "WebGL rendering can improve performance, but it is in beta and may be less " "stable. Some visual differences may appear between the canvas, exports " "(SVG/PDF), and view mode." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Disabled" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Enabled" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Give feedback" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Status" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "WebGL rendering" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Is active" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "When this hook is triggered event details will be delivered" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "You only can delete or modify webhooks created by you." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Content type" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Create webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook created successfully." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhooks are a simple way to allow other websites and apps to be notified " "when certain events happen at Penpot. We’ll send a POST request to each of " "the URLs you provide." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Press the button \"Add webhook\" to add one." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "No webhooks created so far." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Your account" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Email" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Your name" @@ -1438,11 +1500,11 @@ msgstr "Attention" msgid "ds.component-subtitle" msgstr "Components to update:" -#: src/app/main/ui/workspace/plugins.cljs:407, src/app/main/ui/workspace/plugins.cljs:470 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Allow" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:401, src/app/main/ui/workspace/plugins.cljs:464 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Cancel" @@ -1462,7 +1524,7 @@ msgstr "No applicable tokens in active sets or themes." msgid "ds.inputs.numeric-input.no-matches" msgstr "No matches found." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Open token list" @@ -1484,15 +1546,15 @@ msgstr "Auth provider not allowed for this profile" msgid "errors.auth-provider-not-configured" msgstr "Authentication provider not configured." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:757 msgid "errors.auth.unable-to-login" msgstr "Looks like you are not authenticated or session expired." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "The font %s could not be loaded" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "The fonts %s could not be loaded" @@ -1504,61 +1566,61 @@ msgstr "Cannot upload the media file." msgid "errors.character-limit-exceeded" msgstr "Character limit exceeded" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1838 msgid "errors.circular-library-reference" msgstr "Cannot add library: this would create a circular dependency" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "Clipboard API is unavailable. Serve Penpot over HTTPS to enable clipboard " "access" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Your browser cannot do this operation" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "Clipboard access denied. Please allow clipboard permissions in your browser " "to paste content" -#: src/app/main/errors.cljs:782 +#: src/app/main/errors.cljs:959 msgid "errors.comment-error" msgstr "There was an error with the comment" -#: src/app/main/errors.cljs:499 +#: src/app/main/errors.cljs:587 msgid "errors.connection-error" msgstr "Cannot reach the server. Check your connection and try again." -#: src/app/main/errors.cljs:414 +#: src/app/main/errors.cljs:1028 msgid "errors.deprecated" msgstr "" "Sorry! This is an old file that uses a deprecated type of Penpot assets and " "you can't open it." -#: src/app/main/errors.cljs:854 +#: src/app/main/errors.cljs:1031 msgid "errors.deprecated.contact.after" msgstr "so we can help you." -#: src/app/main/errors.cljs:852 +#: src/app/main/errors.cljs:1029 msgid "errors.deprecated.contact.before" msgstr "Although Penpot no longer support this type of Assets, you can" -#: src/app/main/errors.cljs:853 +#: src/app/main/errors.cljs:1030 msgid "errors.deprecated.contact.text" msgstr "contact us" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Cannot drop a parent set to an own child path." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Email already used" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Email already validated." @@ -1570,37 +1632,37 @@ msgstr "You can't use your email as password" msgid "errors.email-does-not-match-invitation" msgstr "Email does not match the invitation." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domain not allowed" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "The email «%s» has many permanent bounce reports." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "The email «%s» has been reported as spam or permanently bounce." -#: src/app/main/data/exports/assets.cljs +#: src/app/main/data/exports/assets.cljs:64, src/app/main/data/exports/assets.cljs:93 msgid "errors.export-failed" msgstr "The export failed. Try again in a moment." -#: src/app/main/data/exports/assets.cljs:346 +#: src/app/main/data/exports/assets.cljs:377 msgid "errors.export-too-large" msgstr "Too large to export, try a smaller scale" -#: src/app/main/errors.cljs:974 +#: src/app/main/errors.cljs:1005 msgid "errors.feature-mismatch" msgstr "" "Looks like you are opening a file that has the feature '%s' enabled but the " "current penpot version does not supports it or has it disabled." -#: src/app/main/errors.cljs:832, src/app/main/errors.cljs:846 +#: src/app/main/errors.cljs:1009, src/app/main/errors.cljs:1023 msgid "errors.feature-not-supported" msgstr "Feature '%s' is not supported." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Must contain at most %s characters." @@ -1608,7 +1670,7 @@ msgstr "Must contain at most %s characters." msgid "errors.field-min-length" msgstr "Must contain at least 1 character." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Empty field" @@ -1616,24 +1678,24 @@ msgstr "Empty field" msgid "errors.field-not-all-whitespace" msgstr "The name must contain some character other than space." -#: src/app/main/errors.cljs:824 +#: src/app/main/errors.cljs:1001 msgid "errors.file-feature-mismatch" msgstr "" "It seems that there is a mismatch between the enabled features and the " "features of the file you are trying to open. Migrations for '%s' need to be " "applied before the file can be opened." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "The font family name can only contain letters, numbers, spaces, hyphens, " "underscores, and dots." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:565, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Something wrong has happened." -#: src/app/main/errors.cljs:761 +#: src/app/main/errors.cljs:938 msgid "errors.internal-worker-error" msgstr "Something wrong has happened with the web worker." @@ -1641,7 +1703,7 @@ msgstr "Something wrong has happened with the web worker." msgid "errors.invalid-color" msgstr "Invalid color" -#: src/app/plugins/utils.cljs:517, ../common/src/app/common/schema/messages.cljc:33, ../common/src/app/common/schema/messages.cljc:87 +#: src/app/plugins/utils.cljs:541, ../common/src/app/common/schema/messages.cljc:33, ../common/src/app/common/schema/messages.cljc:87 msgid "errors.invalid-data" msgstr "Invalid data" @@ -1649,7 +1711,7 @@ msgstr "Invalid data" msgid "errors.invalid-email" msgstr "Enter a valid email please" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Confirmation email must match" @@ -1683,11 +1745,11 @@ msgstr "This invite might be canceled or may be expired." msgid "errors.ldap-disabled" msgstr "LDAP authentication is disabled." -#: src/app/main/errors.cljs:840, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1017, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "You have reached the '%s' quota. Contact support." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "The maximum (%s) number of emails that can be invited in a single request " @@ -1705,19 +1767,19 @@ msgstr "Seems that the contents of the image does not match the file extension." msgid "errors.media-type-not-allowed" msgstr "Seems that this is not a valid image." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "The profile you inviting has emails muted (spam reports or high bounces)." -#: src/app/main/errors.cljs:814 +#: src/app/main/errors.cljs:991 msgid "errors.migration-in-progress" msgstr "Migration in progress" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:884 msgid "errors.only-creator-can-lock" msgstr "Only the version creator can lock it" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:892 msgid "errors.only-creator-can-unlock" msgstr "Only the version creator can unlock it" @@ -1729,7 +1791,7 @@ msgstr "There was a problem leaving the organization. Please try again." msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "The organization owner can't leave the organization." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "That organization doesn't exists" @@ -1741,7 +1803,7 @@ msgstr "Confirmation password must match" msgid "errors.password-too-short" msgstr "Password should at least be 8 characters" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:865 msgid "errors.paste-data-validation" msgstr "Invalid data in clipboard" @@ -1749,11 +1811,11 @@ msgstr "Invalid data in clipboard" msgid "errors.profile-blocked" msgstr "The profile is blocked" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "Your profile has emails muted (spam reports or high bounces)." -#: src/app/main/errors.cljs:724 +#: src/app/main/errors.cljs:901 msgid "errors.profile-props-too-large" msgstr "" "Your profile settings are too large to save. Remove some plugins and try " @@ -1763,37 +1825,74 @@ msgstr "" msgid "errors.registration-disabled" msgstr "The registration is currently disabled." -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:715 msgid "errors.save-failed" msgstr "" -"Autosave has stopped for this session, so your changes since the last successful save cannot be saved. Download the report to help us fix it, then reload the page, or leave this file and open it again, to keep editing." +"Autosave has stopped for this session, so your changes since the last " +"successful save cannot be saved. Download the report to help us fix it, " +"then reload the page, or leave this file and open it again, to keep editing." -#: src/app/main/data/persistence.cljs:310 +#: src/app/main/errors.cljs:706 +msgid "errors.save-permission-lost" +msgid_plural "errors.save-permission-lost" +msgstr[0] "" +"You can no longer edit this file, so 1 change could not be saved. Reload " +"the page to see the file as it is now." +msgstr[1] "" +"You can no longer edit this file, so %s changes could not be saved. Reload " +"the page to see the file as it is now." + +#: src/app/main/data/persistence.cljs:701 +msgid "errors.save-rejected-changes-undone" +msgstr "Some changes couldn't be saved, so your latest changes were undone." + +#: src/app/main/data/persistence.cljs:686 +msgid "errors.save-rejected-changes-undone-in-file" +msgstr "" +"Some changes to “%s” couldn't be saved, so your latest changes to that file " +"were undone." + +#: src/app/main/data/persistence.cljs:696 +msgid "errors.save-rejected-damaged-data" +msgstr "" +"This page has damaged data that can't be repaired automatically, so changes " +"to it can't be saved and were undone. Contact support." + +#: src/app/main/data/persistence.cljs:609 msgid "errors.save-retrying" msgstr "Connection lost. Retrying to save your changes." -#: src/app/main/errors.cljs +#: src/app/main/data/persistence.cljs:608 +msgid "errors.save-retrying-busy" +msgstr "The server is busy. Retrying to save your changes." + +#: src/app/main/errors.cljs:697 msgid "errors.save-still-retrying" msgstr "" -"Your changes are not saved yet because Penpot cannot reach the server. They are kept, and Penpot will keep trying to save them. Keep this tab open until they are saved." +"Your changes are not saved yet because Penpot cannot reach the server. They " +"are kept, and Penpot will keep trying to save them. Keep this tab open " +"until they are saved." -#: src/app/main/data/persistence.cljs:307 -msgid "errors.save-retrying" -msgstr "Connection lost. Retrying to save your changes." +#: src/app/main/errors.cljs:696 +msgid "errors.save-still-retrying-busy" +msgstr "" +"Your changes are not saved yet because the server is busy. They are kept, " +"and Penpot will keep trying to save them. Keep this tab open until they are " +"saved." -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:950 msgid "errors.svg-parser.invalid-svg" msgstr "SVG is invalid or malformed" -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:996 msgid "errors.team-feature-mismatch" msgstr "Detected incompatible feature '%s'" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "Insufficient members to leave team, you probably want to delete it." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "The member you try to assign does not exist." @@ -1801,7 +1900,7 @@ msgstr "The member you try to assign does not exist." msgid "errors.team-leave.only-owner-can-delete" msgstr "Only the owner of a team can delete it." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Owner can't leave team, you must reassign the owner role." @@ -1813,11 +1912,11 @@ msgstr "The team name can't contain any of the following characters:'.', ':' or msgid "errors.token-set-already-exists" msgstr "A set with the same name already exists" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "Cannot complete drop, a set with same name already exists at path." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Theme Option with the same name exists" @@ -1829,11 +1928,15 @@ msgstr "The theme refers to some not existing sets: %s" msgid "errors.tokens.circular-reference" msgstr "Token has circular reference" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "Line Height depends on Font Size. Add a Font Size to get the resolved value." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/data/workspace/tokens/errors.cljs:62 +msgid "errors.tokens.division-by-zero" +msgstr "Invalid token value: division by zero" + +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "This field cannot be empty" @@ -1845,19 +1948,27 @@ msgstr "Token value cannot be empty" msgid "errors.tokens.error-parse" msgstr "Import Error: Could not parse JSON." +#: src/app/main/data/workspace/tokens/errors.cljs:74 +msgid "errors.tokens.function-argument-out-of-range" +msgstr "Invalid token value: the argument of %s() is out of range" + #: src/app/main/data/workspace/tokens/errors.cljs:33, src/app/main/data/workspace/tokens/errors.cljs:38 msgid "errors.tokens.import-error" msgstr "Import Error:" +#: src/app/main/data/workspace/tokens/errors.cljs:66 +msgid "errors.tokens.incompatible-units" +msgstr "Invalid token value: the units %s cannot be combined" + #: src/app/main/data/workspace/tokens/errors.cljs:54 msgid "errors.tokens.invalid-color" msgstr "Invalid color value: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "Invalid token value: you can only reference a font-family token" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Invalid font weight value: use numeric values (100-950) or standard names " @@ -1878,37 +1989,37 @@ msgstr "" "Token names should only contain letters and digits separated by . " "characters and must not start with a $ sign." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "Invalid shadow type: only 'innerShadow' or 'dropShadow' are accepted" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Invalid token value: only none, Uppercase, Lowercase or Capitalize are " "accepted" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "Invalid token value: only none, underline and strike-through are accepted" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "Invalid value: must reference a composite shadow token." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "Invalid value: must reference a composite typography token." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Invalid token value: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Missing token references: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "" "Group name of %s conflicts with a token of the same name in another active " @@ -1922,31 +2033,35 @@ msgstr "No tokens, sets, or themes were found in this file." msgid "errors.tokens.number-too-large" msgstr "Invalid token value. The resolved value is too large: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "Opacity must be between 0 and 100% or 0 and 1 (e.g. 50% or 0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Token has self reference" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "Shadow blur must be greater than or equal to 0." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "Shadow spread must be greater than or equal to 0." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "Stroke width must be greater than or equal to 0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:70 +msgid "errors.tokens.unknown-function" +msgstr "Invalid token value: unknown function %s" + +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Invalid value: % is not allowed." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Invalid value: Units are not allowed." @@ -1954,19 +2069,19 @@ msgstr "Invalid value: Units are not allowed." msgid "errors.unexpected-error" msgstr "An unexpected error occurred." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Unknown token" -#: src/app/main/errors.cljs:732 +#: src/app/main/errors.cljs:909 msgid "errors.version-already-locked" msgstr "This version is already locked" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:876 msgid "errors.version-locked" msgstr "This version is locked and cannot be deleted by others" -#: src/app/main/errors.cljs:836 +#: src/app/main/errors.cljs:1013 msgid "errors.version-not-supported" msgstr "File has an incompatible version number" @@ -1999,39 +2114,39 @@ msgstr "At least 1 uppercase letter" msgid "errors.weak-password.too-short" msgstr "At least 8 characters" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL has stopped working. Please reload the page to reset it" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Oops! The canvas context was lost" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Connection error, URL not reacheable" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL does not pass validation." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Last delivery was not successful." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Error on SSL validation." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Timeout" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Unexpected error on validating" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Unexpected status %s" @@ -2051,17 +2166,17 @@ msgstr "Description" msgid "feedback.description-placeholder" msgstr "Please describe the reason of your feedback" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "We're happy to have you here. If you need help, please search before you " "post." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot community" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Other ways to contact us" @@ -2085,11 +2200,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Contact us" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Here to help with your technical queries." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X support account" @@ -2109,55 +2224,97 @@ msgstr "Idea" msgid "feedback.type.issue" msgstr "Issue" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Download files" +#: src/app/main/ui/exports/files.cljs:392 +msgid "files-download-modal.unsaved-changes" +msgstr "" +"Some of your latest changes are not saved yet, so this download will not " +"include them." -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:467 +msgid "files-export-modal.cancel-export" +msgstr "Cancel export" + +#: src/app/main/ui/exports/files.cljs:471 +msgid "files-export-modal.done" +msgstr "Done" + +#: src/app/main/ui/exports/files.cljs:210 +msgid "files-export-modal.export-error" +msgstr "Couldn't export: %s" + +#: src/app/main/ui/exports/files.cljs:246 +msgid "files-export-modal.exported.message" +msgstr "Check your downloads folder." + +#: src/app/main/ui/exports/files.cljs:239 +msgid "files-export-modal.exported.title" +msgid_plural "files-export-modal.exported.title" +msgstr[0] "1 file exported" +msgstr[1] "%s files exported" + +#: src/app/main/ui/exports/files.cljs:245 +msgid "files-export-modal.exporting.message" +msgstr "Each file downloads as soon as it's ready." + +#: src/app/main/ui/exports/files.cljs:238 +msgid "files-export-modal.exporting.title" +msgid_plural "files-export-modal.exporting.title" +msgstr[0] "Exporting 1 file" +msgstr[1] "Exporting %s files" + +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "Linked library assets won't be included in the file." -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "Treat assets as basic objects" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "" "Files with linked libraries will be included in the export, maintaining " "their linkage. " -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "Export file + libraries" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "" "When imported, you'll be able to link existing libraries with matching " "names." -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "Link matching libraries on import" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "Your file will be exported with library asset merged into the local library." -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "Embed library assets in the file" -#: src/app/main/ui/exports/files.cljs -msgid "files-download-modal.unsaved-changes" -msgstr "Some of your latest changes are not saved yet, so this download will not include them." +#: src/app/main/ui/exports/files.cljs:179 +msgid "files-export-modal.status.exported" +msgstr "Exported" + +#: src/app/main/ui/exports/files.cljs:186 +msgid "files-export-modal.status.waiting" +msgstr "Waiting" + +#: src/app/main/ui/exports/files.cljs:381 +msgid "files-export-modal.title" +msgstr "Export file" #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "An error has occurred" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "Import" @@ -2173,7 +2330,7 @@ msgstr "Import shortcuts" msgid "import-shortcuts.imported" msgstr "Imported" -#: src/app/main/ui/settings/shortcuts.cljs:460, src/app/main/ui/settings/shortcuts.cljs:462 +#: src/app/main/ui/settings/shortcuts.cljs:511, src/app/main/ui/settings/shortcuts.cljs:513 msgid "import-shortcuts.invalid-file" msgstr "This file is not a valid shortcuts export." @@ -2183,7 +2340,9 @@ msgstr "No differences found between the current and imported shortcuts." #: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:153 msgid "import-shortcuts.skipped-entries" -msgstr "These entries can't be imported: they don't match any Penpot shortcut or use a reserved key combination." +msgstr "" +"These entries can't be imported: they don't match any Penpot shortcut or " +"use a reserved key combination." #: src/app/main/ui/components/color_input.cljs:31 msgid "inspect.attributes.color" @@ -2217,7 +2376,7 @@ msgstr "Preview of the shape's fill image" msgid "inspect.attributes.shadow" msgstr "Shadow" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Size and position" @@ -2225,43 +2384,31 @@ msgstr "Size and position" msgid "inspect.attributes.stroke" msgstr "Stroke" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Center" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Inside" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Outside" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Typography" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Font Family" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Font Size" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Font Style" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Letter Spacing" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Line Height" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "Mixed Font Families" @@ -2277,11 +2424,11 @@ msgstr "Strikethrough" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Underline" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Capitalize" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Lower Case" @@ -2293,7 +2440,7 @@ msgstr "None" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Unset" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Upper Case" @@ -2305,6 +2452,10 @@ msgstr "Variant properties" msgid "inspect.attributes.variants" msgstr "Variants properties" +#: src/app/main/ui/inspect/code.cljs:322, src/app/main/ui/inspect/code.cljs:365 +msgid "inspect.code.expand-panel" +msgstr "Expand panel" + #: src/app/main/ui/inspect/right_sidebar.cljs:169 msgid "inspect.color-space-label" msgstr "Select color space" @@ -2417,23 +2568,23 @@ msgstr "Resolved value:" msgid "inspect.tabs.styles.variants-panel" msgstr "Variant Properties" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "Create new access token" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "Press the button \"Create new access token\" to generate one." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "You have no tokens so far." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "Personal access tokens" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "Personal access tokens function like an alternative to our login/password " @@ -2518,53 +2669,53 @@ msgstr "The MCP key will expire on %s" msgid "integrations.mcp-key.will-not-expire" msgstr "The MCP key has no expiration date" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "The Penpot MCP Server enables MCP clients to interact directly with Penpot " "design files." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "How to configure MCP clients" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "This is the server url you'll need to configure your MCP client in order to " "connect it to the Penpot MCP server." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "Regenerate MCP key" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "MCP key" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "The MCP key is needed for the MCP client set up" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Status" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Disabled" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Enabled" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "The MCP key used to connect to the MCP server has expired. As a result, the " "connection cannot be established." -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "" "Please regenerate the MCP key and update your client configuration with the " @@ -2574,10 +2725,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "MCP Server" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "Beta" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "Name" @@ -2628,7 +2775,7 @@ msgstr "Regenerate MCP key" msgid "integrations.regenerate-mcp-key.title.created" msgstr "MCP key regenerated" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "Integrations" @@ -2646,47 +2793,116 @@ msgstr "The token will expire on %s" msgid "integrations.token.will-not-expire" msgstr "The token has no expiration date" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/exports/files.cljs:223 +msgid "jobs.export-cancelled" +msgstr "Export cancelled" + +#: src/app/main/data/imports.cljs:376 +msgid "jobs.import-cancelled" +msgstr "Import cancelled" + +#: src/app/main/ui/jobs/progress.cljs:26 +msgid "jobs.progress.stage.colors" +msgstr "Processing the colors" + +#: src/app/main/ui/jobs/progress.cljs:25 +msgid "jobs.progress.stage.components" +msgstr "Processing the components" + +#: src/app/main/ui/jobs/progress.cljs:21 +msgid "jobs.progress.stage.files" +msgstr "Processing the files" + +#: src/app/main/ui/jobs/progress.cljs:32 +msgid "jobs.progress.stage.manifest" +msgstr "Reading the package" + +#: src/app/main/ui/jobs/progress.cljs:23 +msgid "jobs.progress.stage.media" +msgstr "Processing the media" + +#: src/app/main/ui/jobs/progress.cljs:31 +msgid "jobs.progress.stage.metadata" +msgstr "Reading the metadata" + +#: src/app/main/ui/jobs/progress.cljs:22 +msgid "jobs.progress.stage.pages" +msgstr "Processing the pages" + +#: src/app/main/ui/jobs/progress.cljs:30 +msgid "jobs.progress.stage.relations" +msgstr "Processing the library relations" + +#: src/app/main/ui/jobs/progress.cljs:29 +msgid "jobs.progress.stage.storage-objects" +msgstr "Processing the storage objects" + +#: src/app/main/ui/jobs/progress.cljs:24 +msgid "jobs.progress.stage.thumbnails" +msgstr "Processing the thumbnails" + +#: src/app/main/ui/jobs/progress.cljs:28 +msgid "jobs.progress.stage.tokens" +msgstr "Processing the tokens" + +#: src/app/main/ui/jobs/progress.cljs:27 +msgid "jobs.progress.stage.typographies" +msgstr "Processing the typographies" + +#: src/app/main/ui/jobs/progress.cljs:34 +msgid "jobs.progress.stage.unknown" +msgstr "Working" + +#: src/app/main/ui/jobs/progress.cljs:33 +msgid "jobs.progress.stage.upload" +msgstr "Uploading file…" + +#: src/app/main/ui/dashboard/import.cljs:328, src/app/main/ui/exports/files.cljs:103 +#, unused +msgid "jobs.queued" +msgstr "Waiting job slot" + +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Mark all as read" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Shortcuts" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "About Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Accept" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Active" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Add" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Add custom font" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Adding..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Admin" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "All" @@ -2694,17 +2910,17 @@ msgstr "All" msgid "labels.and" msgstr "and" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Back" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Looks like you need to wait a bit and retry; we are performing small " "maintenance of our servers." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Bad Gateway" @@ -2716,23 +2932,23 @@ msgstr "Blur" msgid "labels.blur-effects" msgstr "Blur effects" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Cancel" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "Check for updates" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "Checking for updates..." -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Close" @@ -2740,7 +2956,7 @@ msgstr "Close" msgid "labels.code" msgstr "Code" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Collapse" @@ -2752,15 +2968,15 @@ msgstr "Color" msgid "labels.comment" msgstr "Comment" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Comments" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Community" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Community & Contributions" @@ -2772,19 +2988,19 @@ msgstr "Computed" msgid "labels.confirm-password" msgstr "Confirm password" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Contact support" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Contact us" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:234, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continue" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "You can continue with a Penpot account" @@ -2796,23 +3012,23 @@ msgstr "Copy" msgid "labels.copy-color" msgstr "Copy color" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Copy link" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-present" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Create" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Create new team" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Enter new team name" @@ -2820,7 +3036,7 @@ msgstr "Enter new team name" msgid "labels.current" msgstr "Current" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Dashboard" @@ -2828,7 +3044,7 @@ msgstr "Dashboard" msgid "labels.default" msgstr "Default" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:334, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:263, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Delete" @@ -2840,47 +3056,43 @@ msgstr "Delete comment" msgid "labels.delete-comment-thread" msgstr "Delete thread" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Delete invitation" -#: src/app/main/ui/dashboard/file_menu.cljs:289 +#: src/app/main/ui/dashboard/file_menu.cljs:288 msgid "labels.delete-multi-files" msgstr "Delete %s files" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "Are you sure you want to delete your profile photo?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "Delete profile photo" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Deleted" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Development" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Discard" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:560, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "Download %s" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "Download" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "Downloading file…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Drafts" @@ -2888,31 +3100,35 @@ msgstr "Drafts" msgid "labels.duplicate" msgstr "Duplicate" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Edit" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Editor" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Empty" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Error" -#: src/app/main/data/workspace/versions.cljs:314 +#: src/app/main/data/workspace/versions.cljs:332 msgid "labels.exit" msgstr "Exit" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/viewer/thumbnails.cljs:96 +msgid "labels.expand" +msgstr "Expand" + +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Expired" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Export" @@ -2924,7 +3140,7 @@ msgstr "Feedback disabled" msgid "labels.feedback-sent" msgstr "Feedback sent" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2932,63 +3148,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Fill" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "Flex layout" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Font Family" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Styles" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fonts" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Github repository" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Give feedback" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Go back" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Graphic design" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "Grid layout" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Help Center" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Help & Learning" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Hide" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Hide resolved comments" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Import" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inactive" @@ -2996,59 +3212,59 @@ msgstr "Inactive" msgid "labels.info" msgstr "Info" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Installed fonts" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "Integrations" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Something bad happened." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "You can retry the operation or contact support to report the error." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Internal Error" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Invitations" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Language" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Layout" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Learning Center" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Libraries & Templates" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:133 msgid "labels.loading" msgstr "Loading…" -#: src/app/main/ui/workspace/sidebar/versions.cljs:251 +#: src/app/main/ui/workspace/sidebar/versions.cljs:257 msgid "labels.lock" msgstr "Lock" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Log in or sign up" @@ -3056,19 +3272,19 @@ msgstr "Log in or sign up" msgid "labels.login" msgstr "Login" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Logout" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Member" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Members" @@ -3080,41 +3296,41 @@ msgstr "Mention" msgid "labels.new-password" msgstr "New password" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Next" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "This instance needs a setup step that comes with the latest version." -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "Check out our" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "Penpot can't start yet." -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "technical guide" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "" "Penpot is temporarily unavailable due to a system issue. Please try again " "shortly." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "You're all caught up! New comment notifications will appear here." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "No pending invitations." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Gather your people and build great things together." @@ -3122,27 +3338,27 @@ msgstr "Gather your people and build great things together." msgid "labels.no-matches" msgstr "No matches" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Oops!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notifications" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 file" msgstr[1] "%s files" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 board" msgstr[1] "%s boards" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 project" @@ -3152,43 +3368,43 @@ msgstr[1] "%s projects" msgid "labels.old-password" msgstr "Old password" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "Open" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Options" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Other (specify)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Other" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Owner" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Password" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Pending" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Penpot Changelog" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot hub" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Pinned Projects" @@ -3196,31 +3412,31 @@ msgstr "Pinned Projects" msgid "labels.post" msgstr "Post" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Previous" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Product or UX design" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Product Management" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profile" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projects" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Recent" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Reference" @@ -3228,27 +3444,27 @@ msgstr "Reference" msgid "labels.refresh" msgstr "Refresh" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Release notes" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:666, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Reload page" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Remove" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Remove member" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:297, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:234, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Rename" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Rename team" @@ -3272,55 +3488,55 @@ msgstr "new reply" msgid "labels.reply.thread" msgstr "Reply" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Resend" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Resend invitation" -#: src/app/main/data/workspace/versions.cljs:246, src/app/main/data/workspace/versions.cljs:319, src/app/main/ui/workspace/sidebar/versions.cljs:238 +#: src/app/main/data/workspace/versions.cljs:260, src/app/main/data/workspace/versions.cljs:337, src/app/main/ui/workspace/sidebar/versions.cljs:244 msgid "labels.restore" msgstr "Restore" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Retry" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Role" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Save" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Search" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Search font" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Select option" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Send" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Sending…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "We are in programmed maintenance of our systems." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Service Unavailable" @@ -3328,74 +3544,74 @@ msgstr "Service Unavailable" msgid "labels.sets" msgstr "Sets" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Settings" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Shadow" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Share" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Libraries" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Show" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Show all comments" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Show comments list" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Show only your mentions" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Show only your comments" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/import.cljs:703 +#: src/app/main/ui/dashboard/import.cljs:808 msgid "labels.skip" msgstr "Skip" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Sources" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "" "Sign-in with your organization's identity provider didn't complete. The " "provider may be unavailable, or your account may not be in its directory " "yet." -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "Try again" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "We couldn't sign you in to %s" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Start" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" @@ -3403,7 +3619,7 @@ msgstr "Status" msgid "labels.stroke" msgstr "Stroke" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Student or teacher" @@ -3415,11 +3631,11 @@ msgstr "Styles" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Switch" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:657 msgid "labels.team-management" msgstr "Team management" @@ -3427,59 +3643,55 @@ msgstr "Team management" msgid "labels.text" msgstr "Text" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Themes" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutorials" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Typography" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Unknown error" -#: src/app/main/ui/workspace/sidebar/versions.cljs:245 +#: src/app/main/ui/workspace/sidebar/versions.cljs:251 msgid "labels.unlock" msgstr "Unlock" -#: src/app/main/ui/dashboard/file_menu.cljs:283 +#: src/app/main/ui/dashboard/file_menu.cljs:282 msgid "labels.unpublish-multi-files" msgstr "Unpublish %s files" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Update" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Update team" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Upload" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Upload custom fonts" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Uploading…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "Uploading file…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variant" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Version %s notes" @@ -3487,7 +3699,7 @@ msgstr "Version %s notes" msgid "labels.view-only" msgstr "View only" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Viewer" @@ -3501,7 +3713,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s warning" msgstr[1] "%s warnings" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -3509,11 +3721,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Write new comment" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(you)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Your account" @@ -3597,6 +3809,44 @@ msgstr "Extend Penpot with community-built plugins for extra functionality." msgid "loader.tips.10.title" msgstr "Plugins Support" +#: src/app/main/ui/mcp_authorize.cljs:155 +msgid "mcp.oauth.access" +msgstr "" +"This client can read and change designs through MCP connections established " +"from Penpot." + +#: src/app/main/ui/mcp_authorize.cljs:153 +msgid "mcp.oauth.account" +msgstr "You are signed in as %s." + +#: src/app/main/ui/mcp_authorize.cljs:162 +msgid "mcp.oauth.approve" +msgstr "Allow access" + +#: src/app/main/ui/mcp_authorize.cljs:133 +msgid "mcp.oauth.cancelled" +msgstr "Connection cancelled" + +#: src/app/main/ui/mcp_authorize.cljs:157 +msgid "mcp.oauth.enable" +msgstr "" +"Allowing access enables the MCP integration and creates your MCP token if " +"needed." + +#: src/app/main/ui/mcp_authorize.cljs:137 +msgid "mcp.oauth.failed" +msgstr "" +"This request has expired, was already used, or could not be completed. " +"Start the connection again from your MCP client." + +#: src/app/main/ui/mcp_authorize.cljs:149 +msgid "mcp.oauth.request" +msgstr "%s wants to connect to Penpot." + +#: src/app/main/ui/mcp_authorize.cljs:129 +msgid "mcp.oauth.title" +msgstr "Connect to Penpot MCP" + #: src/app/main/ui/workspace/colorpicker.cljs:494 msgid "media.choose-image" msgstr "Choose image" @@ -3652,7 +3902,7 @@ msgstr "" "You are the only member of some of the teams. Those teams will be deleted " "along with its projects and files." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "BEFORE YOU LEAVE THE ORGANIZATION" @@ -3660,78 +3910,78 @@ msgstr "BEFORE YOU LEAVE THE ORGANIZATION" msgid "modals.before-leave-organization.warning" msgstr "Any team where you’re the only member will be deleted." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Big nudge" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Confirm new email" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "We'll send you an email to your current email “%s” to verify your identity." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "New email" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Change email" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Change your email" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Create webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Create webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Payload URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Cancel and keep my account" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Yes, delete my account" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "By removing your account you’ll lose all your current projects and archives." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "This action is irreversible. Your account, subscription, and organizations " "will be deleted immediately. You will also lose access to your current " "projects and files." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organizations that will be deleted" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s members" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s teams" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Are you sure you want to delete your account?" @@ -3767,47 +4017,47 @@ msgstr "Are you sure you want to delete this annotation?" msgid "modals.delete-component-annotation.title" msgstr "Delete annotation" -#: src/app/main/ui/dashboard/file_menu.cljs:155 +#: src/app/main/ui/dashboard/file_menu.cljs:154 msgid "modals.delete-file-confirm.accept" msgstr "Delete file" -#: src/app/main/ui/dashboard/file_menu.cljs:154 +#: src/app/main/ui/dashboard/file_menu.cljs:153 msgid "modals.delete-file-confirm.message" msgstr "Are you sure you want to delete this file?" -#: src/app/main/ui/dashboard/file_menu.cljs:153 +#: src/app/main/ui/dashboard/file_menu.cljs:152 msgid "modals.delete-file-confirm.title" msgstr "Deleting file" -#: src/app/main/ui/dashboard/file_menu.cljs:149 +#: src/app/main/ui/dashboard/file_menu.cljs:148 msgid "modals.delete-file-multi-confirm.accept" msgstr "Delete files" -#: src/app/main/ui/dashboard/file_menu.cljs:148 +#: src/app/main/ui/dashboard/file_menu.cljs:147 msgid "modals.delete-file-multi-confirm.message" msgstr "Are you sure you want to delete %s files?" -#: src/app/main/ui/dashboard/file_menu.cljs:147 +#: src/app/main/ui/dashboard/file_menu.cljs:146 msgid "modals.delete-file-multi-confirm.title" msgstr "Deleting %s files" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Are you sure you want to delete this font style? It will not load if is " "used in a file." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Deleting font style" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Are you sure you want to delete this font? It will not load if is used in a " "file." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Deleting font" @@ -3815,19 +4065,19 @@ msgstr "Deleting font" msgid "modals.delete-organization-team-confirm.message" msgstr "Are you sure you want to delete this team that is part of %s org?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Are you sure you want to delete this page?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Delete page" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "Are you sure you want to delete the selected pages?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "Delete pages" @@ -3843,31 +4093,31 @@ msgstr "Are you sure you want to delete this project?" msgid "modals.delete-project-confirm.title" msgstr "Delete project" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Delete file" msgstr[1] "Delete files" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "It isn't activated in any file." msgstr[1] "They aren't activated in any file." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "This library is activated here: " msgstr[1] "This libraries are activated here: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Are you sure you want to delete this file?" msgstr[1] "Are you sure you want to delete these files?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Deleting file" @@ -3887,19 +4137,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Deleting team" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Delete member" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Are you sure you want to delete this member from the team?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Delete team member" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3909,23 +4159,23 @@ msgstr[1] "" "Assets that have already been used in those files will remain there (no " "design will be broken)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Delete webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Are you sure you want to delete this webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Deleting webhook" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Edit webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Edit webhook" @@ -3941,15 +4191,15 @@ msgstr "Import tokens" msgid "modals.import-library-tokens.title" msgstr "Import tokens from library?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Send invitation" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Emails, comma separated" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "Some members are already on the team. We'll invite the rest." @@ -3970,99 +4220,99 @@ msgstr "" "are all already team members. If you need more information, contact the " "organization's owner." -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "Addresses that won't receive an invitation" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "Cancel" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "Only existing members of the organization can be invited." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "Send invites" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "Some invitations can't be sent" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "Some people on your list won't receive the invitations." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "You can invite members to the team so they can access to this file and all " "of the team files." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Invite members to the team" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "As you're the only member of this team, the team will be deleted along with " "its projects and files." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Are you sure you want to leave the %s team?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "You can not leave the team if there is no other member to promote to owner. " "You might want to delete the team." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "You are the owner of this team. Please select another member to promote to " "owner before you leave." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promote and leave" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Select a member to promote" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Before you leave" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Leave team" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Are you sure you want to leave this team?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Leaving team" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "" "You are the owner of some organization's teams. Please promote another " "member to become an owner." -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "You are the only member of some of the teams. Those teams will be deleted " "along with its projects and files." -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "Also, you are the owner of some organization's teams. Please promote " @@ -4082,57 +4332,57 @@ msgstr "" msgid "modals.leave-organization-confirm.title" msgstr "Leaving %s organization?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Move" msgstr[1] "Move" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Are you sure you want to move this library?" msgstr[1] "Are you sure you want to move these libraries?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Move library" msgstr[1] "Move libraries" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Nudge amount" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Transfer ownership" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "If you transfer the ownership, you will change your role to Admin, losing " "some permissions over this team. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "You are the current owner of this team. Are you sure you want to make %s " "the new owner of the team?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "New team owner" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publish" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Your library is empty. Are you sure you want to publish it?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publish empty library" @@ -4154,23 +4404,23 @@ msgstr "Are you sure you want to remove the '%s' team from the '%s' organization msgid "modals.remove-team-organization.title" msgstr "REMOVE TEAM FROM THE ORGANIZATION" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Small nudge" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Unpublish" msgstr[1] "Unpublish" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Are you sure you want to unpublish this library?" msgstr[1] "Are you sure you want to unpublish these libraries?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Unpublish library" @@ -4260,37 +4510,37 @@ msgstr "Activate" msgid "nitrate.code-activation.title" msgstr "Activate Enterprise" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "Contact sales" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "" "To switch to this plan, please contact our sales team. We'll help you " "update your subscription and ensure everything is set up correctly." -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Your organization will be deleted." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "" "Your total storage, auto-version history, and file recovery period will be " "limited." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "" "The teams, projects and files will no longer be part of any organization " "but they will remain available." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "When you downgrade:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "Switch to %s plan?" @@ -4306,15 +4556,15 @@ msgstr "Contact us to try Enterprise for 14 days:" msgid "nitrate.form.contact-upgrade" msgstr "Contact us to upgrade to Enterprise:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "Contact us to try the Enterprise plan for 14 days:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "Contact us to upgrade to the Enterprise plan:" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "" "You won’t be charged right now. Payment will be processed at the end of the " @@ -4334,7 +4584,7 @@ msgstr "" msgid "nitrate.form.enterprise-feature-3" msgstr "Full open-source freedom, with the governance your org needs" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "Everything your teams need to work securely at scale%s" @@ -4342,7 +4592,7 @@ msgstr "Everything your teams need to work securely at scale%s" msgid "nitrate.form.enterprise.price" msgstr "per member per month" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "Try 14 days for free" @@ -4358,7 +4608,7 @@ msgstr "Get started with Enterprise" msgid "nitrate.form.subscribe-with-code" msgstr "Subscribe with an activation code" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "Unlock Enterprise features" @@ -4370,7 +4620,7 @@ msgstr "Try it free for 14 days" msgid "nitrate.modal-success.title" msgstr "Welcome to Enterprise!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "Active until %s" @@ -4378,23 +4628,23 @@ msgstr "Active until %s" msgid "nitrate.subscription.renew-success" msgstr "Your Enterprise subscription has been renewed." -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "Cancel subscription" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "To cancel your Enterprise subscription, please contact us at:" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "Renew with activation code" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "This page doesn't exist" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 error" @@ -4412,11 +4662,11 @@ msgstr "We have sent a recovery email to" msgid "not-found.login.sent-recovery-check" msgstr "Check your email and click on the link to create a new password." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Sign up for free" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "And start using Penpot in seconds!" @@ -4424,69 +4674,69 @@ msgstr "And start using Penpot in seconds!" msgid "not-found.made-with-love" msgstr "Made with LOVE and Open Source" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "You have already requested access to this file." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "You have already requested access to this file or other files or projects " "of this team." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "You have already requested access to this project or other projects or " "files of this team." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "You have already requested access to this project." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "REQUEST ACCESS" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "" "Remember that, if the owner allows it, you're going to be invited to the " "team." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Your request has been sent correctly!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "You don't have access to this file." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Go to Personal Projects" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "If the owner allows it, you're going to be invited to the team." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot file" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "You don't have access to this project." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJECT" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "To access this file, you can ask the team owner." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "To access this project, you can ask the team owner." @@ -4498,31 +4748,31 @@ msgstr "Maintenance break: we will be down for a short maintenance within 5 minu msgid "notifications.by-code.upgrade-version" msgstr "A new version is available." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "This invitation is no longer available." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Invitation deleted successfully" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Invitation sent successfully" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Invitation link copied" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "You can't delete your profile. Reassign your teams before proceed." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profile saved successfully!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Verification email sent to %s. Check your email!" @@ -4558,7 +4808,7 @@ msgstr "Team name" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Create team" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Invite members" @@ -4568,98 +4818,98 @@ msgstr "" "Developers, designers, managers... everyone's welcome. You can invite more " "later." -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Invite with the role:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Let's get started!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Generating real code from designs" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Creating Design Systems" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototyping" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Design the UI/UX of an app" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "What will you be using Penpot for?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Let us know a bit about you to help us make Penpot work for you. Your " "answers will help us prioritize new features and point you in the right " "direction to get started." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Help us get to know you" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Which one of these tools do you use the most?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "What kind of work do you do?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "What is your company size?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Where would you like to get started?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1,001 - 5,000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 - 500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 - 100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 - 1,000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "Just me" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5,001+" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Education" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Personal" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Work" @@ -4709,15 +4959,15 @@ msgstr "Start designing" msgid "options.deleted-token-with-name" msgstr "{%s} token does not exist or has been deleted." -#: src/app/plugins/utils.cljs:555 +#: src/app/plugins/utils.cljs:579 msgid "plugins.validation.invalid-value" msgstr "expected %s, got %s (%s)" -#: src/app/plugins/utils.cljs:569 +#: src/app/plugins/utils.cljs:593 msgid "plugins.validation.message" msgstr "Field %s is invalid: %s" -#: src/app/plugins/utils.cljs:558 +#: src/app/plugins/utils.cljs:582 msgid "plugins.validation.received-value" msgstr "got %s (%s)" @@ -4741,11 +4991,11 @@ msgstr "Reset all shortcuts to default" msgid "restore-shortcuts.restore" msgstr "Reset" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Detach" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Mixed" @@ -4757,35 +5007,35 @@ msgstr "Remove color" msgid "settings.select-this-color" msgstr "Select items using this style" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Comments" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Align bottom" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Align center horizontally" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Align left" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Align right" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Align top" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Align center vertically" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Create board from selection" @@ -4793,55 +5043,55 @@ msgstr "Create board from selection" msgid "shortcuts.bold" msgstr "Toggle bold" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Boolean difference" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Boolean exclude" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Boolean intersection" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Boolean union" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Send to the back" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Send backward" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Bring forward" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Bring to the front" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Clear undo" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copy" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Copy link" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Copy properties" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Create component / variant" @@ -4849,63 +5099,63 @@ msgstr "Create component / variant" msgid "shortcuts.create-new-project" msgstr "Create new" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Cut" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Zoom out" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Delete" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Delete node" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "Delete node and segments" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "Deselect all" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Detach component" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "Disable shortcut" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "Disabled" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "Disabled shortcut" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "Arrow" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Curve" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Ellipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Board" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "Line" @@ -4913,71 +5163,71 @@ msgstr "Line" msgid "shortcuts.draw-nodes" msgstr "Draw path" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Path" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rectangle" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Text" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplicate" -#: src/app/main/ui/shortcuts.cljs:589 +#: src/app/main/ui/shortcuts.cljs:714 msgid "shortcuts.edit" msgstr "Edit %s" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "Already assigned to \"%s\". Saving will move it to this action." -#: src/app/main/ui/shortcuts.cljs:836 +#: src/app/main/ui/shortcuts.cljs:771 msgid "shortcuts.edit-modal.reserved" msgstr "This combination is reserved by Penpot and can't be assigned." -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "Saving will reset this shortcut to default:" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "Shortcut available." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "Customize shortcuts" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Cancel" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Export shapes" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "Find" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "Find and replace" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Zoom to fit all" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Flip horizontally" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Flip vertically" @@ -5001,27 +5251,27 @@ msgstr "Go to shared libraries" msgid "shortcuts.go-to-search" msgstr "Search" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Group" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribute horizontally" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Show / Hide UI" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "Import/Export" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Zoom in" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Insert image" @@ -5029,11 +5279,11 @@ msgstr "Insert image" msgid "shortcuts.italic" msgstr "Toggle italic" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Join nodes" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "Press the new keys" @@ -5041,39 +5291,39 @@ msgstr "Press the new keys" msgid "shortcuts.line-through" msgstr "Toggle line through" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Make corner" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Make curve" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Mask" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Merge nodes" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Move" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Move down fast" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Move left fast" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Move right fast" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Move up fast" @@ -5081,161 +5331,161 @@ msgstr "Move up fast" msgid "shortcuts.move-nodes" msgstr "Move node" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Move down" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Move left" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Move right" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Move up" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Next board" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "No disabled shortcuts." -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "No custom shortcuts yet. Customize one from the All tab." -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "No shortcuts match your search." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "No shortcuts match your search." -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Set opacity to 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Set opacity to 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Set opacity to 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Set opacity to 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Set opacity to 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Set opacity to 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Set opacity to 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Set opacity to 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Set opacity to 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Set opacity to 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Color picker" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Go to viewer comment section" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Go to dashboard" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Go to viewer inspect section" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Go to viewer interactions section" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Go to viewer interactions section" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Go to workspace" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " or " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "Reassigned to \"%s\"" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "Shortcuts page" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Paste" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Paste properties" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "Paste and replace" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "Custom" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Plugins manager" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Previous board" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Redo" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "" "After editing shortcuts, reload any other open Penpot tabs to apply the " "changes." -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Rename" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "Reset to default" @@ -5243,225 +5493,225 @@ msgstr "Reset to default" msgid "shortcuts.reset-all" msgstr "Reset all" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Reset zoom" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Scale" -#: src/app/main/ui/settings/shortcuts.cljs:44, src/app/main/ui/workspace/sidebar/shortcuts.cljs:139 +#: src/app/main/ui/settings/shortcuts.cljs:44, src/app/main/ui/workspace/sidebar/shortcuts.cljs:143 msgid "shortcuts.search" msgstr "Search shortcuts" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Basics" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Dashboard" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Viewer" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Workspace" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Select all" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Select next layer" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Select parent layer" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Select previous layer" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Separate nodes" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "Show measurements" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Show / Hide pixel grid" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Show / Hide shortcuts" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Snap to nodes" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Snap to pixel grid" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Start editing" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Alignment" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Edit" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Generic" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Main menu" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modify layers" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panels" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Paths" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Shapes" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Text" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Tools" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Set thumbnails" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Keyboard shortcuts" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Toggle dynamic alignment" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Toggle assets" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Toggle color palette" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "Toggle comments visibility" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Toggle focus mode" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Toggle fullscreen" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Show / Hide guides" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Toggle history" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Toggle layers" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Add / Remove flex layout" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Add/remove grid layout" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Lock / Unlock" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Lock proportions" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Show / Hide rulers" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Snap to guides" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Snap to ruler guides" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Toggle text palette" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Change theme" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Show / Hide" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Toggle zoom style" @@ -5469,31 +5719,31 @@ msgstr "Toggle zoom style" msgid "shortcuts.underline" msgstr "Toggle underline" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Undo" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Ungroup" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Unmask" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribute vertically" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Zoom lense decrease" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Zoom lense increase" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zoom to selected" @@ -5512,7 +5762,7 @@ msgstr "See Enterprise in action" msgid "subscription.current-plan.enterprise" msgstr "Enterprise" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Enterprise" @@ -5524,7 +5774,7 @@ msgstr "Enterprise Trial" msgid "subscription.current-plan.professional" msgstr "Professional" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "Your subscription" @@ -5651,277 +5901,278 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Inviting people while on the Unlimited plan" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "Payment was not completed. Try again whenever you're ready." -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "" "The payment was not completed. Please try again. If the problem persists, " "contact us: support@penpot.app." -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Subscription" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "Enter activation code" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Add a payment method to continue after your trial" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "Admin Console" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "All Professional plan benefits and:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "All Unlimited plan benefits and:" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "All Unlimited plan benefits, plus:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s editors)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise (trial)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90-day autosave versions and file recovery" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Flat monthly bill" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "Advanced Control:" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "Enterprise Security:" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Multi-Org Management:" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "Native SAML/​​SSO and full Audit Trail logging for compliance reporting." -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "" "Granular plugin whitelisting, including our secure, isolated MCP Server for " "AI connectivity." -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "Support for complex team hierarchies and separate sub-organizations." -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Unlimited storage" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Manage your subscription" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Add payment details" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Add your payment details now to keep your subscription running smoothly " "after the trial and continue supporting our open-source project. You won’t " "be charged yet." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Skip for now and start trial" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Help us grow and make your trial easier" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Currently, your have %s person across your teams who can edit." msgstr[1] "Currently, your have %s people across your teams who can edit." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Heads up: switching to a lower plan means less storage and shorter backups " "and version history." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Editors" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(Owners, Admin and Editors. Viewers doesn't count as Editors)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "You can't set fewer editors than you have now. Change the role " "(editor/admin to viewer) for people who don't actually edit files in the " "team settings." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "Charged after trial. No credit card required right now." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/month" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Apply %s to your teams" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Tip: You can increase your seat count now to stay ahead of invites. At 25+ " "editors across teams, you’ll enjoy a flat $175/month." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Penpot member since: %s." -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "More information" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "org member / month" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Other penpot plans" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "organization per month" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "user / month" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professional" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7-day autosave versions and file recovery" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "All design and prototyping features Community support." -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "Complete control over data and infrastructure." -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "Unlimited users, teams and files." -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10GB of storage" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Unlimited teams. Up to 8 editors across your owned teams." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Recommended" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Your subscription" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Start free trial" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Subscribe" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "You've been on the %s plan since %s." -msgid "subscription.settings.trial-ends-on" -msgstr "Your free trial ends on %s." - -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "You can edit your subscription at any time from the 'Subscription' page in " "your account details." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Enjoy your plan!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Thank your for chosing the Penpot %s plan!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "You are %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:724 +msgid "subscription.settings.trial-ends-on" +msgstr "Your free trial ends on %s." + +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Try it free for 14 days" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Unlimited" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unlimited (trial)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30-day autosave versions and file recovery" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Capped monthly bill at $175" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GB of storage" @@ -5946,45 +6197,45 @@ msgstr "" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "If you'd like to increase this limit, [upgrade your plan|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 invitation selected" msgstr[1] "%s invitations selected" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Font Providers - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Fonts - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projects - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Search - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Shared Libraries - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot | Full-stack design" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Give feedback - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "Integrations - Penpot" @@ -5992,7 +6243,7 @@ msgstr "Integrations - Penpot" msgid "title.settings.notifications" msgstr "Notifications - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Settings - Penpot" @@ -6000,31 +6251,31 @@ msgstr "Settings - Penpot" msgid "title.settings.password" msgstr "Password - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profile - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Invitations - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Members - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Settings - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - View mode - Penpot" -#: src/app/main/ui/workspace.cljs:240 +#: src/app/main/ui/workspace.cljs:241 msgid "title.workspace" msgstr "%s - Penpot" @@ -6032,54 +6283,66 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Detach token" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "No boards found on the page." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Board not found." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer.cljs:127 +msgid "viewer.go-to-first-board" +msgstr "Go to the first board" + +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Comments (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Don't show interactions" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Full Screen" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspect (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interactions" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interactions (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Copy link" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Show interactions" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Show interactions on click" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Sitemap" +#: src/app/main/ui/viewer/thumbnails.cljs:70 +msgid "viewer.thumbnails.scroll-left" +msgstr "Scroll left" + +#: src/app/main/ui/viewer/thumbnails.cljs:76 +msgid "viewer.thumbnails.scroll-right" +msgstr "Scroll right" + #: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:76 msgid "webgl.modals.webgl-unavailable.cta-dashboard" msgstr "Go to dashboard" @@ -6112,67 +6375,67 @@ msgstr "Follow our " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "WebGL troubleshooting guide" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "WebGL rendering disabled" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "WebGL rendering enabled" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "WebGL context was lost" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "WebGL context was recovered" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Last delivery was successful." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Align horizontal center (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribute horizontal spacing (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Align left (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Align right (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Align bottom (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Align vertical center (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribute vertical spacing (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Align top (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Add library" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "All assets" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Colors" @@ -6184,7 +6447,7 @@ msgstr "Add color" msgid "workspace.assets.component-group-options" msgstr "Component group options" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Components" @@ -6192,7 +6455,7 @@ msgstr "Components" msgid "workspace.assets.components.add-component" msgstr "Add component" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s Variants" @@ -6200,11 +6463,11 @@ msgstr "%s Variants" msgid "workspace.assets.create-group" msgstr "Create a group" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Your items are going to be named automatically as \"group name / item name\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Delete" @@ -6212,11 +6475,11 @@ msgstr "Delete" msgid "workspace.assets.delete-group" msgstr "Delete group" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "Delete pages" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplicate" @@ -6224,39 +6487,39 @@ msgstr "Duplicate" msgid "workspace.assets.duplicate-main" msgstr "Duplicate main" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Edit" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filter" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Grid view" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Group" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Group name" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "List view" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "local library" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Manage library" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "No assets found" @@ -6264,7 +6527,7 @@ msgstr "No assets found" msgid "workspace.assets.open-library" msgstr "Open library file" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Rename" @@ -6272,11 +6535,11 @@ msgstr "Rename" msgid "workspace.assets.rename-group" msgstr "Rename group" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Search assets" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Shared library" @@ -6286,47 +6549,47 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 component" msgstr[1] "%s components" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Sort" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Typographies" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Add typography" -#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:461 -msgid "workspace.assets.typography.multiple-texts-selected" -msgstr "Select one text to create a typography style" - -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Size" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Font Style" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Go to style library file to edit" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Letter Spacing" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Line Height" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:297 +msgid "workspace.assets.typography.multiple-texts-selected" +msgstr "Select one text to create a typography style" + +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Text Transform" @@ -6334,15 +6597,15 @@ msgstr "Text Transform" msgid "workspace.assets.ungroup" msgstr "Ungroup" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "Copying image…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "Image copied to the clipboard" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "Error copying image" @@ -6354,85 +6617,85 @@ msgstr "Pick color from screen" msgid "workspace.colorpicker.color-tokens" msgstr "Color tokens" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Components can't be nested inside themselves." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "Some copies could not be switched. Components can't be nested inside " "themselves." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Create area" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "Copy columns" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "Copy rows" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Create board" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Merge cells" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "Paste" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Add 1 column to the right" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Add 1 column to the left" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Delete column" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Delete column and shapes" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Duplicate column" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Add 1 row below" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Add 1 row above" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Delete row" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Delete row and shapes" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Duplicate row" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "Guide color" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "Remove guide" @@ -6440,19 +6703,75 @@ msgstr "Remove guide" msgid "workspace.debug.title" msgstr "Debugging tools" -#: src/app/main/data/exports/assets.cljs +#: src/app/main/data/exports/assets.cljs:388, src/app/main/data/exports/assets.cljs:564 msgid "workspace.export.unsaved-changes" -msgstr "Some of your latest changes are not saved yet, so this export may not include them." +msgstr "" +"Some of your latest changes are not saved yet, so this export may not " +"include them." -#: src/app/main/ui/workspace/sidebar/layers.cljs:907 +#: src/app/main/data/exports/assets.cljs:370, src/app/main/data/exports/assets.cljs:612 +msgid "workspace.export.unsaved-changes-included" +msgstr "Some of your latest changes are not saved yet. This export includes them." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:59 +msgid "workspace.file-restored.accept" +msgstr "Got it" + +#: src/app/main/ui/workspace/file_restored_modal.cljs:30 +msgid "workspace.file-restored.message" +msgstr "" +"%s restored an earlier version of this file. Changes made after it are no " +"longer in the file." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:31 +msgid "workspace.file-restored.message-anonymous" +msgstr "" +"A teammate restored an earlier version of this file. Changes made after it " +"are no longer in the file." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:28 +msgid "workspace.file-restored.message-date" +msgstr "" +"%s restored the version from %s. Changes made after it are no longer in the " +"file." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:29 +msgid "workspace.file-restored.message-date-anonymous" +msgstr "" +"A teammate restored the version from %s. Changes made after it are no " +"longer in the file." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:26 +msgid "workspace.file-restored.message-label" +msgstr "" +"%s restored the version “%s”. Changes made after it are no longer in the " +"file." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:27 +msgid "workspace.file-restored.message-label-anonymous" +msgstr "" +"A teammate restored the version “%s”. Changes made after it are no longer " +"in the file." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:50 +msgid "workspace.file-restored.title" +msgstr "This file was restored" + +#: src/app/main/ui/workspace/file_restored_modal.cljs:55 +msgid "workspace.file-restored.unsaved-changes" +msgstr "" +"Your latest changes couldn't be saved because a version of the file was " +"restored." + +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Focus mode" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Focus off" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Focus on" @@ -6464,247 +6783,251 @@ msgstr "Linear gradient" msgid "workspace.gradients.radial" msgstr "Radial gradient" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Disable dynamic alignment" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Disable proportional scale" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Disable snap to guides" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Disable snap to pixel" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Disable snap to ruler guides" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "Disable WebGL rendering (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Enable dynamic alignment" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Enable proportional scale" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Snap to guides" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Enable snap to pixel" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Snap to ruler guides" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "Enable WebGL rendering (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "Find" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "Find and Replace" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Hide board names" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "Hide comments" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Hide guides" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Hide color palette" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Hide pixel grid" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Hide rulers" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Hide fonts palette" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "Lock guides" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "Connect" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "Disconnect" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "Manage (Status: disabled)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "Manage (Status: enabled)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Edit" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "File" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Help & info" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "MCP server" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferences" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "View" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Redo" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Select all" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Show boards names" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "Show comments" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Show guides" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Show color palette" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Show pixel grid" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Show rulers" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Show fonts palette" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Switch to dark theme" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Switch to light theme" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Switch to system theme" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Undo" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "Unlock guides" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Reset" -#: src/app/main/ui/workspace/left_header.cljs:125 +#: src/app/main/ui/workspace/left_header.cljs:133 msgid "workspace.header.retrying" msgstr "Retrying to save" -#: src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Error on saving" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Saved" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Saving" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Share" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "View mode (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoom" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Fill - Scale to fill" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Fit - Scale down to fit" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Zoom to fit all" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Full screen" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zoom to selected" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1050 +msgid "workspace.layout-grid.editor.columns" +msgstr "Columns" + +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Edit grid" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Exit" @@ -6712,7 +7035,7 @@ msgstr "Exit" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Bottom padding" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Show 4 sided padding options" @@ -6736,6 +7059,18 @@ msgstr "Top padding" msgid "workspace.layout-grid.editor.padding.vertical" msgstr "Vertical padding" +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1051 +msgid "workspace.layout-grid.editor.rows" +msgstr "Rows" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:994 +msgid "workspace.layout-grid.editor.select-column" +msgstr "Select column" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:995 +msgid "workspace.layout-grid.editor.select-row" +msgstr "Select row" + #: src/app/main/ui/workspace/viewport/grid_layout_editor.cljs:61 msgid "workspace.layout-grid.editor.title" msgstr "Editing grid" @@ -6748,45 +7083,49 @@ msgstr "Done" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Locate" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Locate grid layout" -#: src/app/main/ui/workspace/libraries.cljs:898 +#: src/app/main/ui/workspace/viewport/grid_layout_editor.cljs:932 +msgid "workspace.layout-grid.editor.track-options" +msgstr "Track options" + +#: src/app/main/ui/workspace/libraries.cljs:921 msgid "workspace.libraries.add-shared-library" msgstr "Add a shared library" -#: src/app/main/ui/workspace/libraries.cljs:801 +#: src/app/main/ui/workspace/libraries.cljs:823 msgid "workspace.libraries.assets-in-this-file" msgstr "ASSETS IN THIS FILE" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 color" msgstr[1] "%s colors" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "Add color to library" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "There are no color styles in your library yet" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "There are no recent colors yet" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "There are no typography styles in your library yet" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "File library" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Recent colors" @@ -6798,99 +7137,99 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Save color style" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "Toggle color palette" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 component" msgstr[1] "%s components" -#: src/app/main/ui/workspace/libraries.cljs:842 +#: src/app/main/ui/workspace/libraries.cljs:864 msgid "workspace.libraries.connected-libraries" msgstr "Connected library" -#: src/app/main/ui/workspace/libraries.cljs:183 +#: src/app/main/ui/workspace/libraries.cljs:235 msgid "workspace.libraries.connected-through" msgstr "Connected through" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Connected to" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Or add some of these to try:" -#: src/app/main/ui/workspace/libraries.cljs:812 +#: src/app/main/ui/workspace/libraries.cljs:834 msgid "workspace.libraries.empty.no-assets" msgstr "No assets yet" -#: src/app/main/ui/workspace/libraries.cljs:895 +#: src/app/main/ui/workspace/libraries.cljs:918 msgid "workspace.libraries.empty.no-connected-libraries" msgstr "No connected libraries yet." -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "There are no Shared Libraries at your team, you can look for" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "some templates in here" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "File library" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 graphic" msgstr[1] "%s graphics" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "LIBRARIES IN THIS FILE" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "LIBRARIES" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "LIBRARY UPDATES" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Loading…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "There are no Shared Libraries that need update" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "No matches found for “%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Search shared libraries" -#: src/app/main/ui/workspace/libraries.cljs:827, src/app/main/ui/workspace/libraries.cljs:874 +#: src/app/main/ui/workspace/libraries.cljs:849, src/app/main/ui/workspace/libraries.cljs:897 msgid "workspace.libraries.set-as-tokens-source" msgstr "Set as tokens source" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "SHARED LIBRARIES" -#: src/app/main/ui/workspace/libraries.cljs:621 +#: src/app/main/ui/workspace/libraries.cljs:674 msgid "workspace.libraries.shared-library-added" msgstr "Added" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Connect library" @@ -6914,51 +7253,51 @@ msgstr "Unlink all assets" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "Detach all tokens" -#: src/app/main/ui/workspace/libraries.cljs:1136 +#: src/app/main/ui/workspace/libraries.cljs:1159 msgid "workspace.libraries.this-file" msgstr "This file" -#: src/app/main/ui/workspace/libraries.cljs:167, src/app/main/ui/workspace/libraries.cljs:211 +#: src/app/main/ui/workspace/libraries.cljs:219, src/app/main/ui/workspace/libraries.cljs:263 msgid "workspace.libraries.token-sets" msgid_plural "workspace.libraries.token-sets" msgstr[0] "1 set" msgstr[1] "%s sets" -#: src/app/main/ui/workspace/libraries.cljs:170, src/app/main/ui/workspace/libraries.cljs:215 +#: src/app/main/ui/workspace/libraries.cljs:222, src/app/main/ui/workspace/libraries.cljs:267 msgid "workspace.libraries.token-themes" msgid_plural "workspace.libraries.token-themes" msgstr[0] "1 theme" msgstr[1] "%s themes" -#: src/app/main/ui/workspace/libraries.cljs:164, src/app/main/ui/workspace/libraries.cljs:207 +#: src/app/main/ui/workspace/libraries.cljs:216, src/app/main/ui/workspace/libraries.cljs:259 msgid "workspace.libraries.tokens" msgid_plural "workspace.libraries.tokens" msgstr[0] "1 token" msgstr[1] "%s tokens" -#: src/app/main/ui/workspace/libraries.cljs:820, src/app/main/ui/workspace/libraries.cljs:866 +#: src/app/main/ui/workspace/libraries.cljs:842, src/app/main/ui/workspace/libraries.cljs:889 msgid "workspace.libraries.tokens-source" msgstr "Tokens source" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 typography" msgstr[1] "%s typographies" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Disconnect library" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Update" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "see all changes" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "UPDATES" @@ -6966,11 +7305,18 @@ msgstr "UPDATES" msgid "workspace.notification-pill.detail" msgstr "Details" -#: src/app/main/data/workspace/notifications.cljs:282 +#: src/app/main/data/persistence.cljs:669 msgid "workspace.notifications.file-repaired" msgstr "" -"We fixed some issues in this file. You can find a copy of the original version in " -"the version history." +"We fixed some issues in this file. You can find a copy of the original " +"version in the version history." + +#: src/app/main/data/persistence.cljs:662, src/app/main/data/persistence.cljs:691 +msgid "workspace.notifications.file-repaired-changes-undone" +msgstr "" +"We fixed some issues in this file. You can find a copy of the original " +"version in the version history. Some changes couldn't be saved, so your " +"latest changes were undone." #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:784 msgid "workspace.options.add-interaction" @@ -7026,7 +7372,7 @@ msgstr "Toggle blur" msgid "workspace.options.blur-options.toggle-more-options" msgstr "Show/hide more options" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "Cancel" @@ -7038,117 +7384,117 @@ msgstr "Canvas background" msgid "workspace.options.canvas-background-section" msgstr "Canvas background section" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Clip content" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Component" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Annotation" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Copy" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Create an annotation" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Edit an annotation" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Main" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Swap component" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "There are no assets in this library yet" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Unlinked" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Locate conflicting variants" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "This component has conflicting variants. Make sure each variation has a " "unique set of property values." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Locate duplicated variants" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Some variants have identical properties and values" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "These variants have identical properties and values. Adjust the values so " "they can be retrieved." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "This variant has identical properties and values to another variant. Adjust " "the values so they can be retrieved." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Some of these variants have identical properties and values. Adjust the " "values so they can be retrieved." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "This component has variants with invalid names. Make sure every variant is " "following the correct structure." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Locate invalid variants" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Some variants have invalid names" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Locate invalid variants" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "These variants have invalid names." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "This variant has an invalid name." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Some of these variants have invalid names." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[property]=[value], [property]=[value]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Try using the following structure:" @@ -7184,7 +7530,7 @@ msgstr "Have the same hierarchy level." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Groups, boards and layouts are considered equivalent." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "How variants stay connected" @@ -7232,11 +7578,11 @@ msgstr "Top & Bottom" msgid "workspace.options.convert-to-typography" msgstr "Create typography style" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Export" @@ -7244,53 +7590,53 @@ msgstr "Export" msgid "workspace.options.export-multiple" msgstr "Export selection" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Export 1 element" msgstr[1] "Export %s elements" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Add export" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Remove export" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Suffix" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "Export service is busy, please try again later" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "Export cancelled" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "Cancelling..." -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Export complete" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Exporting…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Export failed" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Export unexpectedly slow" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "Waiting..." @@ -7310,7 +7656,7 @@ msgstr "Remove fill" msgid "workspace.options.fill.section" msgstr "Fill section" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Resize board to fit content" @@ -7330,7 +7676,7 @@ msgstr "Flow start" msgid "workspace.options.flows.flow-starts" msgstr "Flow starts" -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:300, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 msgid "workspace.options.font-not-available" msgstr "" "The font used by this text (%s) is no longer available. Reupload the font " @@ -7340,67 +7686,71 @@ msgstr "" msgid "workspace.options.grid.auto" msgstr "Auto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Columns" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Color" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:44 +msgid "workspace.options.grid.params.guide-defaults" +msgstr "Guide defaults" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Gutter" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Height" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margin" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Set as default" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Bottom" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Center" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Left" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Right" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Stretch" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Top" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Use default" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Width" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Rows" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Square" @@ -7412,7 +7762,7 @@ msgstr "Group fill" msgid "workspace.options.group-stroke" msgstr "Group stroke" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Add guide" @@ -7420,19 +7770,19 @@ msgstr "Add guide" msgid "workspace.options.guides.remove-guide" msgstr "Remove guide" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Guides" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Toggle guide" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Height" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspect" @@ -7648,115 +7998,115 @@ msgstr "Interactions" msgid "workspace.options.interactions.add-interaction" msgstr "Add interaction" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Color" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Color burn" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Color dodge" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Darken" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Difference" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Exclusion" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Hard light" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Hue" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Lighten" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminosity" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Multiply" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Overlay" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturation" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Screen" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Soft light" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "Layer menu section" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Toggle layer visibility" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Max height" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Max width" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Min height" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Min width" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "More colors" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "More library colors" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "More color tokens" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:502 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:516 msgid "workspace.options.no-font-results" msgstr "No matching fonts" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "No matching size preset" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacity" @@ -7764,7 +8114,7 @@ msgstr "Opacity" msgid "workspace.options.pixel-grid-color" msgstr "Pixel grid color" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototype" @@ -7800,23 +8150,23 @@ msgstr "Border radius section" msgid "workspace.options.radius.show-single-corners" msgstr "Show independent radius" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Recent" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Retry" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotation" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Search font" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "Search size preset" @@ -7824,11 +8174,11 @@ msgstr "Search size preset" msgid "workspace.options.select-a-shape" msgstr "Select a shape, board or group to drag a connection to other board." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Selected colors" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "Color selection section" @@ -7896,55 +8246,55 @@ msgstr "Toggle shadow" msgid "workspace.options.show-fill-on-export" msgstr "Show in exports" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Show in view mode" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Size" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Size presets" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Lock ratio" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Unlock ratio" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Circle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamond" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Arrow" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "None" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Round" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Square" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rectangle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triangle" @@ -7952,31 +8302,31 @@ msgstr "Triangle" msgid "workspace.options.stroke-color" msgstr "Stroke color" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "Dash" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "Gap" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Stroke width" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "Bottom (block end)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "Left (inline start)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "Right (inline end)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "Top (block start)" @@ -7984,49 +8334,49 @@ msgstr "Top (block start)" msgid "workspace.options.stroke.add-stroke" msgstr "Add stroke color" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Center" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Dashed" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Dotted" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Inside" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Mixed" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Outside" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "Stroke per side" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "" "Strokes per side feature is only available in the new render. Switch render " "to use it." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Remove stroke" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Solid" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "Toggle stroke" @@ -8062,11 +8412,11 @@ msgstr "Auto width" msgid "workspace.options.text-options.grow-fixed" msgstr "Fixed" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Letter Spacing" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Line height" @@ -8114,237 +8464,265 @@ msgstr "Underline (%s)" msgid "workspace.options.use-play-button" msgstr "Use the play button at the header to run the prototype view." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Width" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X axis" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y axis" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Draw nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "Aligned" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "Independent" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "Equal" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "Handler behaviour" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Join nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "To corner (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "To curve (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Merge nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Move nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Separate nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Snap nodes (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Open" -#: src/app/main/ui/workspace/plugins.cljs:242 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Discover [more plugins](%s)" -#: src/app/main/ui/workspace/plugins.cljs:249 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "No plugins installed yet" -#: src/app/main/ui/workspace/plugins.cljs:236 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "The plugin manifest is incorrect." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "You need to be an editor to use this plugin" -#: src/app/main/data/plugins.cljs:158 +#: src/app/main/data/plugins.cljs:160 msgid "workspace.plugins.error.unreachable" msgstr "The plugin URL could not be reached." -#: src/app/main/ui/workspace/plugins.cljs:232 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "The plugin doesn't exist or the URL is not correct." -#: src/app/main/ui/workspace/plugins.cljs:228 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Install" -#: src/app/main/ui/workspace/plugins.cljs:258 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Installed plugins" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Plugins manager" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Plugins" -#: src/app/main/ui/workspace/plugins.cljs:452 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "UPDATE THIS PLUGIN" -#: src/app/main/ui/workspace/plugins.cljs:456 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "The plugin has been modified since you last opened it. It now also wants to " "access:" -#: src/app/main/ui/workspace/plugins.cljs:325 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Start file downloads." -#: src/app/main/ui/workspace/plugins.cljs:332 +#: src/app/main/ui/workspace/plugins.cljs:344 +msgid "workspace.plugins.permissions.allow-global" +msgstr "" +"Keep running while you move between the dashboard and your files, and find " +"and open your projects and files." + +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Store data in the browser." -#: src/app/main/ui/workspace/plugins.cljs:345 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "Read the contents of your clipboard." -#: src/app/main/ui/workspace/plugins.cljs:339 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "Read and write to your clipboard." -#: src/app/main/ui/workspace/plugins.cljs:318 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Read your comments and replies." -#: src/app/main/ui/workspace/plugins.cljs:312 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Read and modify your comments and reply in your name." -#: src/app/main/ui/workspace/plugins.cljs:285 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Read the content of files that users have access to." -#: src/app/main/ui/workspace/plugins.cljs:279 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Read and modify the content of files that users have access to." -#: src/app/main/ui/workspace/plugins.cljs:394 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Please note that this plugin is created by an external party, so ensure you " "trust it before granting access. Your data privacy and security are " "important to us. If you have any concerns, please contact support." -#: src/app/main/ui/workspace/plugins.cljs:305 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Read your libraries and assets." -#: src/app/main/ui/workspace/plugins.cljs:299 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Read and modify your libraries and assets." -#: src/app/main/ui/workspace/plugins.cljs:387 +#: src/app/main/ui/workspace/plugins.cljs:365 +msgid "workspace.plugins.permissions.manage-delete" +msgstr "Delete your files and projects." + +#: src/app/main/ui/workspace/plugins.cljs:351 +msgid "workspace.plugins.permissions.manage-projects" +msgstr "Create, rename, pin and duplicate your projects." + +#: src/app/main/ui/workspace/plugins.cljs:358 +msgid "workspace.plugins.permissions.manage-teams" +msgstr "See, create and rename your teams, and move projects between them." + +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "'%s' PLUGIN WANTS ACCESS TO:" -#: src/app/main/ui/workspace/plugins.cljs:292 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Read the profile information of the current user." -#: src/app/main/ui/workspace/plugins.cljs:254 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Plugins List" -#: src/app/main/ui/workspace/plugins.cljs:191 +#: src/app/main/ui/workspace/plugins.cljs:188 msgid "workspace.plugins.remove-confirmation.message" msgstr "" "Are you sure you want to remove the plugin %s? You can install it again at " "any time." -#: src/app/main/ui/workspace/plugins.cljs:190 +#: src/app/main/ui/workspace/plugins.cljs:187 msgid "workspace.plugins.remove-confirmation.title" msgstr "Remove plugin" -#: src/app/main/ui/workspace/plugins.cljs:101, src/app/main/ui/workspace/plugins.cljs:192 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Remove plugin" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Write a plugin URL" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Plugins" -#: src/app/main/ui/workspace/plugins.cljs:522 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "NOT NOW" -#: src/app/main/ui/workspace/plugins.cljs:515 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Want to take a look? It will open in a new draft for your current team. (If " "not, you can always find it in the installed plugins of any file.)" -#: src/app/main/ui/workspace/plugins.cljs:511 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "'%s' PLUGIN IS INSTALLED FOR YOUR USER!" -#: src/app/main/ui/workspace/plugins.cljs:528 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "TRY PLUGIN" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:46 +#, markdown +msgid "workspace.save-failure.retrying" +msgstr "**Editing is disabled** because changes can’t be saved." + +#: src/app/main/ui/workspace/viewport/top_bar.cljs:47 +#, markdown +msgid "workspace.save-failure.stopped" +msgstr "**Editing is disabled** because changes can’t be saved." + +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Add flex layout" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Add grid layout" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Add layout" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Create variant" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Add new property" @@ -8356,11 +8734,11 @@ msgstr "Send to back" msgid "workspace.shape.menu.backward" msgstr "Send backward" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "Clear board guides" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Combine as variants" @@ -8368,7 +8746,7 @@ msgstr "Combine as variants" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Components need to be in the same page" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Copy" @@ -8412,27 +8790,27 @@ msgstr "Copy as text" msgid "workspace.shape.menu.create-annotation" msgstr "Create annotation" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Selection to board" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Create component" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Create multiple components" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Cut" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Delete" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Delete flow start" @@ -8444,7 +8822,7 @@ msgstr "Detach instance" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Detach instances" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Difference" @@ -8452,15 +8830,15 @@ msgstr "Difference" msgid "workspace.shape.menu.duplicate" msgstr "Duplicate" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Edit" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Exclude" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Flatten" @@ -8472,7 +8850,7 @@ msgstr "Flip horizontal" msgid "workspace.shape.menu.flip-vertical" msgstr "Flip vertical" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Flow start" @@ -8484,31 +8862,31 @@ msgstr "Bring forward" msgid "workspace.shape.menu.front" msgstr "Bring to front" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Group" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Hide" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Show / Hide UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Intersection" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Lock" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Mask" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Paste" @@ -8516,27 +8894,27 @@ msgstr "Paste" msgid "workspace.shape.menu.paste-props" msgstr "Paste properties" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Path" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Remove flex layout" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Remove grid layout" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Remove layout" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Remove property" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "Variant should have at least one property" @@ -8560,11 +8938,11 @@ msgstr "Restore variant" msgid "workspace.shape.menu.select-layer" msgstr "Select layer" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Show" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Show in assets panel" @@ -8572,7 +8950,7 @@ msgstr "Show in assets panel" msgid "workspace.shape.menu.show-main" msgstr "Show main component" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "Stroke to path" @@ -8584,19 +8962,19 @@ msgstr "Remove thumbnail" msgid "workspace.shape.menu.thumbnail-set" msgstr "Set as thumbnail" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Ungroup" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Union" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Unlock" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Unmask" @@ -8612,79 +8990,83 @@ msgstr "Collapse sidebar" msgid "workspace.sidebar.expand" msgstr "Expand sidebar" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "History" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Layers" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Components" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Filter" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Boards" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Groups" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Images" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Masks" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "No matches" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "Replace" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "Replace all" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "Replace with..." -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Find…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "Find and replace" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "Text content" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "Layer names" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Shapes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Texts" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:53 +msgid "workspace.sidebar.options.svg-attrs.remove" +msgstr "Remove attribute" + +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Imported SVG Attributes" @@ -8696,7 +9078,7 @@ msgstr "Pages" msgid "workspace.sidebar.sitemap.add-page" msgstr "Add page" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Sitemap" @@ -8728,7 +9110,7 @@ msgstr "Add set to this group" msgid "workspace.tokens.add-token" msgstr "Add token: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Applied to" @@ -8752,7 +9134,7 @@ msgstr "Base font size must be a value in pixels or unitless." msgid "workspace.tokens.change-token-source" msgstr "Change token source" -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Color" @@ -8764,7 +9146,7 @@ msgstr "Copy token path" msgid "workspace.tokens.create-new-theme" msgstr "Create your first theme now." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Create one." @@ -8784,6 +9166,10 @@ msgstr "Delete token" msgid "workspace.tokens.delete-theme-title" msgstr "Delete theme" +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226 +msgid "workspace.tokens.detach-reference" +msgstr "Detach reference" + #: src/app/main/ui/workspace/tokens/management/context_menu.cljs:430 msgid "workspace.tokens.duplicate" msgstr "Duplicate token" @@ -8792,7 +9178,7 @@ msgstr "Duplicate token" msgid "workspace.tokens.duplicate-group" msgstr "Duplicate Tokens Group" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "copy" @@ -8816,7 +9202,7 @@ msgstr "Edit %s token" msgid "workspace.tokens.enter-token-name" msgstr "Enter %s token name" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "Tokens can't be applied while editing text. Select the text layer instead." @@ -8844,7 +9230,7 @@ msgstr "Preview:" msgid "workspace.tokens.export.single-file" msgstr "Single file" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Font size or {alias}" @@ -8854,7 +9240,7 @@ msgstr "" "Error setting font weight/style. This font style does not exist in the " "current font" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Font weight (300, Bold Italic...) or an {alias}" @@ -8890,7 +9276,7 @@ msgstr "In multiple files, the file name / path are the set names." msgid "workspace.tokens.import-single-file" msgstr "In a single JSON file, the first-level keys should be the token set names." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Import tokens" @@ -8898,17 +9284,17 @@ msgstr "Import tokens" msgid "workspace.tokens.import-warning" msgstr "Importing tokens will override all your current tokens, sets and themes." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Inactive" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "This set is not active. Change theme or activate this set to see changes in " "the viewport" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Use individual tokens" @@ -8928,11 +9314,11 @@ msgstr "Theme" msgid "workspace.tokens.label.theme-placeholder" msgstr "Add a theme (i.e. Light)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Letter spacing or {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Line height (multiplier, px, %) or {alias}" @@ -8948,11 +9334,11 @@ msgstr "Max. size" msgid "workspace.tokens.min-size" msgstr "Min. size" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Missing reference" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Right click to see options" @@ -8968,7 +9354,7 @@ msgstr "No theme active" msgid "workspace.tokens.no-permisions-set" msgstr "You need to be an editor to activate / deactivate sets" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "You need to be an editor to use themes" @@ -8980,7 +9366,7 @@ msgstr "There are no sets defined yet. Create one first." msgid "workspace.tokens.no-sets-yet" msgstr "There are no sets yet." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "There are no themes." @@ -8992,7 +9378,7 @@ msgstr "You currently have no themes." msgid "workspace.tokens.not-remap" msgstr "Don't remap" -#: src/app/main/data/workspace/libraries.cljs:847 +#: src/app/main/data/workspace/libraries.cljs:850 msgid "workspace.tokens.notifications.source-sets-or-themes-updated" msgstr "Tokens source sets or themes have been modified" @@ -9000,7 +9386,7 @@ msgstr "Tokens source sets or themes have been modified" msgid "workspace.tokens.num-active-sets" msgstr "%s active sets" -#: src/app/main/ui/workspace/colorpicker.cljs:438, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:67 +#: src/app/main/ui/workspace/colorpicker.cljs:438, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:112 msgid "workspace.tokens.only-first-fill-or-stroke" msgstr "Design tokens can only be applied to the first fill or stroke in the list." @@ -9008,7 +9394,7 @@ msgstr "Design tokens can only be applied to the first fill or stroke in the lis msgid "workspace.tokens.open-source-new-tab" msgstr "Open tokens' source in a new tab" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Original value: %s" @@ -9020,15 +9406,15 @@ msgstr "Paddings" msgid "workspace.tokens.radius" msgstr "Radius" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "Reference in {%s} is not valid or is not in any active set." -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Enter a token typography alias" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Enter a token shadow alias" @@ -9064,7 +9450,7 @@ msgstr "Rename Tokens Group" msgid "workspace.tokens.rename-group-name-hint" msgstr "Your tokens will automatically be renamed to %s.(suffix).(tokenName)" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Resolved value: %s" @@ -9100,27 +9486,27 @@ msgstr "Here you can configure the base font size, which defines the value of 1r msgid "workspace.tokens.settings" msgstr "Tokens settings" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Add Shadow" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Blur" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Inset" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Remove Shadow" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Spread" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "Blur value cannot be negative" @@ -9128,11 +9514,11 @@ msgstr "Blur value cannot be negative" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "Spread value cannot be negative" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -9148,11 +9534,11 @@ msgstr "Source:" msgid "workspace.tokens.stroke-width" msgstr "Stroke Width" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize or {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through or {alias}" @@ -9174,31 +9560,31 @@ msgstr "This file" msgid "workspace.tokens.token-description" msgstr "Description" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Select font family" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Font family" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Font family or list of fonts separated by comma (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Name" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "A token already exists at the path: %s or at a prefix thereof." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "Name should be at least 1 character" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " is not a valid token name.\n" @@ -9209,11 +9595,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Value" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Enter a value or alias with {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENS - %s" @@ -9231,15 +9617,15 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "Type '%s' is not supported (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Use a reference" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "The value is not valid" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "Arrow (%s)" @@ -9247,104 +9633,116 @@ msgstr "Arrow (%s)" msgid "workspace.toolbar.assets" msgstr "Assets" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Color Palette (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Comments (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Curve (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Ellipse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Board (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Image (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:413 +#: src/app/main/ui/workspace/top_toolbar.cljs:362 msgid "workspace.toolbar.label" msgstr "Drawing tools" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "Line (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "Connect here" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP connected" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/components/mcp_menu.cljs:136 +msgid "workspace.toolbar.mcp-copy-session-id" +msgstr "Copy session ID" + +#: src/app/main/ui/components/mcp_menu.cljs:33 +msgid "workspace.toolbar.mcp-session-copied" +msgstr "Session ID copied" + +#: src/app/main/ui/components/mcp_menu.cljs:132 +msgid "workspace.toolbar.mcp-session-id" +msgstr "Session ID: %s" + +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Move (%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "Palette bar" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/palette.cljs:211 +msgid "workspace.toolbar.palette-options" +msgstr "Palette options" + +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Path (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Plugins (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rectangle (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Text (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Typographies (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Toggle toolbar" -#: src/app/main/ui/workspace/viewport/top_bar.cljs:45 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:64 msgid "workspace.top-bar.read-only.done" msgstr "Done" -#: src/app/main/ui/workspace/viewport/top_bar.cljs:40 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:53 #, markdown msgid "workspace.top-bar.view-only" msgstr "**Inspecting code** (View Only)" -#: src/app/main/ui/workspace/viewport/top_bar.cljs:39 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:50 msgid "workspace.top-bar.webgl-context-lost" msgstr "Rendering unavailable. Refresh to restore editing." -#: src/app/main/ui/workspace/viewport/top_bar.cljs:43 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:60 msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "Refresh" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "There are no history changes so far" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "by %s" @@ -9420,6 +9818,10 @@ msgstr "typography assets" msgid "workspace.undo.entry.new" msgstr "New %s" +#: src/app/main/ui/workspace/sidebar/history.cljs:371 +msgid "workspace.undo.entry.show-details" +msgstr "Show details" + #: src/app/main/ui/workspace/sidebar/history.cljs:132 msgid "workspace.undo.entry.single.circle" msgstr "circle" @@ -9484,19 +9886,19 @@ msgstr "typography asset" msgid "workspace.undo.entry.unknown" msgstr "Operation over %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1500, src/app/main/data/workspace/versions.cljs:255 msgid "workspace.updates.dismiss" msgstr "Dismiss" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1498 msgid "workspace.updates.more-info" msgstr "More info" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1496 msgid "workspace.updates.there-are-updates" msgstr "There are updates in shared libraries" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1502 msgid "workspace.updates.update" msgstr "Update" @@ -9504,27 +9906,31 @@ msgstr "Update" msgid "workspace.versions.autosaved.entry" msgstr "%s autosave versions" -#: src/app/main/data/workspace/versions.cljs:301, src/app/main/ui/workspace/sidebar/versions.cljs:300 +#: src/app/main/data/workspace/versions.cljs:319, src/app/main/ui/workspace/sidebar/versions.cljs:309 msgid "workspace.versions.autosaved.version" msgstr "Autosaved %s" -#: src/app/main/ui/workspace/sidebar/versions.cljs:323 +#: src/app/main/ui/workspace/sidebar/versions.cljs:331 msgid "workspace.versions.button.pin" msgstr "Pin version" -#: src/app/main/ui/workspace/sidebar/versions.cljs:233, src/app/main/ui/workspace/sidebar/versions.cljs:313 +#: src/app/main/ui/workspace/sidebar/versions.cljs:239, src/app/main/ui/workspace/sidebar/versions.cljs:321 msgid "workspace.versions.button.preview" msgstr "Preview version" -#: src/app/main/ui/workspace/sidebar/versions.cljs:318 +#: src/app/main/ui/workspace/sidebar/versions.cljs:326 msgid "workspace.versions.button.restore" msgstr "Restore version" -#: src/app/main/ui/workspace/sidebar/versions.cljs:425, src/app/main/ui/workspace/sidebar/versions.cljs:427 +#: src/app/main/ui/workspace/sidebar/versions.cljs:433, src/app/main/ui/workspace/sidebar/versions.cljs:435 msgid "workspace.versions.button.save" msgstr "Save version" -#: src/app/main/ui/workspace/sidebar/versions.cljs:434 +#: src/app/main/data/workspace/versions.cljs:93 +msgid "workspace.versions.create-failed-unsaved" +msgstr "The version was not created because your changes could not be saved." + +#: src/app/main/ui/workspace/sidebar/versions.cljs:442 msgid "workspace.versions.empty" msgstr "There are no versions yet" @@ -9532,35 +9938,39 @@ msgstr "There are no versions yet" msgid "workspace.versions.expand-snapshot" msgstr "Expand snapshots" -#: src/app/main/ui/workspace/sidebar/versions.cljs:398 +#: src/app/main/ui/workspace/sidebar/versions.cljs:406 msgid "workspace.versions.filter.all" msgstr "All versions" -#: src/app/main/ui/workspace/sidebar/versions.cljs:412 +#: src/app/main/ui/workspace/sidebar/versions.cljs:420 msgid "workspace.versions.filter.label" msgstr "Versions filter" -#: src/app/main/ui/workspace/sidebar/versions.cljs:399 +#: src/app/main/ui/workspace/sidebar/versions.cljs:407 msgid "workspace.versions.filter.mine" msgstr "My versions" -#: src/app/main/ui/workspace/sidebar/versions.cljs:403 +#: src/app/main/ui/workspace/sidebar/versions.cljs:411 msgid "workspace.versions.filter.user" msgstr "%s's versions" -#: src/app/main/ui/workspace/sidebar/versions.cljs:420 +#: src/app/main/ui/workspace/sidebar/versions.cljs:428 msgid "workspace.versions.loading" msgstr "Loading..." -#: src/app/main/data/workspace/versions.cljs:312 +#: src/app/main/data/workspace/versions.cljs:330 msgid "workspace.versions.preview-banner-title" msgstr "Previewing version: %s" -#: src/app/main/data/workspace/versions.cljs:306 +#: src/app/main/data/workspace/versions.cljs:324 msgid "workspace.versions.preview.unnamed" msgstr "Unnamed version" -#: src/app/main/data/workspace/versions.cljs:239 +#: src/app/main/data/workspace/versions.cljs:315, src/app/main/ui/workspace/sidebar/versions.cljs:308 +msgid "workspace.versions.repair.version" +msgstr "Before automatic repair, %s" + +#: src/app/main/data/workspace/versions.cljs:253 msgid "workspace.versions.restore-warning" msgstr "Do you want to restore this version?" @@ -9583,6 +9993,6 @@ msgstr "" "If you'd like to increase this limit, write to us at " "[support@penpot.app](%s)" -#: src/app/main/ui/workspace/sidebar/versions.cljs:462 +#: src/app/main/ui/workspace/sidebar/versions.cljs:470 msgid "workspace.versions.warning.text" msgstr "Autosaved versions will be kept for %s days." diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 5ebc56901b..5871401d67 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-07-31 17:02+0000\n" -"Last-Translator: Anonymous \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Spanish " "\n" "Language: es\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.8.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "¿Tienes ya una cuenta?" @@ -33,7 +33,7 @@ msgstr "" "Este es un servicio de DEMOSTRACIÓN. NO USAR para trabajo real, los " "proyectos serán borrados periodicamente." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "¿Olvidaste tu contraseña?" @@ -41,37 +41,37 @@ msgstr "¿Olvidaste tu contraseña?" msgid "auth.fullname" msgstr "Nombre completo" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Entrar en mi cuenta" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Inicia sesión aquí." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot es la herramienta de diseño libre y open-source para la colaboración " "entre Diseño y Código" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -79,7 +79,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Introduce la nueva contraseña" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "Te uniste a la organización %s" @@ -97,7 +97,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "Hemos enviado a tu buzón un enlace para recuperar tu contraseña." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Te uniste al equipo" @@ -129,7 +129,7 @@ msgstr "¿Olvidaste tu contraseña?" msgid "auth.recovery-submit" msgstr "Cambiar tu contraseña" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "¿No tienes una cuenta?" @@ -141,7 +141,7 @@ msgstr "Cuéntanos cómo quieres que nos dirijamos a tí." msgid "auth.register-account-title" msgstr "Tu nombre" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Crear una cuenta" @@ -154,7 +154,7 @@ msgstr "Crear una cuenta" msgid "auth.terms-and-privacy-agreement" msgstr "Yo acepto los [términos de servicio](%s) y la [política de privacidad](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Términos de servicio" @@ -176,101 +176,101 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "No se encuentra miembros con @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publicar" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Todo usario de Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "¿Estas seguro que quieres eliminar el enlace? Si lo haces, el enlace dejara " "de funcionar para cualquiera" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(actual)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Eliminar enlace" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Obtener enlace" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Enlace copiado satisfactoriamente" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Gestionar permisos" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 página compartida" msgstr[1] "%s páginas compartidas" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Pueden comentar" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Pueden ver código" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Cualquiera con el enlace puede acceder" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Páginas compartidas" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "El enlace para compartir aparecerá aquí" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Sólo integrantes del equipo" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Compartir prototipos" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Selecctionar todas" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Despublicar" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Gestión del equipo" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot está dirigido a equipos. Invita a personas para trabajar " "conjuntamente en proyectos y archivos" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "¡Forma equipo!" -#: src/app/main/ui/dashboard/file_menu.cljs:234 +#: src/app/main/ui/dashboard/file_menu.cljs:233 msgid "dashboard-restore-file-confirmation.description" msgstr "Vas a restaurar %s." -#: src/app/main/ui/dashboard/file_menu.cljs:233 +#: src/app/main/ui/dashboard/file_menu.cljs:232 msgid "dashboard-restore-file-confirmation.title" msgstr "Restaurar archivo" @@ -278,33 +278,33 @@ msgstr "Restaurar archivo" msgid "dashboard.add-file" msgstr "Añadir archivo" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:323, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Añadir como Biblioteca Compartida" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Cambiar correo" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "MOVER EL EQUIPO" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Mover a:" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "" "Los proyectos y archivos permanecerán disponibles para los miembros del " "equipo. El equipo obtendrá la configuración de la nueva organización." -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Elige una organización" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "Cambiar el equipo de organización" @@ -362,23 +362,27 @@ msgstr "Ver changelog completo" msgid "dashboard.check-updates.view-release-notes" msgstr "Ver notas de la versión" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/sidebar.cljs:228 +msgid "dashboard.clear-search" +msgstr "Borrar búsqueda" + +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Vaciar papelera" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:318, src/app/main/data/dashboard.cljs:568, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copia)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Crear nueva organización" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Crear nuevo equipo" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Guardar esta versión" @@ -386,78 +390,88 @@ msgstr "Guardar esta versión" msgid "dashboard.default-team-name" msgstr "Proyectos Personales" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "¿Estás seguro de que quieres eliminar para siempre todos tus proyectos y " "archivos eliminados? Esta es una acción irreversible." -#: src/app/main/ui/dashboard/file_menu.cljs:247 +#: src/app/main/ui/dashboard/file_menu.cljs:246 msgid "dashboard.delete-file-forever-confirmation.description" msgstr "" "¿Estás seguro de que quieres eliminar para siempre %s? Esta es una acción " "irreversible." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:842 msgid "dashboard.delete-files-success-notification" msgstr "%s ficheros han sido eliminados correctamente." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:245, src/app/main/ui/dashboard/file_menu.cljs:247 msgid "dashboard.delete-forever-confirmation.title" msgstr "Eliminar para siempre" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Eliminar proyecto" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "¿Estás seguro de que quieres eliminar para siempre el proyecto %s? Vas a " "eliminarlo para siempre junto con todos los archivos que contiene. Esta es " "una acción irreversible." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:841, src/app/main/data/dashboard.cljs:875 msgid "dashboard.delete-success-notification" msgstr "%s ha sido eliminado correctamente." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Eliminar equipo" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "Tu papelera está vacía. Los archivos y proyectos eliminados aparecerán aquí." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Se eliminará %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Descargar archivo Penpot (.penpot)" -#: src/app/main/ui/dashboard/file_menu.cljs:302, src/app/main/ui/dashboard/project_menu.cljs:94 +#: src/app/main/ui/dashboard/file_menu.cljs:301, src/app/main/ui/dashboard/project_menu.cljs:94 msgid "dashboard.duplicate" msgstr "Duplicar" -#: src/app/main/ui/dashboard/file_menu.cljs:269 +#: src/app/main/ui/dashboard/file_menu.cljs:268 msgid "dashboard.duplicate-multi" msgstr "Duplicar %s archivos" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/data/dashboard.cljs:549 +msgid "dashboard.duplicate-unsaved-changes" +msgid_plural "dashboard.duplicate-unsaved-changes" +msgstr[0] "" +"Se ha duplicado tu archivo, pero la copia no incluye tus últimos cambios " +"porque aún no se han guardado." +msgstr[1] "" +"Se han duplicado tus archivos, pero las copias no incluyen tus últimos " +"cambios porque aún no se han guardado." + +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Cuando un miembro del equipo cree algún borrador, este aparecerá aquí." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Aún no hay borradores." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Cuando un miembro del equipo cree algún archivo, este aparecerá aquí." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Aún no hay archivos." @@ -505,71 +519,71 @@ msgstr "Importa un fichero .penpot" msgid "dashboard.empty-project.start" msgstr "Empieza a crear cosas increíbles" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:845 msgid "dashboard.errors.error-on-delete-files" msgstr "Hubo un error al eliminar archivos." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:878 msgid "dashboard.errors.error-on-delete-project" msgstr "Hubo un error al eliminar el proyecto %s" -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:973, src/app/main/ui/dashboard/file_menu.cljs:226 msgid "dashboard.errors.error-on-restore-file" msgstr "Hubo un error al restaurar el archivo %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restore-files" msgstr "Hubo un error al restaurar los archivos." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:1006 msgid "dashboard.errors.error-on-restoring-project" msgstr "Hubo un error al restaurar el proyecto %s y sus archivos." -#: src/app/main/ui/dashboard/file_menu.cljs:279 +#: src/app/main/ui/dashboard/file_menu.cljs:278 msgid "dashboard.export-binary-multi" msgstr "Descargar %s archivos Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exportar tableros como PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exportar como PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s de %s elementos seleccionados" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exportar" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Puedes añadir configuraciones de exportación a elementos desde las " "propiedades de diseño (al final del lateral derecho)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Información sobre cómo configurar exportaciones en Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "No hay elementos con configuraciones de exportación." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Exportar selección" -#: src/app/main/ui/dashboard/file_menu.cljs:263 +#: src/app/main/ui/dashboard/file_menu.cljs:262 msgid "dashboard.file-menu.delete-files-permanently-option" msgid_plural "dashboard.file-menu.delete-files-permanently-option" msgstr[0] "Eliminar archivo" msgstr[1] "Eliminar archivos" -#: src/app/main/ui/dashboard/file_menu.cljs:261 +#: src/app/main/ui/dashboard/file_menu.cljs:260 msgid "dashboard.file-menu.restore-files-option" msgid_plural "dashboard.file-menu.restore-files-option" msgstr[0] "Restaurar archivo" @@ -583,35 +597,39 @@ msgstr "Vista de cuadrícula" msgid "dashboard.files-layout.list" msgstr "Vista de lista" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/dashboard/fonts.cljs:442, src/app/main/ui/dashboard/fonts.cljs:443 +msgid "dashboard.fonts.delete-variant" +msgstr "Eliminar estilo de fuente" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Fuente no encontrada" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Ignorar todas" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Las fuentes personalizadas que subas aparecerán aquí." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Aún no hay fuentes personalizadas." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Cuando un miembro del equipo suba una fuente personalizada, esta aparecerá " "aquí." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 fuente añadida" msgstr[1] "%s fuentes añadidas" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -621,7 +639,7 @@ msgstr "" "como una **única familia de fuentes**. Se pueden cargar fuentes con los " "siguientes formatos: **TTF, OTF and WOFF** (con uno es suficiente)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -631,11 +649,11 @@ msgstr "" "te puede interesar leer más sobre licencias tipográficas: [font " "licensing](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Cargar todas" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -646,23 +664,23 @@ msgstr "" "[Transfonter](https://transfonter.org/) para generar fuentes web y corregir " "posibles errores." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Ir a la Admin Console" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "Última modificación %s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importar archivos Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "¡Vaya! No hemos podido importar el fichero" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "1 biblioteca fue vinculada automáticamente por nombre." @@ -672,53 +690,79 @@ msgstr[1] "%s bibliotecas fueron vinculadas automáticamente por nombre." msgid "dashboard.import.bad-url" msgstr "La importación ha fallado. La URL de la plantilla es incorrecta" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:625 +msgid "dashboard.import.check.message" +msgstr "" +"Estamos comprobando que cada archivo se puede importar. Solo tardará un " +"momento." + +#: src/app/main/ui/dashboard/import.cljs:624 +msgid "dashboard.import.check.title" +msgstr "Comprobando tus archivos..." + +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "Confirmar vínculos de biblioteca" -#: src/app/main/ui/dashboard/import.cljs:709 +#: src/app/main/ui/dashboard/import.cljs:814 msgid "dashboard.import.connect-selected-libraries" msgstr "Conectar bibliotecas seleccionadas" -#: src/app/main/ui/dashboard/import.cljs:296 +#: src/app/main/ui/dashboard/import.cljs:288 msgid "dashboard.import.file-name-label" msgstr "Nombre del archivo" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "No hemos podido verificar este archivo." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Parece que este archivo está dañado." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "No todos los ficheros han sido importados" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Estos ficheros tienen errores:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Los ficheros con error no serán importados." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "Se ha producido un error al procesar este archivo." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 fichero se ha importado correctamente." msgstr[1] "%s ficheros se han importado correctamente." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Algunos ficheros contenían objetos erroneos que no han sido importados." -#: src/app/main/ui/dashboard/import.cljs:710 +#: src/app/main/ui/dashboard/import.cljs:600 +msgid "dashboard.import.importing.done" +msgid_plural "dashboard.import.importing.done" +msgstr[0] "1 hecho" +msgstr[1] "%s hechos" + +#: src/app/main/ui/dashboard/import.cljs:637 +msgid "dashboard.import.importing.message" +msgstr "Reconstruyendo páginas, componentes y recursos en tu proyecto." + +#: src/app/main/ui/dashboard/import.cljs:598 +msgid "dashboard.import.importing.title" +msgid_plural "dashboard.import.importing.title" +msgstr[0] "Importando 1 archivo" +msgstr[1] "Importando %s archivos" + +#: src/app/main/ui/dashboard/import.cljs:815 msgid "dashboard.import.next-file" msgstr "Siguiente archivo" @@ -726,99 +770,123 @@ msgstr "Siguiente archivo" msgid "dashboard.import.no-perms" msgstr "No tienes permisos para importar en este equipo" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Procesando colores" +#: src/app/main/ui/dashboard/import.cljs:634 +msgid "dashboard.import.queued.message" +msgstr "" +"Tus archivos ya están subidos. La importación empieza en cuanto haya un " +"hueco libre." -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Procesando componentes" +#: src/app/main/ui/dashboard/import.cljs:633 +msgid "dashboard.import.queued.title" +msgstr "Esperando a empezar..." -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Procesando media" +#: src/app/main/ui/dashboard/import.cljs:628 +msgid "dashboard.import.ready.message" +msgstr "Tus archivos se pueden importar. Continúa con la subida." -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Procesando página: %s" +#: src/app/main/ui/dashboard/import.cljs:627 +msgid "dashboard.import.ready.title" +msgstr "Archivos listos" -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Procesando tipografías" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Enviando datos al servidor (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Enviando fichero: %s" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "" "Algunas bibliotecas no pudieron vincularse automáticamente. Selecciona la " "biblioteca correcta para cada una:" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "Revisa los vínculos de biblioteca antes de confirmar:" -#: src/app/main/ui/dashboard/import.cljs:459 +#: src/app/main/ui/dashboard/import.cljs:447 msgid "dashboard.import.resolve-libraries.connect-to" msgstr "conectar a" -#: src/app/main/ui/dashboard/import.cljs:453 +#: src/app/main/ui/dashboard/import.cljs:441 msgid "dashboard.import.resolve-libraries.original-library" msgstr "biblioteca original" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:331 +msgid "dashboard.import.status.ready" +msgstr "Listo" + +#: src/app/main/ui/dashboard/import.cljs:568 +msgid "dashboard.import.steps.check" +msgstr "Comprobación" + +#: src/app/main/ui/dashboard/import.cljs:570 +msgid "dashboard.import.steps.import" +msgstr "Importación" + +#: src/app/main/ui/dashboard/import.cljs:573 +msgid "dashboard.import.steps.label" +msgstr "Pasos de la importación" + +#: src/app/main/ui/dashboard/import.cljs:569 +msgid "dashboard.import.steps.upload" +msgstr "Subida" + +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "auto-vinculada" -#: src/app/main/ui/dashboard/import.cljs:529 +#: src/app/main/ui/dashboard/import.cljs:517 msgid "dashboard.import.summary.manually-linked" msgstr "vinculada manualmente" -#: src/app/main/ui/dashboard/import.cljs:537 +#: src/app/main/ui/dashboard/import.cljs:525 msgid "dashboard.import.summary.new" msgstr "Nueva" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "Ninguna biblioteca seleccionada" -#: src/app/main/ui/dashboard/import.cljs:534 +#: src/app/main/ui/dashboard/import.cljs:522 msgid "dashboard.import.summary.original" msgstr "Original" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/import.cljs:1130 +msgid "dashboard.import.title" +msgstr "Importar archivo" + +#: src/app/main/ui/dashboard/import.cljs:631 +msgid "dashboard.import.upload.message" +msgstr "" +"Enviando tus archivos. Mantén este diálogo abierto hasta que termine la " +"subida." + +#: src/app/main/ui/dashboard/import.cljs:630 +msgid "dashboard.import.upload.title" +msgstr "Subiendo tus archivos..." + +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Vas a eliminar las invitaciones para:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Vas a reenviar las invitaciones para:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Eliminar invitaciones" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Reenviar invitaciones" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "No tienes permiso para invitar a personas a unirse a este equipo, ni para " "editar o eliminar invitaciones." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Invitar a la gente" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "No tienes permiso para invitar a personas a este equipo" @@ -830,27 +898,27 @@ msgstr "Abandonar organización" msgid "dashboard.leave-organization.toast" msgstr "Ya no eres miembro de la organización %s." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Abandonar equipo" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliotecas y plantillas" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "Aquí tienes algunas Bibliotecas y plantillas que puedes añadir a tu proyecto" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Explora más y descubre cómo contribuir" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Hubo un problema importando la plantilla. No ha podido ser importada." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliotecas" @@ -858,7 +926,7 @@ msgstr "Bibliotecas" msgid "dashboard.loading-files" msgstr "cargando tus ficheros …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "cargando tus fuentes …" @@ -866,11 +934,11 @@ msgstr "cargando tus fuentes …" msgid "dashboard.mark-all-as-read.success" msgstr "Se han marcado todas las noficaciones como leídas" -#: src/app/main/ui/dashboard/file_menu.cljs:308, src/app/main/ui/dashboard/project_menu.cljs:101 +#: src/app/main/ui/dashboard/file_menu.cljs:307, src/app/main/ui/dashboard/project_menu.cljs:101 msgid "dashboard.move-to" msgstr "Mover a" -#: src/app/main/ui/dashboard/file_menu.cljs:272 +#: src/app/main/ui/dashboard/file_menu.cljs:271 msgid "dashboard.move-to-multi" msgstr "Mover %s archivos a" @@ -878,67 +946,67 @@ msgstr "Mover %s archivos a" msgid "dashboard.move-to-other-team" msgstr "Mover a otro equipo" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nuevo Archivo" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:525, src/app/main/data/dashboard.cljs:668 msgid "dashboard.new-file-prefix" msgstr "Nuevo Archivo" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nuevo proyecto" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:270, src/app/main/data/dashboard.cljs:671 msgid "dashboard.new-project-prefix" msgstr "Nuevo Proyecto" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "No se encuentra “%s“" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "No tienes permiso para añadir equipos a ninguna de tus organizaciones." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:185 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 msgid "dashboard.no-organizations-yet" msgstr "Todavía no hay organizaciones." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "No tienes permiso para crear equipos en la organización %s. Si necesitas " "más información, contacta con el propietario de la organización." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "No tienes permiso para eliminar equipos que pertenecen a la organización " "%s. Si necesitas más información, contacta con el propietario de la " "organización." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "No tienes permiso para mover equipos que son parte de la organización %s. " "Si necesitas más información, contacta con el propietario de la " "organización." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Los proyectos fijados aparecerán aquí" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notificaciones" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Tu dirección de correo ha sido actualizada" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Tu dirección de correo ha sido verificada" @@ -950,35 +1018,35 @@ msgstr "Configuración de notificaciones actualizada" msgid "dashboard.notifications.password-saved" msgstr "¡Contraseña guardada!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Ver notificaciones" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s integrantes" -#: src/app/main/ui/dashboard/file_menu.cljs:294 +#: src/app/main/ui/dashboard/file_menu.cljs:293 msgid "dashboard.open-in-new-tab" msgstr "Abrir en una pestaña nueva" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opciones" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Ordenar por rol" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Ordenar por estado" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:753 msgid "dashboard.organization-deleted" msgstr "La organización %s se ha borrado." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "Otros equipos" @@ -1002,11 +1070,11 @@ msgstr "Ahora eres el dueño del equipo." msgid "dashboard.permissions-change.viewer" msgstr "Ahora eres lector del equipo." -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "Proyectos Personales" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Anclar/Desanclar" @@ -1022,27 +1090,27 @@ msgstr "No se puede analizar el manifiest de la extensión" msgid "dashboard.plugins.try-plugin" msgstr "Prueba la extensión: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:786 msgid "dashboard.progress-notification.deleting-files" msgstr "Eliminando archivos…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:907 msgid "dashboard.progress-notification.restoring-files" msgstr "Restaurando archivos…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:787 msgid "dashboard.progress-notification.slow-delete" msgstr "Eliminación inesperadamente lenta" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:908 msgid "dashboard.progress-notification.slow-restore" msgstr "Restauración inesperadamente lenta" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Proyectos" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "¿Quieres borrar tu cuenta?" @@ -1050,114 +1118,114 @@ msgstr "¿Quieres borrar tu cuenta?" msgid "dashboard.removed-from-team" msgstr "Ya no eres parte del equipo “%s“." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "Vas a restaurar todos tus proyectos y archivos. Esto puede tardar un poco." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Restaurar todos los proyectos y archivos" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Restaurar todo" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:967 msgid "dashboard.restore-files-success-notification" msgstr "%s ficheros han sido restaurado correctamente." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Restaurar proyecto" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Vas a restaurar el proyecto %s y todos los archivos que contiene." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Restaurar proyecto" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:939, src/app/main/data/dashboard.cljs:966, src/app/main/data/dashboard.cljs:1003, src/app/main/ui/dashboard/file_menu.cljs:223 msgid "dashboard.restore-success-notification" msgstr "%s ha sido restaurado correctamente." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Guardar opciones" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Buscar…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Buscando “%s“…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Organizaciones" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "Equipos" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "AÑADIR A UNA ORGANIZACIÓN" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "Elige una organización:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" solo permite que los miembros de la organización se unan a sus " "equipos. Cualquier invitación pendiente a personas fuera de la organización " "será revocada." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Las invitaciones pendientes a usuarios externos serán canceladas." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Aquí encontrarás las organizaciones de las que eres parte donde tienes " "permiso para mover el equipo." -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "" "Aquí encontrarás las organizaciones de las que eres parte donde tienes " "permiso para crear o añadir equipos." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "Elige una organización" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "Añadir el equipo a una organización" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Cambiar el idioma de la interfaz" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Selecciona un tema" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Dark (por defecto)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Light" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Sistema" @@ -1221,15 +1289,15 @@ msgstr "Actualizar configuración" msgid "dashboard.settings.notifications.title" msgstr "Notificaciones" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Ver todos los archivos" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Histórico de versiones" -#: src/app/main/ui/dashboard/file_menu.cljs:129 +#: src/app/main/ui/dashboard/file_menu.cljs:128 msgid "dashboard.success-delete-file" msgid_plural "dashboard.success-delete-file" msgstr[0] "Tu archivo ha sido borrado con éxito" @@ -1239,7 +1307,7 @@ msgstr[1] "Tus archivos han sido borrado con éxito" msgid "dashboard.success-delete-project" msgstr "Tu proyecto ha sido borrado con éxito" -#: src/app/main/ui/dashboard/file_menu.cljs:124 +#: src/app/main/data/dashboard.cljs:553 msgid "dashboard.success-duplicate-file" msgid_plural "dashboard.success-delete-file" msgstr[0] "Tu archivo ha sido duplicado con éxito" @@ -1249,11 +1317,11 @@ msgstr[1] "Tus archivos han sido duplicado con éxito" msgid "dashboard.success-duplicate-project" msgstr "Tu proyecto ha sido duplicado con éxito" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:161, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Tu archivo ha sido movido con éxito" -#: src/app/main/ui/dashboard/file_menu.cljs:161 +#: src/app/main/ui/dashboard/file_menu.cljs:160 msgid "dashboard.success-move-files" msgstr "Tus archivos han sido movidos con éxito" @@ -1261,182 +1329,182 @@ msgstr "Tus archivos han sido movidos con éxito" msgid "dashboard.success-move-project" msgstr "Tu proyecto ha sido movido con éxito" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s ahora pertenece a %s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Información del equipo" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Integrantes del equipo" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s ya no forma parte de %s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Organización del equipo" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Añadir a una organización" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "No tienes permiso para añadir este equipo a ninguna de tus organizaciones." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Cambiar el equipo de organización" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Solo se puede invitar a personas que estén dentro de tu organización." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "Este equipo no pertenece a ninguna organización" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Eliminar equipo de la organización" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Proyectos del equipo" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Añadir a tu proyecto" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema visual" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Resultados de búsqueda" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Los archivos eliminados permanecerán en la papelera durante" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s días. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "Después de eso, serán eliminados permanentemente." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Si cambias de opinión, puedes restaurarlos o eliminarlos permanentemente " "desde el menú de cada archivo." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Escribe algo para buscar" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:321, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Despublicar Biblioteca" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Actualizar opciones" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:721 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "Ya no perteneces a la organización %s" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Beta" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "El renderizado WebGL puede mejorar el rendimiento, pero está en beta y " "puede ser menos estable. Pueden aparecer diferencias visuales entre el " "lienzo, las exportaciones (SVG/PDF) y el modo de vista." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Desactivado" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Activado" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Enviar comentarios" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Estado" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "Renderizado WebGL" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Activo" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Cuando se active este webhook se enviarán detalles del evento" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Sólo puedes borrar o modificar webhooks creados por ti." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Tipo de contenido" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Crear webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook creado con éxito." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Los webhooks son una forma simple de permitir notificar a otros sitios web " "y aplicaciones cuando ocurren ciertos eventos en Penpot. Enviaremos una " "petición POST a cada una de las URLs que indiques." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Pulsa el botón \"Crear webhook\" para añadir uno." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "No hay ningún webhook aún." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Tu cuenta" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Correo" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Tu nombre" @@ -1452,11 +1520,11 @@ msgstr "Atención" msgid "ds.component-subtitle" msgstr "Componentes a actualizar:" -#: src/app/main/ui/workspace/plugins.cljs:407, src/app/main/ui/workspace/plugins.cljs:470 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Permitir" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:401, src/app/main/ui/workspace/plugins.cljs:464 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Cancelar" @@ -1476,7 +1544,7 @@ msgstr "No hay tokens aplicables en sets o temas activos." msgid "ds.inputs.numeric-input.no-matches" msgstr "No hay coincidencias" -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Abrir lista de tokens" @@ -1498,15 +1566,15 @@ msgstr "El proveedor de autenticación no permitido para este perfil de usuario" msgid "errors.auth-provider-not-configured" msgstr "Proveedor de autenticación no configurado." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:757 msgid "errors.auth.unable-to-login" msgstr "Parece que no has iniciado sesión, o la sesión ha expirado." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "No se ha podido cargar la fuente %s" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "No se han podido cargar las fuentes %s" @@ -1518,57 +1586,63 @@ msgstr "No se puede cargar el archivo multimedia." msgid "errors.character-limit-exceeded" msgstr "Se ha superado el límite de caracteres" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1838 msgid "errors.circular-library-reference" msgstr "No se puede añadir la biblioteca: crearía una dependencia circular" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "La API del portapapeles no está disponible. Sirve Penpot mediante HTTPS " "para activar el acceso al portapapeles." -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Tu navegador no puede realizar esta operación" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "Acceso al portapapeles denegado. Permite el acceso al portapapeles en tu " "navegador para pegar contenido." -#: src/app/main/errors.cljs:782 +#: src/app/main/errors.cljs:959 msgid "errors.comment-error" msgstr "Ha habido un error con el comentario" -#: src/app/main/errors.cljs:851 +#: src/app/main/errors.cljs:587 +msgid "errors.connection-error" +msgstr "" +"No se puede conectar con el servidor. Comprueba tu conexión e inténtalo de " +"nuevo." + +#: src/app/main/errors.cljs:1028 msgid "errors.deprecated" msgstr "" "¡Lo sentimos! Este es un fichero antiguo que utiliza un tipo de recurso de " "Penpot obsoleto, y no puedes abrirlo." -#: src/app/main/errors.cljs:854 +#: src/app/main/errors.cljs:1031 msgid "errors.deprecated.contact.after" msgstr "para que podamos ayudarte." -#: src/app/main/errors.cljs:852 +#: src/app/main/errors.cljs:1029 msgid "errors.deprecated.contact.before" msgstr "Aunque Penpot ya no da soporte a este tipo de Recursos, puedes" -#: src/app/main/errors.cljs:853 +#: src/app/main/errors.cljs:1030 msgid "errors.deprecated.contact.text" msgstr "contactar con nosotros" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "No se puede soltar un set padre en una ruta hija propia." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Este correo ya está en uso" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Este correo ya está validado." @@ -1580,37 +1654,37 @@ msgstr "No puedes usar tu correo electrónico como contraseña" msgid "errors.email-does-not-match-invitation" msgstr "El correo no coincide con la invitación." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Dominio no permitido" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "El correo electrónico «%s» tiene varios reportes de rebote permanente." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "El email «%s» tiene reportes de spam o de rebote permanente." -#: src/app/main/data/exports/assets.cljs +#: src/app/main/data/exports/assets.cljs:64, src/app/main/data/exports/assets.cljs:93 msgid "errors.export-failed" msgstr "La exportación ha fallado. Inténtalo de nuevo en un momento." -#: src/app/main/data/exports/assets.cljs:346 +#: src/app/main/data/exports/assets.cljs:377 msgid "errors.export-too-large" msgstr "El archivo es demasiado grande, prueba con otra escala" -#: src/app/main/errors.cljs:974 +#: src/app/main/errors.cljs:1005 msgid "errors.feature-mismatch" msgstr "" "Parece que está abriendo un archivo que tiene la función '%s' habilitada, " "pero la versión actual de penpot no la admite o la tiene deshabilitada." -#: src/app/main/errors.cljs:832, src/app/main/errors.cljs:846 +#: src/app/main/errors.cljs:1009, src/app/main/errors.cljs:1023 msgid "errors.feature-not-supported" msgstr "Caracteristica no soportada: '%s'." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Debe contener como máximo %s caracteres." @@ -1618,7 +1692,7 @@ msgstr "Debe contener como máximo %s caracteres." msgid "errors.field-min-length" msgstr "Debe contener como mínimo 1 caracter." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Campo vacio" @@ -1626,24 +1700,24 @@ msgstr "Campo vacio" msgid "errors.field-not-all-whitespace" msgstr "Debe contener algún carácter diferente de espacio." -#: src/app/main/errors.cljs:824 +#: src/app/main/errors.cljs:1001 msgid "errors.file-feature-mismatch" msgstr "" "Parece que hay discordancia entre las features habilitadas y las features " "del fichero que se esta intentando abrir. Falta aplicar migraciones para " "'%s' antes de poder abrir el fichero." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "El nombre de la familia tipográfica solo puede contener letras, números, " "espacios, guiones, guiones bajos y puntos." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:565, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Ha ocurrido algún error." -#: src/app/main/errors.cljs:761 +#: src/app/main/errors.cljs:938 msgid "errors.internal-worker-error" msgstr "Ha ocurrido un problema con el web worker." @@ -1651,7 +1725,7 @@ msgstr "Ha ocurrido un problema con el web worker." msgid "errors.invalid-color" msgstr "Color no válido" -#: src/app/plugins/utils.cljs:517, ../common/src/app/common/schema/messages.cljc:33, ../common/src/app/common/schema/messages.cljc:87 +#: src/app/plugins/utils.cljs:541, ../common/src/app/common/schema/messages.cljc:33, ../common/src/app/common/schema/messages.cljc:87 msgid "errors.invalid-data" msgstr "Datos no válidos" @@ -1659,7 +1733,7 @@ msgstr "Datos no válidos" msgid "errors.invalid-email" msgstr "Por favor, escribe un email válido" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "El correo de confirmación debe coincidir" @@ -1695,11 +1769,11 @@ msgstr "Esta invitación puede haber sido cancelada o ha expirado." msgid "errors.ldap-disabled" msgstr "La autheticacion via LDAP esta deshabilitada." -#: src/app/main/errors.cljs:840, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1017, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Ha alcalzando el maximo de la quota '%s'. Contacte con soporte tecnico." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Se ha alcanzado el número máximo (%s) de correos electrónicos que se pueden " @@ -1719,21 +1793,21 @@ msgstr "" msgid "errors.media-type-not-allowed" msgstr "Parece que no es una imagen válida." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "El perfil que esta invitando tiene los emails silenciados (por reportes de " "spam o alto índice de rebote)." -#: src/app/main/errors.cljs:814 +#: src/app/main/errors.cljs:991 msgid "errors.migration-in-progress" msgstr "Migración en proceso" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:884 msgid "errors.only-creator-can-lock" msgstr "Solo el creador de la versión puede bloquearla" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:892 msgid "errors.only-creator-can-unlock" msgstr "Solo el creador de la versión puede desbloquearla" @@ -1747,7 +1821,7 @@ msgstr "" msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "El dueño de la organización no puede abandonarla." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "Esa organización no existe" @@ -1759,7 +1833,7 @@ msgstr "La contraseña de confirmación debe coincidir" msgid "errors.password-too-short" msgstr "La contraseña debe tener 8 caracteres como mínimo" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:865 msgid "errors.paste-data-validation" msgstr "Datos inválidos en el portapapeles" @@ -1767,13 +1841,13 @@ msgstr "Datos inválidos en el portapapeles" msgid "errors.profile-blocked" msgstr "El perfil esta blockeado" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Tu perfil tiene los emails silenciados (por reportes de spam o alto índice " "de rebote)." -#: src/app/main/errors.cljs:724 +#: src/app/main/errors.cljs:901 msgid "errors.profile-props-too-large" msgstr "" "La configuración de tu perfil es demasiado grande y no se puede guardar. " @@ -1783,31 +1857,80 @@ msgstr "" msgid "errors.registration-disabled" msgstr "El registro está actualmente desactivado." -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:715 msgid "errors.save-failed" msgstr "" -"El autoguardado se ha detenido en esta sesión, así que los cambios desde el último guardado correcto no se podrán guardar. Descarga el informe para ayudarnos a arreglarlo y recarga la página, o sal de este fichero y vuelve a abrirlo, para seguir editando." +"El autoguardado se ha detenido en esta sesión, así que los cambios desde el " +"último guardado correcto no se podrán guardar. Descarga el informe para " +"ayudarnos a arreglarlo y recarga la página, o sal de este fichero y vuelve " +"a abrirlo, para seguir editando." -#: src/app/main/errors.cljs +#: src/app/main/errors.cljs:706 +msgid "errors.save-permission-lost" +msgid_plural "errors.save-permission-lost" +msgstr[0] "" +"Ya no puedes editar este archivo, así que 1 cambio no se ha podido guardar. " +"Recarga la página para ver el archivo como está ahora." +msgstr[1] "" +"Ya no puedes editar este archivo, así que %s cambios no se han podido " +"guardar. Recarga la página para ver el archivo como está ahora." + +#: src/app/main/data/persistence.cljs:701 +msgid "errors.save-rejected-changes-undone" +msgstr "" +"Algunos cambios no se han podido guardar, así que se han deshecho tus " +"últimos cambios." + +#: src/app/main/data/persistence.cljs:686 +msgid "errors.save-rejected-changes-undone-in-file" +msgstr "" +"Algunos cambios en «%s» no se han podido guardar, así que se han deshecho " +"tus últimos cambios en ese archivo." + +#: src/app/main/data/persistence.cljs:696 +msgid "errors.save-rejected-damaged-data" +msgstr "" +"Esta página tiene datos dañados que no se pueden reparar automáticamente, " +"así que sus cambios no se pueden guardar y se han deshecho. Contacta con " +"soporte." + +#: src/app/main/data/persistence.cljs:609 +msgid "errors.save-retrying" +msgstr "Se ha perdido la conexión. Reintentando guardar los cambios." + +#: src/app/main/data/persistence.cljs:608 +msgid "errors.save-retrying-busy" +msgstr "El servidor está ocupado. Reintentando guardar tus cambios." + +#: src/app/main/errors.cljs:697 msgid "errors.save-still-retrying" msgstr "" -"Tus cambios aún no se han guardado porque Penpot no puede conectar con el servidor. Se conservan y Penpot seguirá intentando guardarlos. Mantén esta pestaña abierta hasta que se guarden." +"Tus cambios aún no se han guardado porque Penpot no puede conectar con el " +"servidor. Se conservan y Penpot seguirá intentando guardarlos. Mantén esta " +"pestaña abierta hasta que se guarden." -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:696 +msgid "errors.save-still-retrying-busy" +msgstr "" +"Tus cambios aún no se han guardado porque el servidor está ocupado. Se " +"conservan y Penpot seguirá intentando guardarlos. Mantén esta pestaña " +"abierta hasta que se guarden." + +#: src/app/main/errors.cljs:950 msgid "errors.svg-parser.invalid-svg" msgstr "El SVG no es válido o está mal formado" -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:996 msgid "errors.team-feature-mismatch" msgstr "Detectada funcionalidad incompatible '%s'" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "No hay miembros suficientes para salir del equipo, probablemente quieras " "borrarlo." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "El integrante que intentas designar no existe." @@ -1815,7 +1938,7 @@ msgstr "El integrante que intentas designar no existe." msgid "errors.team-leave.only-owner-can-delete" msgstr "Sólo puede borrar un equipo su propietario." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "El propietario no puede salir del equipo, tienes que asignar la propiedad a " @@ -1831,13 +1954,13 @@ msgstr "" msgid "errors.token-set-already-exists" msgstr "Ya existe un set con el mismo nombre" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "No se ha podido mover el set, un set con el mismo nombre ya existe en la " "esa ruta." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Ya existe un theme con este nombre" @@ -1849,13 +1972,17 @@ msgstr "El tema referencia sets que no existen: %s" msgid "errors.tokens.circular-reference" msgstr "El token tiene una referencia circular" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "El Line Height depende del Font Size. Añade un Font Size para obtener el " "valor computado." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/data/workspace/tokens/errors.cljs:62 +msgid "errors.tokens.division-by-zero" +msgstr "Valor de token no válido: división por cero" + +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "Este campo no puede estar vacío" @@ -1867,19 +1994,27 @@ msgstr "El valor del token no puede estar vacío" msgid "errors.tokens.error-parse" msgstr "Error al importar: No se pudo procesar el JSON." +#: src/app/main/data/workspace/tokens/errors.cljs:74 +msgid "errors.tokens.function-argument-out-of-range" +msgstr "Valor de token no válido: el argumento de %s() está fuera de rango" + #: src/app/main/data/workspace/tokens/errors.cljs:33, src/app/main/data/workspace/tokens/errors.cljs:38 msgid "errors.tokens.import-error" msgstr "Error al importar:" +#: src/app/main/data/workspace/tokens/errors.cljs:66 +msgid "errors.tokens.incompatible-units" +msgstr "Valor de token no válido: las unidades %s no se pueden combinar" + #: src/app/main/data/workspace/tokens/errors.cljs:54 msgid "errors.tokens.invalid-color" msgstr "Valor de color inválido: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "Valor de token no válido: solo puedes referenciar tokens tipo font-family" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Valor de peso de fuente inválido: usa valores numéricos (100-950) o nombres " @@ -1901,39 +2036,39 @@ msgstr "" "Los nombres de token solo pueden contener letras y dígitos separados por " "caracteres . y no pueden empezar con un signo $." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "Tipo de sombra no válida: solo se aceptan 'innerShadow' o 'dropShadow'" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Valor de token no válido: solo se aceptan none, Uppercase, Lowercase o " "Capitalize." -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "" "Valor de token no válido: solo none, underline y strike-through son " "aceptados" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "Valor no válido: debe hacer referencia a un token de sombra compuesto." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "Valor no válido: debe hacer referencia a un token tipográfico compuesto." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Valor de token no válido: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Referencias de tokens no encontradas: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "El grupo de %s colisiona con un token del mismo nombre en otro set activo." @@ -1945,31 +2080,35 @@ msgstr "No se han encontrado tokens, sets o temas en este archivo." msgid "errors.tokens.number-too-large" msgstr "Valor de token no valido. El valor resuelto es muy grande: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "La opacidad debe estar entre 0 y 100% o 0 y 1 (p.e. 50% o 0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "El token tiene una autoreferencia" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "El desenfoque (blur) de la sombra debe ser mayor o igual a 0." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "La extensión (spread) de la sombra debe ser mayor o igual a 0." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "Stroke width debe ser mayor o igual a 0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:70 +msgid "errors.tokens.unknown-function" +msgstr "Valor de token no válido: función desconocida %s" + +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Valor no válido: no se permite %." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Valor no válido: No se permiten unidades." @@ -1977,19 +2116,19 @@ msgstr "Valor no válido: No se permiten unidades." msgid "errors.unexpected-error" msgstr "Ha ocurrido un error inesperado." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Token desconocido" -#: src/app/main/errors.cljs:732 +#: src/app/main/errors.cljs:909 msgid "errors.version-already-locked" msgstr "Esta versión ya está bloqueada" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:876 msgid "errors.version-locked" msgstr "Esta versión está bloqueada y otras personas no pueden eliminarla" -#: src/app/main/errors.cljs:836 +#: src/app/main/errors.cljs:1013 msgid "errors.version-not-supported" msgstr "El fichero tiene un número de versión incompatible" @@ -2022,39 +2161,39 @@ msgstr "Al menos 1 letra mayúscula" msgid "errors.weak-password.too-short" msgstr "Al menos 8 caracteres" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL ha dejado de funcionar. Por favor, recarga la página para restaurarlo" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Ups! Se ha perdido el contexto del canvas" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Error de conexion, la url no es alcanzable" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "La URL no pasa la validación." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Hubo un problema en el último envío." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Error en la validación SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Timeout" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Error inesperado al validar" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Estado inesperado %s" @@ -2074,17 +2213,17 @@ msgstr "Descripción" msgid "feedback.description-placeholder" msgstr "Describe el motivo de tu comentario" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Estamos encantados de tenerte por aquí. Si necesitas ayuda, busca, escribe " "o pregunta lo que necesites." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Comunidad de Penpot" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Otras formas de contactarnos" @@ -2109,11 +2248,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Contáctanos" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Cuenta habilitada para responder todas tus dudas técnicas." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Cuenta de X para soporte" @@ -2133,57 +2272,99 @@ msgstr "Idea" msgid "feedback.type.issue" msgstr "Problema" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Descargar ficheros" +#: src/app/main/ui/exports/files.cljs:392 +msgid "files-download-modal.unsaved-changes" +msgstr "" +"Algunos de tus últimos cambios aún no se han guardado, así que esta " +"descarga no los incluirá." -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:467 +msgid "files-export-modal.cancel-export" +msgstr "Cancelar exportación" + +#: src/app/main/ui/exports/files.cljs:471 +msgid "files-export-modal.done" +msgstr "Listo" + +#: src/app/main/ui/exports/files.cljs:210 +msgid "files-export-modal.export-error" +msgstr "No se ha podido exportar: %s" + +#: src/app/main/ui/exports/files.cljs:246 +msgid "files-export-modal.exported.message" +msgstr "Revisa tu carpeta de descargas." + +#: src/app/main/ui/exports/files.cljs:239 +msgid "files-export-modal.exported.title" +msgid_plural "files-export-modal.exported.title" +msgstr[0] "1 archivo exportado" +msgstr[1] "%s archivos exportados" + +#: src/app/main/ui/exports/files.cljs:245 +msgid "files-export-modal.exporting.message" +msgstr "Cada archivo se descarga en cuanto está listo." + +#: src/app/main/ui/exports/files.cljs:238 +msgid "files-export-modal.exporting.title" +msgid_plural "files-export-modal.exporting.title" +msgstr[0] "Exportando 1 archivo" +msgstr[1] "Exportando %s archivos" + +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "" "Las recursos de las bibliotecas compartidas no se incluirán en la " "exportación." -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "Tratar los recursos como objetos básicos" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "" "Los ficheros con librerias compartidas se inclurán en el paquete de " "exportación y mantendrán los enlaces." -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "Exportar archivo + librerías" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "Al importar, podrás vincular bibliotecas existentes con el mismo nombre." -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "Vincular librerías al importar" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "" "Tu fichero será exportado con todos los recursos externos dentro de la " "libreria del propio fichero." -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "Incluir librerias compartidas dentro de las librerias del fichero" -#: src/app/main/ui/exports/files.cljs -msgid "files-download-modal.unsaved-changes" -msgstr "Algunos de tus últimos cambios aún no se han guardado, así que esta descarga no los incluirá." +#: src/app/main/ui/exports/files.cljs:179 +msgid "files-export-modal.status.exported" +msgstr "Exportado" + +#: src/app/main/ui/exports/files.cljs:186 +msgid "files-export-modal.status.waiting" +msgstr "En espera" + +#: src/app/main/ui/exports/files.cljs:381 +msgid "files-export-modal.title" +msgstr "Exportar archivo" #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "Ha ocurrido un error" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "Importar" @@ -2199,7 +2380,7 @@ msgstr "Importar atajos" msgid "import-shortcuts.imported" msgstr "Importado" -#: src/app/main/ui/settings/shortcuts.cljs:460, src/app/main/ui/settings/shortcuts.cljs:462 +#: src/app/main/ui/settings/shortcuts.cljs:511, src/app/main/ui/settings/shortcuts.cljs:513 msgid "import-shortcuts.invalid-file" msgstr "Este archivo no es una exportación de atajos válida." @@ -2209,7 +2390,9 @@ msgstr "No se encontraron diferencias entre los atajos actuales y los importados #: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:153 msgid "import-shortcuts.skipped-entries" -msgstr "Estas entradas no se pueden importar: no corresponden a ningún atajo de Penpot o usan una combinación reservada." +msgstr "" +"Estas entradas no se pueden importar: no corresponden a ningún atajo de " +"Penpot o usan una combinación reservada." #: src/app/main/ui/components/color_input.cljs:31 msgid "inspect.attributes.color" @@ -2243,7 +2426,7 @@ msgstr "Previsualización de la imagen de relleno" msgid "inspect.attributes.shadow" msgstr "Sombra" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Tamaño y posición" @@ -2251,43 +2434,31 @@ msgstr "Tamaño y posición" msgid "inspect.attributes.stroke" msgstr "Borde" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centro" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Interior" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Exterior" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografía" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Familia tipográfica" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Tamaño de fuente" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Estilo de fuente" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Espaciado de letras" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Interlineado" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "Varias Familias tipográficas" @@ -2303,11 +2474,11 @@ msgstr "Tachado" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Subrayar" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Primera en mayúscula" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Minúsculas" @@ -2319,7 +2490,7 @@ msgstr "Ninguna" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Sin asignar" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Mayúsculas" @@ -2331,6 +2502,10 @@ msgstr "Propiedades de la variante" msgid "inspect.attributes.variants" msgstr "Propiedades de las variantes" +#: src/app/main/ui/inspect/code.cljs:322, src/app/main/ui/inspect/code.cljs:365 +msgid "inspect.code.expand-panel" +msgstr "Expandir panel" + #: src/app/main/ui/inspect/right_sidebar.cljs:169 msgid "inspect.color-space-label" msgstr "Seleccionar espacio de color" @@ -2445,23 +2620,23 @@ msgstr "Valor resuelto:" msgid "inspect.tabs.styles.variants-panel" msgstr "Propiedades de las variantes" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "Crear nuevo token de acceso" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "Pulsa el botón \"Crear nuevo token de acceso\" para generar uno." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "Todavía no tienes ningún token." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "Tokens de acceso personales" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "Los tokens de acceso personales funcionan como una alternativa a nuestro " @@ -2546,53 +2721,53 @@ msgstr "La clave MCP expirará el %s" msgid "integrations.mcp-key.will-not-expire" msgstr "La clave MCP no tiene fecha de expiración" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "El servidor MCP de Penpot permite que los clientes MCP interactúen " "directamente con los archivos de diseño de Penpot." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "Cómo configurar clientes MCP" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "Esta es la URL del servidor que necesitarás configurar en tu cliente MCP " "para conectarlo al servidor MCP de Penpot." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "Regenerar clave MCP" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "Clave MCP" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "La clave MCP es necesaria para la configuración del cliente MCP" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Estado" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Deshabilitado" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Habilitado" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "La clave MCP utilizada para conectarse al servidor MCP ha expirado. Como " "resultado, no se puede establecer la conexión." -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "" "Por favor, regenera la clave MCP y actualiza la configuración de tu cliente " @@ -2602,10 +2777,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "Servidor MCP" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "Beta" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "Nombre" @@ -2656,7 +2827,7 @@ msgstr "Regenerar clave MCP" msgid "integrations.regenerate-mcp-key.title.created" msgstr "Clave MCP regenerada" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "Integraciones" @@ -2674,47 +2845,116 @@ msgstr "El token expirará el %s" msgid "integrations.token.will-not-expire" msgstr "El token no tiene fecha de expiración" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/exports/files.cljs:223 +msgid "jobs.export-cancelled" +msgstr "Exportación cancelada" + +#: src/app/main/data/imports.cljs:376 +msgid "jobs.import-cancelled" +msgstr "Importación cancelada" + +#: src/app/main/ui/jobs/progress.cljs:26 +msgid "jobs.progress.stage.colors" +msgstr "Procesando colores" + +#: src/app/main/ui/jobs/progress.cljs:25 +msgid "jobs.progress.stage.components" +msgstr "Procesando componentes" + +#: src/app/main/ui/jobs/progress.cljs:21 +msgid "jobs.progress.stage.files" +msgstr "Procesando archivos" + +#: src/app/main/ui/jobs/progress.cljs:32 +msgid "jobs.progress.stage.manifest" +msgstr "Leyendo el paquete" + +#: src/app/main/ui/jobs/progress.cljs:23 +msgid "jobs.progress.stage.media" +msgstr "Procesando media" + +#: src/app/main/ui/jobs/progress.cljs:31 +msgid "jobs.progress.stage.metadata" +msgstr "Leyendo metadatos" + +#: src/app/main/ui/jobs/progress.cljs:22 +msgid "jobs.progress.stage.pages" +msgstr "Procesando páginas" + +#: src/app/main/ui/jobs/progress.cljs:30 +msgid "jobs.progress.stage.relations" +msgstr "Procesando relaciones de biblioteca" + +#: src/app/main/ui/jobs/progress.cljs:29 +msgid "jobs.progress.stage.storage-objects" +msgstr "Procesando objetos de almacenamiento" + +#: src/app/main/ui/jobs/progress.cljs:24 +msgid "jobs.progress.stage.thumbnails" +msgstr "Procesando miniaturas" + +#: src/app/main/ui/jobs/progress.cljs:28 +msgid "jobs.progress.stage.tokens" +msgstr "Procesando tokens" + +#: src/app/main/ui/jobs/progress.cljs:27 +msgid "jobs.progress.stage.typographies" +msgstr "Procesando tipografías" + +#: src/app/main/ui/jobs/progress.cljs:34 +msgid "jobs.progress.stage.unknown" +msgstr "Trabajando" + +#: src/app/main/ui/jobs/progress.cljs:33 +msgid "jobs.progress.stage.upload" +msgstr "Subiendo archivo…" + +#: src/app/main/ui/dashboard/import.cljs:328, src/app/main/ui/exports/files.cljs:103 +#, unused +msgid "jobs.queued" +msgstr "Esperando hueco para el job" + +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Marcar todo como leído" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Atajos de teclado" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Acerca de Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Aceptar" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Activo" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Añadir" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Añadir fuente personalizada" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Añadiendo..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Administración" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Todo" @@ -2722,17 +2962,17 @@ msgstr "Todo" msgid "labels.and" msgstr "y" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Volver" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Parece que necesitas esperar un poco y volverlo a intentar; estamos " "realizando operaciones de mantenimiento en nuestros servidores." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Error del servidor (Bad Gateway)" @@ -2744,23 +2984,23 @@ msgstr "Desenfoque" msgid "labels.blur-effects" msgstr "Efectos de desenfoque" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Cancelar" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "Comprobar actualizaciones" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "Comprobando actualizaciones..." -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Cerrar" @@ -2768,7 +3008,7 @@ msgstr "Cerrar" msgid "labels.code" msgstr "Código" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Colapsar" @@ -2780,15 +3020,15 @@ msgstr "Color" msgid "labels.comment" msgstr "Comentario" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Comentarios" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Comunidad" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Comunidad y contribuciones" @@ -2800,19 +3040,19 @@ msgstr "Calculado" msgid "labels.confirm-password" msgstr "Confirmar contraseña" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Contacta con soporte" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Contáctanos" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:234, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continuar" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Puedes continuar con una cuenta de Penpot" @@ -2824,23 +3064,23 @@ msgstr "Copiar" msgid "labels.copy-color" msgstr "Copiar color" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Copiar enlace" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "© Kaleidos, 2019-presente" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Crear" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Crea un nuevo equipo" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Introduce un nuevo nombre de equipo" @@ -2848,7 +3088,7 @@ msgstr "Introduce un nuevo nombre de equipo" msgid "labels.current" msgstr "Valor Actual" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Panel" @@ -2856,7 +3096,7 @@ msgstr "Panel" msgid "labels.default" msgstr "Valor por defecto" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:334, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:263, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Borrar" @@ -2868,47 +3108,43 @@ msgstr "Eliminar comentario" msgid "labels.delete-comment-thread" msgstr "Eliminar hilo" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Eliminar invitation" -#: src/app/main/ui/dashboard/file_menu.cljs:289 +#: src/app/main/ui/dashboard/file_menu.cljs:288 msgid "labels.delete-multi-files" msgstr "Borrar %s archivos" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "¿Seguro que quieres eliminar tu foto de perfil?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "Eliminar foto de perfil" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Eliminados" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Desarrollo" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Descartar" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:560, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "Descargar %s" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "Descargar" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "Descargando archivo…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Borradores" @@ -2916,31 +3152,35 @@ msgstr "Borradores" msgid "labels.duplicate" msgstr "Duplicar" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Editar" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Edición" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Vacío" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Error" -#: src/app/main/data/workspace/versions.cljs:314 +#: src/app/main/data/workspace/versions.cljs:332 msgid "labels.exit" msgstr "Salir" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/viewer/thumbnails.cljs:96 +msgid "labels.expand" +msgstr "Expandir" + +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Expirada" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exportar" @@ -2952,7 +3192,7 @@ msgstr "El modulo de recepción de opiniones esta deshabilitado" msgid "labels.feedback-sent" msgstr "Opinión enviada" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2960,63 +3200,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Relleno" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "Flex layout" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Familia de fuente" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Estilos" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fuentes" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Repositorio de Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Danos tu opinión" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Volver" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Diseño gráfico" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "Grid layout" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Centro de ayuda" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Ayuda y aprendizaje" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Ocultar" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Ocultar comentarios resueltos" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importar" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inactivo" @@ -3024,61 +3264,61 @@ msgstr "Inactivo" msgid "labels.info" msgstr "Información" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Fuentes instaladas" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "Integraciones" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Ha ocurrido algo extraño." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "" "Puedes reintentar la operación o contacta con soporte para reportar el " "error." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Error interno" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Invitaciones" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Idioma" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Layout" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Centro de aprendizaje" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Bibliotecas y Plantillas" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:133 msgid "labels.loading" msgstr "Cargando…" -#: src/app/main/ui/workspace/sidebar/versions.cljs:251 +#: src/app/main/ui/workspace/sidebar/versions.cljs:257 msgid "labels.lock" msgstr "Bloquear" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Entra o regístrate" @@ -3086,19 +3326,19 @@ msgstr "Entra o regístrate" msgid "labels.login" msgstr "Entrar" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Salir" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Integrante" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Integrantes" @@ -3110,43 +3350,43 @@ msgstr "Mencionar" msgid "labels.new-password" msgstr "Nueva contraseña" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Siguiente" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "" "Esta instancia requiere un paso de configuración que se incluye en la " "versión más reciente." -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "Echa un vistazo a nuestra" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "Penpot no puede iniciarse aún." -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "technical guide" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "" "Penpot no está disponible temporalmente debido a un problema del sistema. " "Por favor, inténtalo de nuevo en unos momentos." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "¡Ya estás al día! Nuevas notificaciones de comentarios aparecerán aquí." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "No hay invitaciones pendientes." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Reúne a tu gente y construid juntos grandes cosas." @@ -3154,27 +3394,27 @@ msgstr "Reúne a tu gente y construid juntos grandes cosas." msgid "labels.no-matches" msgstr "No hay coincidencias" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "¡Uy!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notificaciones" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 archivo" msgstr[1] "%s archivos" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 tablero" msgstr[1] "%s tableros" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 proyecto" @@ -3184,43 +3424,43 @@ msgstr[1] "%s proyectos" msgid "labels.old-password" msgstr "Contraseña anterior" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "Abrir" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Opciones" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Otra (especifica)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Otra" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Propiedad" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Contraseña" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Pendiente" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Registro de cambios de Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot hub" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Proyectos fijados" @@ -3228,31 +3468,31 @@ msgstr "Proyectos fijados" msgid "labels.post" msgstr "Publicar" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Anterior" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Diseño de Producto o UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Gestión de Productos" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Perfil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Proyectos" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Recientes" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Referencia" @@ -3260,27 +3500,27 @@ msgstr "Referencia" msgid "labels.refresh" msgstr "Recargar" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Notas de versión" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:666, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Recargar página" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Quitar" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Eliminar integrante" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:297, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:234, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Renombrar" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Renombra el equipo" @@ -3304,55 +3544,55 @@ msgstr "nueva respuesta" msgid "labels.reply.thread" msgstr "Responder" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Reenviar" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Reenviar invitacion" -#: src/app/main/data/workspace/versions.cljs:246, src/app/main/data/workspace/versions.cljs:319, src/app/main/ui/workspace/sidebar/versions.cljs:238 +#: src/app/main/data/workspace/versions.cljs:260, src/app/main/data/workspace/versions.cljs:337, src/app/main/ui/workspace/sidebar/versions.cljs:244 msgid "labels.restore" msgstr "Restaurar" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Reintentar" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rol" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Guardar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Buscar" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Buscar fuente" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Selecciona una opción" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Enviar" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Enviando…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Estamos en una operación de mantenimiento programado de nuestros sistemas." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "El servicio no está disponible" @@ -3360,74 +3600,74 @@ msgstr "El servicio no está disponible" msgid "labels.sets" msgstr "Sets" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Configuración" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Sombra" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Compartir" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibliotecas" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Mostrar" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Mostrar todos los comentarios" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Mostrar lista de comentarios" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Mostrar sólo tus menciones" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Mostrar sólo tus comentarios" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/import.cljs:703 +#: src/app/main/ui/dashboard/import.cljs:808 msgid "labels.skip" msgstr "Saltar" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Recursos" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "" "El inicio de sesión con el proveedor de identidad de tu organización no se " "completó. Es posible que el proveedor no esté disponible o que tu cuenta " "aún no esté en su directorio." -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "Intentar de nuevo" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "No pudimos iniciar sesión en %s" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Comenzar" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" @@ -3435,7 +3675,7 @@ msgstr "Status" msgid "labels.stroke" msgstr "Borde" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Estudiante o profesor" @@ -3447,11 +3687,11 @@ msgstr "Estilos" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Intercambiar" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:657 msgid "labels.team-management" msgstr "Gestión de equipos" @@ -3459,59 +3699,55 @@ msgstr "Gestión de equipos" msgid "labels.text" msgstr "Texto" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Temas" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutoriales" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Tipografía" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Error desconocido" -#: src/app/main/ui/workspace/sidebar/versions.cljs:245 +#: src/app/main/ui/workspace/sidebar/versions.cljs:251 msgid "labels.unlock" msgstr "Desbloquear" -#: src/app/main/ui/dashboard/file_menu.cljs:283 +#: src/app/main/ui/dashboard/file_menu.cljs:282 msgid "labels.unpublish-multi-files" msgstr "Despublicar %s archivos" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Actualizar" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Actualiza el equipo" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Cargar" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Cargar fuente" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Subiendo…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "Subiendo archivo…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variante" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Notas versión %s" @@ -3519,7 +3755,7 @@ msgstr "Notas versión %s" msgid "labels.view-only" msgstr "Solo lectura" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Lector" @@ -3533,7 +3769,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s aviso" msgstr[1] "%s avisos" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -3541,11 +3777,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Escribir un nuevo comentario" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(tú)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Tu cuenta" @@ -3635,6 +3871,16 @@ msgstr "" msgid "loader.tips.10.title" msgstr "Soporte de Plugins" +#: src/app/main/ui/mcp_authorize.cljs:155 +msgid "mcp.oauth.access" +msgstr "" +"Este cliente puede leer y modificar diseños mediante conexiones MCP " +"establecidas desde Penpot." + +#: src/app/main/ui/mcp_authorize.cljs:133 +msgid "mcp.oauth.cancelled" +msgstr "Conexión cancelada" + #: src/app/main/ui/workspace/colorpicker.cljs:494 msgid "media.choose-image" msgstr "Elegir imagen" @@ -3690,7 +3936,7 @@ msgstr "" "Eres el único miembro de algunos equipos. Esos equipos se van a borrar, " "junto con sus proyectos y ficheros." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "ANTES DE ABANDONAR LA ORGANIZACIÓN" @@ -3698,78 +3944,78 @@ msgstr "ANTES DE ABANDONAR LA ORGANIZACIÓN" msgid "modals.before-leave-organization.warning" msgstr "Se van a borrar todos los equipos en los que eres el único miembro." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Máximo" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Verificar el nuevo correo" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "Enviaremos un mensaje a tu correo actual “%s” para verificar tu identidad." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Nuevo correo" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Cambiar correo" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Cambiar tu correo" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Crear webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Crear webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Payload URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Cancelar y mantener mi cuenta" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Si, borrar mi cuenta" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Si borras tu cuenta perderás todos tus proyectos y archivos." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "Esta acción es irreversible. Tu cuenta, suscripción y organizaciones se " "eliminarán inmediatamente. También perderás el acceso a tus proyectos y " "archivos actuales." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organizaciones que se eliminarán" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s miembros" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s equipos" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "¿Seguro que quieres borrar tu cuenta?" @@ -3805,47 +4051,47 @@ msgstr "¿Seguro que quieres borrar esta nota?" msgid "modals.delete-component-annotation.title" msgstr "Borrar nota" -#: src/app/main/ui/dashboard/file_menu.cljs:155 +#: src/app/main/ui/dashboard/file_menu.cljs:154 msgid "modals.delete-file-confirm.accept" msgstr "Borrar archivo" -#: src/app/main/ui/dashboard/file_menu.cljs:154 +#: src/app/main/ui/dashboard/file_menu.cljs:153 msgid "modals.delete-file-confirm.message" msgstr "¿Seguro que quieres borrar este archivo?" -#: src/app/main/ui/dashboard/file_menu.cljs:153 +#: src/app/main/ui/dashboard/file_menu.cljs:152 msgid "modals.delete-file-confirm.title" msgstr "Borrando archivo" -#: src/app/main/ui/dashboard/file_menu.cljs:149 +#: src/app/main/ui/dashboard/file_menu.cljs:148 msgid "modals.delete-file-multi-confirm.accept" msgstr "Borrar archivos" -#: src/app/main/ui/dashboard/file_menu.cljs:148 +#: src/app/main/ui/dashboard/file_menu.cljs:147 msgid "modals.delete-file-multi-confirm.message" msgstr "¿Seguro que quieres borrar %s archivos?" -#: src/app/main/ui/dashboard/file_menu.cljs:147 +#: src/app/main/ui/dashboard/file_menu.cljs:146 msgid "modals.delete-file-multi-confirm.title" msgstr "Borrando %s archivos" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Estas seguro de querer eliminar esta estilo de fuente? Dejara de cargar si " "es usada en algun fichero." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Eliminando estilo de fuente" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "¿Seguro que quieres eliminar esta fuente? Si está siendo usada en algún " "fichero no se cargará." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Eliminando fuente" @@ -3855,19 +4101,19 @@ msgstr "" "¿Estás seguro de que deseas eliminar este equipo que forma parte de la " "organización %s?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "¿Seguro que quieres borrar esta página?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Borrar página" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "¿Seguro que quieres borrar las páginas seleccionadas?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "Borrar páginas" @@ -3883,31 +4129,31 @@ msgstr "¿Seguro que quieres eliminar este proyecto?" msgid "modals.delete-project-confirm.title" msgstr "Eliminar proyecto" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Borrar archivo" msgstr[1] "Borrar archivos" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "No está activa en ningún fichero." msgstr[1] "No están activas en ningún fichero." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Esta biblioteca está activa aquí: " msgstr[1] "Estas bibliotecas están activas aquí: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "¿Seguro que quieres borrar este archivo?" msgstr[1] "¿Seguro que quieres borrar estos archivos?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Borrando archivo" @@ -3927,19 +4173,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Eliminando equipo" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Eliminando miembro" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "¿Seguro que quieres eliminar este integrante del equipo?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Eliminar integrante del equipo" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3949,23 +4195,23 @@ msgstr[1] "" "Los elementos que hayan sido utilizados en esos archivos permanecerán allí " "(¡ningún diseño se romperá!)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Borrar webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "¿Seguro que quieres borrar este webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Borrando webhook" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Modificar webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Modificar webhook" @@ -3981,15 +4227,15 @@ msgstr "Importar tokens" msgid "modals.import-library-tokens.title" msgstr "¿Importar tokens de la biblioteca?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Enviar invitacion" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Emails, separados por coma" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Algunas direcciones de correo ya se encuentran entre los miembros. Estas " @@ -4013,99 +4259,99 @@ msgstr "" "todas esas personas ya son miembros del equipo. Si necesitas más " "información, contacta con la persona propietaria de la organización." -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "Direcciones que no recibirán una invitación" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "Cancelar" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "Solo se puede invitar a miembros existentes de la organización." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "Enviar invitaciones" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "Algunas invitaciones no se pueden enviar" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "Algunas personas de tu lista no recibirán las invitaciones." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Puedes invitar miembros al equipo para que puedan acceder a este fichero y " "a todos los demás ficheros del equipo." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Invitar a miembros al equipo" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Al ser la única persona integrante de este equipo, el equipo será eliminado " "junto con sus proyectos y archivos." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "¿Seguro que quieres abandonar el equipo %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "No puede abandonar el equipo si no hay otro miembro al que promocionar a " "dueño. Quizás quiere borrar el equipo." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Tienes la propiedad de este equipo. Por favor selecciona otra persona " "integrante para promover al rol Propiedad." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promocionar y abandonar" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Selecciona un miembro a promocionar" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Antes de que abandones" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Abandonar el equipo" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "¿Seguro que quieres abandonar este equipo?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Abandonando el equipo" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Tienes la propiedad de algunos equipos de esta organización. Por favor " "selecciona otra persona integrante para promover al rol Propiedad." -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Eres el único miembro de algunos equipos. Esos equipos se van a borrar, " "junto con sus proyectos y ficheros." -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "Además, tienes la propiedad de algunos equipos de esta organización. Por " @@ -4125,57 +4371,57 @@ msgstr "" msgid "modals.leave-organization-confirm.title" msgstr "¿Abandonando la organización %s?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Mover" msgstr[1] "Mover" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "¿Seguro que quieres mover esta biblioteca?" msgstr[1] "¿Seguro que quieres mover estas bibliotecas?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Mover biblioteca" msgstr[1] "Mover bibliotecas" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Desplazamiento" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Transferir propiedad" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Si transfieres la propiedad, tu rol cambiará a Administración, perdiendo " "algunos permisos sobre este equipo. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Tienes la propiedad de este equipo. ¿Seguro que quieres transferir la " "propiedad del equipo a %s?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Nueva propiedad del equipo" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publicar" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Tu biblioteca está vacía. ¿Seguro que quieres publicarla?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publicar biblioteca vacía" @@ -4197,23 +4443,23 @@ msgstr "¿Estás seguro de que quieres eliminar el equipo %s de la organización msgid "modals.remove-team-organization.title" msgstr "ELIMINAR EQUIPO DE LA ORGANIZACIÓN" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Mínimo" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Despublicar" msgstr[1] "Despublicar" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "¿Seguro que quieres despublicar esta biblioteca?" msgstr[1] "¿Seguro que quieres despublicar estas bibliotecas?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Despublicar biblioteca" @@ -4303,38 +4549,38 @@ msgstr "Activar" msgid "nitrate.code-activation.title" msgstr "Activar Enterprise" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "Contactar con ventas" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "" "Para cambiar a este plan, contacta con nuestro equipo de ventas. Te " "ayudaremos a actualizar tu suscripción y a asegurarnos de que todo esté " "configurado correctamente." -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Tu organización será eliminada." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "" "Tu almacenamiento total, el historial de versiones automático y el período " "de recuperación de archivos serán limitados." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "" "Los equipos, proyectos y archivos dejarán de pertenecer a la organización " "pero seguirán disponibles." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "Al bajar de plan:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "¿Cambiar al plan %s?" @@ -4352,17 +4598,17 @@ msgstr "Contáctanos para probar Enterprise durante 14 días:" msgid "nitrate.form.contact-upgrade" msgstr "Contáctanos para actualizar a Enterprise:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "" "Ponte en contacto con nosotros para probar el plan Enterprise durante 14 " "días:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "Ponte en contacto con nosotros para actualizar al plan Enterprise:" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "" "No se te cobrará nada ahora. El pago se procesará al finalizar el período " @@ -4382,7 +4628,7 @@ msgstr "" msgid "nitrate.form.enterprise-feature-3" msgstr "Total libertad open-source, con la gobernanza que tu organización necesita" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "Todo lo que tus equipos necesitan para trabajar de forma segura a escala%s" @@ -4390,7 +4636,7 @@ msgstr "Todo lo que tus equipos necesitan para trabajar de forma segura a escala msgid "nitrate.form.enterprise.price" msgstr "por miembro al mes" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "Pruébalo gratis durante 14 días" @@ -4406,7 +4652,7 @@ msgstr "Empieza a utilizar Enterprise" msgid "nitrate.form.subscribe-with-code" msgstr "Suscribete con un código de activación" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "Desbloquea las funciones Enterprise" @@ -4418,7 +4664,7 @@ msgstr "Pruébalo gratis durante 14 días" msgid "nitrate.modal-success.title" msgstr "¡Bienvenido a Enterprise!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "Activo hasta el %s" @@ -4426,25 +4672,25 @@ msgstr "Activo hasta el %s" msgid "nitrate.subscription.renew-success" msgstr "Tu suscripción de Enterprise se ha renovado." -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "Cancelar subscripción" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "" "Para cancelar tu suscripción a Enterprise, ponte en contacto con nosotros " "en:" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "Renovar con código de activación" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Esta página no existe" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Error 404" @@ -4462,11 +4708,11 @@ msgstr "Hemos enviado un email de recuperación a" msgid "not-found.login.sent-recovery-check" msgstr "Revisa tu correo y haz clic en el enlace para crear una nueva contraseña." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Regístrate gratis" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "¡Y comienza a usar Penpot en segundos!" @@ -4474,67 +4720,67 @@ msgstr "¡Y comienza a usar Penpot en segundos!" msgid "not-found.made-with-love" msgstr "Hecho con AMOR y Software Libre" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Ya has solicitado acceso a este archivo." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Ya has solicitado acceso a este archivo o a otros archivos o proyectos del " "mismo equipo." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Ya has solicitado acceso a este proyecto o a otros proyectos o archivos del " "mismo equipo." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Ya has solicitado acceso a este proyecto." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "SOLICITAR ACCESO" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Recuerda que, si el propietario la aprueba, entrarás en el equipo." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "¡Tu solicitud se ha enviado correctamente!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "No tienes permiso para acceder a este archivo." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Ir a tus Proyectos Personales" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Si el propietario lo aprueba, te invitará al equipo." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Archivo de Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "No tienes permiso para acceder a este proyecto." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROYECTO" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Para acceder a este archivo, puedes pedir permiso al propietario del equipo." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "" "Para acceder a este proyecto, puedes pedir permiso al propietario del " @@ -4550,31 +4796,31 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Una nueva versión está disponible." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "Esta invitación ya no está disponible." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Invitación eliminada con éxito" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Invitación enviada con éxito" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Enlace de invitacion copiado" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "No puedes borrar tu perfil. Reasigna tus equipos antes de seguir." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Perfil guardado correctamente!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Verificación de email enviada a %s. Comprueba tu correo." @@ -4612,7 +4858,7 @@ msgstr "Nombre del equipo" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Crear equipo" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Invitar integrantes" @@ -4622,98 +4868,98 @@ msgstr "" "Personas de desarrollo, diseño, gestión... todo el mundo es bienvenido. " "Podrás invitar a más gente posteriormente." -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Invitar con el rol:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "¡Empecemos!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Generando código a partir de diseños" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Creando sistemas de diseño" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototipado" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Diseñando el UX/UI de una aplicación" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "¿Para qué utilizarás Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Cuéntanos un poco sobre tí para ayudarnos a que Penpot se adapte mejor a " "tí. Tus respuestas nos ayudarán a priorizar nuevas funcionalidades y a " "saber cómo podemos acompañarte mejor." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Ayúdanos a conocerte" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "¿Cuál de estas herramientas utilizas más?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "¿Qué tipo de trabajo haces?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "¿Cuál es el tamaño de tu empresa?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "¿Por dónde te apetecería empezar?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1001 - 5000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 - 500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 - 100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 - 1000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "Sólo yo" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5001+" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Educación" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Personal" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Trabajo" @@ -4763,15 +5009,15 @@ msgstr "Empezar a diseñar" msgid "options.deleted-token-with-name" msgstr "El token {%s} no existe o ha sido borrado." -#: src/app/plugins/utils.cljs:555 +#: src/app/plugins/utils.cljs:579 msgid "plugins.validation.invalid-value" msgstr "esperado %s, obtenido %s (%s)" -#: src/app/plugins/utils.cljs:569 +#: src/app/plugins/utils.cljs:593 msgid "plugins.validation.message" msgstr "El campo %s no es válido: %s" -#: src/app/plugins/utils.cljs:558 +#: src/app/plugins/utils.cljs:582 msgid "plugins.validation.received-value" msgstr "obtenido %s (%s)" @@ -4795,11 +5041,11 @@ msgstr "Restablecer todos los atajos por defecto" msgid "restore-shortcuts.restore" msgstr "Restablecer" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Desvincular" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Varios" @@ -4811,35 +5057,35 @@ msgstr "Eliminar color" msgid "settings.select-this-color" msgstr "Seleccionar elementos que usan este estilo" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Añadir comentarios" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Alinear abajo" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Alinear al centro horizontalmente" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Alinear a la izquierda" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Alinear a la derecha" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Alinear arriba" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Alinear al centro verticalmente" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Crear tablero desde la selección" @@ -4847,55 +5093,55 @@ msgstr "Crear tablero desde la selección" msgid "shortcuts.bold" msgstr "Alternar negrita" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Diferencia" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Exclusión" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Interescción" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Unión" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Enviar detrás" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Enviar al fondo" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Mover al frente" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Mover hacia adelante" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Limpiar historial" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copiar" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Copiar enlace" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Copiar propiedades" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Crear componente / variante" @@ -4903,63 +5149,63 @@ msgstr "Crear componente / variante" msgid "shortcuts.create-new-project" msgstr "Crear nuevo" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Cortar" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Reducir zoom" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Borrar" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Borrar nodo" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "Borrar nodo y segmentos" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "Deseleccionar todo" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Desvincular componente" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "Deshabilitar atajo" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "Deshabilitado" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "Atajo deshabilitado" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "Flecha" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Curva" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Tablero" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "Linea" @@ -4967,63 +5213,71 @@ msgstr "Linea" msgid "shortcuts.draw-nodes" msgstr "Dibujar nodos" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Ruta" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rectángulo" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Texto" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplicar" -#: src/app/main/ui/shortcuts.cljs:589 +#: src/app/main/ui/shortcuts.cljs:714 msgid "shortcuts.edit" msgstr "Editar %s" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "Ya asignado a \"%s\". Al guardar, pasará a esta acción." -#: src/app/main/ui/shortcuts.cljs:836 +#: src/app/main/ui/shortcuts.cljs:771 msgid "shortcuts.edit-modal.reserved" msgstr "Penpot reserva esta combinación y no se puede asignar." -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "Al guardar, este atajo volverá a su valor por defecto:" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "Atajo disponible." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "Personalízalos" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Cancelar" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Exportar elementos" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/shortcuts.cljs:187 +msgid "shortcuts.find" +msgstr "Buscar" + +#: src/app/main/data/workspace/shortcuts.cljs:191 +msgid "shortcuts.find-and-replace" +msgstr "Buscar y reemplazar" + +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Zoom abarcar todo" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Voltear horizontalmente" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Voltear verticalmente" @@ -5047,27 +5301,27 @@ msgstr "Ir a bibliotecas compartidas" msgid "shortcuts.go-to-search" msgstr "Buscar" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Agrupar" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribuir horizontalmente" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Mostrar/ocultar interfaz" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "Importar/Exportar" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Aumentar zoom" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Insertar imagen" @@ -5075,11 +5329,11 @@ msgstr "Insertar imagen" msgid "shortcuts.italic" msgstr "Alternar cursiva" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Unir nodos" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "Pulsa las nuevas teclas" @@ -5087,39 +5341,39 @@ msgstr "Pulsa las nuevas teclas" msgid "shortcuts.line-through" msgstr "Alternar tachado" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Convertir en esquina" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Convertir en curva" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Máscara" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Fusionar nodos" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Mover" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Mover rápidamente hacia abajo" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Mover rápidamente hacia la izquierda" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Mover rápidamente hacia la derecha" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Mover rápidamente hacia arriba" @@ -5127,157 +5381,161 @@ msgstr "Mover rápidamente hacia arriba" msgid "shortcuts.move-nodes" msgstr "Mover nodo" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Mover hacia abajo" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Mover hacia la izquierda" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Mover hacia la derecha" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Mover hacia arriba" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Siguiente tablero" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "No hay atajos deshabilitados." -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "Aún no hay atajos personalizados. Personaliza uno desde la pestaña Todo." -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "No hay atajos que coincidan con tu búsqueda." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "No hay atajos que coincidan con tu búsqueda." -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Opacidad 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Opacidad 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Opacidad 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Opacidad 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Opacidad 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Opacidad 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Opacidad 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Opacidad 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Opacidad 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Opacidad 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Abrir selector de color" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Comentarios" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Ir al dashboard" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Ir al inspector" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Ir a interacciones" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Ir al modo de visualización" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Ir al área de trabajo" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " o " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "Reasignado a \"%s\"" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "Página de atajos" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Pegar" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Pegar propiedades" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/data/workspace/shortcuts.cljs:127 +msgid "shortcuts.paste-replace" +msgstr "Pegar y reemplazar" + +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "Personalizados" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Gestor de extensiones" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Tablero anterior" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Rehacer" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "" "Después de editar un atajo, recarga cualquier pestaña de Penpot para " "aplicar los cambios." -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Renombrar" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "Restablecer valor por defecto" @@ -5285,225 +5543,225 @@ msgstr "Restablecer valor por defecto" msgid "shortcuts.reset-all" msgstr "Restablecer todo" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Reiniciar zoom" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Escalado" -#: src/app/main/ui/settings/shortcuts.cljs:44, src/app/main/ui/workspace/sidebar/shortcuts.cljs:139 +#: src/app/main/ui/settings/shortcuts.cljs:44, src/app/main/ui/workspace/sidebar/shortcuts.cljs:143 msgid "shortcuts.search" msgstr "Buscar atajos" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Básicos" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Panel" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Modo visualización" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Área de trabajo" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Seleccionar todo" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Seleccionar capa siguiente" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Seleccionar capa padre" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Seleccionar capa anterior" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Separar nodos" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "Mostrar medidas" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Mostrar rejilla a pixel" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Mostrar atajos de teclado" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Alinear nodos" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Activar alineación a rejilla de pixel" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Comenzar edición" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Alineamiento" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Editar" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Genérico" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu principal" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modificar capas" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navegación" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navegación" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navegación" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Paneles" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Ruta" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formas" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Texto" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Herramientas" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Activar miniaturas" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Atajos de teclado" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Alternar alineación" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Mostrar/ocultar recursos" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Mostrar/ocultar paleta de colores" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "Activar/desactivar visibilidad de comentarios" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Mostrar/ocultar focus mode" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Activar/desactivar pantalla completa" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Mostrar/ocultar guías" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Mostrar/ocultar histórico" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Mostrar/ocultar capas" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Añadir/eliminar flex layout" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Añadir/eliminar grid layout" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Bloquear/Desbloquear" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Bloquear/Desbloquear proporciones" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Mostrar/ocultar reglas" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Alinear a las guías" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Alinear a las guías de reglas" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Mostrar/ocultar paleta de textos" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Cambiar tema" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Mostrar/ocultar elemento" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Alternar estilo de zoom" @@ -5511,31 +5769,31 @@ msgstr "Alternar estilo de zoom" msgid "shortcuts.underline" msgstr "Alternar subrayado" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Deshacer" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Desagrupar" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Deshacer máscara" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribuir verticalmente" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Decrementar zoom a objetivo" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Incrementar zoom a objetivo" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zoom a selección" @@ -5554,7 +5812,7 @@ msgstr "Vea Enterprise en acción" msgid "subscription.current-plan.enterprise" msgstr "Enterprise" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Enterprise" @@ -5566,7 +5824,7 @@ msgstr "Enterprise (Prueba)" msgid "subscription.current-plan.professional" msgstr "Professional" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "Tu suscripción" @@ -5699,288 +5957,289 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Invita a personas mientras estás en el plan Unlimited" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "El pago no se completó. Inténtalo de nuevo cuando quieras." -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "" "No hemos podido iniciar el proceso de pago. Inténtalo de nuevo. Si el " "problema persiste, contáctanos: support@penpot.app." -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Suscripción" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "Introducir código de activación" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Añade un método de pago para continuar después del periodo de prueba" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "Admin Console" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Todas las prestaciones del plan Professional y:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Todas las prestaciones del plan Unlimited y:" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "Todas las ventajas del plan Unlimited, además de:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s editores)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise (prueba)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "Versiones guardadas automáticamente cada 90 días y recuperación de archivos" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Factura mensual fija" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "Control avanzado:" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "Seguridad Enterprise:" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Gestión multiorganización:" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "" "Compatibilidad nativa con SAML/SSO y registro completo de auditoría (Audit " "Trail) para informes de cumplimiento normativo." -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "" "Lista blanca granular de complementos (plugins), incluido nuestro servidor " "MCP seguro y aislado para la conectividad con IA." -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "" "Compatibilidad con jerarquías de equipos complejas y suborganizaciones " "independientes." -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Almacenamiento ilimitado" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Gestionar tu suscripción" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Añadir datos de pago" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Añade ahora tus datos de pago para que tu suscripción siga funcionando sin " "problemas tras finalizar el periodo de prueba y seguir apoyando nuestro " "proyecto de código abierto. Por ahora no se te hará ningún cargo." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Saltar por ahora y comenzar la prueba" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Ayúdanos a crecer y facilita tu periodo de prueba" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Actualmente hay %s persona en tus equipos que pueden editar." msgstr[1] "Actualmente hay %s personas en tus equipos que pueden editar." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Ten en cuenta: cambiar a un plan inferior significa menos almacenamiento y " "copias de seguridad e historial de versiones más cortos." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Editores" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "" "(Propietarios, administradores y editores. Los lectores no cuentan como " "editores)." -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "No puedes establecer menos editores de los que tienes ahora. Cambia el rol " "(de editor/administrador a lector) para las personas que realmente no " "editan archivos en la configuración del equipo." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "" "Se cobrara después del periodo de prueba. No se requiere tarjeta de crédito " "en este momento." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/mes" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Aplica %s a tus equipos" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Consejo: Puedes aumentar ahora el número de asientos para adelantarte a las " "invitaciones. Con más de 25 editores en todos tus equipos, disfrutarás de " "una tarifa plana de 175 $ al mes." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Miembro de penpot desde: %s." -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Más información" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "miembro organización / mes" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Otros planes de penpot" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "organización por mes" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "usuario / mes" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professional" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "Versiones con autoguardado de 7 días y recuperación de archivos" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "" "Todas las funciones de diseño y creación de prototipos. Asistencia de la " "comunidad." -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "Control total sobre los datos y la infraestructura." -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "Número ilimitado de usuarios, equipos y archivos." -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10 GB de almacenamiento" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Equipos ilimitados. Hasta 8 editores en todos tus equipos." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Recomendado" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Tu suscripción" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Comenzar prueba gratuita" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Suscríbete" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "Llevas en el plan %s desde %s." -msgid "subscription.settings.trial-ends-on" -msgstr "Tu prueba gratuita acaba el %s." - -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Puedes modificar tu suscripción en cualquier momento desde la página " "'Suscripción' en los datos de tu cuenta." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "¡Disfruta de tu plan!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "¡Gracias por elegir el plan %s de Penpot!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Eres %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:724 +msgid "subscription.settings.trial-ends-on" +msgstr "Tu prueba gratuita acaba el %s." + +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Pruébalo gratis durante 14 días" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Unlimited" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unlimited (prueba)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "Versiones con autoguardado de 30 días y recuperación de archivos" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Factura mensual limitada en $175" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25 GB de almacenamiento" @@ -6005,45 +6264,45 @@ msgstr "" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "Si quieres aumentar este límite, [mejora tu plan|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 invitación seleccionada" msgstr[1] "%s invitaciones seleccionadas" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Proveedores de fuentes - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Fuentes - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Proyectos - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Buscar - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Bibliotecas Compartidas - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Diseño Libre para Equipos" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Danos tu opinión - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "Integraciones - Penpot" @@ -6051,7 +6310,7 @@ msgstr "Integraciones - Penpot" msgid "title.settings.notifications" msgstr "Notificaciones - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Configuración - Penpot" @@ -6059,31 +6318,31 @@ msgstr "Configuración - Penpot" msgid "title.settings.password" msgstr "Contraseña - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Perfil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Invitaciones - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Integrantes - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Configuración - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Modo de visualización - Penpot" -#: src/app/main/ui/workspace.cljs:240 +#: src/app/main/ui/workspace.cljs:241 msgid "title.workspace" msgstr "%s - Penpot" @@ -6091,54 +6350,66 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Desvincular token" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "No se ha encontrado ningún tablero." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "No se encuentra el tablero." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer.cljs:127 +msgid "viewer.go-to-first-board" +msgstr "Ir al primer tablero" + +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Comentarios (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "No mostrar interacciones" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Pantalla completa" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspector (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interacciones" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interacciones (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Copiar enlace" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Mostrar interacciones" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Mostrar interacciones al hacer click" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Mapa del sitio" +#: src/app/main/ui/viewer/thumbnails.cljs:70 +msgid "viewer.thumbnails.scroll-left" +msgstr "Desplazar a la izquierda" + +#: src/app/main/ui/viewer/thumbnails.cljs:76 +msgid "viewer.thumbnails.scroll-right" +msgstr "Desplazar a la derecha" + #: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:76 msgid "webgl.modals.webgl-unavailable.cta-dashboard" msgstr "Ir al panel" @@ -6172,67 +6443,67 @@ msgstr "Consulta nuestra " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "guía de solución de problemas de WebGL" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "Renderizado WebGL desactivado" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "Renderizado WebGL activado" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "Se perdió el contexto WebGL" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "Se recuperó el contexto WebGL" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "El último envío fue correcto." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Alinear al centro (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribuir espacio horizontal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Alinear a la izquierda (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Alinear a la derecha (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Alinear abajo (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Alinear al centro (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribuir espacio vertical (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Alinear arriba (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Añadir biblioteca" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Todos" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Colores" @@ -6244,7 +6515,7 @@ msgstr "Añadir color" msgid "workspace.assets.component-group-options" msgstr "Opciones del grupo de componentes" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Componentes" @@ -6252,7 +6523,7 @@ msgstr "Componentes" msgid "workspace.assets.components.add-component" msgstr "Añadir componente" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s Variantes" @@ -6260,13 +6531,13 @@ msgstr "%s Variantes" msgid "workspace.assets.create-group" msgstr "Crear un grupo" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Tus elementos se renombrarán automáticamente a \"nombre grupo / nombre " "elemento\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Borrar" @@ -6274,11 +6545,11 @@ msgstr "Borrar" msgid "workspace.assets.delete-group" msgstr "Eliminar grupo" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "Borrar páginas" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplicar" @@ -6286,39 +6557,39 @@ msgstr "Duplicar" msgid "workspace.assets.duplicate-main" msgstr "Duplicar principal" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Editar" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtrar" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Ver como rejilla" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Agrupar" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nombre del grupo" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Ver como lista" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "biblioteca local" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Gestionar biblioteca" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "No se encontraron recursos" @@ -6326,7 +6597,7 @@ msgstr "No se encontraron recursos" msgid "workspace.assets.open-library" msgstr "Abrir el fichero de la biblioteca" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Renombrar" @@ -6334,11 +6605,11 @@ msgstr "Renombrar" msgid "workspace.assets.rename-group" msgstr "Renombrar grupo" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Buscar recursos" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Biblioteca compartida" @@ -6348,47 +6619,47 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 componente" msgstr[1] "%s componentes" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Ordenar" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Tipografías" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Añadir tipografía" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Tamaño" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Estilo de fuente" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Editar en biblioteca de estilo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Interletrado" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Interlineado" -#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:461 +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:297 msgid "workspace.assets.typography.multiple-texts-selected" msgstr "Selecciona un solo texto para crear un estilo de tipografía" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformar texto" @@ -6396,15 +6667,15 @@ msgstr "Transformar texto" msgid "workspace.assets.ungroup" msgstr "Desagrupar" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "Copiando imagen…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "Imagen copiada al portapapeles" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "Error al copiar la imagen" @@ -6416,85 +6687,85 @@ msgstr "Elegir color de la pantalla" msgid "workspace.colorpicker.color-tokens" msgstr "Tokens de color" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Los componentes no pueden anidarse dentro de sí mismos." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "Algunas copias no se han podido intercambiar. Los componentes no pueden " "anidarse dentro de sí mismos." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Crear area" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "Copiar columnas" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "Copiar filas" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Crear tablero" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Fusionar celdas" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "Pegar" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Añadir 1 columna a la derecha" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Añadir 1 columna a la izquierda" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Borrar columna" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Borrar columna con el contenido" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Duplicar columna" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Añadir 1 fila debajo" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Añadir 1 fila encima" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Borrar fila" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Borrar fila con el contenido" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Duplicar fila" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "Color de la guía" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "Eliminar guía" @@ -6502,19 +6773,77 @@ msgstr "Eliminar guía" msgid "workspace.debug.title" msgstr "Herramientas de depuración" -#: src/app/main/data/exports/assets.cljs +#: src/app/main/data/exports/assets.cljs:388, src/app/main/data/exports/assets.cljs:564 msgid "workspace.export.unsaved-changes" -msgstr "Algunos de tus últimos cambios aún no se han guardado, así que puede que esta exportación no los incluya." +msgstr "" +"Algunos de tus últimos cambios aún no se han guardado, así que puede que " +"esta exportación no los incluya." -#: src/app/main/ui/workspace/sidebar/layers.cljs:907 +#: src/app/main/data/exports/assets.cljs:370, src/app/main/data/exports/assets.cljs:612 +msgid "workspace.export.unsaved-changes-included" +msgstr "" +"Algunos de tus últimos cambios aún no se han guardado. Esta exportación los " +"incluye." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:59 +msgid "workspace.file-restored.accept" +msgstr "Entendido" + +#: src/app/main/ui/workspace/file_restored_modal.cljs:30 +msgid "workspace.file-restored.message" +msgstr "" +"%s ha restaurado una versión anterior de este archivo. Los cambios " +"posteriores ya no están en el archivo." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:31 +msgid "workspace.file-restored.message-anonymous" +msgstr "" +"Un miembro del equipo ha restaurado una versión anterior de este archivo. " +"Los cambios posteriores ya no están en el archivo." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:28 +msgid "workspace.file-restored.message-date" +msgstr "" +"%s ha restaurado la versión del %s. Los cambios posteriores ya no están en " +"el archivo." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:29 +msgid "workspace.file-restored.message-date-anonymous" +msgstr "" +"Un miembro del equipo ha restaurado la versión del %s. Los cambios " +"posteriores ya no están en el archivo." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:26 +msgid "workspace.file-restored.message-label" +msgstr "" +"%s ha restaurado la versión «%s». Los cambios posteriores ya no están en el " +"archivo." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:27 +msgid "workspace.file-restored.message-label-anonymous" +msgstr "" +"Un miembro del equipo ha restaurado la versión «%s». Los cambios " +"posteriores ya no están en el archivo." + +#: src/app/main/ui/workspace/file_restored_modal.cljs:50 +msgid "workspace.file-restored.title" +msgstr "Se ha restaurado este archivo" + +#: src/app/main/ui/workspace/file_restored_modal.cljs:55 +msgid "workspace.file-restored.unsaved-changes" +msgstr "" +"No se han podido guardar tus últimos cambios porque se ha restaurado una " +"versión del archivo." + +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Modo foco" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Desactivar modo foco" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Activar modo foco" @@ -6526,243 +6855,251 @@ msgstr "Degradado lineal" msgid "workspace.gradients.radial" msgstr "Degradado radial" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Desactivar alineamiento dinámico" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Desactivar escala proporcional" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Desactivar alinear a las guias" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Desactivar ajuste al pixel" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Desactivar alinear a las guias de reglas" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "Desactivar renderizado WebGL (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Activar alineamiento dinámico" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Activar escala proporcional" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Alinear a las guias" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Activar ajuste al pixel" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Alinear a las guias de reglas" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "Activar renderizado WebGL (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "Buscar" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "Buscar y reemplazar" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Ocultar nombres de tableros" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "Ocultar comentarios" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Ocultar guías" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Ocultar paleta de colores" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Ocultar rejilla de pixeles" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Ocultar reglas" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Ocultar paleta de textos" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "Bloquear guías" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "Conectar" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "Desconectar" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "Gestionar (estado: deshabilitado)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "Gestionar (estado: habilitado)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Editar" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Archivo" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Ayuda e información" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "Servidor MCP" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferencias" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Ver" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Rehacer" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Seleccionar todo" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Mostrar nombres de tableros" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "Mostrar comentarios" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Mostrar guías" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Mostrar paleta de colores" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Mostrar rejilla de pixeles" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Mostrar reglas" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Mostrar paleta de textos" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Cambiar a tema oscuro" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Cambiar a tema claro" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Cambiar a tema del sistema" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Deshacer" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "Desbloquear guías" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Restablecer" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:133 +msgid "workspace.header.retrying" +msgstr "Reintentando guardar" + +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Error al guardar" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Guardado" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Guardando" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Compartir" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Modo de visualización (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoom" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Escalar para rellenar" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Escalar para ajustar" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Zoom abarcar todo" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Pantalla completa" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zoom a selección" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1050 +msgid "workspace.layout-grid.editor.columns" +msgstr "Columnas" + +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Editar grid" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Salir" @@ -6770,7 +7107,7 @@ msgstr "Salir" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Padding inferior" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Mostrar el padding a 4 lados" @@ -6794,6 +7131,18 @@ msgstr "Padding superior" msgid "workspace.layout-grid.editor.padding.vertical" msgstr "Padding vertical" +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1051 +msgid "workspace.layout-grid.editor.rows" +msgstr "Filas" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:994 +msgid "workspace.layout-grid.editor.select-column" +msgstr "Seleccionar columna" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:995 +msgid "workspace.layout-grid.editor.select-row" +msgstr "Seleccionar fila" + #: src/app/main/ui/workspace/viewport/grid_layout_editor.cljs:61 msgid "workspace.layout-grid.editor.title" msgstr "Editando rejilla" @@ -6806,45 +7155,49 @@ msgstr "Hecho" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Mostrar" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Mostrar grid layout" -#: src/app/main/ui/workspace/libraries.cljs:898 +#: src/app/main/ui/workspace/viewport/grid_layout_editor.cljs:932 +msgid "workspace.layout-grid.editor.track-options" +msgstr "Opciones de la pista" + +#: src/app/main/ui/workspace/libraries.cljs:921 msgid "workspace.libraries.add-shared-library" msgstr "Añadir una biblioteca compartida" -#: src/app/main/ui/workspace/libraries.cljs:801 +#: src/app/main/ui/workspace/libraries.cljs:823 msgid "workspace.libraries.assets-in-this-file" msgstr "RECURSOS EN ESTE ARCHIVO" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 color" msgstr[1] "%s colores" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "Añadir color a la biblioteca del archivo" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Todavía no hay estilos de color en tu biblioteca" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "Aún no hay colores recientes" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Todavía no hay tipografías en tu biblioteca" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Biblioteca del archivo" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Colores recientes" @@ -6856,99 +7209,99 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Guardar estilo de color" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "Mostrar/Ocultar paleta de colores" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 componente" msgstr[1] "%s componentes" -#: src/app/main/ui/workspace/libraries.cljs:842 +#: src/app/main/ui/workspace/libraries.cljs:864 msgid "workspace.libraries.connected-libraries" msgstr "Biblioteca conectada" -#: src/app/main/ui/workspace/libraries.cljs:183 +#: src/app/main/ui/workspace/libraries.cljs:235 msgid "workspace.libraries.connected-through" msgstr "Conectada mediante" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Conectada con" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "O añadir algunas de éstas para probar:" -#: src/app/main/ui/workspace/libraries.cljs:812 +#: src/app/main/ui/workspace/libraries.cljs:834 msgid "workspace.libraries.empty.no-assets" msgstr "Aún no hay recursos" -#: src/app/main/ui/workspace/libraries.cljs:895 +#: src/app/main/ui/workspace/libraries.cljs:918 msgid "workspace.libraries.empty.no-connected-libraries" msgstr "Aun no hay bibliotecas conectadas." -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "No hay Bibliotecas Compartidas en tu equipo, puedes buscar" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "algunas plantillas aquí" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Biblioteca del archivo" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 gráfico" msgstr[1] "%s gráficos" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTECAS EN ESTE ARCHIVO" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTECAS" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "ACTUALIZACIONES DE BIBLIOTECAS" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Cargando…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "No hay bibliotecas que necesiten ser actualizadas" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "No se encuentra “%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Buscar bibliotecas compartidas" -#: src/app/main/ui/workspace/libraries.cljs:827, src/app/main/ui/workspace/libraries.cljs:874 +#: src/app/main/ui/workspace/libraries.cljs:849, src/app/main/ui/workspace/libraries.cljs:897 msgid "workspace.libraries.set-as-tokens-source" msgstr "Activar fuente de tokens" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "BIBLIOTECAS COMPARTIDAS" -#: src/app/main/ui/workspace/libraries.cljs:621 +#: src/app/main/ui/workspace/libraries.cljs:674 msgid "workspace.libraries.shared-library-added" msgstr "Añadido" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Conectar biblioteca" @@ -6972,51 +7325,51 @@ msgstr "Desvincular todos los recursos" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "Desvincular todos los tokens" -#: src/app/main/ui/workspace/libraries.cljs:1136 +#: src/app/main/ui/workspace/libraries.cljs:1159 msgid "workspace.libraries.this-file" msgstr "Este archivo" -#: src/app/main/ui/workspace/libraries.cljs:167, src/app/main/ui/workspace/libraries.cljs:211 +#: src/app/main/ui/workspace/libraries.cljs:219, src/app/main/ui/workspace/libraries.cljs:263 msgid "workspace.libraries.token-sets" msgid_plural "workspace.libraries.token-sets" msgstr[0] "1 token set" msgstr[1] "%s token sets" -#: src/app/main/ui/workspace/libraries.cljs:170, src/app/main/ui/workspace/libraries.cljs:215 +#: src/app/main/ui/workspace/libraries.cljs:222, src/app/main/ui/workspace/libraries.cljs:267 msgid "workspace.libraries.token-themes" msgid_plural "workspace.libraries.token-themes" msgstr[0] "1 token theme" msgstr[1] "%s token themes" -#: src/app/main/ui/workspace/libraries.cljs:164, src/app/main/ui/workspace/libraries.cljs:207 +#: src/app/main/ui/workspace/libraries.cljs:216, src/app/main/ui/workspace/libraries.cljs:259 msgid "workspace.libraries.tokens" msgid_plural "workspace.libraries.tokens" msgstr[0] "1 token" msgstr[1] "%s tokens" -#: src/app/main/ui/workspace/libraries.cljs:820, src/app/main/ui/workspace/libraries.cljs:866 +#: src/app/main/ui/workspace/libraries.cljs:842, src/app/main/ui/workspace/libraries.cljs:889 msgid "workspace.libraries.tokens-source" msgstr "Fuente de tokens" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 tipografía" msgstr[1] "%s tipografías" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Desconectar biblioteca" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Actualizar" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "ver todos los cambios" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "ACTUALIZACIONES" @@ -7024,12 +7377,19 @@ msgstr "ACTUALIZACIONES" msgid "workspace.notification-pill.detail" msgstr "Detalles" -#: src/app/main/data/workspace/notifications.cljs:282 +#: src/app/main/data/persistence.cljs:669 msgid "workspace.notifications.file-repaired" msgstr "" "Hemos corregido algunos problemas de este archivo. Hay una copia de la " "versión original en el historial de versiones." +#: src/app/main/data/persistence.cljs:662, src/app/main/data/persistence.cljs:691 +msgid "workspace.notifications.file-repaired-changes-undone" +msgstr "" +"Hemos corregido algunos problemas de este archivo. Hay una copia de la " +"versión original en el historial de versiones. Algunos cambios no se han " +"podido guardar, así que se han deshecho tus últimos cambios." + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:784 msgid "workspace.options.add-interaction" msgstr "Pulsa el botón + para añadir interacciones." @@ -7085,7 +7445,7 @@ msgstr "Mostrar/ocultar desenfoque" msgid "workspace.options.blur-options.toggle-more-options" msgstr "Mostrar/ocultar más opciones" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "Cancelar" @@ -7097,117 +7457,117 @@ msgstr "Color de fondo" msgid "workspace.options.canvas-background-section" msgstr "Sección de fondo de lienzo" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Truncar contenido" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Componente" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Nota" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Copia" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Crear una nota" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Editar una nota" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Principal" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Intercambiar componente" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Aún no hay recursos en esta biblioteca" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Desvinculado" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Localizar variantes en conflicto" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Este componente tiene variantes en conflicto. Comprueba que cada variante " "tenga un conjunto único de propiedades y valores." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Localizar variantes duplicadas" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Algunas variantes tienen propiedades y valores idénticos" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Estas variantes tienen propiedades y valores idénticos. Ajusta los valores " "para que puedan ser encontradas" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Esta variante tiene propiedades y valores idénticos a los de otra variante. " "Ajusta los valores para que puedan ser encontradas" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Algunas de estas variantes tienen propiedades y valores idénticos. Ajusta " "los valores para que puedan ser encontradas" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Este componente tiene variantes con nombres no válidos. Asegúrate de que " "cada variante siga la estructura correcta." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Localizar variantes no válidas" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Algunas variantes tienen nombres no válidos" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Localizar variantes inválidas" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Estas variantes tienen nombres no válidos." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Esta variante tiene un nombre no válido." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Algunas de estas variantes tienen nombres no válidos." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[propiedad]=[valor], [propiedad]=[valor]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Prueba a utilizar la siguiente estructura:" @@ -7245,7 +7605,7 @@ msgstr "Tienen el mismo nivel de jerarquía." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Los grupos, tableros y layouts se consideran equivalentes." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Cómo se mantienen conectadas las variantes" @@ -7293,11 +7653,11 @@ msgstr "Arriba y Abajo" msgid "workspace.options.convert-to-typography" msgstr "Crear estilo de tipografía" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Diseño" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exportar" @@ -7305,53 +7665,53 @@ msgstr "Exportar" msgid "workspace.options.export-multiple" msgstr "Exportar selección" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Exportar 1 elemento" msgstr[1] "Exportar %s elementos" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Añadir exportación" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Eliminar exportación" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Sufijo" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "La cola de exportación está llena, inténtalo de nuevo en unos momentos" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "Exportación cancelada" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "Cancelando..." -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Exportación completa" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Exportando…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Exportación fallida" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Exportación lenta" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "Esperando..." @@ -7371,7 +7731,7 @@ msgstr "Eliminar relleno" msgid "workspace.options.fill.section" msgstr "Sección de relleno" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Redimensionar para ajustar al contenido" @@ -7391,7 +7751,7 @@ msgstr "Inicio de flujo" msgid "workspace.options.flows.flow-starts" msgstr "Inicios de flujo" -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:300, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 msgid "workspace.options.font-not-available" msgstr "" "La fuente utilizada por este texto (%s) ya no está disponible. Vuelve a " @@ -7401,67 +7761,71 @@ msgstr "" msgid "workspace.options.grid.auto" msgstr "Automático" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Columnas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Color" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:44 +msgid "workspace.options.grid.params.guide-defaults" +msgstr "Valores por defecto de la guía" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Espaciado" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Altura" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Establecer valor por defecto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Abajo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Izquierda" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Derecha" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Estirar" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Arriba" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Usar valor por defecto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Ancho" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Filas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Cuadros" @@ -7473,7 +7837,7 @@ msgstr "Relleno de grupo" msgid "workspace.options.group-stroke" msgstr "Borde de grupo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Añadir guía" @@ -7481,19 +7845,19 @@ msgstr "Añadir guía" msgid "workspace.options.guides.remove-guide" msgstr "Eliminar guía" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Guías" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Mostrar/ocultar guía" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Altura" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspeccionar" @@ -7709,115 +8073,115 @@ msgstr "Interacciones" msgid "workspace.options.interactions.add-interaction" msgstr "Añadir interacción" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Color" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Color más oscuro" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Color más suave" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Oscurecer" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Diferencia" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Exclusión" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Luz fuerte" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Tono" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Aclarar" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminosidad" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Multiplicar" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Superponer" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturación" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Trama" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Luz suave" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "Sección del menú de capas" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Mostrar/ocultar capa" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Altura máx." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Ancho máx." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Altura mín." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Ancho mín." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Más colores" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Más colores de la biblioteca" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "Más tokens de color" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:502 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:516 msgid "workspace.options.no-font-results" msgstr "No hay fuentes coincidentes" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "No hay tamaños predefinidos coincidentes" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacidad" @@ -7825,7 +8189,7 @@ msgstr "Opacidad" msgid "workspace.options.pixel-grid-color" msgstr "Color de la rejilla de pixeles" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototipo" @@ -7861,23 +8225,23 @@ msgstr "Sección de radios de borde" msgid "workspace.options.radius.show-single-corners" msgstr "Mostrar radios individuales" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Recientes" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Reintentar" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotación" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Buscar fuente" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "Buscar tamaño predefinido" @@ -7887,11 +8251,11 @@ msgstr "" "Selecciona una figura, tablero o grupo para arrastrar una conexión a otro " "tablero." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Colores seleccionados" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "Sección de colores seleccionados" @@ -7959,55 +8323,55 @@ msgstr "Mostrar/ocultar sombra" msgid "workspace.options.show-fill-on-export" msgstr "Mostrar al exportar" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Mostrar en modo visualización" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Tamaño" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Tamaños predefinidos" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Bloquear proporción" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Desbloquear proporción" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Círculo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamante" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Flecha" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Ninguno" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Redondo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Cuadrado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rectángulo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triángulo" @@ -8015,31 +8379,31 @@ msgstr "Triángulo" msgid "workspace.options.stroke-color" msgstr "Color del trazo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "Guion" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "Espacio" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Ancho del trazo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "Inferior (block end)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "Izquierda (inline start)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "Derecha (inline end)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "Superior (block start)" @@ -8047,49 +8411,49 @@ msgstr "Superior (block start)" msgid "workspace.options.stroke.add-stroke" msgstr "Añadir color de borde" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Rayado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Punteado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Interior" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Mezclado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Exterior" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "Trazo Por Lado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "" "Strokes per side feature is only available in the new render. Switch render " "to use it." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Eliminar borde" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Sólido" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "Mostrar/ocultar borde" @@ -8125,11 +8489,11 @@ msgstr "Ancho automático" msgid "workspace.options.text-options.grow-fixed" msgstr "Fijo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Espaciado entre letras" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Altura de línea" @@ -8177,154 +8541,160 @@ msgstr "Subrayado (%s)" msgid "workspace.options.use-play-button" msgstr "Usa el botón de play de la cabecera para arrancar la vista de prototipo." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Ancho" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Eje X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Eje Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Dibujar nodos (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "Alineado" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "Independiente" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "Igual" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "Comportamiento manejador" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Unir nodos (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Convertir en esquina (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Convertir en curva (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Fusionar nodos (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Mover nodes (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Separar nodos (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Alinear nodos (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Abrir" -#: src/app/main/ui/workspace/plugins.cljs:242 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Descubre [más extensiones](%s)" -#: src/app/main/ui/workspace/plugins.cljs:249 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "No se encuentran extensiones" -#: src/app/main/ui/workspace/plugins.cljs:236 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "El manifiesto de la expansión es incorrecto." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Debes ser un editor para usar este plugin" -#: src/app/main/data/plugins.cljs:158 +#: src/app/main/data/plugins.cljs:160 msgid "workspace.plugins.error.unreachable" msgstr "No se ha podido acceder a la URL de la extensión." -#: src/app/main/ui/workspace/plugins.cljs:232 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "La extensión no existe o la url no es correcta." -#: src/app/main/ui/workspace/plugins.cljs:228 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Instalar" -#: src/app/main/ui/workspace/plugins.cljs:258 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Extensiones instaladas" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Gestor de extensiones" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Extensiones" -#: src/app/main/ui/workspace/plugins.cljs:452 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "EXTENSIÓN ACTUALIZADA" -#: src/app/main/ui/workspace/plugins.cljs:456 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "La extensión ha cambiado desde la última vez que la abriste. Ahora quiere " "acceder a:" -#: src/app/main/ui/workspace/plugins.cljs:325 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Comenzar descargas de ficheros." -#: src/app/main/ui/workspace/plugins.cljs:332 +#: src/app/main/ui/workspace/plugins.cljs:344 +msgid "workspace.plugins.permissions.allow-global" +msgstr "" +"Seguir funcionando mientras te mueves entre el panel y tus archivos, y " +"buscar y abrir tus proyectos y archivos." + +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Guardar datos en el navegador." -#: src/app/main/ui/workspace/plugins.cljs:345 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "Leer el contenido de tu portapapeles." -#: src/app/main/ui/workspace/plugins.cljs:339 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "Leer y escribir en tu portapapeles." -#: src/app/main/ui/workspace/plugins.cljs:318 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Leer tus comentarios y respuestas." -#: src/app/main/ui/workspace/plugins.cljs:312 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Leer y modificar tus comentarios y responder en tu nombre." -#: src/app/main/ui/workspace/plugins.cljs:285 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Leer el contenido de sus archivos." -#: src/app/main/ui/workspace/plugins.cljs:279 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Leer y modificar el contenido de sus archivos." -#: src/app/main/ui/workspace/plugins.cljs:394 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Ten en cuenta que las extensiones están desarrolladas por terceros, " @@ -8332,84 +8702,106 @@ msgstr "" "seguridad es importante para nosotros. Si tienes cualquier duda, contacta " "con soporte." -#: src/app/main/ui/workspace/plugins.cljs:305 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Leer la información de sus bibliotecas y recursos." -#: src/app/main/ui/workspace/plugins.cljs:299 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Leer y modificar la información de sus bibliotecas y recursos." -#: src/app/main/ui/workspace/plugins.cljs:387 +#: src/app/main/ui/workspace/plugins.cljs:365 +msgid "workspace.plugins.permissions.manage-delete" +msgstr "Eliminar tus archivos y proyectos." + +#: src/app/main/ui/workspace/plugins.cljs:351 +msgid "workspace.plugins.permissions.manage-projects" +msgstr "Crear, renombrar, fijar y duplicar tus proyectos." + +#: src/app/main/ui/workspace/plugins.cljs:358 +msgid "workspace.plugins.permissions.manage-teams" +msgstr "Ver, crear y renombrar tus equipos, y mover proyectos entre ellos." + +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "LA EXTENSIÓN '%s' SOLICITA PERMISO PARA ACCEDER:" -#: src/app/main/ui/workspace/plugins.cljs:292 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Leer la información del usuario actual." -#: src/app/main/ui/workspace/plugins.cljs:254 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Lista de extensiones" -#: src/app/main/ui/workspace/plugins.cljs:191 +#: src/app/main/ui/workspace/plugins.cljs:188 msgid "workspace.plugins.remove-confirmation.message" msgstr "" "¿Seguro que quieres eliminar la extensión %s? Puedes instalarla de nuevo en " "cualquier momento." -#: src/app/main/ui/workspace/plugins.cljs:190 +#: src/app/main/ui/workspace/plugins.cljs:187 msgid "workspace.plugins.remove-confirmation.title" msgstr "Eliminar extensión" -#: src/app/main/ui/workspace/plugins.cljs:101, src/app/main/ui/workspace/plugins.cljs:192 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Eliminar extensión" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Intruduzca URL de la extensión" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Extensiones" -#: src/app/main/ui/workspace/plugins.cljs:522 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "AHORA NO" -#: src/app/main/ui/workspace/plugins.cljs:515 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "¿Quieres echar un vistazo?. Crearemos un nuevo borrador en tu equipo " "actual. (Si no, puedes encontrar los plugins instalados en cualquier " "fichero.)" -#: src/app/main/ui/workspace/plugins.cljs:511 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "¡LA EXTENSIÓN '%s' HA SIDO INSTALADA PARA TU USUARIO!" -#: src/app/main/ui/workspace/plugins.cljs:528 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "PROBAR PLUGIN" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:46 +#, markdown +msgid "workspace.save-failure.retrying" +msgstr "**Edición desactivada** porque no se pueden guardar los cambios." + +#: src/app/main/ui/workspace/viewport/top_bar.cljs:47 +#, markdown +msgid "workspace.save-failure.stopped" +msgstr "**Edición desactivada** porque no se pueden guardar los cambios." + +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Añadir flex layout" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Añadir grid layout" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Añadir layout" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Crear variante" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Añadir nueva propiedad" @@ -8421,11 +8813,11 @@ msgstr "Enviar al fondo" msgid "workspace.shape.menu.backward" msgstr "Enviar atrás" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "Eliminar guias del tablero" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Combinar como variantes" @@ -8433,7 +8825,7 @@ msgstr "Combinar como variantes" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Los componentes tienen que estar en la misma página" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Copiar" @@ -8477,27 +8869,27 @@ msgstr "Copiar como texto" msgid "workspace.shape.menu.create-annotation" msgstr "Crear una nota" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Tablero de selección" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Crear componente" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Crear múltiples componentes" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Cortar" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Eliminar" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Eliminar inicio de flujo" @@ -8509,7 +8901,7 @@ msgstr "Desvincular instancia" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Desvincular instancias" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Diferencia" @@ -8517,15 +8909,15 @@ msgstr "Diferencia" msgid "workspace.shape.menu.duplicate" msgstr "Duplicar" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Editar" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Exclusión" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Aplanar" @@ -8537,7 +8929,7 @@ msgstr "Voltear horizontal" msgid "workspace.shape.menu.flip-vertical" msgstr "Voltear vertical" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Inicio de flujo" @@ -8549,31 +8941,31 @@ msgstr "Mover hacia delante" msgid "workspace.shape.menu.front" msgstr "Mover al frente" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Agrupar" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Ocultar" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Mostrar/Ocultar Interfaz" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Intersección" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Bloquear" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Crear máscara" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Pegar" @@ -8581,27 +8973,27 @@ msgstr "Pegar" msgid "workspace.shape.menu.paste-props" msgstr "Pegar propiedades" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Ruta" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Eliminar flex layout" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Eliminar grid layout" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Eliminar layout" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Eliminar propiedad" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "La variante debe tener al menos una propiedad" @@ -8625,11 +9017,11 @@ msgstr "Restaurar variante" msgid "workspace.shape.menu.select-layer" msgstr "Seleccionar capa" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Mostrar" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Ver en el panel de recursos" @@ -8637,7 +9029,7 @@ msgstr "Ver en el panel de recursos" msgid "workspace.shape.menu.show-main" msgstr "Ver componente principal" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "Borde a ruta" @@ -8649,19 +9041,19 @@ msgstr "Quitar miniatura" msgid "workspace.shape.menu.thumbnail-set" msgstr "Poner como miniatura" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Desagrupar" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Unión" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Desbloquear" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Quitar máscara" @@ -8677,79 +9069,83 @@ msgstr "Cerrar barra lateral" msgid "workspace.sidebar.expand" msgstr "Abrir barra lateral" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Historial" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Capas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Componentes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Filtrar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Paneles" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Grupos" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Imágenes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Máscaras" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "No hay coincidencias" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "Reemplazar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "Reemplazar todo" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "Reemplazar con…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Buscar capas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "Buscar y reemplazar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "Contenido de texto" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "Nombres de capa" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Formas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Textos" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:53 +msgid "workspace.sidebar.options.svg-attrs.remove" +msgstr "Eliminar atributo" + +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Atributos del SVG Importado" @@ -8761,7 +9157,7 @@ msgstr "Páginas" msgid "workspace.sidebar.sitemap.add-page" msgstr "Añadir página" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Mapa del sitio" @@ -8793,7 +9189,7 @@ msgstr "Añadir un set a este grupo" msgid "workspace.tokens.add-token" msgstr "Añadir token: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Aplicado a" @@ -8817,7 +9213,7 @@ msgstr "El tamaño de fuente base debe estar en px o sin unidad." msgid "workspace.tokens.change-token-source" msgstr "Cambiar fuente de tokens" -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Color" @@ -8829,7 +9225,7 @@ msgstr "Copiar ruta de token" msgid "workspace.tokens.create-new-theme" msgstr "Crea un nuevo tema ahora." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Crear uno." @@ -8849,6 +9245,10 @@ msgstr "Eliminar token" msgid "workspace.tokens.delete-theme-title" msgstr "Borrar theme" +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226 +msgid "workspace.tokens.detach-reference" +msgstr "Desvincular referencia" + #: src/app/main/ui/workspace/tokens/management/context_menu.cljs:430 msgid "workspace.tokens.duplicate" msgstr "Duplicar token" @@ -8857,7 +9257,7 @@ msgstr "Duplicar token" msgid "workspace.tokens.duplicate-group" msgstr "Duplicar grupo de tokens" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "copia" @@ -8881,7 +9281,7 @@ msgstr "Editar token de %s" msgid "workspace.tokens.enter-token-name" msgstr "Introduce un nombre para el token %s" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "" "No se pueden aplicar tokens mientras se edita texto. Seleccione la capa de " @@ -8911,7 +9311,7 @@ msgstr "Previsualizar:" msgid "workspace.tokens.export.single-file" msgstr "fichero único" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Font size o {alias}" @@ -8921,7 +9321,7 @@ msgstr "" "Error al configurar el font-weight/style de la fuente. Este estilo de " "fuente no existe en la fuente actual." -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Font weight (300, Bold, Regular Italic...) o un {alias}" @@ -8961,7 +9361,7 @@ msgstr "" "En un archivo JSON único, las claves de primer nivel deben ser los nombres " "de los sets de tokens." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Importar tokens" @@ -8969,17 +9369,17 @@ msgstr "Importar tokens" msgid "workspace.tokens.import-warning" msgstr "Al importar tokens sobreescribirás todos tus tokens, sets y themes." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Inactivo" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Este set no está activo. Cambia el tema o activa este set para ver los " "cambios en el viewport" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Usa tokens individuales" @@ -8999,11 +9399,11 @@ msgstr "Tema" msgid "workspace.tokens.label.theme-placeholder" msgstr "Añade un Tema (p. ej. Claro)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Letter spacing o {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Line height (multiplicador, px o %) o {alias}" @@ -9019,11 +9419,11 @@ msgstr "Tamaño máx." msgid "workspace.tokens.min-size" msgstr "Tamaño mín." -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Referencia no encontrada" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Click derecho para ver opciones" @@ -9039,7 +9439,7 @@ msgstr "No hay temas activos" msgid "workspace.tokens.no-permisions-set" msgstr "Debes ser editor para activar / desactivar sets" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Debes ser editor para usar temas" @@ -9051,7 +9451,7 @@ msgstr "Aun no hay sets definidos. Crea uno primero" msgid "workspace.tokens.no-sets-yet" msgstr "Todavía no hay sets." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "No hay temas." @@ -9063,7 +9463,7 @@ msgstr "Actualmente no existen temas." msgid "workspace.tokens.not-remap" msgstr "No actualizar" -#: src/app/main/data/workspace/libraries.cljs:847 +#: src/app/main/data/workspace/libraries.cljs:850 msgid "workspace.tokens.notifications.source-sets-or-themes-updated" msgstr "Los sets o temas de la fuente de tokens se han modificado" @@ -9071,7 +9471,7 @@ msgstr "Los sets o temas de la fuente de tokens se han modificado" msgid "workspace.tokens.num-active-sets" msgstr "%s sets activos" -#: src/app/main/ui/workspace/colorpicker.cljs:438, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:67 +#: src/app/main/ui/workspace/colorpicker.cljs:438, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:112 msgid "workspace.tokens.only-first-fill-or-stroke" msgstr "" "Los tokens de diseño solo se pueden aplicar al primer relleno o trazo de la " @@ -9081,7 +9481,7 @@ msgstr "" msgid "workspace.tokens.open-source-new-tab" msgstr "Abrir la fuente de tokens en una pestaña nueva" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Valor original: %s" @@ -9093,15 +9493,15 @@ msgstr "Paddings" msgid "workspace.tokens.radius" msgstr "Radio" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "La referencia en {%s} no es válida o no se encuentra en ningún set activo." -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Introduce un alias de tipografía de token" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Introduce un alias de sombra de token" @@ -9141,7 +9541,7 @@ msgstr "Renombrar grupo de tokens" msgid "workspace.tokens.rename-group-name-hint" msgstr "Tus tokens serán automáticamente renombrados a %s.(sufijo).(token)" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Valor resuelto: %s" @@ -9179,27 +9579,27 @@ msgstr "" msgid "workspace.tokens.settings" msgstr "Configuración de tokens" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Añadir Sombra" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Blur" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Interior" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Eliminar Sombra" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Spread" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "El valor de blur no puede ser negativo" @@ -9207,11 +9607,11 @@ msgstr "El valor de blur no puede ser negativo" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "El valor de spread no puede ser negativo" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -9223,11 +9623,15 @@ msgstr "Tamaño" msgid "workspace.tokens.source" msgstr "Fuente:" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:311, src/app/main/ui/workspace/tokens/management/context_menu.cljs:335 +msgid "workspace.tokens.stroke-width" +msgstr "Ancho del trazo" + +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize o {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through o {alias}" @@ -9249,31 +9653,31 @@ msgstr "Este archivo" msgid "workspace.tokens.token-description" msgstr "Descripción" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Seleccionar familia tipográfica" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Familia tipográfica" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Familia tipográfica o lista de fuentes separadas por coma (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nombre" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "Ya existe un token en la ruta: %s o en un prefijo del mismo." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "El nombre debería ser de al menos 1 caracter" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " es un nombre de token no válido.\n" @@ -9284,11 +9688,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Valor" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Introduce un valor o un alias usando {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENS - %s" @@ -9307,15 +9711,15 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "El tipo '%s' no está soportado (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Usa una referencia" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "El valor no es válido" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "Flecha (%s)" @@ -9323,104 +9727,116 @@ msgstr "Flecha (%s)" msgid "workspace.toolbar.assets" msgstr "Recursos" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Paleta de colores (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Comentarios (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Curva (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elipse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Tablero (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Imagen (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:413 +#: src/app/main/ui/workspace/top_toolbar.cljs:362 msgid "workspace.toolbar.label" msgstr "Herramientas de dibujo" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "Línea (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "Conectar aquí" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP conectado" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/components/mcp_menu.cljs:136 +msgid "workspace.toolbar.mcp-copy-session-id" +msgstr "Copiar ID de sesión" + +#: src/app/main/ui/components/mcp_menu.cljs:33 +msgid "workspace.toolbar.mcp-session-copied" +msgstr "ID de sesión copiado" + +#: src/app/main/ui/components/mcp_menu.cljs:132 +msgid "workspace.toolbar.mcp-session-id" +msgstr "ID de sesión: %s" + +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Mover (%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "Barra de paletas" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/palette.cljs:211 +msgid "workspace.toolbar.palette-options" +msgstr "Opciones de la paleta" + +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Ruta (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Extensiones (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rectángulo (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Texto (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Tipografías (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Alternar barra de herramientas" -#: src/app/main/ui/workspace/viewport/top_bar.cljs:45 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:64 msgid "workspace.top-bar.read-only.done" msgstr "Hecho" -#: src/app/main/ui/workspace/viewport/top_bar.cljs:40 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:53 #, markdown msgid "workspace.top-bar.view-only" msgstr "**Inspeccionando código** (View only)" -#: src/app/main/ui/workspace/viewport/top_bar.cljs:39 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:50 msgid "workspace.top-bar.webgl-context-lost" msgstr "Renderizado no disponible. Recarga la página para restaurar la edición." -#: src/app/main/ui/workspace/viewport/top_bar.cljs:43 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:60 msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "Recargar" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Todavía no hay cambios en el histórico" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "por %s" @@ -9496,6 +9912,10 @@ msgstr "tipografías" msgid "workspace.undo.entry.new" msgstr "Nuevo %s" +#: src/app/main/ui/workspace/sidebar/history.cljs:371 +msgid "workspace.undo.entry.show-details" +msgstr "Mostrar detalles" + #: src/app/main/ui/workspace/sidebar/history.cljs:132 msgid "workspace.undo.entry.single.circle" msgstr "círculo" @@ -9560,19 +9980,19 @@ msgstr "tipografía" msgid "workspace.undo.entry.unknown" msgstr "Operación sobre %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1500, src/app/main/data/workspace/versions.cljs:255 msgid "workspace.updates.dismiss" msgstr "Ignorar" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1498 msgid "workspace.updates.more-info" msgstr "Más información" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1496 msgid "workspace.updates.there-are-updates" msgstr "Hay actualizaciones en bibliotecas compartidas" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1502 msgid "workspace.updates.update" msgstr "Actualizar" @@ -9580,27 +10000,31 @@ msgstr "Actualizar" msgid "workspace.versions.autosaved.entry" msgstr "%s versiones de autoguardado" -#: src/app/main/data/workspace/versions.cljs:301, src/app/main/ui/workspace/sidebar/versions.cljs:300 +#: src/app/main/data/workspace/versions.cljs:319, src/app/main/ui/workspace/sidebar/versions.cljs:309 msgid "workspace.versions.autosaved.version" msgstr "Autoguardado %s" -#: src/app/main/ui/workspace/sidebar/versions.cljs:323 +#: src/app/main/ui/workspace/sidebar/versions.cljs:331 msgid "workspace.versions.button.pin" msgstr "Fijar versión" -#: src/app/main/ui/workspace/sidebar/versions.cljs:233, src/app/main/ui/workspace/sidebar/versions.cljs:313 +#: src/app/main/ui/workspace/sidebar/versions.cljs:239, src/app/main/ui/workspace/sidebar/versions.cljs:321 msgid "workspace.versions.button.preview" msgstr "Previsualizar versión" -#: src/app/main/ui/workspace/sidebar/versions.cljs:318 +#: src/app/main/ui/workspace/sidebar/versions.cljs:326 msgid "workspace.versions.button.restore" msgstr "Restaurar versión" -#: src/app/main/ui/workspace/sidebar/versions.cljs:425, src/app/main/ui/workspace/sidebar/versions.cljs:427 +#: src/app/main/ui/workspace/sidebar/versions.cljs:433, src/app/main/ui/workspace/sidebar/versions.cljs:435 msgid "workspace.versions.button.save" msgstr "Guardar versión" -#: src/app/main/ui/workspace/sidebar/versions.cljs:434 +#: src/app/main/data/workspace/versions.cljs:93 +msgid "workspace.versions.create-failed-unsaved" +msgstr "No se ha creado la versión porque tus cambios no se han podido guardar." + +#: src/app/main/ui/workspace/sidebar/versions.cljs:442 msgid "workspace.versions.empty" msgstr "No hay versiones aún" @@ -9608,35 +10032,39 @@ msgstr "No hay versiones aún" msgid "workspace.versions.expand-snapshot" msgstr "Expandir versiones" -#: src/app/main/ui/workspace/sidebar/versions.cljs:398 +#: src/app/main/ui/workspace/sidebar/versions.cljs:406 msgid "workspace.versions.filter.all" msgstr "Todas las versiones" -#: src/app/main/ui/workspace/sidebar/versions.cljs:412 +#: src/app/main/ui/workspace/sidebar/versions.cljs:420 msgid "workspace.versions.filter.label" msgstr "Filtro de versiones" -#: src/app/main/ui/workspace/sidebar/versions.cljs:399 +#: src/app/main/ui/workspace/sidebar/versions.cljs:407 msgid "workspace.versions.filter.mine" msgstr "Mis versiones" -#: src/app/main/ui/workspace/sidebar/versions.cljs:403 +#: src/app/main/ui/workspace/sidebar/versions.cljs:411 msgid "workspace.versions.filter.user" msgstr "Versiones de %s" -#: src/app/main/ui/workspace/sidebar/versions.cljs:420 +#: src/app/main/ui/workspace/sidebar/versions.cljs:428 msgid "workspace.versions.loading" msgstr "Cargando..." -#: src/app/main/data/workspace/versions.cljs:312 +#: src/app/main/data/workspace/versions.cljs:330 msgid "workspace.versions.preview-banner-title" msgstr "Previsualizando version: %s" -#: src/app/main/data/workspace/versions.cljs:306 +#: src/app/main/data/workspace/versions.cljs:324 msgid "workspace.versions.preview.unnamed" msgstr "Versión sin nombre" -#: src/app/main/data/workspace/versions.cljs:239 +#: src/app/main/data/workspace/versions.cljs:315, src/app/main/ui/workspace/sidebar/versions.cljs:308 +msgid "workspace.versions.repair.version" +msgstr "Antes de la reparación automática, %s" + +#: src/app/main/data/workspace/versions.cljs:253 msgid "workspace.versions.restore-warning" msgstr "¿Quieres restaurar esta versión?" @@ -9657,6 +10085,6 @@ msgstr "Abrir menu de versiones" msgid "workspace.versions.warning.subtext" msgstr "Si quieres aumentar este límite, contáctanos en [support@penpot.app](%s)" -#: src/app/main/ui/workspace/sidebar/versions.cljs:462 +#: src/app/main/ui/workspace/sidebar/versions.cljs:470 msgid "workspace.versions.warning.text" msgstr "Los autoguardados duran %s días." diff --git a/frontend/translations/es_419.po b/frontend/translations/es_419.po index ad847e60b1..65d01eba60 100644 --- a/frontend/translations/es_419.po +++ b/frontend/translations/es_419.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2026.10.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "¿Ya tienes una cuenta?" @@ -29,7 +29,7 @@ msgstr "" "Este es un servicio DEMO, NO LO UTILICE para trabajos reales, los proyectos " "se borrarán periódicamente." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "¿Has olvidado tu contraseña?" @@ -37,27 +37,27 @@ msgstr "¿Has olvidado tu contraseña?" msgid "auth.fullname" msgstr "Nombre completo" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Inicie sesión aquí" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "Open ID" @@ -79,7 +79,7 @@ msgstr "" "El enlace de recuperación de contraseña ha sido enviado a su bandeja de " "entrada de su correo electrónico." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Se unió al equipo con éxito" @@ -111,11 +111,11 @@ msgstr "¿Has olvidado tu contraseña?" msgid "auth.recovery-submit" msgstr "cambia tu contraseña" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "¿No tienes cuenta aún?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Crea una cuenta" @@ -123,113 +123,113 @@ msgstr "Crea una cuenta" msgid "auth.register-title" msgstr "Crea una cuenta" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Términos de servicio" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publicar" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Todos los usuarios de Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "¿Estás seguro de que deseas eliminar este enlace? Si lo haces ya no estará " "disponible para nadie" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(actual)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Borrar enlace" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Conseguir enlace" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Enlace copiado exitosamente" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Administrar permisos" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 página compartida" msgstr[1] "%s paginas compartidas" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Puedes comentar" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Puedes inspeccionar el código" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Cualquier persona con enlace tendrá acceso" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Páginas compartidas" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "El enlace para compartir aparecerá aquí" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Solo miembros del equipo" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Compartir prototipos" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Seleccionar todo" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Despublicar" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Gestión de equipos" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot está destinado a equipos. Invite a miembros a trabajar juntos en " "proyectos y archivos" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "¡En equipo!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Agregar como biblioteca compartida" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Cambiar el correo electrónico" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copiar)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Crear nuevo equipo" @@ -237,11 +237,11 @@ msgstr "Crear nuevo equipo" msgid "dashboard.default-team-name" msgstr "Tu Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Eliminar equipo" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Descargar el archivo Penpot (.penpot)" @@ -257,73 +257,61 @@ msgstr "Duplicar %s archivos" msgid "dashboard.export-binary-multi" msgstr "Descargar %s archivos Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exportar tableros como PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exportar como PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s de %s elementos seleccionados" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exportar" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Puede agregar configuraciones de exportación a elementos desde las " "propiedades de diseño (en la parte inferior de la barra lateral derecha)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Información sobre cómo configurar las exportaciones en Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "No hay elementos con configuración de exportación." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Selección de exportación" -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Procesamiento de tipografías" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Subiendo datos al servidor (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Subiendo archivo: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Invitar a la gente" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "dejar el equipo" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliotecas y plantillas" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Explore más de ellos y sepa cómo contribuir" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Hubo un problema al importar la plantilla. La plantilla no fue importada." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliotecas" @@ -331,7 +319,7 @@ msgstr "Bibliotecas" msgid "dashboard.loading-files" msgstr "cargando tus archivos…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "cargando tus fuentes…" @@ -347,35 +335,35 @@ msgstr "Mover %s archivos a" msgid "dashboard.move-to-other-team" msgstr "Pasar a otro equipo" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nuevo archivo" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Archivo nuevo" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nuevo proyecto" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nuevo proyecto" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "No se encontraron coincidencias para \"%s\"" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Los proyectos fijados aparecerán aquí" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Su dirección de correo electrónico se ha actualizado correctamente" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "El nombre debe contener como máximo %s caracteres." diff --git a/frontend/translations/eu.po b/frontend/translations/eu.po index 4594f82431..fe7e2fc3c6 100644 --- a/frontend/translations/eu.po +++ b/frontend/translations/eu.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: Mikel Larreategi \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Basque " "\n" "Language: eu\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Baduzu kontua?" @@ -29,7 +29,7 @@ msgstr "" "Hau PROBAK EGITEKO zerbitzua da. EZ ERABILI benetako lana egiteko, hemengo " "proiektuak noizean behin ezabatu egingo dira." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Pasahitza ahaztu duzu?" @@ -37,27 +37,27 @@ msgstr "Pasahitza ahaztu duzu?" msgid "auth.fullname" msgstr "Izen osoa" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Sartu" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -77,7 +77,7 @@ msgstr "Profila ez da oraindik egiaztatu, egiaztatu ezazu jarraitu aurretik." msgid "auth.notifications.recovery-token-sent" msgstr "Eposta mezu bat bidali dizugu zure pasahitza berreskuratzeko." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Taldera ondo gehitu zara" @@ -109,11 +109,11 @@ msgstr "Pasahitza ahaztu duzu?" msgid "auth.recovery-submit" msgstr "Aldatu pasahitza" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Ez duzu konturik?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Sortu kontua" @@ -121,111 +121,111 @@ msgstr "Sortu kontua" msgid "auth.register-title" msgstr "Sortu kontua" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Zerbitzuaren erabilpen-baldintzak" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Argitaratu" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Penpoten erabiltzaile guztiak" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "Esteka ezabatu nahi duzu? Hori egiten baduzu, inori ez dio funtzionatuko" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(oraingoa)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Ezabatu esteka" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Lortu esteka" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Esteka ondo kopiatu da" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Kudeatu baimenak" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "Orrialde 1 partekatuta" msgstr[1] "%s orrialde partekatuta" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Iruzkinak egin ditzakete" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Kodea ikus dezakete" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Esteka duen edonor sar daiteke" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Partekatutako orriak" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Partekatzeko esteka hemen agertuko da" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Taldeko kideak bakarrik" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Partekatu prototipoak" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Aukeratu guztiak" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Argitaraketa atzera bota" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Taldearen kudeaketa" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot taldeentzat sortuta dago. Gonbidatu beste pertsona batzuk proiektu " "eta fitxategietan batera lan egiteko" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Egin taldea!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Gehitu partekatutako liburutegi bezala" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Eposta aldatu" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopia)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Sortu talde berria" @@ -233,11 +233,11 @@ msgstr "Sortu talde berria" msgid "dashboard.default-team-name" msgstr "Zure Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Ezabatu taldea" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Deskargatu Penpot fitxategia (.penpot)" @@ -253,59 +253,59 @@ msgstr "%s fitxategi bizkoiztu" msgid "dashboard.export-binary-multi" msgstr "Deskargatu %s Penpot fitxategi (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Esportatu arbelak PDFra" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Esportatu PDFra" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "Aukeratutakoak: %s / %s" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Esportatu" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Elementuei esportazioko ezarpenak gehitzeko aukeratu duzu diseinuaren " "propietateetatik (eskumako aldearen amaieran)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Penpoten esportazioak konfiguratzeari buruzko informazioa." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Ez dago esportazioko ezarpenik duen elementurik." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Esportatu aukeraketa" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Letra-tipoa ondo ezabatu da" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Baztertu guztiak" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Ez duzu pertsonalizatutako letra-tiporik." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "Letra-tipo 1 gehitu da" msgstr[1] "%s letra-tipo gehitu dira" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -315,7 +315,7 @@ msgstr "" "honetako letra-tipoak kargatu daitezke: **TTF, OTF and WOFF** (batekin " "nahikoa da)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -325,11 +325,11 @@ msgstr "" "lizentzien inguruan irakurtzea ere interesgarria izan daiteke: " "[letra-tipoen lizentziak](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Kargatu guztiak" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -339,71 +339,43 @@ msgstr "" "dezakezu. Gainera, weberako letra-tipoak sortzeko " "[Transfonter](https://transfonter.org/) erabiltzea gomendatzen dugu. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Inportatu Penpot fitxategiak" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Ezin izan dugu fitxategia inportatu" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgstr "%s fitxategi ondo inportatu dira." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Fitxategi batzuk inportatu ez diren objektu akasdunak dituzte." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Koloreak prozesatzen" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Osagaiak prozesatzen" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Media prozesatzen" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "%s orrialdea prozesatzen" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Letra-tipoak prozesatzen" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Datuak zerbitzarira bidaltzen (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Fitxategia bidaltzen: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Gonbidatu taldera" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Utzi taldea" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Liburutegi eta txantiloiak" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Gehiago ikusi eta ikasi nola lagundu" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Arazo bat egon da txantiloia inportatzean. Ezin izan da inportatu." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Partekatutako liburutegiak" @@ -411,7 +383,7 @@ msgstr "Partekatutako liburutegiak" msgid "dashboard.loading-files" msgstr "zure fitxategiak kargatzen…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "zure letra-tipoak kargatzen…" @@ -427,35 +399,35 @@ msgstr "Mugitu %s fitxategi" msgid "dashboard.move-to-other-team" msgstr "Mugitu beste talde batera" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Fitxategi berria" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Fitxategi berria" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Proiektu berria" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Proiektu berria" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Ez da \"%s\" aurkitu" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Ainguratutako proiektuak hemen agertuko dira" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Zure eposta helbidea ondo eguneratu da" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Zure eposta helbidea egiaztatu egin da" @@ -463,7 +435,7 @@ msgstr "Zure eposta helbidea egiaztatu egin da" msgid "dashboard.notifications.password-saved" msgstr "Pasahitza ondo gorde da!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s kide" @@ -471,7 +443,7 @@ msgstr "%s kide" msgid "dashboard.open-in-new-tab" msgstr "Ireki fitxa berrian" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Aukerak" @@ -479,39 +451,39 @@ msgstr "Aukerak" msgid "dashboard.password-change" msgstr "Aldatu pasahitza" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Ainguratu/Desainguratu" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Proiektuak" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Kontua ezabatu nahi duzu?" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Gorde aukerak" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Bilatu…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "\"%s\" bilatzen…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Aldatu hizkuntza" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Aukeratu gaia" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Ikusi fitxategi guztiak" @@ -531,7 +503,7 @@ msgstr "Zure fitxategia ondo bikoiztu da" msgid "dashboard.success-duplicate-project" msgstr "Zure proiektua ondo bikoiztu da" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Zure fitxategia ondo mugitu da" @@ -543,81 +515,81 @@ msgstr "Zure fitxategiak ondo mugitu dira" msgid "dashboard.success-move-project" msgstr "Zure proiektua ondo mugitu da" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Taldearen informazioa" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Taldekideak" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Taldearen proiektuak" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Itxura" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Bilaketaren emaitzak" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Idatzi bilaktzeko zerbaitu" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Atzera bota liburutegia argitaratzea" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Eguneratu aukerak" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Aktibo" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Webhook hau aktibatzen denean gertaeraren xehetasunak bidaliko dira" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Elementu mota" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Sortu webhooka" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhooka ondo sortu da." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhookak beste webgune batzuei Penpoten zerbait gertatu dela jakinarazteko " "modu bat dira. Adierazitako URLtara POST eskaera bat bidaliko dugu." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Sakatu \"Sortu webhooka\" botoia bat gehitzeko." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Ez dago webhookik." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Zure kontua" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Eposta" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Izena" @@ -633,7 +605,7 @@ msgstr "Oharra" msgid "ds.component-subtitle" msgstr "Eguneratzeko dauden osagaiak:" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Utzi" @@ -649,27 +621,27 @@ msgstr "Ziur?" msgid "errors.auth-provider-not-configured" msgstr "Saioa hasteko hornitzailea ez dago konfiguratuta." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Badirudi ez zarela sartu edo zure saioa iraungi egin dela." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Ezin izan da %s letra-tipoa kargatu" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Ezin izan dira %s letra-tipoak kargatu" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Zure nabigatzaileak ezin du hori egin" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Eposta helbide hori erabilita dago" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Eposta helbide hori egiaztatuta dago." @@ -677,28 +649,28 @@ msgstr "Eposta helbide hori egiaztatuta dago." msgid "errors.email-as-password" msgstr "Ezin duzu zure eposta helbidea pasahitz gisa erabiliz" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "«%s» helbideak ez ditu mezuak ondo jasotzen, itzuli egiten ditu." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "" "«%s» helbideak ez ditu mezuak ondo jasotzen, itzuli egiten ditu edo " "spamaren inguruko txostenak ditu." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Badirudi '%s' ezaugarria aktibo duen fitxategi bat irekitzen ari zarela " "baina zure penpot frontendak ezin du hori egin edo ezaugarri hori " "desaktibatuta du." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Ezaugarria ezin da erabili: '%s'." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Erroreren bat gertatu da." @@ -710,7 +682,7 @@ msgstr "Kolorea ez da zuzena" msgid "errors.invalid-email" msgstr "Mesedez, idatzi eposta helbide zuzen bat" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Egiaztapenereko epostak bat etorri behar du aurrekoarekin" @@ -730,7 +702,7 @@ msgstr "Gonbidapen hau bertan behera utzi dute edo iraungi egin da." msgid "errors.ldap-disabled" msgstr "LDAP bidez sartzea desgaituta dago." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "" "Kuotaren maximora heldu zara: '%s'. Jarri kontaktuan laguntza " @@ -748,7 +720,7 @@ msgstr "Irudiaren edukia eta luzapena bat ez datozela dirudi." msgid "errors.media-type-not-allowed" msgstr "Irudia zuzena ez dela dirudi." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Gonbidatzen ari zaren profilak ez ditu eposta bidezko mezuak jasotzen (spam " @@ -766,7 +738,7 @@ msgstr "Pasahitzak gutxienez 8 karaktere izan behar ditu" msgid "errors.profile-blocked" msgstr "Profila blokeatuta dago" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Zure profilak ez ditu eposta mezuak jasotzen (spam gisa markatu delako edo " @@ -776,17 +748,17 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Erregistroa desgaituta dago." -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Ez dago taldetik irteteko behar beste kide, seguru aski ezabatu egin nahiko " "duzu." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Izendatu nahi duzun kidea ez da existitzen." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Jabea ezin da taldetik irten, jabetza beste pertsona bati eman behar diozu." @@ -794,35 +766,35 @@ msgstr "Jabea ezin da taldetik irten, jabetza beste pertsona bati eman behar dio msgid "errors.unexpected-error" msgstr "Errore bat gertatut da." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Tokena ez da zuzena" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Konexio errorea, URLa ezin da ireki" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URLak ez du balidazioa gainditu." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Errore bat gertatu da azken bidalketan." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Errorea gertatu da SSL balidazioan." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Denbora muga gainditu da" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Errore ezezaguna balidazioan" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Espero ez zen egoera %s" @@ -838,11 +810,11 @@ msgstr "Aurreko pasahitza ez da zuzena" msgid "feedback.description" msgstr "Deskribapena" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "Ongi etorri! Laguntza behar baduzu, bilatu, idatzi edo galdetu lasai." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot komunitatea" @@ -856,11 +828,11 @@ msgstr "" "Idatzi zure mezuaren gaia, arazo bat, ideia bat edo zalantza bat den " "esanez. Gure lantaldeko norbaitek ahal bezain laster erantzungo dizu." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Zure zalantza teknikoak erantzuteko kontua." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Laguntzarako X kontua" @@ -892,7 +864,7 @@ msgstr "Deskargatu jatorrizko irudia" msgid "inspect.attributes.shadow" msgstr "Itzala" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Tamaina eta posizioa" @@ -900,39 +872,27 @@ msgstr "Tamaina eta posizioa" msgid "inspect.attributes.stroke" msgstr "Ertza" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Erdia" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Barnealdea" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Kanpoaldea" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Letra-tipo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Letra-tipo familia" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Letra-tipoaren tamaina" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Letra-tipoaren estiloa" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Hizkien tartea" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Lerroartea" @@ -944,11 +904,11 @@ msgstr "Bat ere ez" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Azpimarratu" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Lehenengoa letra larriz" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Letra xeheak" @@ -956,7 +916,7 @@ msgstr "Letra xeheak" msgid "inspect.attributes.typography.text-transform.none" msgstr "Bat ere ez" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Letra larriz" @@ -1020,27 +980,27 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Testua" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Lasterteklak" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Onartu" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Aktibo" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Gehitu letra-tipo pertsonalizatua" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Kudeaketa" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Guztia" @@ -1048,25 +1008,25 @@ msgstr "Guztia" msgid "labels.and" msgstr "eta" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Itzuli" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Pixkat itxaron eta berriz saiatu behar duzula dirudi; mantentze-lanak " "egiten ari gara." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Zerbitzariaren errorea (Bad Gateway)" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Utzi" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Itxi" @@ -1074,11 +1034,11 @@ msgstr "Itxi" msgid "labels.code" msgstr "Kodea" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Iruzkinak" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Komunitatea" @@ -1086,35 +1046,35 @@ msgstr "Komunitatea" msgid "labels.confirm-password" msgstr "Berretsi pasahitza" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Jarraitu" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Penpot kontu batekin jarraitu dezakezu" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Kopiatu esteka" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Sortu" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Sortu talde berria" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Sartu taldearen izena" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Lanlekua" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Ezabatu" @@ -1126,7 +1086,7 @@ msgstr "Ezabatu iruzkina" msgid "labels.delete-comment-thread" msgstr "Ezabatu haria" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Ezabatu gonbidapena" @@ -1134,23 +1094,23 @@ msgstr "Ezabatu gonbidapena" msgid "labels.delete-multi-files" msgstr "Ezabatu %s fitxategi" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Zirriborroak" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Editatu" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Edizioa" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Iraungita" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Esportatu" @@ -1162,39 +1122,39 @@ msgstr "Iritziak jasotzeko osagaia desgaituta dago" msgid "labels.feedback-sent" msgstr "Iritzia ondo bidali da" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Letra-tipo familia" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Estiloak" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Letra-tipoak" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "GitHubeko errepositorioa" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Eman zure iritzia" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Itzuli" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Laguntza zentroa" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Ezkutatu ebatzitzako iruzkinak" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inaktibo" @@ -1202,39 +1162,39 @@ msgstr "Inaktibo" msgid "labels.info" msgstr "Informazioa" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Instalatutako letra-tipoak" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Barneko errorea" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Gonbidapenak" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Hizkuntzak" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Liburutegi eta Txantiloiak" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Sartu edo eman izena" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Irten" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Kidea" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Kideak" @@ -1242,31 +1202,31 @@ msgstr "Kideak" msgid "labels.new-password" msgstr "Pasahitz berria" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Ez duzu iruzkinen inguruko jakinarazpenik." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Ez dago gonbidapenik." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Atx!" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "Fitxategi 1" msgstr[1] "%s fitxategi" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "Arbel 1" msgstr[1] "%s arbel" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "Proiektu 1" @@ -1276,107 +1236,107 @@ msgstr[1] "%s proiektu" msgid "labels.old-password" msgstr "Aurreko pasahitza" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Jabea" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Pasahitza" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Zain" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profila" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Proiektuak" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Bertsioaren oharrak" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Kendu" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Kendu kidea" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Berrizendatu" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Berrizendatu taldea" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Birbidali gonbidapena" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Berriz saiatu" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rola" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Gorde" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Bilatu letra-tipoa" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Bidali" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Bidaltzen…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Gure sistemaren programatutako mantentze-lanak egiten ari gara." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Zerbitzua ez dago martxan" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Ezarpenak" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Partekatutako liburutegiak" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Erakutsi iruzkin guztiak" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Erakutsi iruzkinen zerrenda" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Erakutsi zure iruzkinak bakarrik" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Egoera" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutorialak" @@ -1384,23 +1344,23 @@ msgstr "Tutorialak" msgid "labels.unpublish-multi-files" msgstr "%s fitxategi argitaratzeari utzi" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Eguneratu" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Eguneratu taldea" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Kargatu" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Kargatu letra-tipoa" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Kargatzen…" @@ -1408,11 +1368,11 @@ msgstr "Kargatzen…" msgid "labels.view-only" msgstr "IKUSTEKO BAKARRIK" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Bistarazlea" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhookak" @@ -1420,11 +1380,11 @@ msgstr "Webhookak" msgid "labels.write-new-comment" msgstr "Idatzi iruzkin berria" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(zu)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "zure kontua" @@ -1446,63 +1406,63 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Gehitu \"%s\" partekatutako liburutegi gisa" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Gehienekoa" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Egiaztatu posta elektroniko berria" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Zure egungo \"%s\" helbidera mezu bat bidaliko du zure nortasuna " "egiaztatzeko." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Posta elektroniko berria" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Aldatu posta elektronikoa" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Aldatu zure posta elektronikoa" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Sortu webhooka" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Sortu webhooka" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Informazioaren URLa" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Utzi eta mantendu nire kontua" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Bai, ezabatu nire kontua" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "Zure kontua ezabatzen baduzu zure proiektu eta fitxategi guztiak galduko " "dituzu." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Zure kontua benetan ezabatu nahi duzu?" @@ -1544,31 +1504,31 @@ msgstr "Benetan %s fitxategi ezabatu egin nahi dituzu?" msgid "modals.delete-file-multi-confirm.title" msgstr "%s fitxategi ezabatzen" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Benetan letra-tipo estilo hau ezabatu egin nahi duzu? Ez da kargatuko " "fitxategiren baten erabiltzen baduzu." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Letra-tipo estiloa ezabatzen" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Benetan letra-tipo hau ezabatu egin nahi duzu? Ez da kargatuko fitxategiren " "baten erabiltzen baduzu." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Letra-tipoa ezabatzen" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Benetan orrialde hau ezabatu egin nahi duzu?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Ezabatu orrialdea" @@ -1584,19 +1544,19 @@ msgstr "Benetan proiektu hau ezabatu egin nahi duzu?" msgid "modals.delete-project-confirm.title" msgstr "Ezabatu proiektua" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Ezabatu fitxategia" msgstr[1] "Ezabatu fitxategiak" -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Benetan fitxategi hau ezabatu nahi duzu?" msgstr[1] "Benetan fitxategi hauek ezabatu nahi dituzu?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Ezabatu fitxategia" @@ -1616,135 +1576,135 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Taldea ezabatzen" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Kidea ezabatzen" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Benetan kide hau taldetik ezabatu egin nahi duzu?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Taldekidea ezabatzen" -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Ezabatu webhooka" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Benetan webhook hau ezabatu egin nahi duzu?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Webhooka ezabatzen" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Aldatu webhooka" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Aldatu webhooka" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Bidali gonbidapena" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Posta elektronikoak, komarekin banatuta" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "Eposta helbide batzuk jada taldekideenak dira. Ez da gonbidapenik bidaliko." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Gonbidatu kideak taldera" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Talde honetako kide bakarra zarenez, taldea bere proiektu eta fitxategiekin " "ezabatuko da." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Benetan %s taldea utzi egin nahi duzu?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Ezin duzu taldea utzi ez badago jabe berria izateko hautagairik. Agian " "taldea ezabatu egin nahi duzu." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "Taldearen jabea zara. Aukeratu beste pertsona bat jabe izan dadin." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Jabe egin eta utzi" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Aukeratu jabe berria" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Utzi aurretik" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Utzi taldea" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Benetal taldea utzi egin nahi duzu?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Taldea uzten" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Desplazamendua" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Eman jabetza" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Jabetza ematen baduzu, zure rola Kudeatzaile izatera pasatuko da, " "taldearekiko zure baimen batzuk galduz. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "Taldearen jabea zara. Benetan %s taldearen gabe egin nahi duzu?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Taldearen jabe berria" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Gutxienekoa" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgstr "Argitaratzea atzera bota" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Benetan liburutegi honen argitaratzea atzera bota nahi duzu?" msgstr[1] "Benetan liburutegi hauen argitaratzea atzera bota nahi duzu?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Liburutegiaren argitaratzea atzera bota" @@ -1768,23 +1728,23 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Eguneratu liburutegiaren osagaia" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Gonbidapena ondo bidali da" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Gonbidapenaren esteka kopiatu da" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "Ezin da zure profila ezabatu. Berresleitu zure taldeak jarraitu aurretik." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profila ondo gorde da!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "" "Posta elektronikoa egiaztatzeko mezua ondo bidali da %s helbidera. " @@ -1804,11 +1764,11 @@ msgstr "Ongi etorri Penpotera!" msgid "onboarding.choice.team-up.create-team-desc" msgstr "Taldeari izena eman ostean, pertsona gehiago gonbidatu ditzakezu." -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Gonbidatu kideak" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Gonbidatu rol honekin:" @@ -1854,39 +1814,39 @@ msgstr "Hasi diseinatzen" msgid "profile.recovery.go-to-login" msgstr "Sartu" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Batzuk" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Gehitu iruzkinak" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Lerrokatu behean" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Lerrokatu erdian horizontalki" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Lerrokatu ezkerrean" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Lerrokatu eskuman" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Lerrokatu goian" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Lerrokatu erdian bertikalki" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Sortu arbela hautapenetik" @@ -1894,43 +1854,43 @@ msgstr "Sortu arbela hautapenetik" msgid "shortcuts.bold" msgstr "Aktibatu/desaktibatu beltza" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Diferentzia" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Baztertu" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Ebakidura" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Elkarketa" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Bidali atzera" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Bidali hondora" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Mugitu aurrealdera" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Mugitu aurrera" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Garbitu historiala" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Kopiatu" @@ -1938,35 +1898,35 @@ msgstr "Kopiatu" msgid "shortcuts.create-new-project" msgstr "Sortu berria" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Moztu" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Jaitsi zooma" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Ezabatu" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Ezabatu nodoa" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Banandu osagaian" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Kurba" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipsea" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Arbela" @@ -1974,39 +1934,39 @@ msgstr "Arbela" msgid "shortcuts.draw-nodes" msgstr "Marraztu nodoak" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Bidea" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Laukia" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Testua" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Bikoiztu" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Utzi" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Esportatu osagaiak" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Zoom dena ikusteko" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Irauli horizontalki" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Irauli bertikalki" @@ -2030,23 +1990,23 @@ msgstr "Joan partekatutako liburutegietara" msgid "shortcuts.go-to-search" msgstr "Bilatu" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Taldekatu" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Banatu horizontalki" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Erakutsi/ezkutatu interfazea" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Zooma handitu" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Txertatu irudia" @@ -2054,7 +2014,7 @@ msgstr "Txertatu irudia" msgid "shortcuts.italic" msgstr "Aktibatu/desaktibatu etzana" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Elkartu nodoak" @@ -2062,39 +2022,39 @@ msgstr "Elkartu nodoak" msgid "shortcuts.line-through" msgstr "Aktibatu/desaktibatu marratzea" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Bihurtu ertz" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Bihurtu kurba" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Maskara" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Elkartu nodoak" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Mugitu" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Mugitu behera azkar" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Mugitu ezkerrera azkar" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Mugitu eskumara azar" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Mugitu gora azkar" @@ -2102,289 +2062,289 @@ msgstr "Mugitu gora azkar" msgid "shortcuts.move-nodes" msgstr "Mugitu nodoa" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Mugitu behera" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Mugitu ezkerrera" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Mugitu eskumara" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Mugitu gora" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Hurrengo arbela" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Ez dago emaitzarik" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Opakutasuna %100" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Opakutasuna %10" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Opakutasuna %20" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Opakutasuna %30" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Opakutasuna %40" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Opakutasuna %50" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Opakutasuna %60" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Opakutasuna %70" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Opakutasuna %80" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Opakutasuna %90" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Ireki kolore-hautatzailea" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Iruzkinak" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Joan lan-lekura" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Ikuskagailura joan" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Joan interakzioetara" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Joan ikusteko modura" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Joan zure mahaigainera" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " edo " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Itsatsi" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Aurreko arbela" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Berregin" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Berrezarri zooma" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Oinarrizkoak" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Lanlekua" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Ikusteko modua" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Mahaigaina" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Aukeratu guztia" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Aukeratu hurrengo geruza" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Aukeratu aurreko geruza" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Banatu nodoak" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Erakutsi/ezkutatu pixel sarea" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Erakutsi/ezkutatu lasterbideak" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Lerrokatu nodoak" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Aktibatu sarea pixelera lerrokatzea" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Hasi edizioa" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Lerrokatzea" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Editatu" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Orokorra" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu nagusia" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Aldatu geruzak" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Nabigazioa" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Nabigazioa" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Nabigazioa" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panelak" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Bidea" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formak" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Testuak" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Tresnak" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Aktibatu iruditxoak" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Lasterteklak" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Lerrokatzea aldatu" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Erakutsi/ezkutatu baliabideak" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Erakutsi/ezkutatu kolore paleta" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Erakutsi/ezkutatu foko-modua" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Aktibatu/desaktibatu pantaila osoa" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Erakutsi/Ezkutatu historikoa" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Erakutsi/ezkutatu geruzak" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Gehitu/kendu flex diseinua" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Blokeatu/Desblokeatu" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Blokeatu/Desblokeatu proportzioak" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Erakutsi/Ezkutatu testuen paleta" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Erakutsi/Ezkutatu elementua" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Erakutsi/Ezkutatu zoomaren estiloa" @@ -2392,67 +2352,67 @@ msgstr "Erakutsi/Ezkutatu zoomaren estiloa" msgid "shortcuts.underline" msgstr "Aktibatu/desaktibatu azpimarraketa" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Desegin" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Banatu" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Desegin maskara" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Banatu bertikalki" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Zooma txikitu" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Zooma handitu" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zooma aukeraketara" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Letra-tipoen hornitzaileak - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Letra-tipoak - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Proiektuak - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Bilatu - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Partekatutako liburutegiak - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Taldeentzako diseinu librea" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Eman zure iritzia - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Ezarpenak - Penpot" @@ -2460,27 +2420,27 @@ msgstr "Ezarpenak - Penpot" msgid "title.settings.password" msgstr "Pasahitza - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profila - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Gonbidapenak - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Kideak - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Ezarpenak - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhookak - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Ikusteko modua - Penpot" @@ -2488,99 +2448,99 @@ msgstr "%s - Ikusteko modua - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Ez da arbelik aurkitu." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Ez dago arbel hori." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Iruzkinak (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Ez erakutsi interakzioak" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Pantaila osoa" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Ikuskagailua (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interakzioak" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interakzioak (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Kopiatu esteka" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Erakutsi interakzioak" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Erakutsi interakzioak klik egitean" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Webgunearen mapa" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Azken bidalketa ondo joan da." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Lerrokatu erdian (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Banatu espazioa horizontalki (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Lerrokatu ezkerrean (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Lerrokatu eskuman (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Lerrokatu behean (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Lerrokatu erdian (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Banatu espazioa bertikalki (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Lerrokatu goian (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Guztiak" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Koloreak" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Osagaiak" @@ -2588,17 +2548,17 @@ msgstr "Osagaiak" msgid "workspace.assets.create-group" msgstr "Sortu talde bat" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Zure elementuak berrizendatu egingo dira: \"taldearen izena / elementuaren " "izena\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Ezabatu" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Bikoiztu" @@ -2606,27 +2566,27 @@ msgstr "Bikoiztu" msgid "workspace.assets.duplicate-main" msgstr "Bikoiztu nagusia" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Editatu" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Taldekatu" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Taldearen izena" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "liburutegi lokala" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Ez da baliabiderik aurkitu" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Izena aldatu" @@ -2634,39 +2594,39 @@ msgstr "Izena aldatu" msgid "workspace.assets.rename-group" msgstr "Berrizendatu taldea" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Bilatu baliabideak" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Letra-tipoak" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Tamaina" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Letra-tipoaren estiloa" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Joan estilo liburutegiaren fitxategira editatzeko" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Hizkien tartea" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Lerroaren altuera" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Testua eraldatu" @@ -2674,15 +2634,15 @@ msgstr "Testua eraldatu" msgid "workspace.assets.ungroup" msgstr "Banatu" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Foku-modua" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Fokua kendu" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Fokua gehitu" @@ -2694,151 +2654,151 @@ msgstr "Gradiente lineala" msgid "workspace.gradients.radial" msgstr "Gradiente erradiala" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Desaktibatu lerrokatze dinamikoa" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Desaktibatu eskala proportzionala" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Desaktibatu gidetara atxikitzea" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Desaktibatu pixelera atxikitzea" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Aktibatu lerrokatze dinamikoa" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Aktibatu eskala proportzionala" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Atxikitu gidetara" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Aktibatu pixelera atxikitzea" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Ezkutatu arbelen izenak" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Ezkutatu kolore-paleta" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Ezkutatu pixel sareta" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Ezkutatu erregleank" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Ezkutatu letra-tipoen paleta" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Editatu" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Fitxategia" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Laguntza eta informazioa" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Hobespenak" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Ikusi" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Berregin" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Guztiak aukeratu" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Erakutsi arbelen izenak" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Erakutsi kolore-paleta" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Erakutsi erregelak" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Erakutsi letra-tipoen paleta" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Desegin" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Berrezarri" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Errorea gordetzean" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Gordeta" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Gordetzen" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Ikusteko modua (%s)" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Bete - Eskala betetzeko" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Egokitu - Eskalatu egokitzeko" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Zoom dena ikusteko" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Pantaila osoa" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zooma aukeraketara" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Fitxategien liburutegia" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Azken koloreak" @@ -2850,39 +2810,39 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Gorde kolorearen estiloa" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Fitxategien liburutegia" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "FITXATEGI HONETAKO LIBURUTEGIAK" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "LIBURUTEGIAK" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Ez dago eguneraketa behar duen partekatutako liburutegirik" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Ez da ezer aurkitu bilaketa honentzat: \"%s\"" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Bilatu partekatutako liburutegiak" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "Partekatutako liburutegiak" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Eguneratu" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "EGUNERAKETAK" @@ -2902,11 +2862,11 @@ msgstr "Aukeraketa lausotu" msgid "workspace.options.canvas-background" msgstr "Mihisearen atzeko planoa" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Edukia aukeratu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Osagaia" @@ -2950,11 +2910,11 @@ msgstr "Goian" msgid "workspace.options.constraints.topbottom" msgstr "Goian eta behean" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Diseinua" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Esportatu" @@ -2962,29 +2922,29 @@ msgstr "Esportatu" msgid "workspace.options.export-multiple" msgstr "Esportatu aukeraketa" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Esportatu elementu 1" msgstr[1] "Esportatu %s elementu" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Aurrizkia" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Esportazioa osatu da" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Esportazen…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Esportazioak huts egin du" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Esportazio oso geldoa" @@ -3008,67 +2968,67 @@ msgstr "Fluxua hasi da" msgid "workspace.options.grid.auto" msgstr "Auto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Zutabeak" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Kolorea" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Erretena" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Altuera" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margina" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Defektuzko orri gisa ezarri" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Behean" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Erdiratu" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Ezkerrean" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Eskuman" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Luzatu" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Goian" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Erabili defektuzkoa" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Zabalera" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Lerroak" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Karratua" @@ -3080,11 +3040,11 @@ msgstr "Taldea bete" msgid "workspace.options.group-stroke" msgstr "Taldea trazatu" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Altuera" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Ikuskatu" @@ -3296,99 +3256,99 @@ msgstr "Sakatzean" msgid "workspace.options.interactions" msgstr "Interakzioak" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Kolorea" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Erretako kolorea" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Saihestu kolorea" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Ilundu" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Diferentzia" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Baztertu" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Argi gogorra" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Ñabardura" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Argitu" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Argitasuna" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Biderkatu" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normala" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Geruza" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Asetasuna" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Pantaila" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Argi biguna" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Gehieneko altuera" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Gehieneko zabalera" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Gutxieneko altuera" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Gutxieneko zabalera" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Kolore gehiago" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Liburutegiko kolore gehiago" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opakotasuna" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototipoa" @@ -3412,19 +3372,19 @@ msgstr "Goian ezkerrean" msgid "workspace.options.radius-top-right" msgstr "Goian eskuman" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Azkenak" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Berriz saiatu" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Biraketa" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Bilatu letra-tipoa" @@ -3434,7 +3394,7 @@ msgstr "" "Aukeratu forma bat, arbel bat edo talde bat eta arrastatu konexioa beste " "arbel batera." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Aukeratutako koloreak" @@ -3490,27 +3450,27 @@ msgstr "Aukeraketaren itzalak" msgid "workspace.options.show-fill-on-export" msgstr "Erakutsi esportazioan" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Erakutsi ikusteko moduan" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Tamaina" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Aurredefinitutako tamaina" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Bat ere ez" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Txanda" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Karratua" @@ -3518,35 +3478,35 @@ msgstr "Karratua" msgid "workspace.options.stroke-color" msgstr "Trazuaren kolorea" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Marraren zabalera" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Erdiratu" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Marrekin" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Muntuekin" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Barruan" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Nahastuta" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Kanpoan" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Solidoa" @@ -3582,11 +3542,11 @@ msgstr "Zabalera automatikoa" msgid "workspace.options.text-options.grow-fixed" msgstr "Finkoa" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Hizkien tartea" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Lerroaren altuera" @@ -3630,51 +3590,51 @@ msgstr "Azpimarra (%s)" msgid "workspace.options.use-play-button" msgstr "Erabili goiburuko play botoia prototipoa ikusteko." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Zabalera" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Marraztu nodoak (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Batu nodoak" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Ertzera (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Kurbara %s" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Batu nodoak (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Mugitu nodoak (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Banatu nodoak (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Atxikitu nodoak (%s)" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Gehitu flex diseinua" @@ -3686,27 +3646,27 @@ msgstr "Atzera bota" msgid "workspace.shape.menu.backward" msgstr "Atzera bidali" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Kopiatu" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Aukeraketa arbelera" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Sortu osagaia" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Ebaki" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Ezabatu" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Ezabatu fluxuaren hasiera" @@ -3718,7 +3678,7 @@ msgstr "Askatu instantzia" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Askatu instantziak" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Diferentzia" @@ -3726,15 +3686,15 @@ msgstr "Diferentzia" msgid "workspace.shape.menu.duplicate" msgstr "Bikoiztu" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Editatu" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Baztertu" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Lautu" @@ -3746,7 +3706,7 @@ msgstr "Irauli horizontalki" msgid "workspace.shape.menu.flip-vertical" msgstr "Irauli bertikalki" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Fluxuaren hasiera" @@ -3758,39 +3718,39 @@ msgstr "Ekarri aurrera" msgid "workspace.shape.menu.front" msgstr "Ekarri aurrera" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Taldea" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Ezkutatu" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Erakutsi/Ezkuatu interfazea" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Ebakidura" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Blokeatu" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Maskara aplikatu" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Itsatsi" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Bidea" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Ezabatu flex diseinua" @@ -3806,11 +3766,11 @@ msgstr "Berrezarri osagai nagusia" msgid "workspace.shape.menu.select-layer" msgstr "Aukeratu geruza" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Erakutsi" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Ikusi baliabideen panelean" @@ -3826,19 +3786,19 @@ msgstr "Ezabatu iruditxoa" msgid "workspace.shape.menu.thumbnail-set" msgstr "Ezarri iruditxo gisa" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Banatu" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Batu" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Desblokeatu" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Desegin maskara" @@ -3854,47 +3814,47 @@ msgstr "Itxi alboko barra" msgid "workspace.sidebar.expand" msgstr "Ireki alboko barra" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Historikoa" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Geruzak" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Osagaiak" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Arbelak" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Taldeak" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Irudiak" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Maskarak" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Bilatu geruzak" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Formak" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Testuak" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Inportatutako SVG atributuak" @@ -3902,7 +3862,7 @@ msgstr "Inportatutako SVG atributuak" msgid "workspace.sidebar.sitemap" msgstr "Orrialdeak" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Web mapa" @@ -3910,51 +3870,51 @@ msgstr "Web mapa" msgid "workspace.toolbar.assets" msgstr "Baliabideak" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Kolore paleta (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Iruzkinak (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Kurba (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elipsea (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Arbela (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Irudia (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Mugitu (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Bidea (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Laukia (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Testua (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Letra-motak (%s)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Ez dago historikoaren aldaketarik" @@ -4094,14 +4054,14 @@ msgstr "letra-tipoaren baliabidea" msgid "workspace.undo.entry.unknown" msgstr "Honen gaineko eragiketa: %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Baztertu" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Eguneraketak daude partekatutako liburutegietan" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Eguneratu" diff --git a/frontend/translations/fa.po b/frontend/translations/fa.po index a8c6460fba..8e4f6e7e1a 100644 --- a/frontend/translations/fa.po +++ b/frontend/translations/fa.po @@ -1,17 +1,18 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:14+0000\n" -"Last-Translator: Danial Shirali \n" -"Language-Team: Persian " -"\n" +"PO-Revision-Date: 2026-11-08 09:51+0000\n" +"Last-Translator: Danial Shirali " +"\n" +"Language-Team: Persian \n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "از قبل حساب کاربری دارید؟" @@ -33,7 +34,7 @@ msgstr "" "این یک سرویس آزمایشی است، برای کار واقعی استفاده نکنید، پروژه‌ها به صورت " "دوره‌ای پاک می‌شوند." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "کلمه‌عبور را فراموش کرده‌اید؟" @@ -41,35 +42,35 @@ msgstr "کلمه‌عبور را فراموش کرده‌اید؟" msgid "auth.fullname" msgstr "نام کامل" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "ورود به حسابِ من" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "ورود از اینجا." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot ابزار طراحی رایگان و متن‌بازی برای همکاری در طراحی و کدنویسی است" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "گیتهاب" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "گیتلب" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "گوگل" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -77,7 +78,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "یک رمزعبور جدید تایپ کنید" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "اکنون بخشی از %s هستید" @@ -93,7 +94,7 @@ msgstr "پروفایل تأیید نشده است، لطفاً قبل از اد msgid "auth.notifications.recovery-token-sent" msgstr "لینک بازیابی رمزعبور به صندوق ورودی شما ارسال شد." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "با موفقیت به تیم پیوست" @@ -125,7 +126,7 @@ msgstr "رمزعبور را فراموش کرده‌اید؟" msgid "auth.recovery-submit" msgstr "رمزعبور خود را تغییر دهید" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "هنوز حسابی ندارید؟" @@ -137,7 +138,7 @@ msgstr "اطلاع دهید که شما را با چه نامی در ایمیل msgid "auth.register-account-title" msgstr "نام شما" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "ایجاد حساب‌کاربری" @@ -152,7 +153,7 @@ msgstr "" "من با قوانین و مقررات این سرویس و چگونگی مراقبت از اطلاعات شخصی در این " "سرویس را قبول میکنم." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "شرایط استفاده از خدمات" @@ -174,93 +175,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "برای @%s کسی پیدا نشد" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "انتشار" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "همه کاربران پنپات" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "آیا مطمئنید که می‌خواهید این پیوند را حذف کنید؟ اگر این کار را انجام دهید، " "دیگر برای کسی در دسترس نخواهد بود" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(فعلی)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "از بین بردن لینک" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "دریافت لینک" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "لینک با موفقیت کپی شد" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "مدیریت مجوزها" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "۱ صفحه به اشتراک گذاشته شد" msgstr[1] "%s صفحه به اشتراک گذاشته شدند" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "می‌تواند نظر دهد" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "می‌تواند کد را بررسی کند" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "هر کسی که لینک داشته باشد دسترسی خواهد داشت" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "صفحات به اشتراک گذاشته شده" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "پیوند قابل اشتراک‌گذاری در اینجا ظاهر می‌شود" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "فقط اعضای تیم" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "اشتراک‌گذاری پروتوتایپ‌ها" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "انتخاب همه" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "لغو انتشار" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "مدیریت تیم" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot برای تیم‌ها در نظر گرفته شده است. از اعضا دعوت کنید تا روی پروژه‌ها " "و فایل‌ها با هم کار کنند" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "تیم ایجاد کن!" @@ -276,33 +277,33 @@ msgstr "بازیابی فایل" msgid "dashboard.add-file" msgstr "افزودن فایل" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "افزودن به‌عنوان کتابخانه مشترک" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "تغییر ایمیل" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "انتقال تیم" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "انتقال به:" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "" "پروژه‌ها و فایل‌ها برای اعضای تیم در دسترس خواهند ماند. تیم پیکربندی را از " "سازمان جدید دریافت خواهد کرد." -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "انتخاب یک سازمان" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "تغییر سازمان تیم" @@ -360,23 +361,23 @@ msgstr "مشاهدهٔ گزارش تغییرات کامل" msgid "dashboard.check-updates.view-release-notes" msgstr "مشاهدهٔ یادداشت‌های انتشار" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "خالی کردن سطل زباله" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(کپی)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "ایجاد سازمان" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "ایجاد تیم جدید" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "سنجاق کردن این نسخه" @@ -384,7 +385,7 @@ msgstr "سنجاق کردن این نسخه" msgid "dashboard.default-team-name" msgstr "Penpot شما" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "آیا مطمئنید می‌خواهید همهٔ پروژه‌ها و فایل‌های حذف‌شده‌تان را برای همیشه " @@ -394,43 +395,43 @@ msgstr "" msgid "dashboard.delete-file-forever-confirmation.description" msgstr "آیا مطمئنید می‌خواهید %s را برای همیشه حذف کنید؟ این کار برگشت‌پذیر نیست." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s فایل با موفقیت حذف شدند." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "حذف برای همیشه" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "حذف پروژه" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "آیا مطمئنید می‌خواهید پروژهٔ %s را برای همیشه حذف کنید؟ آن و همهٔ فایل‌های " "موجود در آن برای همیشه حذف خواهند شد. این کار برگشت‌پذیر نیست." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s با موفقیت حذف شد." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "حذف تیم" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "" "سطل زبالهٔ شما خالی است. فایل‌ها و پروژه‌های حذف‌شده اینجا نمایش داده " "خواهند شد." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "در %s حذف خواهد شد" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "دانلود فایل پنپات (.penpot)" @@ -442,19 +443,19 @@ msgstr "تکثیر" msgid "dashboard.duplicate-multi" msgstr "فایل‌های %s را کپی کنید" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "پس از ایجاد پیش‌نویس توسط یکی از اعضای پروژه، اینجا نمایش داده می‌شود." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "هنوز پیش‌نویسی وجود ندارد." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "پس از ایجاد فایل توسط یکی از اعضای پروژه، اینجا نمایش داده می‌شود." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "هنوز فایلی وجود ندارد." @@ -502,23 +503,23 @@ msgstr "درون‌ریزی فایل .penpot" msgid "dashboard.empty-project.start" msgstr "ساخت چیزهای شگفت‌انگیز را شروع کنید" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "هنگام حذف فایل‌ها خطایی رخ داد." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "هنگام حذف پروژهٔ %s خطایی رخ داد." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "هنگام بازیابی فایل %s خطایی رخ داد." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "هنگام بازیابی فایل‌ها خطایی رخ داد." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "هنگام بازیابی پروژهٔ %s و فایل‌های آن خطایی رخ داد." @@ -526,37 +527,37 @@ msgstr "هنگام بازیابی پروژهٔ %s و فایل‌های آن خط msgid "dashboard.export-binary-multi" msgstr "دانلود %s عدد فایل های پنپات (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "خروجی آرت‌بوردها به پی‌دی‌اف" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "اکسپورت به پی‌دی‌اف" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s از %s عناصر انتخاب‌شده" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "اکسپورت" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "می‌توانید تنظیمات اکسپورت را از ویژگی‌های طراحی (در پایین نوار کناری سمت " "راست) به عناصر اضافه کنید." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "اطلاعات نحوه تنظیم اکسپورت در پنپات." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "هیچ عنصری با تنظیمات اکسپورت وجود ندارد." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "انتخاب اکسپورت" @@ -580,33 +581,33 @@ msgstr "نمای شبکه‌ای" msgid "dashboard.files-layout.list" msgstr "نمای فهرستی" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "فونت حذف شد" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "ردکردن همه" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "شما هنوز هیچ فونت سفارشی‌ای نصب نکرده‌اید." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "هنوز فونت سفارشی وجود ندارد." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "پس از بارگذاری فونت سفارشی توسط یکی از اعضای پروژه، اینجا نمایش داده می‌شود." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "۱ فونت اضافه شد" msgstr[1] "%s فونت اضافه شد" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -616,7 +617,7 @@ msgstr "" "فرمت‌های زیر بارگذاری کنید: **TTF، OTF و WOFF** (فقط یکی مورد نیاز خواهد " "بود)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -625,11 +626,11 @@ msgstr "" "اطلاعات بیشتری کسب کنید. همچنین ممکن است بخواهید درباره [مجوز فونت] " "(https://www.typography.com/faq) مطالعه کنید." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "آپلود همه" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -639,23 +640,23 @@ msgstr "" "کنید. همچنین توصیه می‌کنیم برای تولید فونت‌های وب و رفع خطاها از " "[Transfonter](https://transfonter.org/) استفاده کنید. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "رفتن به کنسول مدیریت" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "آخرین ویرایش: %s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "ایمپورت کردن فایل‌های پن‌پات" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "اوه! ما نتوانستیم این فایل را ایمپورت کنیم" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "۱ کتابخانه بر اساس نام، به‌طور خودکار پیوند داده شد." @@ -665,121 +666,125 @@ msgstr[1] "%s کتابخانه بر اساس نام، به‌طور خودکار msgid "dashboard.import.bad-url" msgstr "درون‌ریزی ناموفق بود. نشانی اینترنتی قالب نادرست است" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "پیوندهای کتابخانه را تأیید کنید" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:814 +msgid "dashboard.import.connect-selected-libraries" +msgstr "اتصال کتابخانه‌های انتخاب شده" + +#: src/app/main/ui/dashboard/import.cljs:288 +msgid "dashboard.import.file-name-label" +msgstr "اسم فایل" + +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "نتوانستیم این فایل را تأیید کنیم." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "به نظر می‌رسد این فایل آسیب دیده است." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "همهٔ فایل‌ها درون‌ریزی نشده‌اند" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "فایل‌های زیر خطا دارند:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "فایل‌های دارای خطا بارگذاری نخواهند شد." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "هنگام پردازش فایل مشکلی پیش آمد." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "۱ فایل با موفقیت وارد شد." msgstr[1] "%s فایل با موفقیت وارد شدند." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "برخی از فایل‌ها حاوی آبجکت‌های نامعتبر بودند که حذف شده‌اند." +#: src/app/main/ui/dashboard/import.cljs:815 +msgid "dashboard.import.next-file" +msgstr "فایل بعدی" + #: src/app/main/ui/dashboard.cljs:265 msgid "dashboard.import.no-perms" msgstr "اجازهٔ درون‌ریزی در این تیم را ندارید" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "در حال پردازش رنگ‌ها" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "در حال پردازش کامپوننت‌ها" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "در حال پردازش رسانه‌ها" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "در حال پردازش صفحه: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "در حال پردازش تایپوگرافی‌ها" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "در حال آپلود اطلاعات روی سرور (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "در حال آپلود فایل: %s" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "" "برخی از کتابخانه‌ها به‌طور خودکار پیوند نخوردند. برای هرکدام کتابخانهٔ درست " "را انتخاب کنید:" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "پیش از تأیید، پیوندهای کتابخانه را بررسی کنید:" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:447 +msgid "dashboard.import.resolve-libraries.connect-to" +msgstr "اتصال به" + +#: src/app/main/ui/dashboard/import.cljs:441 +msgid "dashboard.import.resolve-libraries.original-library" +msgstr "کتابخانه اصلی" + +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "پیوند داده‌شده" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:517 +msgid "dashboard.import.summary.manually-linked" +msgstr "دستی لینک شده" + +#: src/app/main/ui/dashboard/import.cljs:525 +msgid "dashboard.import.summary.new" +msgstr "جدید" + +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "هیچ کتابخانه‌ای انتخاب نشده است" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/import.cljs:522 +msgid "dashboard.import.summary.original" +msgstr "اصل" + +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "می‌خواهید دعوت‌نامه‌ها را برای افراد زیر حذف کنید:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "می‌خواهید دعوت‌نامه‌ها را برای افراد زیر دوباره ارسال کنید:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "حذف دعوت‌ها" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "ارسال دوبارهٔ دعوت‌ها" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "اجازهٔ دعوت از افراد برای پیوستن به این تیم یا ویرایش و حذف دعوت‌نامه‌ها را " "ندارید." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "دعوت به تیم" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "اجازهٔ دعوت از افراد به این تیم را ندارید" @@ -791,27 +796,27 @@ msgstr "ترک سازمان" msgid "dashboard.leave-organization.toast" msgstr "دیگر بخشی از سازمان %s نیستید." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "خروج از تیم" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "کتابخانه‌ها و قالب‌ها" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "در اینجا چند کتابخانه و قالب دارید که می‌توانید به پروژه‌تان اضافه کنید" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "بیشتر آنها را کاوش کنید و بدانید که چگونه مشارکت کنید" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "مشکلی در ایمپورت کردن قالب وجود داشت. قالب ایمپورت نشد." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "کتابخانه‌های مشترک" @@ -819,7 +824,7 @@ msgstr "کتابخانه‌های مشترک" msgid "dashboard.loading-files" msgstr "در حال بارگذاری فایل‌های شما …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "در حال بارگیری فونت‌های شما …" @@ -839,61 +844,65 @@ msgstr "انتقال فایل‌های %s به" msgid "dashboard.move-to-other-team" msgstr "انتقال به تیم دیگر" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ فایل جدید" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "فایل جدید" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ پروژه جدید" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "پروژه جدید" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "هیچ منطبقی برای «%s» یافت نشد" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "اجازه ندارید به هیچ‌یک از سازمان‌هایتان تیم اضافه کنید." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 +msgid "dashboard.no-organizations-yet" +msgstr "هنوز هیچ سازمانی وجود ندارد." + +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "اجازه ندارید در سازمان %s تیم ایجاد کنید. برای اطلاعات بیشتر با مالک سازمان " "تماس بگیرید." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "اجازه ندارید تیم‌هایی را که بخشی از سازمان %s هستند حذف کنید. برای اطلاعات " "بیشتر با مالک سازمان تماس بگیرید." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "اجازه ندارید تیم‌هایی را که بخشی از سازمان %s هستند منتقل کنید. برای " "اطلاعات بیشتر با مالک سازمان تماس بگیرید." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "پروژه‌های پین شده در اینجا ظاهر می‌شوند" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "اعلان‌ها" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "آدرس ایمیل شما با موفقیت به‌روز شد" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "آدرس ایمیل شما با موفقیت تایید شد" @@ -905,11 +914,11 @@ msgstr "تنظیمات اعلان به‌روزرسانی شد" msgid "dashboard.notifications.password-saved" msgstr "رمزعبور با موفقیت ذخیره شد!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "مشاهدهٔ اعلان‌ها" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "اعضای %s" @@ -917,23 +926,23 @@ msgstr "اعضای %s" msgid "dashboard.open-in-new-tab" msgstr "باز کردن فایل در تب جدید" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "گزینه‌ها" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "مرتب‌سازی بر اساس نقش" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "مرتب‌سازی بر اساس وضعیت" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "سازمان %s حذف شده است." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "تیم‌های دیگر" @@ -957,11 +966,11 @@ msgstr "اکنون در این تیم مالک هستید." msgid "dashboard.permissions-change.viewer" msgstr "اکنون در این تیم بیننده هستید." -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "پروژه‌های شخصی" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "پین/برداشتن پین" @@ -977,27 +986,27 @@ msgstr "امکان تجزیهٔ مانیفست افزونه وجود ندارد" msgid "dashboard.plugins.try-plugin" msgstr "امتحان کردن افزونه: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "در حال حذف فایل‌ها…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "در حال بازیابی فایل‌ها…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "حذف به‌طور غیرمنتظره‌ای کند است" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "بازیابی به‌طور غیرمنتظره‌ای کند است" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "پروژه‌ها" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "آیا می‌خواهید حساب خود را حذف کنید؟" @@ -1005,115 +1014,115 @@ msgstr "آیا می‌خواهید حساب خود را حذف کنید؟" msgid "dashboard.removed-from-team" msgstr "دیگر عضو تیم «%s» نیستید." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "" "می‌خواهید همهٔ پروژه‌ها و فایل‌هایتان را بازیابی کنید. ممکن است کمی طول " "بکشد." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "بازیابی همهٔ پروژه‌ها و فایل‌ها" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "بازیابی همه" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s فایل با موفقیت بازیابی شدند." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "بازیابی پروژه" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "می‌خواهید پروژهٔ %s و همهٔ فایل‌های موجود در آن را بازیابی کنید." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "بازیابی پروژه" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s با موفقیت بازیابی شد." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "ذخیره تنظیمات" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "جستجو…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "جستجو برای “%s“…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "سازمان‌ها" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "تیم‌ها" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "افزودن به سازمان" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "انتخاب یک سازمان:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "«%s» فقط به اعضای سازمان اجازه می‌دهد به تیم‌هایش بپیوندند. هر دعوت‌نامهٔ " "در انتظار برای افراد خارج سازمان لغو خواهد شد." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "دعوت‌نامه‌های در انتظار برای کاربران خارجی لغو خواهند شد." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "" "در اینجا همهٔ سازمان‌هایی را می‌بینید که اجازه دارید در آن‌ها تیم بسازید یا " "تیم اضافه کنید." -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "" "در اینجا سازمان‌هایی را می‌بینید که عضو آن‌ها هستید و اجازهٔ ایجاد یا " "افزودن تیم در آن‌ها را دارید." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "انتخاب یک سازمان" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "افزودن تیم به سازمان" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "زبان رابط کاربری را انتخاب کنید" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "انتخاب تم" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot تیره (پیش‌فرض)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot روشن" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "پوستهٔ سیستم" @@ -1177,11 +1186,11 @@ msgstr "به‌روزرسانی تنظیمات" msgid "dashboard.settings.notifications.title" msgstr "اعلان‌ها" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "نمایش همه فایل‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "تاریخچهٔ نسخه‌ها" @@ -1205,7 +1214,7 @@ msgstr[1] "فایل‌های شما با موفقیت تکثیر شدند" msgid "dashboard.success-duplicate-project" msgstr "پروژه شما با موفقیت duplicate شد" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "فایل شما با موفقیت منتقل شد" @@ -1217,182 +1226,182 @@ msgstr "فایل‌های شما با موفقیت منتقل شدند" msgid "dashboard.success-move-project" msgstr "پروژه شما با موفقیت منتقل شد" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s اکنون به %s تعلق دارد" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "اطلاعات تیم" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "اعضای تیم" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s دیگر به %s تعلق ندارد" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "سازمان تیم" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "افزودن به یک سازمان" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "اجازه ندارید این تیم را به هیچ‌یک از سازمان‌هایتان اضافه کنید." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "تغییر سازمان تیم" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "فقط افراد داخل سازمان شما را می‌توان دعوت کرد." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "این تیم بخشی از هیچ سازمانی نیست" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "حذف تیم از سازمان" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "پروژه‌های تیم" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "افزودن به پروژهٔ شما" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "تم رابط کاربری" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "نتایج جستجو" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "فایل‌های حذف‌شده در سطل زباله باقی می‌مانند به مدت" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s روز. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "پس از آن، برای همیشه حذف می‌شوند." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "اگر نظرتان عوض شد، می‌توانید آن‌ها را بازیابی کنید یا از منوی هر فایل برای " "همیشه حذفشان کنید." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "برای نمایش نتایج جستجو تایپ کنید" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "لغو انتشار کتابخانه" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "به‌روزرسانی تنظیمات" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "شما دیگر عضو سازمان %s نیستید" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "بتا" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "رندر WebGL می‌تواند عملکرد را بهبود دهد، اما در مرحلهٔ بتا است و ممکن است " "پایداری کمتری داشته باشد. ممکن است تفاوت‌های بصری بین بوم، خروجی‌ها " "(SVG/PDF) و حالت نمایش دیده شود." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "غیرفعال" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "فعال" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "ارسال بازخورد" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "وضعیت" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "رندر WebGL" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "فعال است" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "هنگام فعال شدن این وب‌هوک، جزئیات رویداد تحویل داده می‌شود" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "فقط می‌توانید وب‌هوک‌هایی را حذف یا تغییر دهید که خودتان ایجاد کرده‌اید." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "نوع محتوا" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "ایجاد وب‌هوک" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "وب‌هوک با موفقیت ایجاد شد." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "وب‌هوک‌ها راهی ساده برای اطلاع‌رسانی به وب‌سایت‌ها و برنامه‌های دیگر هنگام " "رخداد رویدادهای خاص در Penpot هستند. یک درخواست POST به هر نشانی‌ای که " "ارائه می‌کنید ارسال می‌کنیم." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "برای افزودن وب‌هوک، دکمهٔ «افزودن وب‌هوک» را فشار دهید." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "هنوز وب‌هوکی ایجاد نشده است." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "حساب شما" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "ایمیل" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "نام شما" @@ -1408,11 +1417,11 @@ msgstr "توجه" msgid "ds.component-subtitle" msgstr "کامپوننت‌ها برای به‌روزرسانی:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "اجازه دادن" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "لغو" @@ -1432,7 +1441,7 @@ msgstr "در مجموعه‌ها یا پوسته‌های فعال، توکن ق msgid "ds.inputs.numeric-input.no-matches" msgstr "موردی پیدا نشد." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "باز کردن فهرست توکن‌ها" @@ -1448,15 +1457,15 @@ msgstr "ارائه‌دهندهٔ احراز هویت برای این پروفا msgid "errors.auth-provider-not-configured" msgstr "ارائه دهنده احراز هویت پیکربندی نشده است." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "به نظر می‌رسد شما احراز هویت نشده‌اید یا جلسه منقضی شده است." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "فونت %s بارگیری نشد" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "فونت‌های %s بارگیری نشدند" @@ -1468,57 +1477,57 @@ msgstr "امکان بارگذاری فایل رسانه‌ای وجود ندار msgid "errors.character-limit-exceeded" msgstr "از حد مجاز نویسه بیشتر است" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "امکان افزودن کتابخانه وجود ندارد: این کار وابستگی دوری ایجاد می‌کند" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "API کلیپ‌بورد در دسترس نیست. برای فعال کردن دسترسی به کلیپ‌بورد، Penpot را " "روی HTTPS ارائه کنید" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "مرورگر شما نمی‌تواند این عملیات را انجام دهد" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "دسترسی به کلیپ‌بورد رد شد. برای جای‌گذاری محتوا، لطفاً اجازه‌های کلیپ‌بورد " "را در مرورگر خود فعال کنید" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "خطایی در دیدگاه رخ داد" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "متأسفیم! این فایل قدیمی از نوع منسوخ‌شده‌ای از دارایی‌های Penpot استفاده " "می‌کند و نمی‌توانید آن را باز کنید." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "تا بتوانیم کمک‌تان کنیم." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "با اینکه Penpot دیگر از این نوع دارایی پشتیبانی نمی‌کند، می‌توانید" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "با ما تماس بگیرید" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "نمی‌توان مجموعهٔ والد را در مسیر فرزند خودش رها کرد." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "ایمیل قبلا استفاده شده است" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "ایمیل قبلاً تأیید شده است." @@ -1530,29 +1539,29 @@ msgstr "شما نمی‌توانید از ایمیل خود به عنوان رم msgid "errors.email-does-not-match-invitation" msgstr "ایمیل با دعوت مطابقت ندارد." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "دامنه مجاز نیست" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "ایمیل «%s» گزارش‌های زیادی از برگشت دائمی دریافت کرده است." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "ایمیل «%s» به‌عنوان هرزنامه گزارش شده یا به‌طور دائمی برگشت خورده است." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "به نظر می‌رسد فایلی را باز می‌کنید که قابلیت «%s» در آن فعال است، اما نسخهٔ " "فعلی Penpot از آن پشتیبانی نمی‌کند یا آن را غیرفعال کرده است." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "قابلیت «%s» پشتیبانی نمی‌شود." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "باید حداکثر شامل %s نویسه باشد." @@ -1560,7 +1569,7 @@ msgstr "باید حداکثر شامل %s نویسه باشد." msgid "errors.field-min-length" msgstr "باید دست‌کم شامل یک نویسه باشد." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "فیلد خالی" @@ -1568,24 +1577,24 @@ msgstr "فیلد خالی" msgid "errors.field-not-all-whitespace" msgstr "نام باید دست‌کم یک نویسهٔ غیر از فاصله داشته باشد." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "به نظر می‌رسد بین قابلیت‌های فعال‌شده و قابلیت‌های فایل موردنظر برای باز " "کردن ناسازگاری وجود دارد. پیش از باز شدن فایل، باید مهاجرت‌های مربوط به " "«%s» اعمال شوند." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "نام خانوادهٔ فونت فقط می‌تواند شامل حروف، اعداد، فاصله‌ها، خط تیره، زیرخط و " "نقطه باشد." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "اشتباهی رخ داده است." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "مشکلی در وب‌ورکر رخ داده است." @@ -1601,7 +1610,7 @@ msgstr "دادهٔ نامعتبر" msgid "errors.invalid-email" msgstr "لطفاً یک ایمیل معتبر وارد کنید" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "ایمیل تأیید باید مطابقت داشته باشد" @@ -1637,11 +1646,11 @@ msgstr "این دعوت ممکن است لغو یا منقضی شده باشد." msgid "errors.ldap-disabled" msgstr "احراز هویت LDAP غیرفعال است." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "به سهمیهٔ «%s» رسیده‌اید. با پشتیبانی تماس بگیرید." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "به حداکثر تعداد (%s) ایمیل‌هایی که می‌توان در یک درخواست دعوت کرد رسیدیم" @@ -1657,21 +1666,21 @@ msgstr "به نظر می‌رسد که محتوای تصویر با پسوند msgid "errors.media-type-not-allowed" msgstr "به نظر می‌رسد که این یک تصویر معتبر نیست." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "پروفایلی که دعوت می‌کنید ارسال ایمیل برایش غیرفعال شده است (گزارش‌های " "هرزنامه یا برگشت‌های زیاد)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "مهاجرت در حال انجام است" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "فقط ایجادکنندهٔ نسخه می‌تواند آن را قفل کند" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "فقط ایجادکنندهٔ نسخه می‌تواند قفل آن را باز کند" @@ -1683,7 +1692,7 @@ msgstr "در ترک سازمان مشکلی پیش آمد. لطفاً دوبار msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "مالک سازمان نمی‌تواند سازمان را ترک کند." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "آن سازمان وجود ندارد" @@ -1695,7 +1704,7 @@ msgstr "رمزعبور تأیید باید مطابقت داشته باشد" msgid "errors.password-too-short" msgstr "رمزعبور باید حداقل 8 کاراکتر باشد" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "دادهٔ نامعتبر در کلیپ‌بورد" @@ -1703,7 +1712,7 @@ msgstr "دادهٔ نامعتبر در کلیپ‌بورد" msgid "errors.profile-blocked" msgstr "پروفایل مسدود شده" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "ارسال ایمیل برای پروفایل شما غیرفعال شده است (گزارش‌های هرزنامه یا " @@ -1713,7 +1722,7 @@ msgstr "" msgid "errors.registration-disabled" msgstr "ثبت‌نام در حال حاضر غیرفعال است." -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:700 msgid "errors.save-failed" msgstr "" "آخرین تغییرات شما هنوز به‌عنوان ذخیره‌شده تأیید نشده‌اند. ممکن است " @@ -1721,19 +1730,19 @@ msgstr "" "مجدد نکنید؛ در غیر این صورت ممکن است تغییراتتان را از دست بدهید. برای " "دریافت کمک با پشتیبانی تماس بگیرید." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG نامعتبر یا بدفرم است" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "قابلیت ناسازگار «%s» شناسایی شد" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "اعضای کافی برای ترک تیم وجود ندارد، احتمالاً می‌خواهید آن را حذف کنید." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "عضوی که می‌خواهید اختصاص دهید وجود ندارد." @@ -1741,7 +1750,7 @@ msgstr "عضوی که می‌خواهید اختصاص دهید وجود ندا msgid "errors.team-leave.only-owner-can-delete" msgstr "فقط مالک تیم می‌تواند آن را حذف کند." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "مالک نمی‌تواند تیم را ترک کند، شما باید نقش مالک را مجدداً اختصاص دهید." @@ -1753,11 +1762,11 @@ msgstr "نام تیم نمی‌تواند شامل هیچ‌یک از نویسه msgid "errors.token-set-already-exists" msgstr "مجموعه‌ای با همین نام از قبل وجود دارد" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "رها کردن تکمیل نمی‌شود؛ مجموعه‌ای با همین نام از قبل در مسیر وجود دارد." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "گزینهٔ پوسته‌ای با همین نام وجود دارد" @@ -1769,13 +1778,13 @@ msgstr "پوسته به مجموعه‌هایی ارجاع می‌دهد که و msgid "errors.tokens.circular-reference" msgstr "توکن ارجاع دوری دارد" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "ارتفاع خط به اندازهٔ فونت وابسته است. برای دریافت مقدار نهایی، اندازهٔ فونت " "اضافه کنید." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "این فیلد نمی‌تواند خالی باشد" @@ -1795,11 +1804,11 @@ msgstr "خطای درون‌ریزی:" msgid "errors.tokens.invalid-color" msgstr "مقدار رنگ نامعتبر: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "مقدار توکن نامعتبر است: فقط می‌توانید به یک توکن خانوادهٔ فونت ارجاع دهید" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "مقدار وزن فونت نامعتبر است: از مقادیر عددی (۱۰۰ تا ۹۵۰) یا نام‌های " @@ -1821,37 +1830,37 @@ msgstr "" "نام توکن‌ها باید فقط شامل حروف و ارقام باشند که با نویسه‌های نقطه از هم جدا " "شده‌اند و نباید با علامت $ شروع شوند." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "نوع سایه نامعتبر است: فقط innerShadow یا dropShadow پذیرفته می‌شود" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "مقدار توکن نامعتبر است: فقط none، Uppercase، Lowercase یا Capitalize " "پذیرفته می‌شوند" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "مقدار توکن نامعتبر است: فقط none، underline و strike-through پذیرفته می‌شوند" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "مقدار نامعتبر است: باید به یک توکن سایهٔ ترکیبی ارجاع دهد." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "مقدار نامعتبر است: باید به یک توکن تایپوگرافی ترکیبی ارجاع دهد." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "مقدار توکن نامعتبر: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "ارجاع‌های مفقود توکن‌ها: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "نام گروه %s با توکنی هم‌نام در مجموعهٔ فعال دیگری تداخل دارد." @@ -1863,31 +1872,31 @@ msgstr "هیچ توکن، مجموعه یا پوسته‌ای در این فای msgid "errors.tokens.number-too-large" msgstr "مقدار توکن نامعتبر است. مقدار نهایی بیش از حد بزرگ است: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "شفافیت باید بین ۰ و ۱۰۰٪ یا بین ۰ و ۱ باشد (مثلاً ۵۰٪ یا ۰٫۵)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "توکن به خودش ارجاع می‌دهد" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "تاری سایه باید بزرگ‌تر یا مساوی ۰ باشد." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "گسترش سایه باید بزرگ‌تر یا مساوی ۰ باشد." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "ضخامت استروک باید بزرگ‌تر یا مساوی ۰ باشد." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "مقدار نامعتبر: علامت % مجاز نیست." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "مقدار نامعتبر: استفاده از واحد مجاز نیست." @@ -1895,19 +1904,19 @@ msgstr "مقدار نامعتبر: استفاده از واحد مجاز نیس msgid "errors.unexpected-error" msgstr "یک خطای غیرمنتظره رخ داد." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "توکن ناشناخته" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "این نسخه از قبل قفل شده است" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "این نسخه قفل است و دیگران نمی‌توانند آن را حذف کنند" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "فایل شمارهٔ نسخهٔ ناسازگاری دارد" @@ -1940,39 +1949,39 @@ msgstr "حداقل ۱ حرف بزرگ انگلیسی" msgid "errors.weak-password.too-short" msgstr "حداقل ۸ نویسه" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL دیگر کار نمی‌کند. لطفاً برای بازنشانی، صفحه را دوباره بارگیری کنید" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "اوه! زمینهٔ بوم از بین رفت" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "خطای اتصال، دسترسی به نشانی اینترنتی ممکن نیست" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "نشانی اینترنتی از اعتبارسنجی عبور نمی‌کند." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "آخرین تحویل موفق نبود." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "خطا در اعتبارسنجی SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "مهلت زمانی تمام شد" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "خطای غیرمنتظره هنگام اعتبارسنجی" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "وضعیت غیرمنتظرهٔ %s" @@ -1992,17 +2001,17 @@ msgstr "شرح" msgid "feedback.description-placeholder" msgstr "لطفاً دلیل بازخورد خود را توضیح دهید" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "ما خوشحالیم که شما اینجا هستید. اگر به کمک نیاز دارید، لطفا قبل از ارسال " "پست جستجو کنید." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "انجمن Penpot" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "روش‌های دیگر تماس با ما" @@ -2026,11 +2035,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "تماس با ما" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "اینجا برای کمک به سوالات فنی شما." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "حساب پشتیبانی در توییتر" @@ -2050,43 +2059,39 @@ msgstr "ایده" msgid "feedback.type.issue" msgstr "مشکل" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "بارگیری فایل‌ها" - -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "دارایی‌های کتابخانهٔ پیوندشده در فایل قرار نمی‌گیرند." -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "دارایی‌ها را به‌عنوان اشیای پایه در نظر بگیرید" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "" "فایل‌های دارای کتابخانه‌های پیوندشده در خروجی قرار می‌گیرند و پیوند آن‌ها " "حفظ می‌شود. " -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "خروجی گرفتن از فایل + کتابخانه‌ها" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "" "پس از وارد کردن، می‌توانید کتابخانه‌های موجود با نام‌های منطبق را پیوند " "دهید." -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "پیوند دادن کتابخانه‌های منطبق هنگام وارد کردن" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "فایل شما با ادغام دارایی کتابخانه در کتابخانهٔ محلی صادر خواهد شد." -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "دارایی‌های کتابخانه را در فایل جاسازی کنید" @@ -2094,7 +2099,7 @@ msgstr "دارایی‌های کتابخانه را در فایل جاسازی msgid "generic.error" msgstr "خطایی رخ داده است" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "اعمال" @@ -2142,7 +2147,7 @@ msgstr "پیش‌نمایش تصویر پرکنندهٔ شکل" msgid "inspect.attributes.shadow" msgstr "سایه" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "اندازه و موقعیت" @@ -2150,43 +2155,31 @@ msgstr "اندازه و موقعیت" msgid "inspect.attributes.stroke" msgstr "استروک" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "مرکز" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "داخل" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "بیرون" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "تایپوگرافی" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "خانواده فونت" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "اندازه فونت" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "استایل فونت" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "فاصله بین حروف" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "ارتفاع خط" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "خانواده‌های فونت ترکیبی" @@ -2198,11 +2191,11 @@ msgstr "هیچ‌یک" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "خط‌زیر" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "بزرگ کردن حرف اول کلمات" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "حروف کوچک" @@ -2214,7 +2207,7 @@ msgstr "هیچ‌یک" msgid "inspect.attributes.typography.text-transform.unset" msgstr "تنظیم‌نشده" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "حروف بزرگ" @@ -2334,23 +2327,23 @@ msgstr "مقدار نهایی:" msgid "inspect.tabs.styles.variants-panel" msgstr "ویژگی‌های گونه" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "ایجاد توکن دسترسی جدید" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "دکمهٔ «ایجاد توکن دسترسی جدید» را فشار دهید تا یکی ایجاد شود." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "تاکنون توکنی ندارید." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "توکن‌های دسترسی شخصی" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "توکن‌های دسترسی شخصی مانند جایگزینی برای سامانهٔ احراز هویت ورود/گذرواژهٔ " @@ -2435,53 +2428,53 @@ msgstr "کلید MCP در %s منقضی می‌شود" msgid "integrations.mcp-key.will-not-expire" msgstr "کلید MCP تاریخ انقضا ندارد" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "سرور MCP Penpot به کلاینت‌های MCP امکان می‌دهد مستقیماً با فایل‌های طراحی " "Penpot تعامل داشته باشند." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "نحوهٔ پیکربندی کلاینت‌های MCP" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "این نشانی سروری است که برای اتصال به سرور MCP Penpot باید در کلاینت MCP خود " "پیکربندی کنید." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "ایجاد دوبارهٔ کلید MCP" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "کلید MCP" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "کلید MCP برای راه‌اندازی کلاینت MCP لازم است" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "وضعیت" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "غیرفعال" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "فعال" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "کلید MCP مورد استفاده برای اتصال به سرور MCP منقضی شده است. در نتیجه، اتصال " "برقرار نمی‌شود." -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "" "لطفاً کلید MCP را دوباره ایجاد کنید و پیکربندی کلاینت خود را با کلید جدید " @@ -2491,10 +2484,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "سرور MCP" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "بتا" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "نام" @@ -2545,7 +2534,7 @@ msgstr "ایجاد دوبارهٔ کلید MCP" msgid "integrations.regenerate-mcp-key.title.created" msgstr "کلید MCP دوباره ایجاد شد" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "یکپارچه‌سازی‌ها" @@ -2563,47 +2552,47 @@ msgstr "توکن در %s منقضی می‌شود" msgid "integrations.token.will-not-expire" msgstr "توکن تاریخ انقضا ندارد" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "علامت‌گذاری همه به‌عنوان خوانده‌شده" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "میانبرها" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "دربارهٔ Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "تایید" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "فعال" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "افزودن" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "اضافه کردن فونت سفارشی" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "در حال افزودن..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "مدیر" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "همه" @@ -2611,17 +2600,17 @@ msgstr "همه" msgid "labels.and" msgstr "و" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "بازگشت" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "به نظر می‌رسد باید کمی صبر کنید و دوباره تلاش کنید; ما در حال انجام تعمیرات " "کوچک روی سرورهای خود هستیم." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "خطای درگاه" @@ -2633,23 +2622,23 @@ msgstr "تاری" msgid "labels.blur-effects" msgstr "جلوه‌های تاری" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "لغو" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "بررسی به‌روزرسانی‌ها" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "در حال بررسی به‌روزرسانی‌ها..." -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "بستن" @@ -2657,7 +2646,7 @@ msgstr "بستن" msgid "labels.code" msgstr "کد" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "جمع کردن" @@ -2669,15 +2658,15 @@ msgstr "رنگ" msgid "labels.comment" msgstr "دیدگاه" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "نظرات" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "انجمن" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "جامعه و مشارکت‌ها" @@ -2689,19 +2678,19 @@ msgstr "محاسبه‌شده" msgid "labels.confirm-password" msgstr "تایید رمزعبور" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "تماس با پشتیبانی" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "تماس با ما" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "ادامه" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "شما می‌توانید با یک حساب Penpot ادامه دهید" @@ -2713,23 +2702,23 @@ msgstr "کپی" msgid "labels.copy-color" msgstr "کپی رنگ" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "کپی پیوند" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © ۲۰۱۹ تا کنون" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "ایجاد" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "ایجاد تیم جدید" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "نام تیم جدید را وارد کنید" @@ -2737,7 +2726,7 @@ msgstr "نام تیم جدید را وارد کنید" msgid "labels.current" msgstr "فعلی" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "داشبورد" @@ -2745,7 +2734,7 @@ msgstr "داشبورد" msgid "labels.default" msgstr "پیش‌فرض" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "حذف" @@ -2757,7 +2746,7 @@ msgstr "حذف نظر" msgid "labels.delete-comment-thread" msgstr "حذف موضوع" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "حذف دعوت" @@ -2765,39 +2754,35 @@ msgstr "حذف دعوت" msgid "labels.delete-multi-files" msgstr "حذف فایل‌های %s" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "آیا مطمئنید می‌خواهید عکس پروفایل خود را حذف کنید؟" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "حذف عکس پروفایل" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "حذف‌شده" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "توسعه" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "صرف‌نظر کردن" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "بارگیری %s" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "بارگیری" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "در حال بارگیری فایل…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "پیش‌نویس‌ها" @@ -2805,19 +2790,19 @@ msgstr "پیش‌نویس‌ها" msgid "labels.duplicate" msgstr "تکثیر" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "ویرایش" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "ویرایشگر" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "خالی" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "خطا" @@ -2825,11 +2810,11 @@ msgstr "خطا" msgid "labels.exit" msgstr "خروج" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "منقضی شده" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "اکسپورت" @@ -2841,7 +2826,7 @@ msgstr "بازخورد غیرفعال شد" msgid "labels.feedback-sent" msgstr "بازخورد ارسال شد" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2849,63 +2834,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "پر" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "چیدمان فلکس" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "خانواده فونت" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "استایل‌ها" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "فونت‌ها" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "مخزن Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "بازخورد بده" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "برگرد" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "طراحی گرافیک" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "چیدمان شبکه" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "مرکز کمک" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "راهنما و یادگیری" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "پنهان کردن" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "پنهان کردن نظرات حل شده" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "درون‌ریزی" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "غیرفعال" @@ -2913,53 +2898,53 @@ msgstr "غیرفعال" msgid "labels.info" msgstr "اطلاعات" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "فونت‌های نصب‌شده" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "یکپارچه‌سازی‌ها" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "مشکلی پیش آمد." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "" "می‌توانید عملیات را دوباره امتحان کنید یا برای گزارش خطا با پشتیبانی تماس " "بگیرید." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "خطای داخلی" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "دعوت‌نامه‌ها" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "زبان" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "چیدمان" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "مرکز یادگیری" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "کتابخانه‌ها و قالب‌ها" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "در حال بارگیری…" @@ -2967,7 +2952,7 @@ msgstr "در حال بارگیری…" msgid "labels.lock" msgstr "قفل کردن" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "ورود یا ثبت نام" @@ -2975,19 +2960,19 @@ msgstr "ورود یا ثبت نام" msgid "labels.login" msgstr "ورود" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "خروج" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "بازاریابی" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "عضو" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "اعضا" @@ -2999,43 +2984,43 @@ msgstr "اشاره" msgid "labels.new-password" msgstr "رمزعبور جدید" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "بعدی" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "" "این نمونه به مرحلهٔ راه‌اندازی‌ای نیاز دارد که همراه با آخرین نسخه ارائه " "شده است." -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "نگاهی به" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "Penpot هنوز نمی‌تواند شروع به کار کند." -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "راهنمای فنی ما بیندازید" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "" "Penpot به‌دلیل مشکل سیستمی موقتاً در دسترس نیست. لطفاً کمی بعد دوباره " "امتحان کنید." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "کارهایتان تمام شده است! اعلان‌های دیدگاه‌های جدید اینجا نمایش داده می‌شوند." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "هیچ دعوتنامه‌ای وجود ندارد." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "افرادتان را گرد هم بیاورید و با هم چیزهای فوق‌العاده بسازید." @@ -3043,27 +3028,27 @@ msgstr "افرادتان را گرد هم بیاورید و با هم چیزها msgid "labels.no-matches" msgstr "موردی یافت نشد" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "اوپس!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "اعلان‌ها" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "۱ فایل" msgstr[1] "%s فایل" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "۱ برد" msgstr[1] "%s برد" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "۱ پروژه" @@ -3073,43 +3058,43 @@ msgstr[1] "%s پروژه" msgid "labels.old-password" msgstr "رمزعبور قدیمی" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "باز کردن" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "گزینه‌ها" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "سایر (مشخص کنید)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "سایر" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "مالک" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "کلمه‌عبور" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "در انتظار" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "تغییرات Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "هاب Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "پروژه‌های سنجاق‌شده" @@ -3117,31 +3102,31 @@ msgstr "پروژه‌های سنجاق‌شده" msgid "labels.post" msgstr "ارسال" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "قبلی" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "طراحی محصول یا تجربهٔ کاربری" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "مدیریت محصول" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "پروفایل" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "پروژه‌ها" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "اخیر" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "مرجع" @@ -3149,27 +3134,27 @@ msgstr "مرجع" msgid "labels.refresh" msgstr "تازه‌سازی" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "یادداشت‌های انتشار" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "بارگیری دوبارهٔ صفحه" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "حذف" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "حذف عضو" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "تغییرنام" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "تغییر نام تیم" @@ -3193,11 +3178,11 @@ msgstr "پاسخ جدید" msgid "labels.reply.thread" msgstr "پاسخ" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "ارسال دوباره" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "فرستادن مجدد دعوتنامه" @@ -3205,43 +3190,43 @@ msgstr "فرستادن مجدد دعوتنامه" msgid "labels.restore" msgstr "بازیابی" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "امتحان دوباره" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "نقش" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "ذخیره" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "جست‌وجو" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "جستجوی فونت" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "انتخاب گزینه" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "ارسال" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "درحال ارسال…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "ما در حال تعمیر و نگهداری برنامه‌ریزی شده سیستم‌های خود هستیم." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "سرویس در دسترس نیست" @@ -3249,69 +3234,69 @@ msgstr "سرویس در دسترس نیست" msgid "labels.sets" msgstr "مجموعه‌ها" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "تنظیمات" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "سایه" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "اشتراک‌گذاری" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "کتابخانه‌های مشترک" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "نمایش" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "نمایش همه نظرات" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "نمایش لیست نظرات" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "فقط اشاره‌های شما را نمایش بده" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "فقط نظرات خودتان را نشان دهید" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "منابع" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "" "ورود با ارائه‌دهندهٔ هویت سازمان شما کامل نشد. ممکن است ارائه‌دهنده در " "دسترس نباشد یا حساب شما هنوز در فهرست آن ثبت نشده باشد." -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "دوباره تلاش کنید" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "ورود شما به %s ممکن نبود" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "شروع" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "وضعیت" @@ -3319,7 +3304,7 @@ msgstr "وضعیت" msgid "labels.stroke" msgstr "استروک" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "دانشجو یا مدرس" @@ -3331,7 +3316,7 @@ msgstr "سبک‌ها" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "تغییر" @@ -3339,19 +3324,19 @@ msgstr "تغییر" msgid "labels.text" msgstr "متن" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "پوسته‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "آموزش‌ها" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "تایپوگرافی" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "خطای ناشناخته" @@ -3363,35 +3348,31 @@ msgstr "باز کردن قفل" msgid "labels.unpublish-multi-files" msgstr "لغو انتشار %s فایل" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "به‌روزرسانی" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "بروزرسانی تیم" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "بارگذاری" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "آپلود فونت‌های سفارشی" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "درحال بارگذاری…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "در حال بارگذاری فایل…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "گونه" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "یادداشت‌های نسخهٔ %s" @@ -3399,7 +3380,7 @@ msgstr "یادداشت‌های نسخهٔ %s" msgid "labels.view-only" msgstr "فقط مشاهده" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "بیننده" @@ -3413,7 +3394,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s هشدار" msgstr[1] "%s هشدار" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "وب‌هوک‌ها" @@ -3421,11 +3402,11 @@ msgstr "وب‌هوک‌ها" msgid "labels.write-new-comment" msgstr "نظر جدید بنویس" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(شما)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "حساب شما" @@ -3568,7 +3549,7 @@ msgstr "" "شما تنها عضو برخی تیم‌ها هستید. آن تیم‌ها همراه با پروژه‌ها و فایل‌هایشان " "حذف خواهند شد." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "پیش از ترک سازمان" @@ -3576,79 +3557,79 @@ msgstr "پیش از ترک سازمان" msgid "modals.before-leave-organization.warning" msgstr "هر تیمی که تنها عضو آن هستید حذف خواهد شد." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "جابه‌جایی بزرگ" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "تایید ایمیل جدید" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "برای تأیید هویت شما یک ایمیل به ایمیل فعلی شما \"%s\" ارسال می‌کنیم." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "ایمیل جدید" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "تغییر ایمیل" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "ایمیل خود را تغییر دهید" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "ایجاد وب‌هوک" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "ایجاد وب‌هوک" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "نشانی اینترنتی payload" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "لغو و نگه‌داشتن حساب من" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "بله، حسابم رو حذف کن" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "با حذف کردن حساب خود، تمام پروژه‌ها و آرشیوهای فعلی خود را از دست خواهید " "داد." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "این اقدام برگشت‌ناپذیر است. حساب، اشتراک و سازمان‌های شما فوراً حذف خواهند " "شد. همچنین دسترسی خود را به پروژه‌ها و فایل‌های فعلی‌تان از دست خواهید داد." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "سازمان‌هایی که حذف خواهند شد" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s عضو" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s تیم" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "آیا مطمئن هستید که می‌خواهید حساب خود را حذف کنید؟" @@ -3708,23 +3689,23 @@ msgstr "آیا مطمئن هستید که می‌خواهید فایل‌های msgid "modals.delete-file-multi-confirm.title" msgstr "در حال حذف %s فایل‌ها" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "آیا مطمئن هستید که می‌خواهید این سبک فونت را حذف کنید؟ اگر در یک فایل " "استفاده شود، بارگیری نمی‌شود." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "درحال حذف استایل فونت" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "آیا مطمئن هستید که می‌خواهید این فونت را حذف کنید؟ اگر در یک فایل استفاده " "شود، بارگیری نمی‌شود." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "درحال حذف فونت" @@ -3732,19 +3713,19 @@ msgstr "درحال حذف فونت" msgid "modals.delete-organization-team-confirm.message" msgstr "آیا مطمئنید می‌خواهید این تیمِ عضو سازمان %s را حذف کنید؟" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "آیا مطمئن هستید که می‌خواهید این صفحه را حذف کنید؟" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "حذف صفحه" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "آیا مطمئنید می‌خواهید صفحات انتخاب‌شده را حذف کنید؟" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "حذف صفحات" @@ -3760,31 +3741,31 @@ msgstr "آیا مطمئن هستید که می‌خواهید این پروژه msgid "modals.delete-project-confirm.title" msgstr "حذف پروژه" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "حذف فایل" msgstr[1] "حذف فایل‌ها" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "در هیچ فایلی فعال نیست." msgstr[1] "در هیچ فایلی فعال نیستند." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "این کتابخانه در اینجا فعال است: " msgstr[1] "این کتابخانه‌ها در اینجا فعال هستند: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "آیا مطمئن هستید که می‌خواهید این فایل را حذف کنید؟" msgstr[1] "آیا مطمئن هستید که می‌خواهید این فایل‌ها را حذف کنید؟" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "درحال حذف فایل" @@ -3804,19 +3785,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "درحال حذف تیم" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "حذف عضو" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "آیا مطمئن هستید که می‌خواهید این عضو را از تیم حذف کنید؟" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "حذف عضو تیم" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3826,23 +3807,23 @@ msgstr[1] "" "دارایی‌هایی که قبلاً در آن فایل‌ها استفاده شده‌اند، همان‌جا باقی می‌مانند " "(طراحی خراب نمی‌شود)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "حذف وب‌هوک" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "آیا مطمئنید می‌خواهید این وب‌هوک را حذف کنید؟" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "حذف وب‌هوک" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "ویرایش وب‌هوک" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "ویرایش وب‌هوک" @@ -3860,15 +3841,15 @@ msgstr "درون‌ریزی توکن‌ها" msgid "modals.import-library-tokens.title" msgstr "توکن‌ها از کتابخانه درون‌ریزی شوند؟" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "ارسال دعوت‌نامه" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "ایمیل‌ها، با کاما از هم جدا شده‌اند" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "برخی اعضا از قبل در تیم هستند. بقیه را دعوت می‌کنیم." @@ -3888,97 +3869,97 @@ msgstr "" "فقط اعضای سازمان «%s» را می‌توان به این تیم دعوت کرد و همهٔ آن‌ها از قبل " "عضو تیم هستند. برای اطلاعات بیشتر با مالک سازمان تماس بگیرید." -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "نشانی‌هایی که دعوت‌نامه دریافت نمی‌کنند" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "لغو" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "فقط اعضای فعلی سازمان را می‌توان دعوت کرد." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "ارسال دعوت‌نامه‌ها" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "برخی دعوت‌نامه‌ها قابل ارسال نیستند" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "برخی افراد فهرست شما دعوت‌نامه را دریافت نخواهند کرد." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "می‌توانید اعضا را به تیم دعوت کنید تا بتوانند به این فایل و همهٔ فایل‌های " "تیم دسترسی داشته باشند." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "دعوت اعضا به تیم" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "از آنجایی که شما تنها عضو این تیم هستید، تیم به همراه پروژه‌ها و فایل‌هایش " "حذف خواهد شد." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "آیا مطمئنید که می‌خواهید تیم %s را ترک کنید؟" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "اگر عضو دیگری برای ارتقا به مالک وجود نداشته باشد، نمی‌توانید تیم را ترک " "کنید. شاید بخواهید تیم را حذف کنید." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "شما مالک این تیم هستید. لطفاً قبل از ترک تیم، عضو دیگری را برای ارتقاء به " "مالک انتخاب کنید." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "ارتقا و خروج" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "انتخاب یک عضو برای ارتقا" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "قبل از رفتن" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "خروج از تیم" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "آیا مطمئنید که می‌خواهید این تیم را ترک کنید؟" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "درحال ترک تیم" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "مالک تیم‌های برخی سازمان‌ها هستید. لطفاً عضو دیگری را به مالک ارتقا دهید." -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "شما تنها عضو برخی تیم‌ها هستید. آن تیم‌ها همراه با پروژه‌ها و فایل‌هایشان " "حذف خواهند شد." -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "همچنین، مالک تیم‌های برخی سازمان‌ها هستید. لطفاً عضو دیگری را به مالک ارتقا " @@ -3998,57 +3979,57 @@ msgstr "" msgid "modals.leave-organization-confirm.title" msgstr "سازمان %s را ترک می‌کنید؟" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "انتقال" msgstr[1] "انتقال" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "آیا مطمئنید می‌خواهید این کتابخانه را منتقل کنید؟" msgstr[1] "آیا مطمئنید می‌خواهید این کتابخانه‌ها را منتقل کنید؟" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "انتقال کتابخانه" msgstr[1] "انتقال کتابخانه‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "مقدار جابه‌جایی" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "انتقال مالکیت" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "اگر مالکیت را منتقل کنید، نقش خود را به مدیر تغییر می‌دهید و برخی از " "مجوزهای این تیم را از دست می‌دهید. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "شما مالک فعلی این تیم هستید. آیا مطمئنید که می‌خواهید %s را مالک جدید تیم " "کنید؟" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "مالک جدید تیم" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "انتشار" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "کتابخانهٔ شما خالی است. آیا مطمئنید می‌خواهید آن را منتشر کنید؟" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "انتشار کتابخانهٔ خالی" @@ -4070,23 +4051,23 @@ msgstr "آیا مطمئنید می‌خواهید تیم «%s» را از ساز msgid "modals.remove-team-organization.title" msgstr "حذف تیم از سازمان" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "جابه‌جایی کوچک" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "لغو انتشار" msgstr[1] "لغو انتشار" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "آیا مطمئنید می‌خواهید انتشار این کتابخانه را لغو کنید؟" msgstr[1] "آیا مطمئنید می‌خواهید انتشار این کتابخانه‌ها را لغو کنید؟" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "لغو انتشار کتابخانه" @@ -4176,38 +4157,38 @@ msgstr "فعال‌سازی" msgid "nitrate.code-activation.title" msgstr "فعال‌سازی Nitrate" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "تماس با فروش" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "" "برای تغییر به این طرح، لطفاً با تیم فروش ما تماس بگیرید. ما به شما کمک " "می‌کنیم اشتراکتان را به‌روزرسانی کنید و مطمئن شویم همه‌چیز به‌درستی تنظیم " "شده است." -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "سازمان شما حذف خواهد شد." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "" "فضای ذخیره‌سازی کل، تاریخچهٔ نسخه‌های خودکار و دورهٔ بازیابی فایل شما محدود " "خواهند شد." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "" "تیم‌ها، پروژه‌ها و فایل‌ها دیگر بخشی از هیچ سازمانی نخواهند بود، اما همچنان " "در دسترس می‌مانند." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "هنگام تنزل طرح:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "به طرح %s تغییر دهید؟" @@ -4223,15 +4204,15 @@ msgstr "برای امتحان کردن طرح سازمانی به‌مدت ۱۴ msgid "nitrate.form.contact-upgrade" msgstr "برای ارتقا به طرح سازمانی با ما تماس بگیرید:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "برای امتحان کردن طرح سازمانی به‌مدت ۱۴ روز با ما تماس بگیرید:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "برای ارتقا به طرح سازمانی با ما تماس بگیرید:" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "" "اکنون هزینه‌ای از شما دریافت نمی‌شود. پرداخت در پایان دورهٔ آزمایشی پردازش " @@ -4251,7 +4232,7 @@ msgstr "" msgid "nitrate.form.enterprise-feature-3" msgstr "آزادی کامل متن‌باز، همراه با حاکمیتی که سازمان شما نیاز دارد" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "همهٔ آنچه تیم‌هایتان برای کار ایمن در مقیاس بزرگ نیاز دارند%s" @@ -4259,7 +4240,7 @@ msgstr "همهٔ آنچه تیم‌هایتان برای کار ایمن در م msgid "nitrate.form.enterprise.price" msgstr "به‌ازای هر عضو در ماه" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "۱۴ روز رایگان امتحان کنید" @@ -4275,7 +4256,7 @@ msgstr "شروع با طرح سازمانی" msgid "nitrate.form.subscribe-with-code" msgstr "اشتراک با کد فعال‌سازی" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "فعال‌سازی ویژگی‌های سازمانی" @@ -4287,7 +4268,7 @@ msgstr "به‌مدت ۱۴ روز رایگان امتحان کنید" msgid "nitrate.modal-success.title" msgstr "به طرح سازمانی خوش آمدید!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "فعال تا %s" @@ -4295,23 +4276,23 @@ msgstr "فعال تا %s" msgid "nitrate.subscription.renew-success" msgstr "اشتراک Nitrate شما تمدید شد." -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "لغو اشتراک" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "برای لغو اشتراک Nitrate خود، لطفاً از این طریق با ما تماس بگیرید:" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "تمدید با کد فعال‌سازی" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "این صفحه وجود ندارد" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "خطای ۴۰۴" @@ -4327,11 +4308,11 @@ msgstr "ایمیل بازیابی را به این نشانی ارسال کرد msgid "not-found.login.sent-recovery-check" msgstr "ایمیلتان را بررسی کنید و برای ایجاد گذرواژهٔ جدید روی پیوند کلیک کنید." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "ثبت‌نام رایگان" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "و در چند ثانیه استفاده از Penpot را شروع کنید!" @@ -4339,67 +4320,67 @@ msgstr "و در چند ثانیه استفاده از Penpot را شروع کن msgid "not-found.made-with-love" msgstr "ساخته‌شده با عشق و متن‌باز" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "قبلاً برای دسترسی به این فایل درخواست داده‌اید." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "قبلاً برای دسترسی به این فایل یا فایل‌ها یا پروژه‌های دیگر این تیم درخواست " "داده‌اید." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "قبلاً برای دسترسی به این پروژه یا پروژه‌ها یا فایل‌های دیگر این تیم درخواست " "داده‌اید." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "قبلاً برای دسترسی به این پروژه درخواست داده‌اید." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "درخواست دسترسی" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "به یاد داشته باشید که اگر مالک اجازه دهد، به تیم دعوت خواهید شد." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "درخواست شما با موفقیت ارسال شد!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "به این فایل دسترسی ندارید." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "رفتن به Penpot شما" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "اگر مالک اجازه دهد، به تیم دعوت خواهید شد." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "فایل Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "به این پروژه دسترسی ندارید." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "پروژه" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "برای دسترسی به این فایل، می‌توانید از مالک تیم درخواست کنید." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "برای دسترسی به این پروژه، می‌توانید از مالک تیم درخواست کنید." @@ -4413,31 +4394,31 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "نسخهٔ جدیدی در دسترس است." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "این دعوت‌نامه دیگر در دسترس نیست." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "دعوت با موفقیت حذف شد" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "دعوت با موفقیت ارسال شد" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "پیوند دعوت کپی شد" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "نمی‌توانید پروفایل خود را حذف کنید. پیش از ادامه، تیم‌هایتان را واگذار کنید." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "پروفایل با موفقیت ذخیره شد!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "ایمیل تأیید به %s ارسال شد. ایمیلتان را بررسی کنید!" @@ -4475,7 +4456,7 @@ msgstr "نام تیم" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "ایجاد تیم" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "دعوت از اعضا" @@ -4485,98 +4466,98 @@ msgstr "" "توسعه‌دهندگان، طراحان، مدیران... همه خوش‌آمدند. بعداً می‌توانید افراد " "بیشتری دعوت کنید." -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "دعوت با نقش زیر:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "شروع کنیم!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "تولید کد واقعی از طرح‌ها" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "ایجاد سیستم‌های طراحی" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "نمونه‌سازی اولیه" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "طراحی رابط کاربری/تجربهٔ کاربری یک برنامه" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "وایرفریم‌سازی" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "از Penpot برای چه کاری استفاده خواهید کرد؟" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "کمی دربارهٔ خودتان به ما بگویید تا کمک کنیم Penpot برای شما بهتر کار کند. " "پاسخ‌های شما به ما کمک می‌کند ویژگی‌های جدید را اولویت‌بندی کنیم و برای " "شروع، شما را در مسیر درست قرار دهیم." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "به ما کمک کنید شما را بشناسیم" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "بیشتر از کدام‌یک از این ابزارها استفاده می‌کنید؟" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "چه نوع کاری انجام می‌دهید؟" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "اندازهٔ شرکت شما چقدر است؟" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "از کجا دوست دارید شروع کنید؟" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "۱٬۰۰۱ - ۵٬۰۰۰" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "۱۰۱ - ۵۰۰" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "۲ - ۱۰۰" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "۵۰۱ - ۱٬۰۰۰" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "فقط من" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "۵٬۰۰۱+" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "آموزش" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "شخصی" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "کار" @@ -4658,11 +4639,11 @@ msgstr "بازیابی پیکربندی پیش‌فرض" msgid "restore-shortcuts.restore" msgstr "بازیابی" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "جدا کردن" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "مخلوط" @@ -4674,35 +4655,35 @@ msgstr "حذف رنگ" msgid "settings.select-this-color" msgstr "انتخاب موارد با استفاده از این سبک" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "نظرات" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "تراز به پایین" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "تراز افقی در مرکز" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "تراز چپ" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "تراز راست" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "تراز بالا" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "تراز عمودی در مرکز" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "ایجاد آرت‌بورد از انتخاب" @@ -4710,55 +4691,55 @@ msgstr "ایجاد آرت‌بورد از انتخاب" msgid "shortcuts.bold" msgstr "تغییر وضعیت پررنگ" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "تفاضل بولی" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "استثنای بولی" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "اشتراک بولی" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "اجتماع بولی" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "فرستادن به پشت" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "فرستادن به عقب" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "آوردن به جلو" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "آوردن به پیش‌زمینه" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "پاک کردن واگرد" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "کپی" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "کپی پیوند" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "کپی ویژگی‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "ایجاد مؤلفه / گونه" @@ -4766,63 +4747,63 @@ msgstr "ایجاد مؤلفه / گونه" msgid "shortcuts.create-new-project" msgstr "ایجاد جدید" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "برش" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "کوچک‌نمایی" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "حذف" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "حذف گره" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "گره و قطعه‌ها را حذف کنید" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "لغو انتخاب همه" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "جدا کردن مؤلفه" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "غیرفعال کردن میانبر" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "غیرفعال" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "میانبر غیرفعال" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "پیکان" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "منحنی" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "بیضی" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "آرت‌بورد" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "خط" @@ -4830,63 +4811,63 @@ msgstr "خط" msgid "shortcuts.draw-nodes" msgstr "رسم مسیر" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "مسیر" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "مستطیل" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "متن" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "تکثیر" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "این ترکیب به «%s» اختصاص داده شده است. ذخیره کردن آن را از آنجا حذف می‌کند." -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "اگر ذخیره کنید، این میانبر به مقدار اصلی برمی‌گردد:" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "این ترکیب کلید در دسترس است و می‌توان آن را بدون تداخل اختصاص داد." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "سفارشی‌سازی در تنظیمات" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "لغو" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "خروجی گرفتن از شکل‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "یافتن" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "یافتن و جایگزینی" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "بزرگ‌نمایی برای نمایش همه" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "حرکت افقی" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "حرکت عمودی" @@ -4910,27 +4891,27 @@ msgstr "رفتن به کتابخانه‌های اشتراکی" msgid "shortcuts.go-to-search" msgstr "جستجو" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "گروه" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "توزیع افقی" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "نمایش / پنهان کردن رابط کاربری" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "درون‌ریزی/خروجی" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "بزرگ‌نمایی" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "درج تصویر" @@ -4938,11 +4919,11 @@ msgstr "درج تصویر" msgid "shortcuts.italic" msgstr "تغییر وضعیت ایتالیک" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "اتصال گره‌ها" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "ترکیب کلید را فشار دهید" @@ -4950,39 +4931,39 @@ msgstr "ترکیب کلید را فشار دهید" msgid "shortcuts.line-through" msgstr "تغییر وضعیت خط‌کشیده" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "ایجاد گوشه" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "ایجاد منحنی" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "ماسک" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "ادغام گره‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "انتقال" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "جابه‌جایی سریع به پایین" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "جابه‌جایی سریع به چپ" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "جابه‌جایی سریع به راست" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "جابه‌جایی سریع به بالا" @@ -4990,379 +4971,379 @@ msgstr "جابه‌جایی سریع به بالا" msgid "shortcuts.move-nodes" msgstr "جابه‌جایی گره" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "جابه‌جایی به پایین" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "جابه‌جایی به چپ" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "جابه‌جایی به راست" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "جابه‌جایی به بالا" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "آرت‌بورد بعدی" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "میانبر غیرفعالی وجود ندارد" -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "برای شروع سفارشی‌سازی میانبرهایتان به بخش «همه» بروید." -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "هیچ میانبری با جست‌وجوی شما مطابقت ندارد" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "هیچ میانبری پیدا نشد" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "تنظیم شفافیت روی ۱۰۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "تنظیم شفافیت روی ۱۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "تنظیم شفافیت روی ۲۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "تنظیم شفافیت روی ۳۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "تنظیم شفافیت روی ۴۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "تنظیم شفافیت روی ۵۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "تنظیم شفافیت روی ۶۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "تنظیم شفافیت روی ۷۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "تنظیم شفافیت روی ۸۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "تنظیم شفافیت روی ۹۰٪" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "انتخاب‌گر رنگ" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "رفتن به بخش دیدگاه‌های نمایشگر" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "رفتن به داشبورد" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "رفتن به بخش بازرسی نمایشگر" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "رفتن به بخش تعامل‌های نمایشگر" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "رفتن به بخش تعامل‌های نمایشگر" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "رفتن به فضای کاری" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " یا " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "این میانبر به %s اختصاص داده شده است" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "صفحهٔ میانبرها" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "چسباندن" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "چسباندن ویژگی‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "جای‌گذاری و جایگزینی" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "شخصی‌سازی‌شده" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "مدیر افزونه‌ها" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "آرت‌بورد قبلی" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "انجام دوباره" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "" "پس از ویرایش میانبرها، برای اعمال تغییرات هر زبانهٔ باز دیگر Penpot را " "دوباره بارگیری کنید." -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "تغییر نام" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "بازنشانی به پیش‌فرض" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "بازنشانی بزرگ‌نمایی" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "مقیاس‌گذاری" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "اصول" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "داشبورد" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "بیننده" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "فضای کاری" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "انتخاب همه" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "انتخاب لایهٔ بعدی" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "انتخاب لایهٔ والد" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "انتخاب لایهٔ قبلی" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "جداسازی گره‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "نمایش اندازه‌گیری‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "نمایش / پنهان کردن شبکهٔ پیکسلی" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "نمایش / پنهان کردن میانبرها" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "چفت شدن به گره‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "چفت شدن به شبکهٔ پیکسلی" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "شروع ویرایش" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "تراز" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "ویرایش" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "عمومی" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "منوی اصلی" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "تغییر لایه‌ها" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "پیمایش" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "پیمایش" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "پیمایش" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "پنل‌ها" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "مسیرها" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "شکل‌ها" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "متن‌ها" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "ابزار" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "بزرگنمایی" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "بزرگنمایی" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "تنظیم تصاویر بندانگشتی" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "میانبرهای صفحه‌کلید" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "تغییر وضعیت تراز پویا" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "تغییر وضعیت دارایی‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "تغییر وضعیت پالت رنگ" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "تغییر وضعیت نمایش دیدگاه‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "تغییر وضعیت حالت تمرکز" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "تغییر وضعیت تمام‌صفحه" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "نمایش / پنهان کردن راهنماها" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "تغییر وضعیت تاریخچه" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "تغییر وضعیت لایه‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "افزودن / حذف چیدمان فلکس" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "افزودن / حذف چیدمان شبکه" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "قفل کردن / باز کردن قفل" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "قفل کردن تناسب‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "نمایش / پنهان کردن خط‌کش‌ها" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "چفت شدن به راهنماها" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "چفت شدن به راهنماهای خط‌کش" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "تغییر وضعیت پالت متن" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "تغییر پوسته" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "نمایش / پنهان کردن" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "تغییر وضعیت سبک بزرگ‌نمایی" @@ -5370,31 +5351,31 @@ msgstr "تغییر وضعیت سبک بزرگ‌نمایی" msgid "shortcuts.underline" msgstr "تغییر وضعیت زیرخط" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "واگرد" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "حذف گروه" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "برداشتن ماسک" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "توزیع به صورت عمودی" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "کاهش بزرگ‌نمایی لنز" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "افزایش بزرگ‌نمایی لنز" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "بزرگ‌نمایی روی انتخاب‌شده" @@ -5413,7 +5394,7 @@ msgstr "طرح سازمانی را در عمل ببینید" msgid "subscription.current-plan.enterprise" msgstr "سازمانی" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "سازمانی" @@ -5425,7 +5406,7 @@ msgstr "دورهٔ آزمایشی سازمانی" msgid "subscription.current-plan.professional" msgstr "حرفه‌ای" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "اشتراک شما" @@ -5558,275 +5539,275 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "دعوت از افراد در طرح نامحدود" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "پرداخت تکمیل نشد. هر زمان آماده بودید دوباره امتحان کنید." -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "" "پرداخت تکمیل نشد. لطفاً دوباره امتحان کنید. اگر مشکل ادامه داشت، با ما تماس " "بگیرید: support@penpot.app." -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "اشتراک" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "کد فعال‌سازی را وارد کنید" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "برای ادامه پس از دورهٔ آزمایشی، یک روش پرداخت اضافه کنید" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "کنسول مدیریت" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "همهٔ مزایای طرح حرفه‌ای و:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "همهٔ مزایای طرح نامحدود و:" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "همهٔ مزایای طرح نامحدود، به‌علاوه:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(× %s ویرایشگر)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "سازمانی" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "سازمانی (آزمایشی)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "نسخه‌های ذخیرهٔ خودکار ۹۰روزه و بازیابی فایل" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "صورتحساب ماهانهٔ ثابت" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "کنترل پیشرفته:" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "امنیت سازمانی:" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "مدیریت چندسازمانی:" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "SAML/SSO بومی و ثبت کامل گزارش حسابرسی برای گزارش‌دهی انطباق." -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "" "فهرست مجاز دقیق افزونه‌ها، ازجمله سرور MCP امن و ایزولهٔ ما برای اتصال هوش " "مصنوعی." -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "پشتیبانی از سلسله‌مراتب پیچیدهٔ تیم‌ها و سازمان‌های فرعی جداگانه." -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "فضای ذخیره‌سازی نامحدود" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "مدیریت اشتراک" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "افزودن جزئیات پرداخت" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "اکنون جزئیات پرداخت خود را اضافه کنید تا پس از دورهٔ آزمایشی اشتراکتان بدون " "مشکل ادامه یابد و همچنان از پروژهٔ متن‌باز ما پشتیبانی کنید. هنوز هزینه‌ای " "از شما دریافت نمی‌شود." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "فعلاً رد شوید و دورهٔ آزمایشی را شروع کنید" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "به رشد ما کمک کنید و دورهٔ آزمایشی‌تان را آسان‌تر کنید" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "در حال حاضر، در تیم‌هایتان %s نفر ویرایشگر دارید که می‌تواند ویرایش کند." msgstr[1] "در حال حاضر، در تیم‌هایتان %s نفر ویرایشگر دارید که می‌توانند ویرایش کنند." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "توجه: تغییر به طرح پایین‌تر به معنای فضای ذخیره‌سازی کمتر و پشتیبان‌گیری و " "تاریخچهٔ نسخهٔ کوتاه‌تر است." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "ویرایشگران" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(مالکان، مدیران و ویرایشگران. بینندگان جزو ویرایشگران محسوب نمی‌شوند)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "نمی‌توانید تعداد ویرایشگران را کمتر از تعداد فعلی تنظیم کنید. برای افرادی " "که واقعاً فایل‌های تیم را ویرایش نمی‌کنند، نقش را از ویرایشگر یا مدیر به " "بیننده تغییر دهید." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "پس از دورهٔ آزمایشی هزینه دریافت می‌شود. اکنون به کارت اعتباری نیاز نیست." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/ماه" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "اعمال %s روی تیم‌هایتان" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "نکته: می‌توانید همین حالا تعداد صندلی‌ها را افزایش دهید تا از دعوت‌ها جلو " "باشید. با ۲۵ ویرایشگر یا بیشتر در تیم‌ها، از صورتحساب ثابت ۱۷۵ دلار در ماه " "بهره‌مند می‌شوید." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "عضویت در Penpot از: %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "اطلاعات بیشتر" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "عضو سازمان / ماه" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "طرح‌های دیگر Penpot" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "سازمان در ماه" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "کاربر / ماه" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "حرفه‌ای" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "نسخه‌های ذخیرهٔ خودکار ۷روزه و بازیابی فایل" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "همهٔ ویژگی‌های طراحی و نمونه‌سازی اولیه. پشتیبانی جامعه." -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "کنترل کامل بر داده‌ها و زیرساخت." -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "کاربران، تیم‌ها و فایل‌های نامحدود." -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "۱۰ گیگابایت فضای ذخیره‌سازی" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "تیم‌های نامحدود. حداکثر ۸ ویرایشگر در تیم‌هایی که مالکشان هستید." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "پیشنهادی" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "اشتراک شما" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "شروع دورهٔ آزمایشی رایگان" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "مشترک شدن" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "از %s در طرح %s هستید." -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "هر زمان می‌توانید اشتراک خود را از صفحهٔ «اشتراک» در جزئیات حسابتان ویرایش " "کنید." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "از طرحتان لذت ببرید!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "از انتخاب طرح %s Penpot سپاسگزاریم!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "شما %s هستید!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "به‌مدت ۱۴ روز رایگان امتحان کنید" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "نامحدود" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "نامحدود (آزمایشی)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "نسخه‌های ذخیرهٔ خودکار ۳۰روزه و بازیابی فایل" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "سقف صورتحساب ماهانه ۱۷۵ دلار" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "۲۵ گیگابایت فضای ذخیره‌سازی" @@ -5849,45 +5830,45 @@ msgstr "اگر می‌خواهید این حد را افزایش دهید، با msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "اگر می‌خواهید این حد را افزایش دهید، [طرح خود را ارتقا دهید|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "۱ دعوت انتخاب‌شده" msgstr[1] "%s دعوت انتخاب‌شده" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "ارائه‌دهندگان فونت - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "فونت‌ها - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "پروژه‌ها - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "جستجو - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "کتابخانه‌های مشترک - %s - پن‌پات" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - طراحی آزاد برای تیم‌ها" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "بازخورد بدهید - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "یکپارچه‌سازی‌ها - Penpot" @@ -5895,7 +5876,7 @@ msgstr "یکپارچه‌سازی‌ها - Penpot" msgid "title.settings.notifications" msgstr "اعلان‌ها - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "تنظیمات - Penpot" @@ -5903,27 +5884,27 @@ msgstr "تنظیمات - Penpot" msgid "title.settings.password" msgstr "رمزعبور - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "پروفایل - پن‌پات" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "دعوت‌نامه‌ها - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "اعضا - %s - پن‌پات" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "تنظیمات - %s - پن‌پات" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "وب‌هوک‌ها - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - حالت مشاهده - پن‌پات" @@ -5935,51 +5916,51 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "جدا کردن توکن" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "هیچ آرت‌بوردی در صفحه یافت نشد." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "آرت‌بورد یافت نشد." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "نظرات (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "تعاملات را نشان ندهید" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "تمام صفحه" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "بازرسی (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "تعاملات" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "تعاملات (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "کپی کردن لینک" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "نشان دادن تعاملات" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "نمایش تعاملات در کلیک" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "نقشه سایت" @@ -6015,67 +5996,67 @@ msgstr "راهنمای " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "راهنمای رفع اشکال WebGL" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "رندر WebGL غیرفعال شد" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "رندر WebGL فعال شد" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "زمینهٔ WebGL از بین رفت" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "زمینهٔ WebGL بازیابی شد" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "آخرین تحویل موفق بود." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "تراز کردن مرکز افقی (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "توزیع فاصله افقی (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "تراز به چپ (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "تراز کردن به راست (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "تراز کردن پایین (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "تراز کردن مرکز عمودی (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "توزیع فاصله عمودی (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "تراز کردن بالا (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "افزودن کتابخانه" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "تمام دارایی‌ها" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "رنگ‌ها" @@ -6087,7 +6068,7 @@ msgstr "افزودن رنگ" msgid "workspace.assets.component-group-options" msgstr "گزینه‌های گروه مؤلفه" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "کامپوننت‌ها" @@ -6095,7 +6076,7 @@ msgstr "کامپوننت‌ها" msgid "workspace.assets.components.add-component" msgstr "افزودن مؤلفه" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s گونه" @@ -6103,11 +6084,11 @@ msgstr "%s گونه" msgid "workspace.assets.create-group" msgstr "ایجاد یک گروه" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "آیتم‌های شما به طور خودکار به عنوان \"نام گروه / نام آیتم\" نامگذاری می‌شوند" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "حذف" @@ -6115,11 +6096,11 @@ msgstr "حذف" msgid "workspace.assets.delete-group" msgstr "حذف گروه" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "حذف صفحات" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "تکثیر" @@ -6127,39 +6108,39 @@ msgstr "تکثیر" msgid "workspace.assets.duplicate-main" msgstr "تکثیر اصلی" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "ویرایش" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "فیلتر" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "نمای شبکه‌ای" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "گروه" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "نام گروه" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "نمای فهرستی" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "کتابخانهٔ محلی" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "مدیریت کتابخانه" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "هیچ دارایی‌ای پیدا نشد" @@ -6167,7 +6148,7 @@ msgstr "هیچ دارایی‌ای پیدا نشد" msgid "workspace.assets.open-library" msgstr "باز کردن فایل کتابخانه" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "تغییرنام" @@ -6175,11 +6156,11 @@ msgstr "تغییرنام" msgid "workspace.assets.rename-group" msgstr "تغییر نام گروه" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "جست‌وجوی دارایی‌ها" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "کتابخانهٔ اشتراکی" @@ -6189,43 +6170,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "۱ مؤلفه" msgstr[1] "%s مؤلفه" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "مرتب‌سازی" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "تایپوگرافی‌ها" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "افزودن تایپوگرافی" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "اندازه" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "استایل فونت" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "برای ویرایش، به فایل کتابخانهٔ سبک بروید" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "فاصلهٔ حروف" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "ارتفاع خط" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "مثال" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "تبدیل متن" @@ -6233,15 +6214,15 @@ msgstr "تبدیل متن" msgid "workspace.assets.ungroup" msgstr "حذف گروه" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "در حال کپی تصویر…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "تصویر در کلیپ‌بورد کپی شد" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "خطا در کپی تصویر" @@ -6249,85 +6230,85 @@ msgstr "خطا در کپی تصویر" msgid "workspace.colorpicker.color-tokens" msgstr "توکن‌های رنگ" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "مؤلفه‌ها نمی‌توانند درون خودشان تو در تو شوند." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "برخی کپی‌ها قابل تعویض نبودند. مؤلفه‌ها نمی‌توانند درون خودشان تو در تو " "شوند." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "ایجاد ناحیه" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "کپی ستون‌ها" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "کپی ردیف‌ها" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "ایجاد آرت‌بورد" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "ادغام سلول‌ها" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "جای‌گذاری" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "افزودن ۱ ستون به راست" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "افزودن ۱ ستون به چپ" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "حذف ستون" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "حذف ستون و شکل‌ها" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "تکثیر ستون" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "افزودن ۱ ردیف در پایین" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "افزودن ۱ ردیف در بالا" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "حذف ردیف" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "حذف ردیف و شکل‌ها" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "تکثیر ردیف" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "رنگ راهنما" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "حذف راهنما" @@ -6335,15 +6316,15 @@ msgstr "حذف راهنما" msgid "workspace.debug.title" msgstr "ابزارهای اشکال‌زدایی" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "حالت تمرکز" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "خاموش کردن تمرکز" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "روشن کردن تمرکز" @@ -6355,243 +6336,243 @@ msgstr "گرادیان خطی" msgid "workspace.gradients.radial" msgstr "گرادیان شعاعی" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "غیرفعال کردن تراز پویا" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "غیرفعال کردن مقیاس‌گذاری متناسب" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "غیرفعال کردن چسبیدن به راهنماها" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "غیرفعال کردن چفت شدن به پیکسل" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "غیرفعال کردن چفت شدن به راهنماهای خط‌کش" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "غیرفعال کردن رندر WebGL (بتا)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "فعال کردن تراز پویا" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "فعال کردن مقیاس‌گذاری متناسب" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "چسبیدن به راهنماها" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "فعال کردن چفت شدن به پیکسل" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "چفت شدن به راهنماهای خط‌کش" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "فعال کردن رندر WebGL (بتا)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "یافتن" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "یافتن و جایگزینی" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "پنهان کردن نام آرت‌بوردها" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "پنهان کردن دیدگاه‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "پنهان کردن راهنماها" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "پنهان کردن پالت رنگ" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "پنهان کردن شبکهٔ پیکسلی" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "پنهان کردن خط‌کش‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "پنهان کردن پالت فونت‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "قفل کردن راهنماها" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "اتصال" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "قطع اتصال" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "مدیریت (وضعیت: غیرفعال)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "مدیریت (وضعیت: فعال)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "ویرایش" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "فایل" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "راهنما و اطلاعات" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "سرور MCP" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "اولویت‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "بازدید" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "انجام دوباره" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "انتخاب همه" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "نمایش نام آرت‌بوردها" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "نمایش دیدگاه‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "نمایش راهنماها" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "نمایش پالت رنگ" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "نمایش شبکهٔ پیکسلی" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "نمایش خط‌کش‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "نمایش پالت فونت‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "رفتن به پوستهٔ تیره" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "رفتن به پوستهٔ روشن" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "رفتن به پوستهٔ سیستم" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "واگرد" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "باز کردن قفل راهنماها" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "بازنشانی" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "خطا در ذخیره" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "ذخیره‌شد" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "ذخیره‌کردن" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "اشتراک‌گذاری" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "حالت نمایش (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "بزرگ‌نمایی" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "پر کردن - مقیاس‌گذاری برای پر کردن" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "جا دادن - کوچک‌نمایی برای جا شدن" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "بزرگ‌نمایی برای نمایش همه" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "تمام‌صفحه" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "بزرگ‌نمایی روی انتخاب‌شده" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "ویرایش شبکه" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "خروج" @@ -6599,7 +6580,7 @@ msgstr "خروج" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "فاصلهٔ داخلی پایین" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "نمایش گزینه‌های فاصلهٔ داخلی چهارطرفه" @@ -6635,37 +6616,37 @@ msgstr "انجام شد" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "یافتن" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "یافتن چیدمان شبکه" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "۱ رنگ" msgstr[1] "%s رنگ" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "افزودن رنگ به کتابخانه" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "هنوز هیچ سبک رنگی در کتابخانهٔ شما وجود ندارد" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "هنوز رنگ اخیری وجود ندارد" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "هنوز هیچ سبک تایپوگرافی در کتابخانهٔ شما وجود ندارد" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "کتابخانهٔ فایل" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "رنگ‌های اخیر" @@ -6677,75 +6658,75 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "ذخیره استایل رنگ" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "تغییر وضعیت پالت رنگ" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "۱ مؤلفه" msgstr[1] "%s مؤلفه" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "متصل به" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "یا برای امتحان، یکی از این موارد را اضافه کنید:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "در تیم شما کتابخانهٔ اشتراکی‌ای وجود ندارد؛ می‌توانید در اینجا جست‌وجو کنید" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "چند قالب در اینجا" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "کتابخانهٔ فایل" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "۱ گرافیک" msgstr[1] "%s گرافیک" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "کتابخانه‌ها در این فایل" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "کتابخانه‌ها" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "به‌روزرسانی‌های کتابخانه" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "در حال بارگیری…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "هیچ کتابخانه مشترکی وجود ندارد که نیاز به به‌روزرسانی داشته باشد" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "برای «%s» موردی پیدا نشد" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "جستجوی کتابخانه‌های مشترک" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "کتابخانه‌های مشترک" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "اتصال کتابخانه" @@ -6769,25 +6750,25 @@ msgstr "لغو پیوند همهٔ دارایی‌ها" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "جدا کردن همهٔ توکن‌ها" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "۱ تایپوگرافی" msgstr[1] "%s تایپوگرافی" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "قطع اتصال کتابخانه" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "به‌روزرسانی" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "مشاهدهٔ همهٔ تغییرات" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "به‌روزرسانی‌ها" @@ -6849,7 +6830,7 @@ msgstr "تغییر وضعیت تاری" msgid "workspace.options.blur-options.toggle-more-options" msgstr "نمایش/پنهان کردن گزینه‌های بیشتر" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "لغو" @@ -6857,117 +6838,117 @@ msgstr "لغو" msgid "workspace.options.canvas-background" msgstr "پس‌زمینهٔ بوم" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "برش محتوا" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "کامپوننت" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "حاشیه‌نویسی" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "کپی" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "ایجاد حاشیه‌نویسی" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "ویرایش حاشیه‌نویسی" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "اصلی" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "تعویض مؤلفه" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "هنوز هیچ دارایی‌ای در این کتابخانه وجود ندارد" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "پیوندنشده" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "یافتن گونه‌های متعارض" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "این مؤلفه گونه‌های متعارض دارد. مطمئن شوید هر گونه مجموعهٔ منحصربه‌فردی از " "مقادیر ویژگی‌ها داشته باشد." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "یافتن گونه‌های تکراری" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "برخی گونه‌ها ویژگی‌ها و مقادیر یکسان دارند" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "این گونه‌ها ویژگی‌ها و مقادیر یکسان دارند. مقادیر را تغییر دهید تا بتوان " "آن‌ها را بازیابی کرد." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "این گونه ویژگی‌ها و مقادیر یکسانی با گونهٔ دیگری دارد. مقادیر را تغییر دهید " "تا بتوان آن را بازیابی کرد." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "برخی از این گونه‌ها ویژگی‌ها و مقادیر یکسان دارند. مقادیر را تغییر دهید تا " "بتوان آن‌ها را بازیابی کرد." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "این مؤلفه گونه‌هایی با نام‌های نامعتبر دارد. مطمئن شوید هر گونه از ساختار " "درست پیروی می‌کند." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "یافتن گونه‌های نامعتبر" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "برخی گونه‌ها نامعتبرند" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "یافتن گونه‌های نامعتبر" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "نام این گونه‌ها نامعتبر است." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "نام این گونه نامعتبر است." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "نام برخی از این گونه‌ها نامعتبر است." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[property]=[value], [property]=[value]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "ساختار زیر را امتحان کنید:" @@ -7003,7 +6984,7 @@ msgstr "سطح یکسانی در سلسله‌مراتب دارند." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "گروه‌ها، آرت‌بوردها و چیدمان‌ها معادل در نظر گرفته می‌شوند." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "گونه‌ها چگونه متصل می‌مانند" @@ -7051,11 +7032,11 @@ msgstr "بالا و پایین" msgid "workspace.options.convert-to-typography" msgstr "ایجاد سبک تایپوگرافی" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "طراحی" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "اکسپورت" @@ -7063,53 +7044,53 @@ msgstr "اکسپورت" msgid "workspace.options.export-multiple" msgstr "خروجی گرفتن از انتخاب" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "خروجی ۱ عنصر" msgstr[1] "خروجی %s عنصر" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "افزودن خروجی" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "حذف خروجی" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "پسوند" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "سرویس خروجی‌گیری مشغول است؛ لطفاً بعداً دوباره تلاش کنید" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "خروجی‌گیری لغو شد" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "در حال لغو..." -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "خروجی با موفقیت انجام شد" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "درحال گرفتن خروجی…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "خروجی ناموفق بود" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "خروجی به‌طور غیرمنتظره‌ای کند است" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "در انتظار..." @@ -7129,7 +7110,7 @@ msgstr "حذف پر" msgid "workspace.options.fill.section" msgstr "بخش پر" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "تغییر اندازهٔ آرت‌بورد برای جا دادن محتوا" @@ -7153,67 +7134,67 @@ msgstr "شروع‌های جریان" msgid "workspace.options.grid.auto" msgstr "خودکار" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "ستون‌ها" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "رنگ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "فاصلهٔ بین ستون‌ها" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "ارتفاع" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "لبه" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "تنظیم به عنوان پیش‌فرض" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "پایین" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "مرکز" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "چپ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "راست" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "کشیده" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "بالا" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "استفاده از پیش‌فرض" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "پهنا" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "ردیف‌ها" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "مربع" @@ -7225,7 +7206,7 @@ msgstr "پر گروه" msgid "workspace.options.group-stroke" msgstr "استروک گروه" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "افزودن راهنما" @@ -7233,19 +7214,19 @@ msgstr "افزودن راهنما" msgid "workspace.options.guides.remove-guide" msgstr "حذف راهنما" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "راهنماها" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "تغییر وضعیت راهنما" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "بلندی" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "بازرسی" @@ -7461,115 +7442,115 @@ msgstr "تعاملات" msgid "workspace.options.interactions.add-interaction" msgstr "افزودن تعامل" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "رنگ" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "تیره‌سازی رنگ" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "روشن‌سازی رنگ" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "تاریک" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "تفاوت" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "استثنا" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "نور سخت" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "رنگ" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "روشن کردن" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "درخشندگی" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "تکثیر" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "معمولی" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "پوشش" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "اشباع" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "صفحه نمایش" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "نور نرم" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "بخش منوی لایه" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "تغییر وضعیت نمایش لایه" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "بیشینهٔ ارتفاع" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "بیشینهٔ عرض" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "کمینهٔ ارتفاع" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "کمینهٔ عرض" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "رنگ‌های بیشتر" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "رنگ‌های بیشترِ کتابخانه" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "توکن‌های رنگ بیشتر" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "پیش‌تنظیم اندازهٔ منطبقی پیدا نشد" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "شفافیت" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "پروتوتایپ" @@ -7605,23 +7586,23 @@ msgstr "بخش شعاع حاشیه" msgid "workspace.options.radius.show-single-corners" msgstr "نمایش شعاع‌های مستقل" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "اخیر" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "تلاش دوباره" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "چرخش" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "جست‌وجوی فونت" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "جست‌وجوی پیش‌تنظیم اندازه" @@ -7629,11 +7610,11 @@ msgstr "جست‌وجوی پیش‌تنظیم اندازه" msgid "workspace.options.select-a-shape" msgstr "یک شکل، برد یا گروه را برای کشیدن اتصال به تابلوی دیگر انتخاب کنید." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "رنگ‌های انتخاب‌شده" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "بخش انتخاب رنگ" @@ -7701,55 +7682,55 @@ msgstr "تغییر وضعیت سایه" msgid "workspace.options.show-fill-on-export" msgstr "نمایش در خروجی‌ها" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "نمایش در حالت نمایش" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "اندازه" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "پیش‌تنظیم‌های اندازه" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "قفل نسبت" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "باز کردن قفل نسبت" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "دایره" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "لوزی" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "پیکان" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "هیچ‌یک" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "گردی" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "مربع" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "مستطیل" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "مثلث" @@ -7757,31 +7738,31 @@ msgstr "مثلث" msgid "workspace.options.stroke-color" msgstr "رنگ استروک" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "خط‌چین" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "فاصله" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "ضخامت استروک" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "پایین (انتهای بلوک)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "چپ (ابتدای درون‌خطی)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "راست (انتهای درون‌خطی)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "بالا (شروع بلوک)" @@ -7789,49 +7770,49 @@ msgstr "بالا (شروع بلوک)" msgid "workspace.options.stroke.add-stroke" msgstr "افزودن رنگ استروک" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "مرکز" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "نقطه‌چین" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "خط‌چین" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "داخل" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "مخلوط" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "خارج" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "استروک برای هر طرف" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "" "قابلیت استروک برای هر طرف فقط در رندر جدید در دسترس است. برای استفاده از " "آن، رندر را به رندر جدید تغییر دهید." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "حذف رنگ استروک" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "یک‌دست" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "تغییر وضعیت استروک" @@ -7867,11 +7848,11 @@ msgstr "عرض خودکار" msgid "workspace.options.text-options.grow-fixed" msgstr "درست شد" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "فاصله بین حروف" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "ارتفاع خط" @@ -7919,224 +7900,224 @@ msgstr "خط‌زیر (%s)" msgid "workspace.options.use-play-button" msgstr "از دکمه پخش در هدر برای اجرای نمای پروتوتایپ استفاده کنید." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "پهنا" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "رسم گره‌ها (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "هم‌راستا" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "مستقل" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "برابر" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "رفتار دستگیره" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "اتصال گره‌ها (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "تبدیل به گوشه (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "تبدیل به منحنی (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "ادغام گره‌ها (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "انتقال گره‌ها (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "جداسازی گره‌ها (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "چفت کردن گره‌ها (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "باز کردن" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "[افزونه‌های بیشتر](%s) را کشف کنید" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "هنوز افزونه‌ای نصب نشده است" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "مانیفست افزونه نادرست است." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "برای استفاده از این افزونه باید ویرایشگر باشید" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "افزونه وجود ندارد یا نشانی اینترنتی صحیح نیست." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "نصب" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "افزونه‌های نصب‌شده" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "مدیر افزونه‌ها" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "افزونه‌ها" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "به‌روزرسانی این افزونه" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "افزونه از آخرین باری که آن را باز کردید تغییر کرده است. اکنون می‌خواهد به " "این مورد نیز دسترسی داشته باشد:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "شروع بارگیری فایل‌ها." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "ذخیرهٔ داده در مرورگر." -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "خواندن محتوای کلیپ‌بورد شما." -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "خواندن و نوشتن در کلیپ‌بورد شما." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "خواندن دیدگاه‌ها و پاسخ‌های شما." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "خواندن و تغییر دیدگاه‌ها و پاسخ دادن از طرف شما." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "خواندن محتوای فایل‌هایی که کاربران به آن‌ها دسترسی دارند." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "خواندن و تغییر محتوای فایل‌هایی که کاربران به آن‌ها دسترسی دارند." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "توجه داشته باشید این افزونه توسط شخص ثالث ساخته شده است؛ بنابراین پیش از " "اعطای دسترسی، از اعتماد خود به آن مطمئن شوید. حریم خصوصی و امنیت داده‌های " "شما برای ما مهم است. اگر نگرانی دارید، با پشتیبانی تماس بگیرید." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "خواندن کتابخانه‌ها و دارایی‌های شما." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "خواندن و تغییر کتابخانه‌ها و دارایی‌های شما." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "افزونهٔ «%s» می‌خواهد به موارد زیر دسترسی داشته باشد:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "خواندن اطلاعات پروفایل کاربر فعلی." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "فهرست افزونه‌ها" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "حذف افزونه" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "نشانی افزونه را بنویسید" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "افزونه‌ها" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "اکنون نه" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "می‌خواهید نگاهی بیندازید؟ این افزونه در پیش‌نویس جدیدی برای تیم فعلی شما " "باز می‌شود. (اگر نه، همیشه می‌توانید آن را در افزونه‌های نصب‌شدهٔ هر فایلی " "پیدا کنید.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "افزونهٔ «%s» برای کاربر شما نصب شد!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "امتحان کردن افزونه" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "افزودن چیدمان فلکس" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "افزودن چیدمان شبکه" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "افزودن چیدمان" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "ایجاد گونه" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "افزودن ویژگی جدید" @@ -8148,11 +8129,11 @@ msgstr "فرستادن به پشت" msgid "workspace.shape.menu.backward" msgstr "فرستادن به عقب" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "پاک کردن راهنماهای آرت‌بورد" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "ترکیب به‌صورت گونه" @@ -8160,7 +8141,7 @@ msgstr "ترکیب به‌صورت گونه" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "مؤلفه‌ها باید در یک صفحه باشند" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "کپی" @@ -8204,27 +8185,27 @@ msgstr "کپی به‌صورت متن" msgid "workspace.shape.menu.create-annotation" msgstr "ایجاد حاشیه‌نویسی" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "تبدیل انتخاب به آرت‌بورد" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "ایجاد کامپوننت" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "ایجاد چند مؤلفه" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "برش" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "حذف" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "حذف شروع جریان" @@ -8236,7 +8217,7 @@ msgstr "جدا کردن نمونه" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "جدا کردن نمونه‌ها" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "تفاوت" @@ -8244,15 +8225,15 @@ msgstr "تفاوت" msgid "workspace.shape.menu.duplicate" msgstr "تکرار" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "ویزایش" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "مستثنا کردن" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "صاف" @@ -8264,7 +8245,7 @@ msgstr "حرکت افقی" msgid "workspace.shape.menu.flip-vertical" msgstr "چرخش عمودی" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "شروع فلو" @@ -8276,31 +8257,31 @@ msgstr "جلو بیاورید" msgid "workspace.shape.menu.front" msgstr "به جلو بیاورید" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "گروه" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "مخفی" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "نمایش/پنهان کردن رابط کاربری" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "تقاطع" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "قفل" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "ماسک" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "چسباندن" @@ -8308,27 +8289,27 @@ msgstr "چسباندن" msgid "workspace.shape.menu.paste-props" msgstr "جای‌گذاری ویژگی‌ها" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "مسیر" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "حذف چیدمان فلکس" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "حذف چیدمان شبکه" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "حذف چیدمان" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "حذف ویژگی" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "گونه باید دست‌کم یک ویژگی داشته باشد" @@ -8352,11 +8333,11 @@ msgstr "بازیابی گونه" msgid "workspace.shape.menu.select-layer" msgstr "انتخاب لایه" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "نمایش" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "نمایش در پنل دارایی" @@ -8364,7 +8345,7 @@ msgstr "نمایش در پنل دارایی" msgid "workspace.shape.menu.show-main" msgstr "نمایش کامپوننت اصلی" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "تبدیل استروک به مسیر" @@ -8376,19 +8357,19 @@ msgstr "حذف تصویر کوچک" msgid "workspace.shape.menu.thumbnail-set" msgstr "تنظیم بعنوان تصویر کوچک" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "حذف گروه" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "متحد" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "بازکردن قفل" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "حذف ماسک" @@ -8404,79 +8385,79 @@ msgstr "جمع کردن نوار کناری" msgid "workspace.sidebar.expand" msgstr "گسترش نوار کناری" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "تاریخچه" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "لایه‌ها" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "کامپوننت‌ها" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "فیلتر" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "آرت‌بوردها" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "گروه‌ها" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "تصاویر" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "ماسک‌ها" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "موردی یافت نشد" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "جایگزینی" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "جایگزینی همه" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "جایگزینی با..." -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "جست‌وجوی لایه‌ها…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "یافتن و جایگزینی" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "محتوای متن" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "نام لایه‌ها" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "شکل‌ها" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "متن‌ها" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "ویژگی‌های SVG ایمپورت شد" @@ -8488,7 +8469,7 @@ msgstr "صفحات" msgid "workspace.sidebar.sitemap.add-page" msgstr "افزودن صفحه" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "نقشه سایت" @@ -8516,7 +8497,7 @@ msgstr "افزودن مجموعه به این گروه" msgid "workspace.tokens.add-token" msgstr "افزودن توکن: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "اعمال‌شده روی" @@ -8536,7 +8517,7 @@ msgstr "اندازهٔ فونت پایه" msgid "workspace.tokens.base-font-size.error" msgstr "اندازهٔ فونت پایه باید مقداری برحسب پیکسل یا بدون واحد باشد." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "رنگ" @@ -8548,7 +8529,7 @@ msgstr "کپی مسیر توکن" msgid "workspace.tokens.create-new-theme" msgstr "همین حالا اولین پوسته‌تان را ایجاد کنید." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "ایجاد یکی." @@ -8572,7 +8553,7 @@ msgstr "تکثیر توکن" msgid "workspace.tokens.duplicate-group" msgstr "تکثیر گروه توکن‌ها" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "کپی" @@ -8596,7 +8577,7 @@ msgstr "ویرایش توکن %s" msgid "workspace.tokens.enter-token-name" msgstr "نام توکن %s را وارد کنید" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "" "توکن‌ها هنگام ویرایش متن قابل اعمال نیستند. به‌جای آن، لایهٔ متن را انتخاب " @@ -8626,7 +8607,7 @@ msgstr "پیش‌نمایش:" msgid "workspace.tokens.export.single-file" msgstr "یک فایل" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "اندازهٔ فونت یا {alias}" @@ -8634,7 +8615,7 @@ msgstr "اندازهٔ فونت یا {alias}" msgid "workspace.tokens.font-variant-not-found" msgstr "خطا در تنظیم وزن یا سبک فونت. این سبک فونت در فونت فعلی وجود ندارد" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "وزن فونت (۳۰۰، ایتالیک پررنگ و...) یا {alias}" @@ -8670,7 +8651,7 @@ msgstr "در چند فایل، نام/مسیر فایل نام مجموعه‌ه msgid "workspace.tokens.import-single-file" msgstr "در یک فایل JSON، کلیدهای سطح اول باید نام مجموعه‌های توکن باشند." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "درون‌ریزی توکن‌ها" @@ -8680,17 +8661,17 @@ msgstr "" "درون‌ریزی توکن‌ها همهٔ توکن‌ها، مجموعه‌ها و پوسته‌های فعلی شما را بازنویسی " "می‌کند." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "غیرفعال" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "این مجموعه فعال نیست. برای مشاهدهٔ تغییرات در نمای بوم، پوسته را تغییر دهید " "یا این مجموعه را فعال کنید" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "استفاده از توکن‌های منفرد" @@ -8710,11 +8691,11 @@ msgstr "پوسته" msgid "workspace.tokens.label.theme-placeholder" msgstr "افزودن پوسته (مثلاً روشن)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "فاصلهٔ حروف یا {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "ارتفاع خط (ضریب، پیکسل، ٪) یا {alias}" @@ -8730,11 +8711,11 @@ msgstr "بیشینهٔ اندازه" msgid "workspace.tokens.min-size" msgstr "کمینهٔ اندازه" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "ارجاع مفقود" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "برای دیدن گزینه‌ها کلیک راست کنید" @@ -8750,7 +8731,7 @@ msgstr "هیچ پوسته‌ای فعال نیست" msgid "workspace.tokens.no-permisions-set" msgstr "برای فعال/غیرفعال کردن مجموعه‌ها باید ویرایشگر باشید" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "برای استفاده از پوسته‌ها باید ویرایشگر باشید" @@ -8762,7 +8743,7 @@ msgstr "هنوز مجموعه‌ای تعریف نشده است. ابتدا یک msgid "workspace.tokens.no-sets-yet" msgstr "هنوز مجموعه‌ای وجود ندارد." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "هیچ پوسته‌ای وجود ندارد." @@ -8778,7 +8759,7 @@ msgstr "نگاشت مجدد نکن" msgid "workspace.tokens.num-active-sets" msgstr "%s مجموعهٔ فعال" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "مقدار اصلی: %s" @@ -8790,15 +8771,15 @@ msgstr "فاصله‌های داخلی" msgid "workspace.tokens.radius" msgstr "شعاع" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "ارجاع در {%s} معتبر نیست یا در هیچ مجموعهٔ فعالی نیست." -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "نام مستعار توکن تایپوگرافی را وارد کنید" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "نام مستعار توکن سایه را وارد کنید" @@ -8838,7 +8819,7 @@ msgstr "تغییر نام گروه توکن‌ها" msgid "workspace.tokens.rename-group-name-hint" msgstr "توکن‌های شما به‌طور خودکار به %s.(suffix).(tokenName) تغییر نام می‌دهند" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "مقدار نهایی: %s" @@ -8876,27 +8857,27 @@ msgstr "" msgid "workspace.tokens.settings" msgstr "تنظیمات توکن‌ها" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "افزودن سایه" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "تاری" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "داخلی" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "حذف سایه" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "گسترش" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "مقدار تاری نمی‌تواند منفی باشد" @@ -8904,11 +8885,11 @@ msgstr "مقدار تاری نمی‌تواند منفی باشد" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "مقدار گستره نمی‌تواند منفی باشد" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -8916,11 +8897,11 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "اندازه" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | حروف بزرگ | حروف کوچک | بزرگ‌کردن حرف اول یا {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | زیرخط | خط‌خورده یا {alias}" @@ -8938,31 +8919,31 @@ msgstr "فهرست پوسته‌ها" msgid "workspace.tokens.token-description" msgstr "توضیحات" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "انتخاب خانوادهٔ فونت" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "خانوادهٔ فونت" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "خانوادهٔ فونت یا فهرست فونت‌ها که با ویرگول (،) جدا شده‌اند" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "نام" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "توکنی در مسیر %s یا یکی از پیشوندهای آن از قبل وجود دارد." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "نام باید حداقل ۱ نویسه داشته باشد" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " معتبر نیست.\n" @@ -8973,11 +8954,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "مقدار" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "مقداری یا نام مستعار را با {alias} وارد کنید" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "توکن‌ها - %s" @@ -8996,15 +8977,15 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "نوع «%s» پشتیبانی نمی‌شود (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "استفاده از ارجاع" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "مقدار معتبر نیست" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "پیکان (%s)" @@ -9012,75 +8993,71 @@ msgstr "پیکان (%s)" msgid "workspace.toolbar.assets" msgstr "دارایی‌ها" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "پالت رنگ (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "نظرات (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "منحنی (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "بیضی (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "آرت‌بورد (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "تصویر (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "خط (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "اتصال در اینجا" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP متصل شد" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "انتقال (%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "نوار پالت" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "مسیر (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "افزونه‌ها (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "مستطیل (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "متن (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "تایپوگرافی‌ها (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "تغییر وضعیت نوار ابزار" @@ -9103,11 +9080,11 @@ msgstr "" msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "تازه‌سازی" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "هیچ تاریخچه تغییری تاکنون وجود ندارد" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "توسط %s" @@ -9247,19 +9224,19 @@ msgstr "دارایی‌های تایپوگرافی" msgid "workspace.undo.entry.unknown" msgstr "عملیات بیش‌از %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "رد" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "اطلاعات بیشتر" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "به‌روزرسانی در کتابخانه‌های مشترک وجود دارد" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "به‌روزرسانی" @@ -9368,3 +9345,33 @@ msgstr "نسخه‌های ذخیرهٔ خودکار به مدت %s روز نگه #~ msgid "viewer.header.share.placeholder" #~ msgstr "لینک اشتراک‌گذاری در اینجا ظاهر می‌شود" + +#: src/app/main/ui/dashboard/sidebar.cljs:228 +msgid "dashboard.clear-search" +msgstr "پاک کردن جستجو" + +#: src/app/main/ui/dashboard/fonts.cljs:442, src/app/main/ui/dashboard/fonts.cljs:443 +msgid "dashboard.fonts.delete-variant" +msgstr "حذف سبک فونت" + +#: src/app/main/ui/dashboard/import.cljs:625 +msgid "dashboard.import.check.message" +msgstr "مطمئن شوید که هر فایل قابل وارد کردن است. این فقط یک لحظه طول می‌کشد." + +#: src/app/main/ui/dashboard/import.cljs:624 +msgid "dashboard.import.check.title" +msgstr "در حال بررسی فایل‌های شما..." + +#: src/app/main/ui/dashboard/import.cljs:637 +msgid "dashboard.import.importing.message" +msgstr "بازسازی صفحات، کامپوننت‌ها و دارایی‌ها در پروژه شما." + +#: src/app/main/ui/dashboard/import.cljs:634 +msgid "dashboard.import.queued.message" +msgstr "" +"فایل‌های شما آپلود شده‌اند. به محض اینکه یک جایگاه خالی شود، وارد کردن شروع " +"می‌شود." + +#: src/app/main/ui/dashboard/import.cljs:633 +msgid "dashboard.import.queued.title" +msgstr "منتظر شروع..." diff --git a/frontend/translations/fin_FI.po b/frontend/translations/fin_FI.po index 0bd29642e7..ce845cdf3f 100644 --- a/frontend/translations/fin_FI.po +++ b/frontend/translations/fin_FI.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2026.6.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Onko sinulla jo käyttäjä?" @@ -29,7 +29,7 @@ msgstr "" "Tämä on DEMO versio, ÄLÄ KÄYTÄ oikeaan työhön, projektit tullaan määräajoin " "poistamaan." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Unohditko salasanasi?" @@ -37,27 +37,27 @@ msgstr "Unohditko salasanasi?" msgid "auth.fullname" msgstr "Koko nimi" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Kirjaudu sisään" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -77,7 +77,7 @@ msgstr "Käyttäjäsi ei ole vahvistettu, vahvista se jatkaaksesi." msgid "auth.notifications.recovery-token-sent" msgstr "Salasanan vaihtoon tarvittava linkki lähetetty sähköpostiisi." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Ryhmään liittyminen onnistui" @@ -109,11 +109,11 @@ msgstr "Unohtuiko salasana?" msgid "auth.recovery-submit" msgstr "Vaihda salasanasi" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Ei käyttäjää?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Luo uusi käyttäjä" @@ -121,97 +121,97 @@ msgstr "Luo uusi käyttäjä" msgid "auth.register-title" msgstr "Luo uusi käyttäjä" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Käyttöehdot" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Julkaise" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Kaikki Penpotin käyttäjät" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Oletko varma, että haluat poistaa tämän linkin? Sen jälkeen kukaan ei voi " "käyttää sitä" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(nykyinen)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Poista linkki" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Hanki linkki" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Linkin kopiointi onnistui" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Muokkaa käyttöoikeuksia" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "Yksi sivu jaettu" msgstr[1] "%s sivua jaettu" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Voi kommentoida" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Voi tarkastella koodia" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Kaikilla linkin saaneilla on käyttöoikeus" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Sivut jaettu" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Jaettava linkki ilmestyy tähän" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Vain ryhmän jäsenet" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Jaa prototyypit" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Valitse kaikki" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Peruuta julkaisu" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Ryhmän hallinta" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot on tarkoitettu ryhmille. Kutsu jäseniä työstääksenne projekteja " "yhdessä" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Ryhmäydy!" diff --git a/frontend/translations/fo.po b/frontend/translations/fo.po index 10b25342bd..ce18633625 100644 --- a/frontend/translations/fo.po +++ b/frontend/translations/fo.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2026.10.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Hevur tú longu ein brúkara?" @@ -29,7 +29,7 @@ msgstr "" "Hetta er ein ROYNDAR tænasta, IKKI BRÚKA til veruligt arbeiði, " "verkætlanirnar verða slettaðar regluliga." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Gloymt loyniorðið?" @@ -37,37 +37,37 @@ msgstr "Gloymt loyniorðið?" msgid "auth.fullname" msgstr "Fullfíggja navn" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Rita inn á mína konto" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Innrita her." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot er eitt ókeypis formgevingstól við opnari keldukotu, at nýta til " "Formgávu og Forritanar samstarv" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -89,7 +89,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "Leinkjan til at endurseta títt loyniorð er send til tín postkassa." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Sameinaðan í toymið var væleydnað" @@ -121,7 +121,7 @@ msgstr "Gloymt loyniorð?" msgid "auth.recovery-submit" msgstr "Broyt títt loyniorð" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Onga konto enn?" @@ -135,7 +135,7 @@ msgstr "" msgid "auth.register-account-title" msgstr "Navn títt" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Stovna konto" @@ -143,111 +143,111 @@ msgstr "Stovna konto" msgid "auth.register-title" msgstr "Stovna eina konto" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Treytir" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Gev út" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Allir Penpot brúkarar" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Ert tú vís(ur) í, at tú vilt strika hetta leinkið? Gert tú tað, er tað ikki " "longur tøkt hjá nøkrum" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(núverandi)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Strika leinki" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Fá leinkið" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Leinkið avritað" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Fyrisit heimildir" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 síða deild" msgstr[1] "%s síður deildar" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Kann viðmerkja" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Kann skoða kotu" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Ein og hvør við leinkjuni hevur atgongd" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Síður deildar" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Leinkja, ið kann deilast, verur at síggja her" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Einans limir í toymi" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Deil frumsnið" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Vel alt" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Angra útgevan" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Toymisleiðsla" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "Penpot er fyri toymum. Bjóða limum at arbeiða saman á verkætlanir og fílur" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Toyma upp!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Legg afturat sum Deilt Savn" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Broyt teldupost" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(avrita)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Stovna nýtt toymi" @@ -255,11 +255,11 @@ msgstr "Stovna nýtt toymi" msgid "dashboard.default-team-name" msgstr "Títt Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Strika toymi" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Heinta Penpot fílu (.penpot)" @@ -275,97 +275,69 @@ msgstr "Tvítak %s fílur" msgid "dashboard.export-binary-multi" msgstr "Heinta %s Penpot fílur (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Útflyt borð sum PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Útflyt til PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s av %s lutum eru valdir" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Útflyt" -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Upplýsingar um hvussu tú setur útflytingar í Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Har eru ongin lutir við útflytsstillingum." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Valt til útflyting" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Stavsniði er strika" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Tú hevur enn onki serstavsnið innlagt." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 stavsnið lagt afturat" msgstr[1] "% stavsnið løgd afturat" -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Legg øll afturat" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Innflyt Penpot fílur" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Ups! Tað riggaði ikki at innflyta hesa fílu" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgstr "% fílur eru innfluttir." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Viðgerð litir" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Viðgerð staklutir" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Viðgerð miðlar" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Viðger síðu: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Viðgerð stavsnið" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Sendur upp dátur til ambætara (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Innleggur fílu: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Bjóða við í toymi" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Far úr toymu" @@ -373,7 +345,7 @@ msgstr "Far úr toymu" msgid "dashboard.loading-files" msgstr "lesur tínar fílur inn…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "lesur tíni stavsnið inn…" @@ -389,35 +361,35 @@ msgstr "Flyt %s fílur til" msgid "dashboard.move-to-other-team" msgstr "Flyt til eitt annað toymi" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nýggja fílu" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Nýggja fílu" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nýggj verkætlan" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nýggj verkætlan" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Onki samsvar funnið fyri \"%\"" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Festar verkætlanir verða víst her" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Tín teldupostadressa er dagførd" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Tín teldupostadressa er váttta" @@ -425,7 +397,7 @@ msgstr "Tín teldupostadressa er váttta" msgid "dashboard.notifications.password-saved" msgstr "Loyniorði er goymt!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s limir" @@ -433,7 +405,7 @@ msgstr "%s limir" msgid "dashboard.open-in-new-tab" msgstr "Lat fílu upp í nýggjum skiljiblaði" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Valmøguleikar" @@ -441,39 +413,39 @@ msgstr "Valmøguleikar" msgid "dashboard.password-change" msgstr "Broyt loyniorð" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Fest/Loys" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Verkætlanir" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Vilt tú strika tína konto?" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Goym stillingar" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Leita…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Leitar eftir \"%s\"…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Vel mál til takførisflatu" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Vel tema" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Vís allar fílurnar" @@ -485,7 +457,7 @@ msgstr "Tín fíla er strikað" msgid "dashboard.success-delete-project" msgstr "Tín verkætlan er strikað" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Tín fíla er flutt" @@ -497,27 +469,27 @@ msgstr "Tínar fílur eru fluttar" msgid "dashboard.success-move-project" msgstr "Tín verkætlan er flutt" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Toymisupplýsingar" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Toymislimir" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Toymisverkætlanir" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Takførisflatastílur" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Leitiúrslit" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Skriva fyri at leita eftir úrslitum" @@ -537,13 +509,13 @@ msgstr "Vangamyndin er stongd" msgid "labels.delete-multi-files" msgstr "Strikað %s fílur" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 fíla" msgstr[1] "%s fílur" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Vangamynd" @@ -579,32 +551,32 @@ msgstr "Ert tú vísur í, at tú vil strikað %s fílur?" msgid "modals.delete-file-multi-confirm.title" msgstr "Strikar %s fílur" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Strike fílu" msgstr[1] "Strika fílur" -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Ert tú vísur í, at tú vilt strikað hesa fílu?" msgstr[1] "Ert tú vísur í, at tú vilt strikað hesar fílur?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Strikar fílu" msgstr[1] "Strikar fílur" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Vangamynd - Penpot" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Fílu" diff --git a/frontend/translations/fr.po b/frontend/translations/fr.po index 63c56a4fc0..2f3dad955d 100644 --- a/frontend/translations/fr.po +++ b/frontend/translations/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:13+0000\n" -"Last-Translator: Surfoo \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: French " "\n" "Language: fr\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Vous avez déjà un compte ?" @@ -33,7 +33,7 @@ msgstr "" "Il s’agit d’un service de DÉMONSTRATION. NE L'UTILISEZ PAS pour travailler " "vraiment. Les projets seront supprimés périodiquement." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Mot de passe oublié ?" @@ -41,37 +41,37 @@ msgstr "Mot de passe oublié ?" msgid "auth.fullname" msgstr "Nom complet" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Connexion à mon compte" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Connectez-vous ici." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot est l'outil de design libre et Open Source pour la collaboration " "entre Design et Développement" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -79,7 +79,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Saisissez un nouveau mot de passe" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "Vous faites désormais partie de %s" @@ -95,7 +95,7 @@ msgstr "Le profil n’est pas vérifié. Avant de continuer, vérifiez le profil msgid "auth.notifications.recovery-token-sent" msgstr "Lien de récupération de mot de passe envoyé." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Vous avez bien rejoint l’équipe" @@ -127,7 +127,7 @@ msgstr "Mot de passe oublié ?" msgid "auth.recovery-submit" msgstr "Changez votre mot de passe" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Pas encore de compte ?" @@ -141,7 +141,7 @@ msgstr "" msgid "auth.register-account-title" msgstr "Votre nom" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Créer un compte" @@ -156,7 +156,7 @@ msgstr "" "J'accepte les [termes du service](%s) et la [politique de " "confidentialité](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Conditions générales d'utilisation" @@ -178,93 +178,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Aucun membre trouvé avec @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publier" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Tous les utilisateurs de Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Voulez -vous vraiment supprimer ce lien ? Si oui, plus personne ne pourra y " "accéder" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(actuel)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Supprimer le lien" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Obtenir le lien" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Lien copié" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Modifier les permissions" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 page partagée" msgstr[1] "%s pages partagées" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Peut commenter" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Peut inspecter le code" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "N'importe qui possédant ce lien peut y accéder" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Pages partagées" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Le lien à partager apparaîtra ici" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Seulement les membres de l'équipe" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Partager les prototypes" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Tout sélectionner" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Dépublier" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Gestion de l'équipe" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot est conçu pour les équipes. Invitez les membres pour collaborer sur " "des projets et des fichiers" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Faites une équipe  !" @@ -280,33 +280,33 @@ msgstr "Restaurer le fichier" msgid "dashboard.add-file" msgstr "Ajouter un fichier" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Ajouter comme bibliothèque partagée" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Changer l'adresse e‑mail" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "DÉPLACER L'ÉQUIPE" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Déplacer vers :" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "" "Les projets et fichiers resteront disponibles pour les membres de l'équipe. " "L'équipe récupérera la configuration de la nouvelle organisation." -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Sélectionner une organisation" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "Changer l'organisation de l'équipe" @@ -367,23 +367,23 @@ msgstr "Voir le journal des modifications complet" msgid "dashboard.check-updates.view-release-notes" msgstr "Voir les notes de version" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Vider la corbeille" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copie)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Créer une organisation" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" -msgstr "+ Créer une nouvelle équipe" +msgstr "+ Créer une équipe" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Sauvegarder cette version" @@ -391,7 +391,7 @@ msgstr "Sauvegarder cette version" msgid "dashboard.default-team-name" msgstr "Votre Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Voulez-vous vraiment supprimer définitivement tous vos projets et fichiers " @@ -403,44 +403,44 @@ msgstr "" "Voulez-vous vraiment supprimer définitivement %s ? Cette action est " "irréversible." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s fichiers ont bien été effacés." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Supprimer définitivement" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Supprimer le projet" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Voulez-vous vraiment supprimer définitivement le projet %s ? Vous allez le " "supprimer définitivement avec tous les fichiers qu'il contient. Cette " "action est irréversible." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s a bien été supprimé." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Supprimer l’équipe" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "" "Votre corbeille est vide. Les fichiers et projets supprimés apparaîtront " "ici." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Sera supprimé %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Télécharger le fichier Penpot (.penpot)" @@ -452,19 +452,19 @@ msgstr "Dupliquer" msgid "dashboard.duplicate-multi" msgstr "Dupliquer %s fichiers" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Lorsqu'un membre de l'équipe aura créé un brouillon, il sera affiché ici." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Aucun brouillon pour l'instant." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Lorsqu'un membre de l'équipe aura créé un fichier, il sera affiché ici." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Aucun fichier pour l'instant." @@ -512,23 +512,23 @@ msgstr "Importer un fichier .penpot" msgid "dashboard.empty-project.start" msgstr "Commencez à fabriquer des choses géniales" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "Une erreur s'est produite lors de l'élimination des fichiers." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "Une erreur s'est produite lors de la suppression du projet %s." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "Une erreur s'est produite lors de la restauration du fichier %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "Une erreur s'est produite lors de la restauration des fichiers." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "" "Une erreur s'est produite lors de la restauration du projet %s et de ses " @@ -538,37 +538,37 @@ msgstr "" msgid "dashboard.export-binary-multi" msgstr "Télécharger %s fichiers Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exporter les plans de travail au format PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exporter en PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s éléments sur les %s sélectionnés" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exporter" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Vous pouvez ajouter des paramètres d'exportation aux éléments dans les " "propriétés de design (en bas de la barre latérale de droite)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Information sur la configuration de l'exportation dans Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Aucun élément ne comporte de paramètres d'exportation." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Exporter la sélection" @@ -592,59 +592,59 @@ msgstr "Vue en grille" msgid "dashboard.files-layout.list" msgstr "Vue en liste" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Police supprimée" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Tout ignorer" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Les polices personnalisées installées apparaîtront ici." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Aucune police personnalisée pour l'instant." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" -"Dès qu'un membre de l'équipe télécharge une police personnalisée, elle sera " +"Dès qu'un membre de l'équipe téléverse une police personnalisée, elle sera " "affichée ici." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 police ajoutée" msgstr[1] "%s polices ajoutées" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" -"Toute police Web que vous téléchargez sera ajoutée à la liste de polices de " +"Toute police Web que vous téléversez sera ajoutée à la liste de polices de " "caractères disponible dans les propriétés de texte des fichiers de cette " "équipe. Les polices du même nom de famille de polices seront groupées en " "**une seule famille de polices**. Vous pouvez télécharger les polices au " -"formats suivants : **TTF, OTF et WOFF** (un seul format est nécessaire)." +"formats suivants : **TTF, OTF et WOFF** (un seul format est nécessaire)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" -"Ne téléchargez que des polices que vous possédez ou dont la licence vous " +"Ne téléversez que des polices que vous possédez ou dont la licence vous " "permet de les utiliser dans Penpot. Vous trouverez plus d'informations dans " "la section Propriété des Contenus des [conditions générales d'utilisation " "de Penpot](%s). Vous pouvez également vous renseigner sur les [licences de " "polices](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" -msgstr "Uploader tout" +msgstr "Tout téléverser" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -656,23 +656,23 @@ msgstr "" "recommandons d'utiliser [Transfonter](https://transfonter.org/) pour " "générer des polices web et corriger les erreurs. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Aller à la console d'administration" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "Dernière modification %s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" -msgstr "Importer fichiers" +msgstr "Importer des fichiers Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Oups ! Nous n'avons pas pu importer ce fichier" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "1 bibliothèque a été liée automatiquement par son nom." @@ -682,121 +682,125 @@ msgstr[1] "%s bibliothèques ont été liées automatiquement par leur nom." msgid "dashboard.import.bad-url" msgstr "Échec de l'importation. L'URL du modèle est incorrect" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "Confirmer les liens des bibliothèques" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:814 +msgid "dashboard.import.connect-selected-libraries" +msgstr "Connecter les bibliothèques sélectionnées" + +#: src/app/main/ui/dashboard/import.cljs:288 +msgid "dashboard.import.file-name-label" +msgstr "Nom du fichier" + +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "Nous n'avons pas pu vérifier ce fichier." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Ce fichier semble endommagé." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Les fichiers n'ont pas tous été importés" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Les fichiers suivants comportent des erreurs :" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Les fichiers comportant des erreurs ne seront pas chargés." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "Une erreur s'est produite lors du traitement de ce fichier." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 fichier a bien été importé." msgstr[1] "%s fichiers ont bien été importés." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Certains fichiers contenaient des objets non valides qui ont été enlevés." +#: src/app/main/ui/dashboard/import.cljs:815 +msgid "dashboard.import.next-file" +msgstr "Fichier suivant" + #: src/app/main/ui/dashboard.cljs:265 msgid "dashboard.import.no-perms" msgstr "Vous n'avez pas la permission nécessaire d'importer dans cette équipe" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Traitement des couleurs" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Traitement des composants" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Médias en cours de traitement" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Traitement de la page : %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Traitement des typographies" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Envoi des données au serveur (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Envoi du fichier : %s" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "" "Certaines bibliothèques n'ont pas pu être liées automatiquement. " "Sélectionnez la bonne bibliothèque pour chacune :" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "Vérifiez les liens des bibliothèques avant de confirmer :" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:447 +msgid "dashboard.import.resolve-libraries.connect-to" +msgstr "connecter à" + +#: src/app/main/ui/dashboard/import.cljs:441 +msgid "dashboard.import.resolve-libraries.original-library" +msgstr "bibliothèque d'origine" + +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "Lié" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:517 +msgid "dashboard.import.summary.manually-linked" +msgstr "lié manuellement" + +#: src/app/main/ui/dashboard/import.cljs:525 +msgid "dashboard.import.summary.new" +msgstr "Nouveau" + +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "Aucune bibliothèque sélectionnée" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/import.cljs:522 +msgid "dashboard.import.summary.original" +msgstr "Original" + +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Vous allez supprimer les invitations envoyées à :" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Vous allez renvoyer les invitations à :" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Supprimer les invitations" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Renvoyer les invitations" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "Vous n'avez pas la permission d'inviter des personnes à rejoindre cette " "équipe, ni de modifier ou supprimer des invitations." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Inviter des personnes" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "Vous n'avez pas la permission d'inviter des personnes dans cette équipe" @@ -808,31 +812,31 @@ msgstr "Quitter l'organisation" msgid "dashboard.leave-organization.toast" msgstr "Vous ne faites plus partie de l'organisation %s." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Quitter l’équipe" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliothèques et Modèles" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Voici quelques bibliothèques et modèles que vous pouvez ajouter à votre " "projet" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "En explorez plus et découvrir comment contribuer" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "" "Il y avait un problème pendant l'importation de la modèle. La modèle n'est " "pas importé." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliothèques" @@ -840,7 +844,7 @@ msgstr "Bibliothèques" msgid "dashboard.loading-files" msgstr "chargement de vos fichiers…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "chargement de vos polices…" @@ -860,65 +864,69 @@ msgstr "Déplacer %s fichiers vers" msgid "dashboard.move-to-other-team" msgstr "Déplacer vers une autre équipe" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nouveau fichier" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Nouveau fichier" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nouveau projet" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nouveau projet" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Aucune correspondance pour « %s »" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "" "Vous n'avez pas la permission d'ajouter des équipes à aucune de vos " "organisations." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 +msgid "dashboard.no-organizations-yet" +msgstr "Aucune organisation pour l'instant." + +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "Vous n'êtes pas autorisé à créer des équipes au sein de l'organisation %s. " "Pour plus d'informations, contactez le propriétaire de l'organisation." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "Vous n'êtes pas autorisé à supprimer des équipes appartenant à " "l'organisation %s. Pour plus d'informations, contactez le propriétaire de " "l'organisation." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "Vous n'êtes pas autorisé à déplacer des équipes appartenant à " "l'organisation %s. Pour plus d'informations, contactez le propriétaire de " "l'organisation." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Les projets épinglés apparaîtront ici" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notifications" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Votre adresse e‑mail a bien été mise à jour" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Votre adresse e‑mail a bien été vérifiée" @@ -930,11 +938,11 @@ msgstr "Les paramètres des notifications ont été mis à jour" msgid "dashboard.notifications.password-saved" msgstr "Mot de passe enregistré !" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Voir les notifications" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s membres" @@ -942,23 +950,23 @@ msgstr "%s membres" msgid "dashboard.open-in-new-tab" msgstr "Ouvrir le fichier dans un nouvel onglet" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Options" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Classer par fonction" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Classer par statut" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "L'organisation %s a été supprimée." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "Autres équipes" @@ -982,11 +990,11 @@ msgstr "Vous êtes maintenant propriétaire dans cette équipe." msgid "dashboard.permissions-change.viewer" msgstr "Vous êtes maintenant lecteur dans cette équipe." -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "Projets personnels" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Épingler/Désépingler" @@ -1002,27 +1010,27 @@ msgstr "Impossible de traiter la définition de l'extension" msgid "dashboard.plugins.try-plugin" msgstr "Essayer l'extension : " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "Élimination des fichiers…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "Restauration des fichiers…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "La suppression prend plus de temps que prévu" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "La restauration prend plus de temps que prévu" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projets" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Voulez-vous supprimer votre compte ?" @@ -1030,118 +1038,118 @@ msgstr "Voulez-vous supprimer votre compte ?" msgid "dashboard.removed-from-team" msgstr "Vous ne faites plus partie de l'équipe « %s »." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "" "Vous allez restaurer tous vos projets et fichiers. Cela peut prendre un " "certain temps." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Restaurer tous les projets et fichiers" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Tout restaurer" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s fichiers ont bien été restaurés." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Restaurer le projet" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Vous allez restaurer le projet %s et tous les fichiers qu'il contient." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Restaurer le projet" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s a bien été restauré." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Enregistrer les paramètres" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Rechercher…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Recherche de « %s »…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Organisations" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "Équipes" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "AJOUTER À L'ORGANISATION" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "Choisissez une organisation :" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "« %s » n'autorise que les membres de l'organisation à rejoindre ses " "équipes. Toutes les invitations en attente adressées à des personnes " "extérieures à l'organisation seront révoquées." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "" "Les invitations en attente destinées à des utilisateurs externes seront " "annulées." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Vous trouverez ici toutes vos organisations où vous êtes autorisé à créer " "ou ajouter des équipes." -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "" "Vous trouverez ici les organisations dont vous faites partie et où vous " "êtes autorisé à créer ou ajouter des équipes." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "Sélectionner une organisation" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "Ajouter l'équipe à une organisation" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Sélectionnez la langue de l’interface" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Sélectionnez un thème" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Thème sombre Penpot (par défaut)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Thème clair Penpot" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Thème système" @@ -1205,11 +1213,11 @@ msgstr "Mettre à jour les paramètres" msgid "dashboard.settings.notifications.title" msgstr "Notifications" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Voir tous les fichiers" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Historique des versions" @@ -1233,7 +1241,7 @@ msgstr[1] "Vos fichiers ont bien été dupliqués" msgid "dashboard.success-duplicate-project" msgstr "Votre projet a bien été dupliqué" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Votre fichier a bien été déplacé" @@ -1245,163 +1253,163 @@ msgstr "Vos fichiers ont bien été déplacés" msgid "dashboard.success-move-project" msgstr "Votre projet a bien été déplacé" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s appartient désormais à %s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Information de l’équipe" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Membres de l’équipe" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s n'appartient plus à %s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Organisation de l'équipe" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Ajouter à une organisation" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "" "Vous n'avez pas la permission d'ajouter cette équipe à aucune de vos " "organisations." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Changer l'organisation de l'équipe" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Seules les personnes de votre organisation peuvent être invitées." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "Cette équipe ne fait partie d'aucune organisation" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Retirer l'équipe de l'organisation" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Projets de l’équipe" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Ajouter à votre projet" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Thème de l’interface" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Résultats de recherche" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Les fichiers supprimés resteront dans la corbeille pendant" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s jours. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "Après cette durée, ils seront définitivement supprimés." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Si vous changez d'avis, vous pouvez les restaurer ou les éliminer " "définitivement via le menu de chaque fichier." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Écrivez pour rechercher" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Supprimer la bibliothèque" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Mettre à jour les paramètres" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "Vous n'êtes plus membre de l'organisation %s" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Bêta" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "Le rendu WebGL peut améliorer les performances, mais il est en bêta et peut " "être moins stable. Des différences visuelles peuvent apparaître entre le " "canevas, les exports (SVG/PDF) et le mode de visualisation." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Désactivé" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Activé" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Donner votre avis" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Statut" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "Rendu WebGL" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Actif" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Quand ce webhook sera activé, les détails de l'évènement seront envoyés" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "" "Uniquement vous pouvez supprimer ou modifier les webhooks que vous avez " "créés." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Type de contenu" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Créer un webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook créé." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Les webhooks sont une manière simple de permettre à d'autres site web et " @@ -1409,23 +1417,23 @@ msgstr "" "dans Penpot. Nous enverrons une requête POST à toutes les URLs que vous " "avez indiquées." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Appuyez sur le bouton « Ajouter un webhook » pour en ajouter un." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Aucun webhook créé jusqu’à présent." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Votre compte" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E‑mail" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Votre nom complet" @@ -1441,11 +1449,11 @@ msgstr "Attention" msgid "ds.component-subtitle" msgstr "Composants à mettre à jour :" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Autoriser" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Annuler" @@ -1465,7 +1473,7 @@ msgstr "Aucun token n'est applicable dans la collection ou le thème actif." msgid "ds.inputs.numeric-input.no-matches" msgstr "Aucune correspondance." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Ouvrir la liste des tokens" @@ -1475,6 +1483,12 @@ msgstr "" "Le token {%s} ne fait partie d'aucune collection active ou a une valeur " "invalide." +#: src/app/main/ui/auth/login.cljs:113 +msgid "errors.account-locked" +msgstr "" +"Trop de tentatives échouées. Votre compte est verrouillé pendant %s " +"minutes. Veuillez réessayer plus tard." + #: src/app/main/data/auth.cljs:344 msgid "errors.auth-provider-not-allowed" msgstr "Le fournisseur d'authentification n'est pas autorisé pour ce profil" @@ -1483,82 +1497,86 @@ msgstr "Le fournisseur d'authentification n'est pas autorisé pour ce profil" msgid "errors.auth-provider-not-configured" msgstr "Fournisseur d'authentification non configuré." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Il semblerait que vous n'êtes pas authentifié ou que votre session a expiré." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "La police %s n'a pas pu être chargée" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Les polices %s n'ont pas pu être chargées" #: src/app/main/data/workspace/media.cljs:237 msgid "errors.cannot-upload" -msgstr "Impossible de télécharger le fichier média." +msgstr "Impossible de téléverser le fichier média." #: src/app/main/ui/comments.cljs:769, src/app/main/ui/comments.cljs:799, src/app/main/ui/comments.cljs:898 msgid "errors.character-limit-exceeded" msgstr "Limite de caractères dépassée" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "" "Impossible d'ajouter la bibliothèque : cela créerait une dépendance " "circulaire" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "L'API du presse-papiers n'est pas disponible. Utilisez Penpot en HTTPS pour " "activer l'accès au presse-papiers" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Votre navigateur ne peut pas effectuer cette opération" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "Accès au presse-papiers refusé. Veuillez autoriser l'accès au " "presse-papiers dans les paramètres de votre navigateur pour coller du " "contenu" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "Une erreur s'est produite avec le commentaire" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:586 +msgid "errors.connection-error" +msgstr "Impossible de joindre le serveur. Vérifiez votre connexion et réessayez." + +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Désolé, il s'agit d'un ancien fichier qui utilise un type de ressource " "Penpot obsolète. Vous ne pouvez pas l'ouvrir." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "afin que nous puissions vous aider." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Même si Penpot ne prend plus en charge ce type de ressource, vous pouvez" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "nous contacter" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "" "Impossible de mettre une collection parent dans le chemin d'un de ses " "enfants." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Adresse e‑mail déjà utilisée" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Adresse e‑mail déjà validée." @@ -1570,30 +1588,30 @@ msgstr "Vous ne pouvez pas utiliser votre adresse e-mail comme mot de passe" msgid "errors.email-does-not-match-invitation" msgstr "L'adresse e-mail ne correspond pas à celle de l'invitation." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domaine non autorisé" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "L'adresse e-mail « %s » a un taux de rebond trop élevé." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "L'e-mail « %s » a été signalé comme spam ou a été rejeté." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Vous semblez ouvrir un fichier pour lequel la fonctionnalité « %s » " "activée, mais votre interface Penpot ne la prend pas en charge ou l'a " "désactivée." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "La fonctionnalité « %s » n'est pas prise en charge." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Doit contenir au plus %s caractères." @@ -1601,7 +1619,7 @@ msgstr "Doit contenir au plus %s caractères." msgid "errors.field-min-length" msgstr "Doit contenir au moins 1 caractère." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Champ vide" @@ -1609,24 +1627,24 @@ msgstr "Champ vide" msgid "errors.field-not-all-whitespace" msgstr "Le nom ne doit pas contenir d'espaces." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Il semble y avoir une incompatibilité entre les fonctionnalités actives et " "celles du fichier que tentez d'ouvrir. Vous devez activer les migrations " "pour « %s » avant de pouvoir ouvrir le fichier." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "Le nom de la famille de police ne peut contenir que des lettres, chiffres, " "espaces, tirets, traits de soulignement et points." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Un problème s’est produit." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Un problème est survenu avec le web worker." @@ -1642,7 +1660,7 @@ msgstr "Données non valides" msgid "errors.invalid-email" msgstr "Entrez une adresse e-mail valide" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "L’adresse e‑mail de confirmation doit correspondre" @@ -1679,13 +1697,13 @@ msgstr "Cette invitation a peut-être été annulée ou est arrivée à expirati msgid "errors.ldap-disabled" msgstr "Authentification LDAP désactivée." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "" "Vous avez atteint le quota maximal de « %s ». Contactez l'assistance " "technique." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Le nombre maximal (%s) d'e-mails qui peuvent être invités dans une seule " @@ -1703,21 +1721,21 @@ msgstr "Le contenu de l’image semble ne pas correspondre à l’extension de f msgid "errors.media-type-not-allowed" msgstr "L’image ne semble pas être valide." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "L'adresse e-mail du profil que vous invitez est ignorée (signalée comme " "spam ou taux de rebond élevé)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migration en cours" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Seul le créateur d'une version peut la verrouiller" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Seul le créateur d'une version peut la déverrouiller" @@ -1729,7 +1747,7 @@ msgstr "Un problème est survenu en quittant l'organisation. Veuillez réessayer msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "Le propriétaire de l'organisation ne peut pas quitter l'organisation." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "Cette organisation n'existe pas" @@ -1741,7 +1759,7 @@ msgstr "Le mot de passe de confirmation doit correspondre" msgid "errors.password-too-short" msgstr "Le mot de passe doit contenir au moins 8 caractères" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Données dans le presse-papier non valides" @@ -1749,7 +1767,7 @@ msgstr "Données dans le presse-papier non valides" msgid "errors.profile-blocked" msgstr "Le profil est bloqué" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "L'adresse e-mail de votre profil est ignorée (signalée comme spam ou taux " @@ -1759,7 +1777,7 @@ msgstr "" msgid "errors.registration-disabled" msgstr "L’enregistrement est actuellement désactivé." -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:700 msgid "errors.save-failed" msgstr "" "Vos dernières modifications ne sont pas confirmées comme enregistrées. " @@ -1767,21 +1785,25 @@ msgstr "" "et ne rechargez pas cet onglet : vous risquez de perdre des modifications. " "Contactez le support pour obtenir de l'aide." -#: src/app/main/errors.cljs:764 +#: src/app/main/data/persistence.cljs:447 +msgid "errors.save-retrying" +msgstr "Connexion perdue. Nouvelle tentative d'enregistrement de vos modifications." + +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "Le fichier SVG n'est pas valide ou est mal formé" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Fonctionnalité incompatible détectée « %s »" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Il n'y a pas assez de membres pour quitter l'équipe, vous voulez " "probablement la supprimer." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Le membre que vous essayez d'affecter n'existe pas." @@ -1789,7 +1811,7 @@ msgstr "Le membre que vous essayez d'affecter n'existe pas." msgid "errors.team-leave.only-owner-can-delete" msgstr "Seul le propriétaire d'une équipe peut la supprimer." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "Le propriétaire ne peut pas quitter l'équipe, vous devez réaffecter le rôle " @@ -1805,13 +1827,13 @@ msgstr "" msgid "errors.token-set-already-exists" msgstr "Il existe déjà une collection portant ce nom" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "Impossible de déposer. Il existe déjà une collection portant ce nom à ce " "chemin." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Il existe déjà une option de thème portant ce nom" @@ -1823,13 +1845,13 @@ msgstr "Le thème fait référence à des collections inexistantes : %s" msgid "errors.tokens.circular-reference" msgstr "Le token contient une référence circulaire" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "L'interlignage dépend de la taille de la police. Ajoutez une taille de " "police pour obtenir la valeur déduite." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "Ce champ ne peut pas être vide" @@ -1849,13 +1871,13 @@ msgstr "Erreur d'importation :" msgid "errors.tokens.invalid-color" msgstr "Couleur non valide : %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "" "Valeur de token invalide : vous ne pouvez référencer qu'un token de type " "famille de police" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Valeur de graisse de police invalide : utilisez des valeurs numériques " @@ -1878,43 +1900,43 @@ msgstr "" "séparés par des caractères « . » et ne doivent pas commencer par le symbole " "« $ »." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "" "Type d'ombre invalide : seules les valeurs 'innerShadow' ou 'dropShadow' " "sont acceptées" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Valeur du token non valide : seules les valeurs Aucune, Majuscules, " "Minuscules ou Première lettre en capitale sont acceptées" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "" "Valeur du token non valide : seules les valeurs none, underline et " "strike-through sont acceptées" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "Valeur invalide : doit référencer un token d'ombre composite." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "" "Valeur non valide : elle doit faire référence à un token de typographie " "composite." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Valeur du token non valide : %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Références du token manquantes : " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "" "Le nom de groupe de %s entre en conflit avec un token du même nom dans une " @@ -1928,33 +1950,33 @@ msgstr "Aucun token, collection ou thème n'ont été trouvés dans ce fichier." msgid "errors.tokens.number-too-large" msgstr "Valeur du token non valide. La valeur est trop grande : %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "" "L'opacité doit être comprise entre 0 % et 100 % ou entre 0 et 1 (ex : 50 % " "ou 0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Le token s'auto-référence" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "Le flou de l'ombre doit être supérieur ou égal à 0." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "L'étalement de l'ombre doit être supérieur ou égal à 0." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "La largueur du tracé doit être plus grand ou égal à 0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Valeur non valide : % n'est pas autorisé." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Valeur non valide : les unités ne sont pas autorisées." @@ -1962,21 +1984,21 @@ msgstr "Valeur non valide : les unités ne sont pas autorisées." msgid "errors.unexpected-error" msgstr "Une erreur inattendue s’est produite." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Token inconnu" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Cette version est déjà verrouillée" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "" "Cette version est verrouillée et ne peut pas être supprimée par d'autres " "personnes" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Le numéro de version du fichier est incompatible" @@ -2009,39 +2031,39 @@ msgstr "Au moins 1 lettre majuscule" msgid "errors.weak-password.too-short" msgstr "Au moins 8 caractères" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL ne fonctionne plus. Rechargez la page pour le réinitialiser" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Oups ! Le contexte du canevas a été perdu" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Erreur de connexion, URL inaccessible" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "L'URL ne passe pas la validation." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Il y a eu une erreur dans le dernier envoi." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Erreur lors de la validation SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Délai d'attente dépassé" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Erreur inattendue lors de la validation" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Statut inattendu %s" @@ -2061,17 +2083,17 @@ msgstr "Description" msgid "feedback.description-placeholder" msgstr "Décrivez la raison de votre commentaire" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Nous sommes ravis de vous voir ici. Si vous avez besoin d'aide, effectuez " "une recherche avant de publier une question." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Communauté Penpot" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Autres méthodes de contact" @@ -2096,11 +2118,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Contact" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Nous sommes là pour répondre à vos questions techniques." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Compte d’assistance X" @@ -2120,47 +2142,43 @@ msgstr "Idée" msgid "feedback.type.issue" msgstr "Problème" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Télécharger les fichiers" - -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "" "Les ressources des bibliothèques liées ne seront pas incluses dans le " "fichier." -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "Considérer les ressources comme des objets basiques" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "" "Les fichiers avec des bibliothèques liées seront inclus dans l'exportation, " "en conservant leur liaison. " -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "Exporter le fichier + les bibliothèques" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "" "Une fois importé, vous pourrez lier les bibliothèques existantes portant " "des noms correspondants." -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "Lier les bibliothèques correspondantes à l'importation" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "" "Votre fichier sera exporté avec les ressources de la bibliothèque " "fusionnées dans la bibliothèque locale." -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "Intégrer les ressources de la bibliothèque dans le fichier" @@ -2168,7 +2186,7 @@ msgstr "Intégrer les ressources de la bibliothèque dans le fichier" msgid "generic.error" msgstr "Une erreur s’est produite" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "Appliquer" @@ -2216,7 +2234,7 @@ msgstr "Aperçu de l'image de remplissage de la forme" msgid "inspect.attributes.shadow" msgstr "Ombre" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Taille et position" @@ -2224,43 +2242,31 @@ msgstr "Taille et position" msgid "inspect.attributes.stroke" msgstr "Contour" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centre" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Intérieur" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Extérieur" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Typographie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Police de caractères" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Taille de police" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Style de police" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Interlettrage" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Interlignage" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "Familles de polices mixtes" @@ -2268,15 +2274,19 @@ msgstr "Familles de polices mixtes" msgid "inspect.attributes.typography.text-decoration.none" msgstr "Aucune" +#: src/app/main/ui/inspect/attributes/text.cljs:117 +msgid "inspect.attributes.typography.text-decoration.strikethrough" +msgstr "Barré" + #: src/app/main/ui/inspect/attributes/text.cljs:115 msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Souligné" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Première lettre en capitale" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Minuscule" @@ -2288,7 +2298,7 @@ msgstr "Aucune" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Non assigné" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Majuscules" @@ -2334,6 +2344,10 @@ msgstr "Copier" msgid "inspect.subtitle.main" msgstr "Composant principal" +#: src/app/main/ui/inspect/right_sidebar.cljs:176 +msgid "inspect.tabs-switcher-label" +msgstr "Changer d'onglet d'inspection" + #: src/app/main/ui/inspect/right_sidebar.cljs:142 msgid "inspect.tabs.code.selected.circle" msgstr "Cercle" @@ -2396,7 +2410,7 @@ msgstr "Copier dans le Presse-papiers" #: src/app/main/ui/inspect/styles/style_box.cljs:67, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:183 msgid "inspect.tabs.styles.toggle-style" -msgstr "Afficher/Masquer le panneau %s" +msgstr "Afficher/cacher le panneau %s" #: src/app/main/ui/inspect/styles/style_box.cljs:26 msgid "inspect.tabs.styles.token-panel" @@ -2410,23 +2424,23 @@ msgstr "Valeur déduite :" msgid "inspect.tabs.styles.variants-panel" msgstr "Propriétés de la variante" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" -msgstr "Créer un nouveau token d'accès" +msgstr "Créer un token d'accès" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" -msgstr "Cliquez sur le bouton « Créer un nouveau token d'accès » pour en générer un." +msgstr "Cliquez sur le bouton « Créer un token d'accès » pour en générer un." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "Vous n'avez aucun token pour l'instant." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "Tokens d'accès personnels" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "Les tokens d'accès personnels constituent une alternative à notre système " @@ -2511,53 +2525,53 @@ msgstr "La clé MCP expirera le %s" msgid "integrations.mcp-key.will-not-expire" msgstr "La clé MCP n'a pas de date d'expiration" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "Le serveur MCP de Penpot permet aux clients MCP d'interagir directement " "avec les fichiers de conception Penpot." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "Comment configurer les clients MCP" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "Voici l'URL du serveur à utiliser pour configurer votre client MCP et le " "connecter au serveur MCP de Penpot." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "Régénérer la clé MCP" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "Clé MCP" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "La clé MCP est nécessaire pour configurer le client MCP" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Statut" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Désactivé" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Activé" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "La clé MCP utilisée pour se connecter au serveur MCP a expiré. La connexion " "ne peut donc pas être établie." -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "" "Veuillez régénérer la clé MCP et mettre à jour la configuration de votre " @@ -2567,10 +2581,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "Serveur MCP" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "Bêta" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "Nom" @@ -2621,7 +2631,7 @@ msgstr "Régénérer la clé MCP" msgid "integrations.regenerate-mcp-key.title.created" msgstr "Clé MCP régénérée" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "Intégrations" @@ -2639,47 +2649,47 @@ msgstr "Le token expirera le %s" msgid "integrations.token.will-not-expire" msgstr "Le token n'a pas de date d'expiration" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Tout marquer comme lu" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Raccourcis" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "À propos de Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Accepter" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Activé" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Ajouter" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Ajouter police personnalisée" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Ajout…" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Administration" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Tous" @@ -2687,17 +2697,17 @@ msgstr "Tous" msgid "labels.and" msgstr "et" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Retour" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Il semble que vous deviez attendre un peu et réessayer ; nous effectuons " "une petite maintenance de nos serveurs." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Bad Gateway" @@ -2709,23 +2719,23 @@ msgstr "Flou" msgid "labels.blur-effects" msgstr "Effets de flou" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Annuler" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canevas" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "Rechercher des mises à jour" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "Recherche de mises à jour…" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Fermer" @@ -2733,7 +2743,7 @@ msgstr "Fermer" msgid "labels.code" msgstr "Code" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Réduire" @@ -2745,15 +2755,15 @@ msgstr "Couleur" msgid "labels.comment" msgstr "Commentaire" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Commentaires" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Communauté" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Communauté et contributions" @@ -2765,19 +2775,19 @@ msgstr "Calculé" msgid "labels.confirm-password" msgstr "Confirmer le mot de passe" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Contacter l'assistance technique" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Contact" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continuer" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Vous pouvez continuer avec un compte Penpot" @@ -2789,23 +2799,23 @@ msgstr "Copier" msgid "labels.copy-color" msgstr "Copier la couleur" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Copier le lien" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © depuis 2019" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Créer" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Créer une équipe" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Entrer le nom de la nouvelle équipe" @@ -2813,7 +2823,7 @@ msgstr "Entrer le nom de la nouvelle équipe" msgid "labels.current" msgstr "Actuel" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Tableau de bord" @@ -2821,7 +2831,7 @@ msgstr "Tableau de bord" msgid "labels.default" msgstr "Par défaut" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Supprimer" @@ -2833,7 +2843,7 @@ msgstr "Supprimer le commentaire" msgid "labels.delete-comment-thread" msgstr "Supprimer le fil" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Supprimer l'invitation" @@ -2841,39 +2851,35 @@ msgstr "Supprimer l'invitation" msgid "labels.delete-multi-files" msgstr "Supprimer %s fichiers" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "Voulez-vous vraiment supprimer votre photo de profil ?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "Supprimer la photo de profil" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Supprimés" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Développeur" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Rejeter" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "Télécharger %s" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "Télécharger" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "Téléchargement du fichier…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Brouillons" @@ -2881,19 +2887,19 @@ msgstr "Brouillons" msgid "labels.duplicate" msgstr "Dupliquer" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Modifier" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Éditeur" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Vide" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Erreur" @@ -2901,11 +2907,11 @@ msgstr "Erreur" msgid "labels.exit" msgstr "Quitter" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Arrivée à expiration" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exporter" @@ -2917,7 +2923,7 @@ msgstr "Avis désactivés" msgid "labels.feedback-sent" msgstr "Avis envoyé" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2925,63 +2931,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Remplir" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "Disposition flex" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Famille de polices" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Styles" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Polices" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Référentiel GitHub" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Donnez votre avis" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Retour" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Designer graphique" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "Disposition en grille" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Centre d'aide" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Aide et formation" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Cacher" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" -msgstr "Masquer les commentaires résolus" +msgstr "Cacher les commentaires résolus" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importer" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inactif" @@ -2989,53 +2995,53 @@ msgstr "Inactif" msgid "labels.info" msgstr "Information" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Polices installées" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "Intégrations" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Un incident est survenu." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "" "Vous pouvez réessayer d'effectuer l'opération ou contacter l'assistance " "technique pour signaler l'erreur." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Erreur interne" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Invitations" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Langue" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Disposition" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Centre de formation" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Bibliothèques et modèles" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Chargement…" @@ -3043,7 +3049,7 @@ msgstr "Chargement…" msgid "labels.lock" msgstr "Verrouiller" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Se connecter ou s'inscrire" @@ -3051,19 +3057,19 @@ msgstr "Se connecter ou s'inscrire" msgid "labels.login" msgstr "Connexion" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Se déconnecter" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Membre" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Membres" @@ -3075,43 +3081,43 @@ msgstr "Mention" msgid "labels.new-password" msgstr "Nouveau mot de passe" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Suivant" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "" "Cette instance nécessite une étape de configuration incluse dans la " "dernière version." -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "Consultez notre" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "Penpot ne peut pas encore démarrer." -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "guide technique" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "" "Penpot est temporairement indisponible en raison d'un problème système. " "Veuillez réessayer sous peu." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Vous n’avez aucune notification de commentaire en attente." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Aucune invitation en attente." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Rassemblez votre équipe et créez de grandes choses ensemble." @@ -3119,27 +3125,27 @@ msgstr "Rassemblez votre équipe et créez de grandes choses ensemble." msgid "labels.no-matches" msgstr "Aucune correspondance" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Oups !" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notifications" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 fichier" msgstr[1] "%s fichiers" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 plan de travail" msgstr[1] "%s plans de travail" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 projet" @@ -3149,43 +3155,43 @@ msgstr[1] "%s projets" msgid "labels.old-password" msgstr "Ancien mot de passe" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "Ouvrir" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Options" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Autres (précisez)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Autre" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Propriétaire" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Mot de passe" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "En attente" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Journal des modifications de Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot hub" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Projets épinglés" @@ -3193,31 +3199,31 @@ msgstr "Projets épinglés" msgid "labels.post" msgstr "Envoyer" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Précédent" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Product Design ou UX Design" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Gestionnaire de produit ou de projet" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projets" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Récents" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Référence" @@ -3225,27 +3231,27 @@ msgstr "Référence" msgid "labels.refresh" msgstr "Actualiser" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Notes de version" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Recharger la page" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Supprimer" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Supprimer le membre" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Renommer" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Renommer l'équipe" @@ -3269,11 +3275,11 @@ msgstr "nouvelle réponse" msgid "labels.reply.thread" msgstr "Répondre" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Renvoyer" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Renvoyer l'invitation" @@ -3281,43 +3287,43 @@ msgstr "Renvoyer l'invitation" msgid "labels.restore" msgstr "Restaurer" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Réessayer" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rôle" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Enregistrer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Rechercher" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Rechercher une police" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Sélectionnez une option" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Envoyer" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Envoi…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Nous sommes en maintenance planifiée de nos systèmes." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Service non disponible" @@ -3325,70 +3331,74 @@ msgstr "Service non disponible" msgid "labels.sets" msgstr "Collections" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Configuration" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Ombre" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Partager" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibliothèques" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Afficher" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Afficher tous les commentaires" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Afficher la liste des commentaires" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Afficher uniquement vos mentions" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Afficher uniquement vos commentaires" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/import.cljs:808 +msgid "labels.skip" +msgstr "Passer" + +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Sources" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "" "La connexion avec le fournisseur d'identité de votre organisation n'a pas " "abouti. Le fournisseur est peut-être indisponible, ou votre compte n'est " "peut-être pas encore présent dans son annuaire." -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "Réessayer" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "Nous n'avons pas pu vous connecter à %s" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Commencer" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Statut" @@ -3396,7 +3406,7 @@ msgstr "Statut" msgid "labels.stroke" msgstr "Contour" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Étudiant ou enseignant" @@ -3408,27 +3418,31 @@ msgstr "Styles" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Changer" +#: src/app/main/ui/dashboard/organization_team_switch.cljs:657 +msgid "labels.team-management" +msgstr "Gestion de l'équipe" + #: src/app/main/ui/inspect/styles/style_box.cljs:30 msgid "labels.text" msgstr "Texte" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Thèmes" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutoriels" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Typographie" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Erreur inconnue" @@ -3440,35 +3454,31 @@ msgstr "Déverrouiller" msgid "labels.unpublish-multi-files" msgstr "Dépublier %s fichiers" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Mettre à jour" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Mettre à jour l'équipe" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" -msgstr "Télécharger" +msgstr "Téléverser" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" -msgstr "Télécharger des polices personnalisées" +msgstr "Téléverser des polices personnalisées" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" -msgstr "Téléchargement…" +msgstr "Envoi en cours…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "Envoi du fichier…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variante" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Notes de la version %s" @@ -3476,7 +3486,7 @@ msgstr "Notes de la version %s" msgid "labels.view-only" msgstr "Lecture seule" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Spectateur" @@ -3490,7 +3500,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s avertissement" msgstr[1] "%s avertissements" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -3498,11 +3508,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Écrire un nouveau commentaire" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(vous)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Votre compte" @@ -3648,7 +3658,7 @@ msgstr "" "Vous êtes le seul membre de certaines équipes. Ces équipes seront " "supprimées avec leurs projets et fichiers." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "AVANT DE QUITTER L'ORGANISATION" @@ -3656,82 +3666,82 @@ msgstr "AVANT DE QUITTER L'ORGANISATION" msgid "modals.before-leave-organization.warning" msgstr "Toute équipe dont vous êtes le seul membre sera supprimée." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Grand nudge" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Vérifier la nouvelle adresse e‑mail" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Nous enverrons un e‑mail à votre adresse actuelle « %s » pour vérifier " "votre identité." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Nouvel e‑mail" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Changer adresse e‑mail" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Changez votre adresse e‑mail" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Créer un webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Créer un webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL de charge utile" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Annuler et conserver mon compte" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Oui, supprimer mon compte" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "En supprimant votre compte, vous perdrez tous vos projets et archives " "actuelles." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "Cette action est irréversible. Votre compte, votre abonnement et vos " "organisations seront supprimés immédiatement. Vous perdrez également " "l'accès à vos projets et fichiers actuels." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organisations qui seront supprimées" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s membres" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s équipes" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Voulez-vous vraiment supprimer votre compte ?" @@ -3791,23 +3801,23 @@ msgstr "Voulez-vous vraiment supprimer %s fichiers ?" msgid "modals.delete-file-multi-confirm.title" msgstr "Suppression de %s fichiers" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Voulez-vous vraiment supprimer ce style de police ? Il ne sera pas chargé " "s'il est utilisé dans un fichier." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Suppression du style de police" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Voulez-vous vraiment supprimer cette police ? Elle ne se chargera pas si " "elle est utilisée dans un fichier." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Suppression de la police" @@ -3817,19 +3827,19 @@ msgstr "" "Voulez-vous vraiment supprimer cette équipe qui fait partie de " "l'organisation %s ?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Voulez-vous vraiment supprimer cette page ?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Supprimer une page" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "Voulez-vous vraiment supprimer les pages sélectionnées ?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "Supprimer les pages" @@ -3845,31 +3855,31 @@ msgstr "Voulez-vous vraiment supprimer ce projet ?" msgid "modals.delete-project-confirm.title" msgstr "Supprimer un projet" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Supprimer le fichier" msgstr[1] "Supprimer les fichiers" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Activé dans aucun fichier." msgstr[1] "Activés dans aucun fichier." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Cette bibliothèque est active ici : " msgstr[1] "Ces bibliothèques sont actives ici : " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Voulez-vous vraiment supprimer ce fichier ?" msgstr[1] "Voulez-vous vraiment supprimer ces fichiers ?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Suppression du fichier" @@ -3889,19 +3899,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Suppression d’une équipe" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Supprimer le membre" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Voulez-vous vraiment supprimer ce membre de l’équipe ?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Supprimer un membre d’équipe" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3911,23 +3921,23 @@ msgstr[1] "" "Les éléments déjà utilisés dans ces fichiers resteront en place (aucun " "design ne sera altéré)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Supprimer le webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Vous confirmez vouloir supprimer le webhook ?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Suppression du webhook en cours" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Modifier le webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Modifier le webhook" @@ -3945,15 +3955,15 @@ msgstr "Importer les tokens" msgid "modals.import-library-tokens.title" msgstr "Importer les tokens depuis la bibliothèque ?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Envoyer l'invitation" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Adresse e-mail, séparées par des virgules" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Certains emails appartiennent à des membres actuels de l'équipe. Les " @@ -3977,99 +3987,99 @@ msgstr "" "équipe, et ils en sont déjà tous membres. Pour plus d'informations, " "contactez le propriétaire de l'organisation." -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "Adresses qui ne recevront pas d'invitation" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "Annuler" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "Seuls les membres existants de l'organisation peuvent être invités." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "Envoyer les invitations" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "Certaines invitations ne peuvent pas être envoyées" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "Certaines personnes de votre liste ne recevront pas les invitations." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Vous pouvez inviter des membres dans l'équipe pour qu'ils accèdent à ce " "fichier ainsi que tous les autres fichiers de l'équipe." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Inviter des membres dans l'équipe" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Comme vous êtes le seul membre de l'équipe, celle-ci sera supprimée avec " "ses projets et fichiers." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Voulez-vous vraiment quitter l'équipe %s ?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Vous ne pouvez pas quitter l'équipe si il n'y a aucun membre à promouvoir " "comme propriétaire. Vous pourriez plutôt supprimer l'équipe." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Vous êtes le propriétaire de %s. Avant de partir, sélectionnez un autre " "membre à promouvoir comme propriétaire." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promouvoir et quitter" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Sélectionnez un membre à promouvoir" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Sélectionnez un membre à promouvoir" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Quitter l’équipe" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Voulez-vous vraiment quitter cette équipe ?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Quitter l’équipe" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Vous êtes propriétaire de certaines équipes de l'organisation. Veuillez " "promouvoir un autre membre pour qu'il devienne propriétaire." -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Vous êtes le seul membre de certaines équipes. Ces équipes seront " "supprimées avec leurs projets et fichiers." -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "De plus, vous êtes propriétaire de certaines équipes de l'organisation. " @@ -4089,57 +4099,57 @@ msgstr "" msgid "modals.leave-organization-confirm.title" msgstr "Quitter l'organisation %s ?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" -msgstr[0] "Déplacement" -msgstr[1] "Déplacements" +msgstr[0] "Déplacer" +msgstr[1] "Déplacer" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Voulez-vous vraiment déplacer cette librairie ?" msgstr[1] "Voulez-vous vraiment déplacer ces librairies ?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Déplacer librairie" msgstr[1] "Déplacer librairies" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Taille du décalage" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Promouvoir" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Si vous transférez la propriété, vous changerez votre rôle en Admin, " "perdant ainsi certaines permissions sur cette équipe. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Vous êtes le propriétaire actuel de cette équipe. Voulez-vous vraiment " "promouvoir %s comme nouveau propriétaire de l'équipe ?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Promouvoir propriétaire" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publier" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Votre bibliothèque est vide. Voulez-vous la publier quand même ?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publier la bibliothèque vide" @@ -4161,23 +4171,23 @@ msgstr "Voulez-vous vraiment retirer l'équipe « %s » de l'organisation « %s msgid "modals.remove-team-organization.title" msgstr "RETIRER L'ÉQUIPE DE L'ORGANISATION" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Petit nudge" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Dépublier" msgstr[1] "Dépublier" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Voulez-vous vraiment supprimer cette bibliothèque ?" msgstr[1] "Voulez-vous vraiment supprimer ces bibliothèques ?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Supprimer la bibliothèque" @@ -4233,11 +4243,11 @@ msgstr "" #: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:121 msgid "nitrate.code-activation.footer-after" -msgstr " et contactez-nous :" +msgstr "Envoyez le fichier à" #: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:126 msgid "nitrate.code-activation.footer-before" -msgstr "Besoin d'un code ? Téléchargez votre " +msgstr "et nous vous enverrons votre code." #: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:119 msgid "nitrate.code-activation.footer-download" @@ -4267,38 +4277,38 @@ msgstr "Activer" msgid "nitrate.code-activation.title" msgstr "Activer Nitrate" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "Contacter l'équipe commerciale" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "" "Pour passer à cet abonnement, veuillez contacter notre équipe commerciale. " "Nous vous aiderons à mettre à jour votre abonnement et à vérifier que tout " "est correctement configuré." -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Votre organisation sera supprimée." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "" "Votre stockage total, l'historique des versions automatiques et la durée de " "récupération des fichiers seront limités." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "" "Les équipes, projets et fichiers ne feront plus partie d'aucune " "organisation, mais resteront disponibles." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "Lorsque vous rétrogradez :" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "Passer à l'abonnement %s ?" @@ -4314,15 +4324,15 @@ msgstr "Contactez-nous pour essayer Entreprise pendant 14 jours :" msgid "nitrate.form.contact-upgrade" msgstr "Contactez-nous pour passer à Entreprise :" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "Contactez-nous pour essayer l'abonnement Entreprise pendant 14 jours :" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "Contactez-nous pour passer à l'abonnement Entreprise :" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "" "Vous ne serez pas facturé maintenant. Le paiement sera prélevé à la fin de " @@ -4346,7 +4356,7 @@ msgstr "" "Toute la liberté de l'open source, avec la gouvernance dont votre " "organisation a besoin" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "" "Tout ce dont vos équipes ont besoin pour travailler en toute sécurité à " @@ -4356,7 +4366,7 @@ msgstr "" msgid "nitrate.form.enterprise.price" msgstr "par membre et par mois" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "Essayez 14 jours gratuitement" @@ -4372,7 +4382,7 @@ msgstr "Commencer avec Entreprise" msgid "nitrate.form.subscribe-with-code" msgstr "S'abonner avec un code d'activation" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "Débloquez les fonctionnalités Entreprise" @@ -4384,7 +4394,7 @@ msgstr "Essayez gratuitement pendant 14 jours" msgid "nitrate.modal-success.title" msgstr "Bienvenue dans Entreprise !" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "Actif jusqu'au %s" @@ -4392,23 +4402,23 @@ msgstr "Actif jusqu'au %s" msgid "nitrate.subscription.renew-success" msgstr "Votre abonnement Nitrate a été renouvelé." -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "Annuler l'abonnement" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "Pour annuler votre abonnement Nitrate, veuillez nous contacter à :" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "Renouveler avec un code d'activation" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Cette page n'existe pas" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Erreur 404" @@ -4424,15 +4434,13 @@ msgstr "Nous avons envoyé un e-mail de récupération à" #: src/app/main/ui/auth/recovery_request.cljs:114 msgid "not-found.login.sent-recovery-check" -msgstr "" -"Vérifiez vos e-mails et cliquez sur le lien pour créer un nouveau mot de " -"passe." +msgstr "Vérifiez vos e-mails et cliquez sur le lien pour créer un mot de passe." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "S'inscrire gratuitement" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "Et utilisez Penpot dans les secondes qui suivent !" @@ -4440,69 +4448,69 @@ msgstr "Et utilisez Penpot dans les secondes qui suivent !" msgid "not-found.made-with-love" msgstr "Fait avec AMOUR et logiciel libre" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Vous avez déjà demandé l'accès à ce fichier." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Vous avez déjà demandé l'accès à ce fichier ou d'autres fichiers ou projets " "de cette équipe." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Vous avez déjà demandé l'accès à ce projet ou d'autres projets ou fichiers " "de cette équipe." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Vous avez déjà demandé l'accès à ce projet." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "DEMANDER L'ACCÈS" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "" "N'oubliez pas que, si le propriétaire l'accepte, vous allez être invités " "dans l'équipe." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Votre demande a bien été envoyée !" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Vous n'avez pas accès à ce fichier." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Aller à votre Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Si le propriétaire l'accepte, vous allez être invités dans l'équipe." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Fichier Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Vous n'avez pas accès à ce projet." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJET" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Pour accéder à ce fichier, vous pouvez demander au propriétaire de l'équipe." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Pour accéder à ce projet, vous pouvez demander au propriétaire de l'équipe." @@ -4516,33 +4524,33 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Il y a une nouvelle version disponible." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "Cette invitation n'est plus disponible." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "L'invitation a bien été supprimée" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "E‑mail d'invitation envoyé !" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Lien d'invitation copié" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Vous ne pouvez pas supprimer votre profil. Réassignez vos équipes avant de " "continuer." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil enregistré !" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "E‑mail de vérification envoyé à %s. Vérifiez votre e‑mail !" @@ -4582,7 +4590,7 @@ msgstr "Nom de l'équipe" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Créer l'équipe" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Inviter des membres" @@ -4592,98 +4600,98 @@ msgstr "" "Développeurs, designers, gestionnaires… tout le monde est le bienvenu. Vous " "pourrez en inviter d'autres plus tard." -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Inviter avec le rôle :" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "C'est parti !" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Générer du vrai code à partir des designs" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Création des Design Systems" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototyper" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Designer l'UX/UI pour une app" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Faire du wireframe" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Pour quoi utiliserez-vous Penpot ?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Laissez nous vous connaitre un peu pour nous aider à faire fonctionner " "Penpot pour vous. Vos questions nous aiderons à prioriser les nouvelles " "fonctionnalités et vous diriger dans la bonne direction pour commencer." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Aidez-nous à vous connaitre" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Lequel de ces outils utilisez-vous le plus ?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Quel genre de travail faites-vous ?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Quelle est la taille de votre entreprise ?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Par où souhaitez-vous commencer ?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1 001 - 5 000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 - 500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 - 100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 - 1 000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "Juste moi" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5 001+" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Éducation" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Personnel" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Travail" @@ -4765,11 +4773,11 @@ msgstr "Restaurer la configuration par défaut" msgid "restore-shortcuts.restore" msgstr "Restaurer" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Détacher" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Divers" @@ -4781,35 +4789,35 @@ msgstr "Supprimer la couleur" msgid "settings.select-this-color" msgstr "Sélectionnez les éléments qui utilisent ce style" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Commentaires" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Aligner en bas" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Aligner horizontalement au centre" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Aligner à gauche" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Aligner à droite" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Aligner en haut" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Aligner verticalement au centre" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Créer un plan de travail à partir de la sélection" @@ -4817,55 +4825,55 @@ msgstr "Créer un plan de travail à partir de la sélection" msgid "shortcuts.bold" msgstr "Basculer en gras" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Soustraction booléenne" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Exclusion booléenne" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Intersection booléenne" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Union booléenne" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Mettre à l'arrière-plan" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Déplacer en arrière" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Déplacer vers l'avant" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Mettre au premier plan" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" -msgstr "Rétablir" +msgstr "Effacer l'historique d'annulation" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copier" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Copier le lien" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Copier les propriétés" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Créer un composant/une variante" @@ -4873,63 +4881,63 @@ msgstr "Créer un composant/une variante" msgid "shortcuts.create-new-project" msgstr "Créer" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Couper" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Dézoomer" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Supprimer" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Supprimer le nœud" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "Supprimer le nœud et les segments" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "Tout désélectionner" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Détacher le composant" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "Désactiver le raccourci" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "Désactivé" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "Raccourci désactivé" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "Flèche" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Courbe" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Ellipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Plan de travail" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "Ligne" @@ -4937,67 +4945,67 @@ msgstr "Ligne" msgid "shortcuts.draw-nodes" msgstr "Dessiner un tracé" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Tracé" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rectangle" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Texte" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Dupliquer" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "" "Combinaison déjà attribuée à « %s ». L'enregistrer la supprimera de cet " "emplacement." -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "Si vous enregistrez, ce raccourci reviendra à sa valeur d'origine :" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "" "Cette combinaison de touches est disponible et peut être attribuée sans " "conflit." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "Personnaliser dans les paramètres" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Annuler" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Exporter les formes" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "Rechercher" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "Rechercher et remplacer" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Zoom ajusté" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Retourner horizontalement" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Retourner verticalement" @@ -5021,27 +5029,27 @@ msgstr "Accéder aux bibliothèques partagées" msgid "shortcuts.go-to-search" msgstr "Recherche" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Grouper" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Répartir horizontalement" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" -msgstr "Afficher/masquer l'interface utilisateur" +msgstr "Afficher/cacher l'interface utilisateur" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "Importer/Exporter" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Zoom avant" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Insérer une image" @@ -5049,11 +5057,11 @@ msgstr "Insérer une image" msgid "shortcuts.italic" msgstr "Basculer en italique" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Joindre les nœuds" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "Appuyez sur la combinaison de touches" @@ -5061,39 +5069,39 @@ msgstr "Appuyez sur la combinaison de touches" msgid "shortcuts.line-through" msgstr "Activer/désactiver texte barré" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Créer un coin" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Faire une courbe" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Masque" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Fusionner les nœuds" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Déplacer" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Déplacement rapide vers le bas" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Déplacement rapide vers la gauche" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Déplacez-vous rapidement vers la droite" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Déplacement rapide vers le haut" @@ -5101,379 +5109,379 @@ msgstr "Déplacement rapide vers le haut" msgid "shortcuts.move-nodes" msgstr "Déplacer le nœud" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Déplacer vers le bas" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Déplacer vers la gauche" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Déplacer vers la droite" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Déplacer vers le haut" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Plan de travail suivant" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "Il n'y a aucun raccourci désactivé" -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "Rendez-vous dans Tous pour commencer à personnaliser vos raccourcis." -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "Aucun raccourci ne correspond à votre recherche" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Aucun raccourci trouvé" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Définir l'opacité à 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Définir l'opacité à 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Définir l'opacité à 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Définir l'opacité à 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Définir l'opacité à 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Définir l'opacité à 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Définir l'opacité à 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Définir l'opacité à 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Définir l'opacité à 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Définir l'opacité à 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Sélecteur de couleurs" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Accéder aux commentaires des spectateurs" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Accéder au tableau de bord" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Aller à l'inspecteur" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Accéder aux interactions des spectateurs" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Accéder aux interactions des spectateurs" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Accéder à l’espace de travail" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " ou " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "Ce raccourci a été réattribué à %s" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "Page des raccourcis" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Coller" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Coller les propriétés" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "Coller et remplacer" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "Personnalisé" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Gestionnaire d'extensions" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Plan de travail précédent" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Rétablir" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "" "Après avoir modifié les raccourcis, actualisez les autres onglets Penpot " "ouverts pour appliquer les changements." -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Renommer" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "Réinitialiser par défaut" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Réinitialiser le zoom" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Échelle" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Bases" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Tableau de bord" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Spectateur" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Espace de travail" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Tout sélectionner" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Sélectionner le calque suivant" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Sélectionner le calque parent" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Sélectionner le calque précédent" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Séparer les nœuds" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "Afficher les mesures" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" -msgstr "Afficher/masquer la grille de pixels" +msgstr "Afficher/cacher la grille de pixels" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" -msgstr "Afficher/masquer les raccourcis" +msgstr "Afficher/cacher les raccourcis" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Aligner sur les nœuds" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Aligner sur la grille de pixels" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Commencer l'édition" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Alignement" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Modifier" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Générique" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu principal" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modifier les calques" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panneaux" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Tracés" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formes" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Textes" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Outils" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Définir les vignettes" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Raccourcis clavier" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Activer/désactiver l'alignement dynamique" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Activer/désactiver les ressources" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Activer/désactiver la palette de couleurs" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" -msgstr "Afficher/masquer les commentaires" +msgstr "Afficher/cacher les commentaires" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Activer/désactiver le mode focus" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Activer/désactiver le plein écran" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" -msgstr "Afficher/Masquer les repères" +msgstr "Afficher/cacher les repères" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Activer/désactiver l'historique" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Activer/désactiver les calques" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Ajouter/supprimer la disposition flex" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Ajouter/Supprimer la disposition en grille" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Verrou sélectionné" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Verrouiller les proportions" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" -msgstr "Afficher/Masquer les règles" +msgstr "Afficher/cacher les règles" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Aligner sur les repères" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Aligner sur les repères de règles" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" -msgstr "Afficher/masquer la palette de texte" +msgstr "Afficher/cacher la palette de texte" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Changer de thème" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" -msgstr "Afficher/Masquer" +msgstr "Afficher/cacher" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Alterner le style de zoom" @@ -5481,31 +5489,31 @@ msgstr "Alterner le style de zoom" msgid "shortcuts.underline" msgstr "Activer/désactiver le soulignement" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" -msgstr "Annuler" +msgstr "Annuler la dernière action" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Dissocier" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Démasquer" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribuer verticalement" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Réduire le zoom" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Augmenter le zoom" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zoomer sur la sélection" @@ -5524,7 +5532,7 @@ msgstr "Découvrir Entreprise en action" msgid "subscription.current-plan.enterprise" msgstr "Entreprise" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Entreprise" @@ -5536,7 +5544,7 @@ msgstr "Entreprise (essai)" msgid "subscription.current-plan.professional" msgstr "Professionnel" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "Votre abonnement" @@ -5670,293 +5678,293 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Invitation de personnes dans le cadre de l'abonnement Illimité" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "Le paiement n'a pas été finalisé. Réessayez quand vous êtes prêt." -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "" "Le paiement n'a pas été finalisé. Veuillez réessayer. Si le problème " "persiste, contactez-nous : support@penpot.app." -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Abonnement" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "Saisir le code d'activation" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Ajouter une méthode de paiement pour continuer à l'utiliser après l'essai" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "Console d'administration" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Tous les avantages de l'abonnement Professionnel et :" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Tous les avantages de l'abonnement Illimité et :" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "Tous les avantages de l'abonnement Illimité, plus :" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(× %s éditeurs)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Entreprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Entreprise (essai)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "" "Versions à enregistrement automatique pendant 90 jours et récupération de " "fichiers" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Facture mensuelle forfaitaire" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "Contrôle avancé :" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "Sécurité Entreprise :" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Gestion multi-organisations :" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "" "SAML/​​SSO natif et journalisation complète de la piste d'audit pour les " "rapports de conformité." -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "" "Liste blanche de plugins granulaire, incluant notre serveur MCP sécurisé et " "isolé pour la connectivité IA." -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "" "Prise en charge des hiérarchies d'équipes complexes et des " "sous-organisations distinctes." -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Stockage illimité" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Gérer votre abonnement" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Ajouter les détails de paiement" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Ajoutez vos détails de paiement dès maintenant pour que votre abonnement ne " "soit pas interrompu après la période d'essai et pour continuer à soutenir " "notre projet Open Source. Vous n'allez pas être facturé pour le moment." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Ignorer pour le moment et commencer l'essai" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Aidez-nous à nous développer et à faciliter votre essai" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Il y a actuellement %s personne dans vos équipe qui peut être éditeur." msgstr[1] "Il y a actuellement %s personnes dans vos équipe qui peuvent être éditeurs." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Attention : en passant à un abonnement inférieur, le stockage est moins " "important et les sauvegardes et les version d'historique sont plus courtes." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Éditeur" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "" "(Propriétaires, administrateur et éditeurs. Les spectateurs ne sont pas " "comptabilisés comme éditeurs.)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "Vous pouvez réduire votre nombre actuel d'éditeurs. Dans les paramètres de " "l'équipe, modifiez le rôle (d'éditeur/admin en spectateur) des personnes " "qui ne modifient pas vraiment des fichiers." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "" "Facturé après la période d'essai. Aucune carte bancaire n'est requise pour " "le moment." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "7 $ par éditeur/mois × %s" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Appliquer %s à vos équipes" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Conseil : vous pouvez augmenter le nombre de vos utilisateurs dès " "maintenant pour anticiper les invitations. Au-delà de 25 éditeurs dans vos " "équipes, un forfait de 175 $/mois vous sera facturé." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Membre Penpot depuis %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Plus d'informations" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "membre d'organisation / mois" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Autres abonnements Penpot" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "organisation par mois" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "utilisateur / mois" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professionnel" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "" "Versions à enregistrement automatique pendant 7 jours et récupération de " "fichiers" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "" "Toutes les fonctionnalités de conception et de prototypage Support " "communautaire." -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "Contrôle total sur les données et l'infrastructure." -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "Utilisateurs, équipes et fichiers illimités." -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10 Go de stockage" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "" "Nombre d'équipes illimité. Jusqu'à 8 éditeurs dans les équipes dont vous " "êtes propriétaire." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Recommandé" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Votre abonnement" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Commencer l'essai gratuit" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "S'abonner" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "Vous êtes sur l'abonnement %s depuis %s." -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Vous pouvez modifier votre abonnement à tout moment dans la page Abonnement " "des informations de votre compte." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Profitez bien de votre abonnement !" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Merci d'avoir choisi l'abonnement Penpot %s !" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Vous êtes %s !" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Essayez gratuitement pendant 14 jours" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Illimité" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Illimité (essai)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "" "Versions à enregistrement automatique pendant 30 jours et récupération de " "fichiers" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Facture mensuelle plafonnée" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25 Go de stockage" @@ -5985,45 +5993,45 @@ msgstr "" "Si vous souhaitez augmenter cette limite, [mettez votre abonnement à " "niveau|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 invitation sélectionnée" msgstr[1] "%s invitations sélectionnées" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s – Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Fournisseurs de polices – %s – Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Polices – %s – Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projets – %s – Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Rechercher – %s – Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Bibliothèques partagées – %s – Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot – Liberté de conception pour les équipes" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Donnez votre avis – Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "Intégrations - Penpot" @@ -6031,7 +6039,7 @@ msgstr "Intégrations - Penpot" msgid "title.settings.notifications" msgstr "Notifications – Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Paramètres – Penpot" @@ -6039,27 +6047,27 @@ msgstr "Paramètres – Penpot" msgid "title.settings.password" msgstr "Mot de passe – Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil – Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Invitations – %s – Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Membres – %s – Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Paramètres – %s – Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks – %s – Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s – Mode spectateur – Penpot" @@ -6071,51 +6079,51 @@ msgstr "%s – Penpot" msgid "token-actions.detach-token" msgstr "Détacher le token" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Aucun plan de travail trouvé sur la page." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Plan de travail introuvable." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Commentaires (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Ne pas afficher les interactions" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Plein écran" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspecter (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interactions" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interactions (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Copier le lien" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Afficher les interactions" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Afficher les interactions au clic" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Plan du site" @@ -6153,67 +6161,67 @@ msgstr "Suivez notre " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "guide de dépannage WebGL" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "Rendu WebGL désactivé" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "Rendu WebGL activé" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "Le contexte WebGL a été perdu" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "Le contexte WebGL a été restauré" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Le dernier envoi a réussi." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Aligner horizontalement au centre (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Répartir l’espacement horizontal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Aligner à gauche (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Aligner à droite (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Aligner en bas (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Aligner verticalement au centre (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Répartir l’espacement vertical (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Aligner en haut (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Ajouter une bibliothèque" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Toutes les ressources" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Couleurs" @@ -6225,7 +6233,7 @@ msgstr "Ajouter une couleur" msgid "workspace.assets.component-group-options" msgstr "Options de groupe de composants" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Composants" @@ -6233,7 +6241,7 @@ msgstr "Composants" msgid "workspace.assets.components.add-component" msgstr "Ajouter un composant" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s variantes" @@ -6241,13 +6249,13 @@ msgstr "%s variantes" msgid "workspace.assets.create-group" msgstr "Créer un groupe" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Vos éléments seront nommées automatiquement sous la forme « nom du groupe / " "nom de l'élément »" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Supprimer" @@ -6255,11 +6263,11 @@ msgstr "Supprimer" msgid "workspace.assets.delete-group" msgstr "Supprimer le groupe" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "Supprimer les pages" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Dupliquer" @@ -6267,39 +6275,39 @@ msgstr "Dupliquer" msgid "workspace.assets.duplicate-main" msgstr "Dupliquer le principal" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Modifier" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtrer" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Vue en grille" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Grouper" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nom du groupe" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Vue en liste" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "Bibliothèque locale" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Gérer la bibliothèque" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Aucune ressource trouvée" @@ -6307,7 +6315,7 @@ msgstr "Aucune ressource trouvée" msgid "workspace.assets.open-library" msgstr "Ouvrir le fichier de la bibliothèque" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Renommer" @@ -6315,11 +6323,11 @@ msgstr "Renommer" msgid "workspace.assets.rename-group" msgstr "Renommer le groupe" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Chercher des ressources" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Bibliothèque partagée" @@ -6329,43 +6337,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 composant" msgstr[1] "%s composants" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Trier" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Typographies" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Ajouter une typographie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Taille" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Style de police" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Accéder au fichier de bibliothèque de styles à modifier" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Interlettrage" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Interlignage" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformer le texte" @@ -6373,101 +6381,105 @@ msgstr "Transformer le texte" msgid "workspace.assets.ungroup" msgstr "Dissocier" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "Copie de l'image…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "Image copiée dans le presse-papiers" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "Erreur lors de la copie de l'image" +#: src/app/main/ui/workspace/colorpicker.cljs:447 +msgid "workspace.colorpicker.color-picker" +msgstr "Prélever une couleur à l'écran" + #: src/app/main/ui/workspace/colorpicker.cljs:439, src/app/main/ui/workspace/colorpicker.cljs:454 msgid "workspace.colorpicker.color-tokens" msgstr "Tokens de couleur" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Impossible d'imbriquer des composants dans eux-mêmes." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "Certaines copies n'ont pas pu être basculées. Les composants ne peuvent pas " "être imbriqués en eux-mêmes." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Créer une zone" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "Copier les colonnes" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "Copier les lignes" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Créer un tableau" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Fusionner les cellules" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "Coller" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Ajouter une colonne à droite" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Ajouter une colonne à gauche" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Supprimer la colonne" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Supprimer la colonne et le contenu" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Dupliquer la colonne" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Ajouter une ligne en-dessous" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Ajouter une ligne au-dessus" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Supprimer la ligne" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Supprimer la ligne et son contenu" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Dupliquer la ligne" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "Couleur du guide" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "Supprimer le guide" @@ -6475,15 +6487,15 @@ msgstr "Supprimer le guide" msgid "workspace.debug.title" msgstr "Outils de débogage" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Mode focus" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Focus désactivé" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Focus activé" @@ -6495,243 +6507,247 @@ msgstr "Dégradé linéaire" msgid "workspace.gradients.radial" msgstr "Dégradé radial" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Désactiver l’alignement dynamique" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Désactiver l'échelle proportionnelle" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Désactiver l’alignement sur les repères" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Désactiver l’accrochage au pixel" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Désactiver l'alignement sur les repères de règles" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "Désactiver le rendu WebGL (bêta)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Activer l’alignement dynamique" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Activer l'échelle proportionnelle" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Aligner sur les repères" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Activer l’alignement au pixel" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Aligner sur les repères de règles" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "Activer le rendu WebGL (bêta)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "Rechercher" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "Rechercher et remplacer" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" -msgstr "Masquer le nom des plans de travail" +msgstr "Cacher le nom des plans de travail" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" -msgstr "Masquer les commentaires" +msgstr "Cacher les commentaires" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" -msgstr "Masquer les repères" +msgstr "Cacher les repères" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" -msgstr "Masquer la palette de couleurs" +msgstr "Cacher la palette de couleurs" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" -msgstr "Masquer la grille de pixels" +msgstr "Cacher la grille de pixels" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" -msgstr "Masquer les règles" +msgstr "Cacher les règles" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" -msgstr "Masquer la palette de polices" +msgstr "Cacher la palette de polices" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "Verrouiller les guides" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "Connecter" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "Déconnecter" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "Gérer (statut : désactivé)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "Gérer (statut : activé)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Modifier" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Fichier" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Aide et information" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "Serveur MCP" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Préférences" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Vue" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Rétablir" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Tout sélectionner" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Afficher le nom des plans de travail" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "Afficher les commentaires" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Afficher les repères" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Afficher la palette de couleurs" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Afficher la grille de pixels" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Afficher les règles" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Afficher la palette des polices" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Basculer vers le thème sombre" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Basculer vers le thème clair" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Activer le thème système" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Annuler" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "Déverrouiller les guides" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Réinitialiser" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:133 +msgid "workspace.header.retrying" +msgstr "Nouvelle tentative d'enregistrement" + +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Erreur d’enregistrement" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Enregistré" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Enregistrement" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Partager" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Mode spectateur (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoom" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Remplir l’écran" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Ajuster à l’écran" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Zoomer pour montrer tous les éléments" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Plein écran" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zoomer sur la sélection" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Modifier la grille" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Quitter" @@ -6739,7 +6755,7 @@ msgstr "Quitter" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Marge intérieure du bas" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Afficher les options de marge intérieure sur les 4 côtés" @@ -6775,37 +6791,37 @@ msgstr "Terminé" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Montrer" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Montrer la disposition en grille" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 couleur" msgstr[1] "%s couleurs" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "Ajouter la couleur à la bibliothèque" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Il n'y a pas encore de styles de couleur dans votre bibliothèque" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "Aucune couleur récente pour l'instant" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Il n'y a pas encore de styles typographiques dans votre bibliothèque" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Bibliothèque du fichier" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Dernières couleurs utilisées" @@ -6817,77 +6833,101 @@ msgstr "RVBA" msgid "workspace.libraries.colors.save-color" msgstr "Enregistrer le style de couleur" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" -msgstr "Afficher/masquer la palette de couleurs" +msgstr "Afficher/cacher la palette de couleurs" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 composant" msgstr[1] "%s composants" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:864 +msgid "workspace.libraries.connected-libraries" +msgstr "Bibliothèque connectée" + +#: src/app/main/ui/workspace/libraries.cljs:235 +msgid "workspace.libraries.connected-through" +msgstr "Connectée via" + +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Connecté à" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Ou ajouter une parmi celles-ci pour essayer :" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:834 +msgid "workspace.libraries.empty.no-assets" +msgstr "Aucune ressource pour l'instant" + +#: src/app/main/ui/workspace/libraries.cljs:918 +msgid "workspace.libraries.empty.no-connected-libraries" +msgstr "Aucune bibliothèque connectée pour l'instant." + +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "" "Il n'y a pas de bibliothèque partagée dans votre équipe, vous pouvez " "regarder" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "quelques modèles par ici" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Bibliothèque du fichier" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 graphique" msgstr[1] "%s graphiques" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTHÈQUES DANS CE FICHIER" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTHÈQUES" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "MISES À JOUR DE LA BIBLIOTHÈQUE" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Chargement…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Aucune bibliothèque partagée n’a besoin d’être mise à jour" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Aucune correspondance pour « %s »" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Rechercher des bibliothèques partagées" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:849, src/app/main/ui/workspace/libraries.cljs:897 +msgid "workspace.libraries.set-as-tokens-source" +msgstr "Définir comme source des tokens" + +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "BIBLIOTHÈQUES PARTAGÉES" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:674 +msgid "workspace.libraries.shared-library-added" +msgstr "Ajoutée" + +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Connecter librairie" @@ -6911,25 +6951,47 @@ msgstr "Dissocier toutes les ressources" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "Détacher tous les tokens" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:219, src/app/main/ui/workspace/libraries.cljs:263 +msgid "workspace.libraries.token-sets" +msgid_plural "workspace.libraries.token-sets" +msgstr[0] "1 collection" +msgstr[1] "%s collections" + +#: src/app/main/ui/workspace/libraries.cljs:222, src/app/main/ui/workspace/libraries.cljs:267 +msgid "workspace.libraries.token-themes" +msgid_plural "workspace.libraries.token-themes" +msgstr[0] "1 thème" +msgstr[1] "%s thèmes" + +#: src/app/main/ui/workspace/libraries.cljs:216, src/app/main/ui/workspace/libraries.cljs:259 +msgid "workspace.libraries.tokens" +msgid_plural "workspace.libraries.tokens" +msgstr[0] "1 token" +msgstr[1] "%s tokens" + +#: src/app/main/ui/workspace/libraries.cljs:842, src/app/main/ui/workspace/libraries.cljs:889 +msgid "workspace.libraries.tokens-source" +msgstr "Source des tokens" + +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 typographie" msgstr[1] "%s typographies" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Déconnecter librairie" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Mettre à jour" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "Voir tous les changements" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "MISES À JOUR" @@ -6990,9 +7052,9 @@ msgstr "Activer/Désactiver le flou" #: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:138 msgid "workspace.options.blur-options.toggle-more-options" -msgstr "Afficher/masquer plus d'options" +msgstr "Afficher/cacher plus d'options" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "Annuler" @@ -7000,117 +7062,121 @@ msgstr "Annuler" msgid "workspace.options.canvas-background" msgstr "Couleur de fond du canevas" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:75 +msgid "workspace.options.canvas-background-section" +msgstr "Section du fond du canevas" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Tronquer le contenu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Composant" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Note" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Copier" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Créer une note" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Modifier une note" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Principal" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Échanger composant" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Cette librairie ne contient aucun élément pour le moment" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Délié" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Chercher les variantes en conflit" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Ce composant comporte des variantes en conflit. Assurez-vous que chaque " "variante possède une collection de valeurs de propriétés unique." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Chercher des variantes en double" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Certaines variantes possèdent des propriétés et des valeurs identiques" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Ces variantes possèdent des propriétés et des valeurs identiques. Ajustez " "les valeurs afin de pouvoir les extraire." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Cette variante possède des propriétés et des valeurs identiques à celles " "d'une autre variante." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Certaines de ces variantes possèdent des propriétés et des valeurs " "identiques." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Ce composant comporte des variantes dont le nom n'est pas valide. " "Assurez-vous que chaque variante respecte la structure appropriée." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Chercher les variantes non valides" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Le nom de certaines variantes n'est pas valide" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Chercher les variantes non valides" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Le nom de ces variantes n'est pas valide." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Le nom de cette variante n'est pas valide." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Le nom de certaines de ces variantes n'est pas valide." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[propriété]=[valeur], [propriété]=[valeur]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Essayez d'utiliser la structure suivante :" @@ -7151,7 +7217,7 @@ msgstr "" "Les groupes, les tableaux de bord et les dispositions sont considérés " "équivalents." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Comment les variantes restent connectées" @@ -7199,11 +7265,11 @@ msgstr "Haut et bas" msgid "workspace.options.convert-to-typography" msgstr "Créer un style de typographie" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Conception" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exporter" @@ -7211,53 +7277,53 @@ msgstr "Exporter" msgid "workspace.options.export-multiple" msgstr "Exporter la sélection" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Exporter 1 élément" msgstr[1] "Exporter %s éléments" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Ajouter une exportation" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Supprimer l'exportation" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Suffixe" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "Le service d'export est occupé, veuillez réessayer plus tard" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "Export annulé" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "Annulation…" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Exportation terminée" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Exportation…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "L'exportation a échoué" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "L'exportation est d'une lenteur inattendue" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "En attente…" @@ -7277,7 +7343,7 @@ msgstr "Supprimer la couleur de remplissage" msgid "workspace.options.fill.section" msgstr "Section de remplissage" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Ajuster la taille du plan de travail au contenu" @@ -7297,71 +7363,77 @@ msgstr "Début du flux" msgid "workspace.options.flows.flow-starts" msgstr "Début des flux" +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:300, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 +msgid "workspace.options.font-not-available" +msgstr "" +"La police utilisée par ce texte (%s) n'est plus disponible. Téléverser à " +"nouveau la police pour créer un style typographique" + #: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:34 msgid "workspace.options.grid.auto" msgstr "Automatique" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Colonnes" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Couleur" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Gouttière" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Hauteur" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Marge" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Définir par défaut" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Bas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centre" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Gauche" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Droite" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Étirer" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Haut" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Utiliser la valeur par défaut" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Largeur" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Lignes" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Carré" @@ -7373,7 +7445,7 @@ msgstr "Remplissage de groupe" msgid "workspace.options.group-stroke" msgstr "Contour de groupe" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Ajouter un repère" @@ -7381,19 +7453,19 @@ msgstr "Ajouter un repère" msgid "workspace.options.guides.remove-guide" msgstr "Supprimer le repère" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Repères" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" -msgstr "Afficher/Masquer le repère" +msgstr "Afficher/cacher le repère" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Hauteur" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspecter" @@ -7609,115 +7681,119 @@ msgstr "Interactions" msgid "workspace.options.interactions.add-interaction" msgstr "Ajouter une interaction" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Couleur" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Densité plus forte" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Densité plus faible" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Assombrir" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Différence" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Exclusion" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Lumière crue" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Teinte" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Éclaircir" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminosité" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Produit" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Incrustation" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturation" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Superposition" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Lumière tamisée" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "Section du menu du calque" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Afficher/Cacher le calque" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Hauteur max" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Largeur max" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Hauteur min" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Largeur min" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Plus de couleurs" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Plus de couleurs de la bibliothèque" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "Plus de tokens de couleur" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "Aucun préréglage de taille correspondant" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacité" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:99, src/app/main/ui/workspace/sidebar/options/page.cljs:106 +msgid "workspace.options.pixel-grid-color" +msgstr "Couleur de la grille de pixels" + +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototype" @@ -7753,23 +7829,23 @@ msgstr "Section du rayon de bordure" msgid "workspace.options.radius.show-single-corners" msgstr "Afficher les rayons indépendants" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Récentes" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Réessayer" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotation" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Rechercher une police" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "Rechercher un préréglage de taille" @@ -7779,11 +7855,11 @@ msgstr "" "Sélectionnez une forme, un plan de travail ou un groupe pour faire glisser " "une connexion vers un autre plan de travail." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Couleurs sélectionnées" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "Section de sélection des couleurs" @@ -7851,55 +7927,55 @@ msgstr "Afficher/Cacher l'ombre" msgid "workspace.options.show-fill-on-export" msgstr "Afficher dans les exportations" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Montrer en mode spectateur" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Taille" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Tailles prédéfinies" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Bloquer le ratio" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Débloquer le ratio" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Cercle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamant" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Flèche" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Aucune" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Rond" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Carré" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rectangle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triangle" @@ -7907,31 +7983,31 @@ msgstr "Triangle" msgid "workspace.options.stroke-color" msgstr "Couleur du tracé" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "Trait" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "Espace" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Largeur du tracé" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "Bas (fin de bloc)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "Gauche (début en ligne)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "Droite (fin en ligne)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "Haut (début de bloc)" @@ -7939,51 +8015,51 @@ msgstr "Haut (début de bloc)" msgid "workspace.options.stroke.add-stroke" msgstr "Ajouter une couleur du tracé" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centre" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Tirets" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Pointillé" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Intérieur" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Mixte" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Extérieur" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "Contour par côté" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "" "La fonctionnalité de contours par côté n'est disponible que dans le nouveau " "moteur de rendu. Changez de moteur de rendu pour l'utiliser." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Supprimer la couleur du tracé" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Solide" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" -msgstr "Afficher/masquer le contour" +msgstr "Afficher/cacher le contour" #: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:135 msgid "workspace.options.text-options.align-bottom" @@ -8017,11 +8093,11 @@ msgstr "Largeur automatique" msgid "workspace.options.text-options.grow-fixed" msgstr "Fixe" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Interlettrage" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Interlignage" @@ -8071,150 +8147,154 @@ msgstr "" "Utilisez le bouton de lecture dans l’en‑tête pour exécuter la vue du " "prototype." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Largeur" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Axe X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Axe Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Dessiner des nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "Aligné" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "Indépendant" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "Symétrique" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "Comportement des poignées" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Joindre les nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Convertir en coin (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Convertir en courbe (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Fusionner les nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Déplacer les nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Dissocier les nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Aligner les nœuds (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Ouvrir" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Découvrir [plus d'extensions](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Aucune extension installée pour le moment" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Le fichier de définition de l'extension est incorrect." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Vous devez être éditeur pour utiliser cette extension" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/data/plugins.cljs:160 +msgid "workspace.plugins.error.unreachable" +msgstr "L'URL de l'extension est inaccessible." + +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Cette extension n'existe pas ou l'URL n'est pas correcte." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Installer" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Extensions installées" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Gestionnaire d'extensions" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Extensions" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "METTRE À JOUR CETTE EXTENSION" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Cette extension a été modifiée depuis que vous l'avez ouverte. Elle " "souhaite maintenant accéder à :" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Télécharger des fichiers." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Stockez les données dans le navigateur." -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "Lire le contenu de votre presse-papiers." -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "Lire et écrire dans votre presse-papiers." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Lire vos commentaires et réponses." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Lire et modifier vos commentaires et répondre en votre nom." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Lire le contenu des fichiers dont l'utilisateur a accès." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Lire et modifier le contenu des fichiers dont l'utilisateur a accès." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Notez que cette extension est créée par un utilisateur externe. Avant de " @@ -8222,74 +8302,74 @@ msgstr "" "confiance. La confidentialité de vos données et votre sécurité sont " "importantes pour nous. Si vous avez des questions, contactez l'assistance." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Lire vos bibliothèques et composants." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Lire et modifier vos bibliothèques et composants." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "L'EXTENSION « %s » SOUHAITE ACCÉDER À :" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Lire les informations du profil de l'utilisateur." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Liste des extensions" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Supprimer l'extension" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Entrez l'URL d'une extension" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Extensions" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "PAS MAINTENANT" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Envie d'y jeter un œil ? Cela ouvrira un nouveau brouillon pour votre " "équipe actuelle. (Si non, vous pouvez toujours le trouver dans les " "extensions installés dans n'importe quel fichier.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "L'EXTENSION « %s » EST INSTALLÉE POUR VOTRE UTILISATEUR !" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "ESSAYER L'EXTENSION" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Ajouter une disposition flex" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Ajouter une disposition en grille" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Ajouter une disposition" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Créer une variante" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Ajouter une nouvelle propriété" @@ -8301,11 +8381,11 @@ msgstr "Envoyer au fond" msgid "workspace.shape.menu.backward" msgstr "Éloigner" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "Effacer les guides du plan de travail" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Combiner en variantes" @@ -8313,7 +8393,7 @@ msgstr "Combiner en variantes" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Les composants doivent se trouver sur la même page" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Copier" @@ -8357,27 +8437,27 @@ msgstr "Copier en texte" msgid "workspace.shape.menu.create-annotation" msgstr "Créer une note" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Créer un plan de travail depuis la sélection" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Créer un composant" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Créer plusieurs composants" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Couper" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Supprimer" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Supprimer le début du flux" @@ -8389,7 +8469,7 @@ msgstr "Détacher l’instance" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Détacher les instances" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Différence" @@ -8397,15 +8477,15 @@ msgstr "Différence" msgid "workspace.shape.menu.duplicate" msgstr "Dupliquer" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Modifier" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Exclure" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Aplatir" @@ -8417,7 +8497,7 @@ msgstr "Retourner horizontalement" msgid "workspace.shape.menu.flip-vertical" msgstr "Retourner verticalement" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Début du flux" @@ -8429,31 +8509,31 @@ msgstr "Avancer" msgid "workspace.shape.menu.front" msgstr "Amener au premier plan" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Groupe" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" -msgstr "Masquer" +msgstr "Cacher" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" -msgstr "Afficher/Masquer l'interface" +msgstr "Afficher/cacher l'interface" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Intersection" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Bloquer" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Masque" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Coller" @@ -8461,27 +8541,27 @@ msgstr "Coller" msgid "workspace.shape.menu.paste-props" msgstr "Coller les propriétés" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Chemin" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Supprimer la disposition flex" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Supprimer la disposition en grille" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Supprimer la disposition" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Supprimer la propriété" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "La variante doit comporter au moins une propriété" @@ -8505,11 +8585,11 @@ msgstr "Rétablir la variante" msgid "workspace.shape.menu.select-layer" msgstr "Sélectionner le calque" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Montrer" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Afficher dans le panneau des ressources" @@ -8517,7 +8597,7 @@ msgstr "Afficher dans le panneau des ressources" msgid "workspace.shape.menu.show-main" msgstr "Afficher le composant principal" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "Convertir le contour en chemin" @@ -8529,19 +8609,19 @@ msgstr "Supprimer la miniature" msgid "workspace.shape.menu.thumbnail-set" msgstr "Définir comme miniature" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Dissocier" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Union" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Débloquer" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Supprimer le masque" @@ -8557,79 +8637,79 @@ msgstr "Réduire la barre latérale" msgid "workspace.sidebar.expand" msgstr "Ouvrir la barre latérale" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Historique" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Calques" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Composants" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Filtrer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Plans de travail" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Groupes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Images" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Masques" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "Aucune correspondance" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "Remplacer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "Tout remplacer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "Remplacer par…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Rechercher…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "Rechercher et remplacer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "Contenu du texte" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "Noms des calques" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Formes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Textes" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Attributs SVG importés" @@ -8641,7 +8721,7 @@ msgstr "Pages" msgid "workspace.sidebar.sitemap.add-page" msgstr "Ajouter une page" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Plan du site" @@ -8661,6 +8741,10 @@ msgstr "%s thèmes actifs" msgid "workspace.tokens.add-new-theme" msgstr "Ajouter un nouveau thème" +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:106 +msgid "workspace.tokens.add-set" +msgstr "Ajouter une collection" + #: src/app/main/ui/workspace/tokens/sets/context_menu.cljs:62 msgid "workspace.tokens.add-set-to-group" msgstr "Ajouter la collection à ce groupe" @@ -8669,7 +8753,7 @@ msgstr "Ajouter la collection à ce groupe" msgid "workspace.tokens.add-token" msgstr "Ajouter un token : %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Appliqué à" @@ -8689,7 +8773,11 @@ msgstr "Taille de police de base" msgid "workspace.tokens.base-font-size.error" msgstr "La taille de base de police doit avoir une valeur en pixels ou sans unité." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:98 +msgid "workspace.tokens.change-token-source" +msgstr "Changer la source des tokens" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Couleur" @@ -8701,7 +8789,7 @@ msgstr "Copier le chemin du token" msgid "workspace.tokens.create-new-theme" msgstr "Créez votre premier thème dès à présent." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "En créer un." @@ -8709,6 +8797,10 @@ msgstr "En créer un." msgid "workspace.tokens.create-token" msgstr "Créer un token %s" +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:101 +msgid "workspace.tokens.current-tokens-source-tooltip" +msgstr "C'est la source actuelle des tokens" + #: src/app/main/ui/workspace/tokens/management/context_menu.cljs:437 msgid "workspace.tokens.delete" msgstr "Supprimer le token" @@ -8725,7 +8817,7 @@ msgstr "Dupliquer le token" msgid "workspace.tokens.duplicate-group" msgstr "Dupliquer le groupe de tokens" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "copie" @@ -8749,7 +8841,7 @@ msgstr "Modifier le token %s" msgid "workspace.tokens.enter-token-name" msgstr "Entrez le nom du token %s" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "" "Les tokens ne peuvent pas être appliqués pendant l'édition de texte. " @@ -8779,7 +8871,7 @@ msgstr "Prévisualisation :" msgid "workspace.tokens.export.single-file" msgstr "Fichier unique" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Taille de police ou {alias}" @@ -8789,7 +8881,7 @@ msgstr "" "Erreur lors de la définition de la graisse/du style de la police. Ce style " "de police n'existe pas dans la police active" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Font weight (300, Bold, Regular, Italic...) ou {alias}" @@ -8829,7 +8921,7 @@ msgstr "" "Dans un fichier JSON, les clés de premier niveau doivent correspondre aux " "noms de collection des tokens." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Importer des tokens" @@ -8839,17 +8931,17 @@ msgstr "" "L'importation des tokens va remplacer tous vos tokens, collections et " "thèmes actuels." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Inactive" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Cette collection n'est pas active. Changez de thème ou activez cette " "collection pour afficher les modifications dans la fenêtre d'affichage" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Utiliser des tokens individuels" @@ -8869,11 +8961,11 @@ msgstr "Thème" msgid "workspace.tokens.label.theme-placeholder" msgstr "Ajouter un thème (ex : Clair)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Interlettrage ou {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Entrer l'interlignage — multiplicateur, px, % ou {alias}" @@ -8889,11 +8981,11 @@ msgstr "Taille max." msgid "workspace.tokens.min-size" msgstr "Taille min." -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Référence manquante" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Cliquez avec le bouton droit pour afficher les options" @@ -8909,7 +9001,7 @@ msgstr "Aucun thème actif" msgid "workspace.tokens.no-permisions-set" msgstr "Vous devez être éditeur pour activer/désactiver des collections" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Vous devez être éditeur pour utiliser des thèmes" @@ -8921,7 +9013,7 @@ msgstr "Aucune collection n'est définie. Créez-en d'abord une." msgid "workspace.tokens.no-sets-yet" msgstr "Il n'y a pas encore de collections." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Aucun thème." @@ -8933,11 +9025,25 @@ msgstr "Vous n'avez actuellement aucun thème." msgid "workspace.tokens.not-remap" msgstr "Ne pas remapper" +#: src/app/main/data/workspace/libraries.cljs:848 +msgid "workspace.tokens.notifications.source-sets-or-themes-updated" +msgstr "Les collections ou thèmes de la source des tokens ont été modifiés" + #: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:132 msgid "workspace.tokens.num-active-sets" msgstr "%s collections actives" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/colorpicker.cljs:438, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:112 +msgid "workspace.tokens.only-first-fill-or-stroke" +msgstr "" +"Les design tokens ne peuvent s'appliquer qu'au premier remplissage ou " +"contour de la liste." + +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:104 +msgid "workspace.tokens.open-source-new-tab" +msgstr "Ouvrir la source des tokens dans un nouvel onglet" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Valeur initiale : %s" @@ -8949,17 +9055,17 @@ msgstr "Marges intérieures" msgid "workspace.tokens.radius" msgstr "Rayons" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "" "La référence dans {%s} n'est pas valide ou ne fait partie d'aucune " "collection active." -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Entrer un alias de typographie pour un token" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Entrer un alias d'ombre pour un token" @@ -8999,7 +9105,7 @@ msgstr "Renommer le groupe de tokens" msgid "workspace.tokens.rename-group-name-hint" msgstr "Vos tokens seront automatiquement renommés en %s.(suffix).(tokenName)" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Valeur résolue : %s" @@ -9037,27 +9143,27 @@ msgstr "" msgid "workspace.tokens.settings" msgstr "Paramètres des tokens" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Ajouter une ombre" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Flou" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Intérieure" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Supprimer l'ombre" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Portée" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "La valeur de flou ne peut pas être négative" @@ -9065,11 +9171,11 @@ msgstr "La valeur de flou ne peut pas être négative" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "La valeur de la portée ne peut pas être négative" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -9077,11 +9183,19 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "Taille" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:75 +msgid "workspace.tokens.source" +msgstr "Source :" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:311, src/app/main/ui/workspace/tokens/management/context_menu.cljs:335 +msgid "workspace.tokens.stroke-width" +msgstr "Largeur du contour" + +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "Aucune | Majuscules | Minuscules | Première lettre en capitale ou {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through ou {alias}" @@ -9095,35 +9209,39 @@ msgstr "" msgid "workspace.tokens.themes-list" msgstr "Liste des thèmes" +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:80 +msgid "workspace.tokens.this-file" +msgstr "Ce fichier" + #: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:334, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:335 msgid "workspace.tokens.token-description" msgstr "Description" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Sélectionner une famille de polices" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Famille de polices" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Famille de polices ou liste de polices séparées par une virgule (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nom" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "Il existe déjà un token au chemin : %s ou à un préfixe de celui-ci." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "Le nom doit comporter au moins 1 caractère" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " n'est pas un nom de token valide.\n" @@ -9134,11 +9252,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Valeur" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Entrez une valeur ou un alias avec {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENS – %s" @@ -9157,15 +9275,15 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "Le type « %s » n'est pas pris en charge (%s) :" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Utiliser une référence" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "La valeur n'est pas valide" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "Flèche (%s)" @@ -9173,75 +9291,75 @@ msgstr "Flèche (%s)" msgid "workspace.toolbar.assets" msgstr "Ressources" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Palette de couleurs (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Commentaires (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Courbe (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Ellipse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Plan de travail (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Image (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:362 +msgid "workspace.toolbar.label" +msgstr "Outils de dessin" + +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "Ligne (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "Se connecter ici" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP connecté" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Déplacer (%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "Barre de palette" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Chemin (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Extensions (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rectangle (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Texte (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Polices (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Activer/Désactiver la barre d'outils" @@ -9262,11 +9380,11 @@ msgstr "Rendu indisponible. Actualisez pour reprendre l'édition." msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "Actualiser" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Il n’y a aucun changement dans l’historique pour l’instant" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "par %s" @@ -9406,19 +9524,19 @@ msgstr "typographie" msgid "workspace.undo.entry.unknown" msgstr "Opération sur %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Ignorer" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Plus d'information" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Il y a des mises à jour dans les Bibliothèques Partagées" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Mettre à jour" diff --git a/frontend/translations/fr_CA.po b/frontend/translations/fr_CA.po index e5b34259e6..96888d0813 100644 --- a/frontend/translations/fr_CA.po +++ b/frontend/translations/fr_CA.po @@ -1,17 +1,17 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:14+0000\n" -"Last-Translator: Alexis Morin \n" -"Language-Team: French (Canada) " -"\n" +"PO-Revision-Date: 2026-11-09 10:07+0000\n" +"Last-Translator: Alexis Morin \n" +"Language-Team: French (Canada) \n" "Language: fr_CA\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Tu as déjà un compte?" @@ -33,7 +33,7 @@ msgstr "" "Ceci est un service de DÉMONSTRATION. NE PAS UTILISER pour du vrai travail. " "Les projets seront supprimés périodiquement." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Mot de passe oublié?" @@ -41,37 +41,37 @@ msgstr "Mot de passe oublié?" msgid "auth.fullname" msgstr "Nom complet" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Connexion à mon compte" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Se connecter ici." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot est un outil de design gratuit à code source ouvert pour la " "collaboration entre le design et le code" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -79,7 +79,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Nouveau mot de passe" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "Tu fais maintenant partie de %s" @@ -97,7 +97,7 @@ msgstr "" "Un lien de réinitialisation du mot de passe a été envoyé a ta boîte de " "réception." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Tu as bien rejoint l'équipe" @@ -129,7 +129,7 @@ msgstr "Mot de passe oublié?" msgid "auth.recovery-submit" msgstr "Modifier le mot de passe" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Tu n'as pas encore de compte?" @@ -143,7 +143,7 @@ msgstr "" msgid "auth.register-account-title" msgstr "Ton nom" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Créer un compte" @@ -158,7 +158,7 @@ msgstr "" "J'accepte les [conditions d'utilisation](%s) et la [politique de " "confidentialité](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Conditions d'utilisation" @@ -180,93 +180,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Aucune personne trouvée pour @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publier" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Tous les usagers de Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Veux-tu vraiment supprimer ce lien? Cas advenant, il ne sera plus " "disponible à quiconque" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(courant)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Supprimer le lien" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Obtenir le lien" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Lien copié avec succès" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Gérer les permissions" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 page partagée" msgstr[1] "%s pages partagées" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Peut commenter" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Peut inspecter le code" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "N'importe qui avec le lien peut accéder" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Pages partagées" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Le lien de partage apparaîtra ici" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Seulement les membres de l'équipe" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Partager les prototypes" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Tout sélectionner" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Dépublier" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Gestion de l'équipe" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot est conçu pour les équipes. Invite des membres à travailler ensemble " "sur des projets et fichiers" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Collaborer!" @@ -282,47 +282,108 @@ msgstr "Restaurer le fichier" msgid "dashboard.add-file" msgstr "Ajouter un fichier" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Ajouter une bibliothèque partagée" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Changer l'adresse courriel" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "DÉPLACER L'ÉQUIPE" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Déplacer vers :" -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:611 +msgid "dashboard.change-organization-modal.description" +msgstr "" +"Les projets et fichiers resteront disponibles aux membres de l'équipe. " +"L'équipe recevra la configuration de la nouvelle organisation." + +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Sélectionner une organisation" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "Changer l'organisation de l'équipe" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/check_updates.cljs:334 +msgid "dashboard.check-updates.available-message" +msgstr "" +"Les nouvelles versions incluent des améliorations à la performance, des " +"nouvelles fonctionnalités et des correctifs qui maintiennent ton " +"installation à jour." + +#: src/app/main/ui/dashboard/check_updates.cljs:309 +msgid "dashboard.check-updates.available-title" +msgstr "UNE NOUVELLE VERSION DE PENPOT EST DISPONIBLE" + +#: src/app/main/ui/dashboard/check_updates.cljs:341 +msgid "dashboard.check-updates.highlights-title" +msgstr "Nouveautés - Points forts" + +#: src/app/main/ui/dashboard/check_updates.cljs:318 +msgid "dashboard.check-updates.installed-version" +msgstr "Version installée" + +#: src/app/main/ui/dashboard/check_updates.cljs:326 +msgid "dashboard.check-updates.latest-version" +msgstr "Dernière version" + +#: src/app/main/ui/dashboard/check_updates.cljs:235 +msgid "dashboard.check-updates.try-again" +msgstr "Réessayer" + +#: src/app/main/ui/dashboard/check_updates.cljs:230 +msgid "dashboard.check-updates.unable-hint" +msgstr "Vérifier le réseau ou la configuration de l'instance et réessayer." + +#: src/app/main/ui/dashboard/check_updates.cljs:226 +msgid "dashboard.check-updates.unable-message" +msgstr "Cette installation n'a pu rejoindre le service des versions de Penpot." + +#: src/app/main/ui/dashboard/check_updates.cljs:215 +msgid "dashboard.check-updates.unable-title" +msgstr "IMPOSSIBLE DE VÉRIFIER LES MISES À JOUR" + +#: src/app/main/ui/dashboard/check_updates.cljs:265 +msgid "dashboard.check-updates.uptodate-message" +msgstr "Tu utilises la version stable la plus récente :" + +#: src/app/main/ui/dashboard/check_updates.cljs:254 +msgid "dashboard.check-updates.uptodate-title" +msgstr "PENPOT EST À JOUR" + +#: src/app/main/ui/dashboard/check_updates.cljs:371 +msgid "dashboard.check-updates.view-changelog" +msgstr "Voir le journal des modifications" + +#: src/app/main/ui/dashboard/check_updates.cljs:374 +msgid "dashboard.check-updates.view-release-notes" +msgstr "Voir les notes de la version" + +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Vider la corbeille" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copier)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Crée une nouvelle organisation" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Créer une nouvelle équipe" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Épingler cette version" @@ -330,7 +391,7 @@ msgstr "Épingler cette version" msgid "dashboard.default-team-name" msgstr "Ton Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Es-tu certain de supprimer pour toujours tous tes projets et fichiers " @@ -342,42 +403,42 @@ msgstr "" "Es-tu certain de vouloir supprimer %s pour toujours ? Ceci est une action " "irréversible." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s fichiers ont été supprimés avec succès." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Supprimer pour toujours" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Supprimer le projet" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Es-tu certain de vouloir supprimer le projet %s pour toujours ? Tu vas le " "supprimer pour toujours ainsi que tous ses fichiers inclus. Ceci est une " "action irréversible." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s a été supprimé avec succès." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Supprimer l'équipe" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "La corbeille est vide. Les fichiers et projets supprimés s'afficheront ici." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Sera supprimé %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Télécharger le fichier Penpot (.penpot)" @@ -389,19 +450,19 @@ msgstr "Dupliquer" msgid "dashboard.duplicate-multi" msgstr "Dupliquer %s fichiers" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Quand un membre de l'équipe crée un brouillon, il s'affichera ici." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Aucun brouillon." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Quand un membre de l'équipe crée un fichier, il s'affichera ici." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Aucun fichier." @@ -449,23 +510,23 @@ msgstr "Importer un fichier .penpot" msgid "dashboard.empty-project.start" msgstr "Crée des choses incroyables" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "Une erreur s'est produite en supprimant les fichiers." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "Une erreur s'est produite en supprimant le projet %s." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "Une erreur s'est produite en restaurant le fichier %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "Une erreur s'est produite en restaurant les fichiers." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "Une erreur s'est produite en restaurant le projet %s et ses fichiers." @@ -473,37 +534,37 @@ msgstr "Une erreur s'est produite en restaurant le projet %s et ses fichiers." msgid "dashboard.export-binary-multi" msgstr "Télécharger %s fichiers Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exporter les tableaux en PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exporter en PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s de %s éléments sélectionnés" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exporter" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Tu peux ajouter des paramètres d'exportation aux éléments dans les " "propriétés design (au bas du panneau de droite)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Info sur la configuration des exportations dans Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Aucun élément n'a de paramètres d'exportation." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Exporter la sélection" @@ -527,35 +588,35 @@ msgstr "Vue en grille" msgid "dashboard.files-layout.list" msgstr "Vue en liste" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Police de caractères manquante" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Tout ignorer" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Les polices de caractères téléversées apparaîtront ici." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Aucune police de caractères personalisée." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Dès qu'un membre du projet téléverse une police de caractères " "personnalisée, elle apparaîtra ici." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 police ajoutée" msgstr[1] "%s polices ajoutées" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -565,7 +626,7 @@ msgstr "" "**une seule famille de polices**. Les formats suivants sont supportés: " "**TTF, OTF et WOFF** (un seul format est nécessaire)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -575,11 +636,11 @@ msgstr "" "aussi en lire plus sur les [licences de police de " "caractères](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Tout téléverser" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -590,114 +651,154 @@ msgstr "" "plus, nous recommendons l'usage de [Transfonter](https://transfonter.org/) " "pour générer des polices web et corriger les erreurs. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Aller à la console admin" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/grid.cljs:253 +msgid "dashboard.grid.last-modified-at" +msgstr "Dernière modification %s" + +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importer fichiers Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Oups! Nous n'avons pas pu importer ce fichier" +#: src/app/main/ui/dashboard/import.cljs:698 +msgid "dashboard.import.auto-linked-libraries" +msgid_plural "dashboard.import.auto-linked-libraries" +msgstr[0] "1 bibliothèque a été liée automatiquement par nom." +msgstr[1] "%s bibliothèques ont été liées automatiquement par nom." + #: src/app/main/ui/dashboard.cljs:264 msgid "dashboard.import.bad-url" msgstr "L'importation a échoué. Le modèle d'URL est erroné" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:838 +msgid "dashboard.import.confirm-library-links" +msgstr "Confirmer les liens de bibliothèque" + +#: src/app/main/ui/dashboard/import.cljs:814 +msgid "dashboard.import.connect-selected-libraries" +msgstr "Connecter les bibliothèques sélectionnées" + +#: src/app/main/ui/dashboard/import.cljs:288 +msgid "dashboard.import.file-name-label" +msgstr "Nom du fichier" + +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "Nous ne pouvions vérifier ce fichier." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Ce fichier semble endommagé." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Pas tous les fichiers n'ont pu être importés" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Les fichiers suivants contiennent des erreurs :" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Les fichier erronés ne sont pas téléversés." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "Une erreur s'est produite lors du traitement de ce fichier." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 fichier correctement importé." msgstr[1] "%s fichiers correctement importés." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Certains fichiers contenaient des objets invalides qui ont été retirés." +#: src/app/main/ui/dashboard/import.cljs:815 +msgid "dashboard.import.next-file" +msgstr "Fichier suivant" + #: src/app/main/ui/dashboard.cljs:265 msgid "dashboard.import.no-perms" msgstr "Tu n'as pas la permission d'importer dans cette équipe" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Traitement des couleurs" +#: src/app/main/ui/dashboard/import.cljs:431 +msgid "dashboard.import.resolve-libraries" +msgstr "" +"Certaines bibliothèques n'ont pu être liées automatiquement. Sélectionner " +"la bonne bibliothèque pour chacun :" -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Traitement des composantes" +#: src/app/main/ui/dashboard/import.cljs:553 +msgid "dashboard.import.resolve-libraries-summary" +msgstr "Réviser les liens de bibliothèque avant de confirmer :" -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Traitement des médias" +#: src/app/main/ui/dashboard/import.cljs:447 +msgid "dashboard.import.resolve-libraries.connect-to" +msgstr "connecter à" -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Traitement de la page : %s" +#: src/app/main/ui/dashboard/import.cljs:441 +msgid "dashboard.import.resolve-libraries.original-library" +msgstr "bibliothèque originale" -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Traitement des polices de caractères" +#: src/app/main/ui/dashboard/import.cljs:509 +msgid "dashboard.import.summary.linked" +msgstr "liée automatiquement" -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Téléversement des données au serveur (%s/%s)" +#: src/app/main/ui/dashboard/import.cljs:517 +msgid "dashboard.import.summary.manually-linked" +msgstr "liée manuellement" -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Téléversement du fichier : %s" +#: src/app/main/ui/dashboard/import.cljs:525 +msgid "dashboard.import.summary.new" +msgstr "Nouveau" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/import.cljs:546 +msgid "dashboard.import.summary.no-selection" +msgstr "Aucune bibliothèque sélectionnée" + +#: src/app/main/ui/dashboard/import.cljs:522 +msgid "dashboard.import.summary.original" +msgstr "Original" + +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Tu vas supprimer les invitations de :" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Tu vas envoyer à nouveau les invitations de :" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Supprimer les invitations" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Envoyer les invitations à nouveau" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "Tu n'as pas la permission d'inviter des gens à cette équipe, de modifier ou " "supprimer des invitations." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Inviter des gens" +#: src/app/main/ui/dashboard/team.cljs:136 +msgid "dashboard.invite-profile-disabled" +msgstr "Tu n'as pas la permission d'inviter des gens à cette équipe" + #: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:235, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:266 msgid "dashboard.leave-organization" msgstr "Quitter l'organisation" @@ -706,29 +807,29 @@ msgstr "Quitter l'organisation" msgid "dashboard.leave-organization.toast" msgstr "Tu ne fais plus partie de l'organisation %s." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Quitter l'équipe" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliothèques et modèles" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "Voici des bibliothèques et modèles que tu peux ajouter à ton projet" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Explore davantage et découvre comment contribuer" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "" "Un problème est survenu lors de l'importation du modèle. Le modèle n'a pas " "été importé." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliothèques" @@ -736,7 +837,7 @@ msgstr "Bibliothèques" msgid "dashboard.loading-files" msgstr "chargement des fichiers …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "chargement des polices de caractères …" @@ -756,61 +857,65 @@ msgstr "Déplacer %s fichiers vers" msgid "dashboard.move-to-other-team" msgstr "Déplacer vers une autre équipe" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nouveau fichier" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Nouveau fichier" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nouveau projet" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nouveau projet" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Aucun résultat de recherche pour « %s »" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "Tu n'as pas la permission d'ajouter des équipes à tes organisations." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 +msgid "dashboard.no-organizations-yet" +msgstr "Aucune organisation." + +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "Tu ne peux pas créer des équipes au sein l'organisation %s. Pour plus " "d'informations, contacter le propriétaire de l'organisation." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "Tu n'as pas le droit de supprimer des équipes au sein de l'organisation %s. " "Pour plus d'informations, contacter le propriétaire de l'organisation." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "Tu n'as pas le droit de déplacer des équipes au sein l'organisation %s. " "Pour plus d'informations, contacter le propriétaire de l'organisation." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Les projets épinglés sont affichés ici" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notifications" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Ton adresse courriel a été mise à jour" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Ton adresse courriel a été vérifiée avec succès" @@ -822,11 +927,11 @@ msgstr "Paramètres de notification mis à jour" msgid "dashboard.notifications.password-saved" msgstr "Mot de passe sauvegardé!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Voir les notifications" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s membres" @@ -834,22 +939,26 @@ msgstr "%s membres" msgid "dashboard.open-in-new-tab" msgstr "Ouvrir le fichier dans un nouvel onglet" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Options" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Ordre par rôle" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Ordre par statut" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "L'organisation %s a été supprimée." +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 +msgid "dashboard.other-teams" +msgstr "Autres équipes" + #: src/app/main/ui/settings/password.cljs:112, src/app/main/ui/settings/password.cljs:126 msgid "dashboard.password-change" msgstr "Modifier le mot de passe" @@ -870,7 +979,11 @@ msgstr "Tu es maintenant propriétaire de cette équipe." msgid "dashboard.permissions-change.viewer" msgstr "Tu es maintenant visionneur.e sur cette équipe." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 +msgid "dashboard.personal-projects" +msgstr "Projets personnels" + +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Épingler/Désépingler" @@ -886,27 +999,27 @@ msgstr "Impossible d'analyser le manifeste du plugiciel" msgid "dashboard.plugins.try-plugin" msgstr "Essayer le plugiciel : " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "Suppression des fichiers…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "Restauration des fichiers…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "La suppression est inopinément lente" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "Restauration inopinément lente" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projets" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Tu veux supprimer ton compte ?" @@ -914,108 +1027,114 @@ msgstr "Tu veux supprimer ton compte ?" msgid "dashboard.removed-from-team" msgstr "Tu ne fais plus partie de l'équipe « %s »." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "Tu vas restaurer tous tes projets et fichiers. Ça pourrait être long." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Restaurer tous les projets et fichiers" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Tout restaurer" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s fichiers ont été restaurés avec succès." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Restaurer le projet" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Tu vas restaurer le projet %s et tous les fichiers qu'il contient." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Restaurer le projet" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s a été restauré avec succès." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Sauvegarder les paramètres" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Recherche…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Recherche pour « %s »…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Organisations" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "Équipes" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "AJOUTER À L'ORGANISATION" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "Choisir une organisation :" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "« %s » ne permet qu'aux membres d'organisations de joindre ses équipes. Les " "invitations en attente à des gens externes à l'organisation seront " "révoquées." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Les invitations en attente à des usagers externes seront annulées." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Ici tu trouveras toutes les organisations auxquelles tu as le droit de " "créer ou ajouter des équipes." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:519 +msgid "dashboard.select-organization-modal.permission-info-add" +msgstr "" +"Ici tu trouveras les organisations auxquelles tu appartiens où tu peux " +"créer ou ajouter des équipes." + +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "Choisir une organisation :" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "Ajouter une équipe à l'organisation" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Changer la langue d'interface" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Choisir un thème" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot sombre (défaut)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot clair" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Thème du système" @@ -1079,11 +1198,11 @@ msgstr "Sauvegarder les paramètres" msgid "dashboard.settings.notifications.title" msgstr "Notifications" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Voir tous les fichiers" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Historique des versions" @@ -1107,7 +1226,7 @@ msgstr[1] "Les fichiers ont été dupliqués avec succès" msgid "dashboard.success-duplicate-project" msgstr "Le projet a été dupliqué avec succès" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Le fichier a été déplacé avec succès" @@ -1119,184 +1238,184 @@ msgstr "Les fichiers ont été déplacés avec succès" msgid "dashboard.success-move-project" msgstr "Le projet a été déplacé avec succès" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s appartient maintenant à %s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Information de l'équipe" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Membres de l'équipe" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s n'appartient plus à %s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Organisation de l'équipe" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Ajouter à l'organisation" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "Tu n'as pas le droit d'ajouter cette équipe à tes organisations." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Changer l'organisation de l'équipe" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Seules les personnes de ton organisation peuvent être invitées." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "Cette équipe ne fait partie d'aucune organisation" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Retirer une équipe de l'organisation" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Projets de l'équipe" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Ajouter à ton projet" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Thème visuel" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Résultats de recherche" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Les fichiers supprimés resteront dans la corbeille pour" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s jours. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "Ensuite, ils sont supprimés de manière permanente." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Si tu changes d'avis, tu peux les restaurer ou les supprimer de manière " "permanente à partir du menu de chaque fichier." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Écris quelque chose pour lancer une recherche" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Dépublier la bibliothèque" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Sauvegarder les paramètres" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "Tu n'es plus membre de l'organisation %s" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Bêta" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "Le rendu WebGL peut améliorer la performance, mais est en bêta et peut être " "moins stable. Des différences visuelles peuvent apparaître entre le " "canevas, les exportations (SVG/PDF) et le mode visionnement." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Désactivé" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Activé" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Partage ton avis" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Statut" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "Rendu WebGL" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Actif" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "" "Les détails de l'événement sont envoyés à l'activation du lien de rappel " "(webhook)" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Tu peux seulement supprimer ou modifier les liens de rappel que tu as créés." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Type de contenu" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Créer un lien de rappel" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Lien de rappel créé avec succès." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Les liens de rappel (webhooks) permettent à d'autres sites ou applis d'être " "notifiés de certains événements Penpot. Nous envoyons une requête POST à " "chacune des URLs fournies." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Clique le bouton \"Créer un lien de rappel\" pour en créer un." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Aucun lien de rappel (webhook) n'a été créé." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Ton compte" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Adresse courriel" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Ton nom" @@ -1312,11 +1431,11 @@ msgstr "Attention" msgid "ds.component-subtitle" msgstr "Composantes à mettre à jour :" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Permettre" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Annuler" @@ -1336,7 +1455,7 @@ msgstr "Aucun token applicable dans les collections ou thèmes actifs." msgid "ds.inputs.numeric-input.no-matches" msgstr "Aucun résultat trouvé." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Ouvrir la liste de tokens" @@ -1346,6 +1465,12 @@ msgstr "" "Le token {%s} n'est dans aucune collection active ou possède une valeur " "invalide." +#: src/app/main/ui/auth/login.cljs:113 +msgid "errors.account-locked" +msgstr "" +"Trop d'essais erronés. Ton compte a été verrouillé pour %s minutes. " +"Réessayer plus tard." + #: src/app/main/data/auth.cljs:344 msgid "errors.auth-provider-not-allowed" msgstr "Fournisseur d'authentification non permis pour ce profil utilisateur" @@ -1354,15 +1479,15 @@ msgstr "Fournisseur d'authentification non permis pour ce profil utilisateur" msgid "errors.auth-provider-not-configured" msgstr "Fournisseur d'authentification non configuré." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Tu n'es pas authentifié ou la session a expiré." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "La police de caractères %s n'a pu être chargée" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Les polices de caractères %s n'ont pu être chargées" @@ -1374,59 +1499,65 @@ msgstr "Impossible de téléverser le fichier média." msgid "errors.character-limit-exceeded" msgstr "Limite de caractères dépassée" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "" "Impossible d'ajouter la bibliothèque : cela créerait une dépendance " "circulaire" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "L'API presse-papiers est indisponible. Servir Penpot via HTTPS pour activer " "le presse-papiers" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Le navigateur ne peut faire cette opération" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "Accès au presse-papiers refusé. Merci de permettre le presse-papiers dans " "le navigateur pour coller du contenu" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "Une erreur est survenue avec le commentaire" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:586 +msgid "errors.connection-error" +msgstr "" +"Impossible de joindre le serveur. Vérifier la connexion et essayer à " +"nouveau." + +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Désolé! Il est impossible d'ouvrir ce fichier car ce dernier utilise un " "type d'atout Penpot désuet." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "pour que nous puissions t'aider." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Même si Penpot ne supporte plus ce type d'atout, tu peux" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "nous contacter" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Impossible de placer une collection parente à l'intérieur d'elle-même." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Ce courriel est déjà en utilisation" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Ce courriel est déjà validé." @@ -1438,29 +1569,29 @@ msgstr "Tu ne peux pas utiliser ton adresse courriel comme mot de passe" msgid "errors.email-does-not-match-invitation" msgstr "L'adresse courriel ne correspond pas à l'invitation." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Ce domaine n'est pas permis" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "L'adresse courriel « %s » a généré plusieurs rapports de rebond permanent." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "L'adresse courriel « %s » est rapportée comme pourriel ou rebond permanent." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Il sembe que tu ouvres un fichier qui utilise la fonctionalité « %s » mais " "la version actuelle de Penpot ne la supporte pas ou elle a été désactivée." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "La fonction « %s » n'est pas supportée." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Doit contenier un maximum de %s caractères." @@ -1468,7 +1599,7 @@ msgstr "Doit contenier un maximum de %s caractères." msgid "errors.field-min-length" msgstr "Doit contenir au moins 1 caractère." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Champ vide" @@ -1476,25 +1607,25 @@ msgstr "Champ vide" msgid "errors.field-not-all-whitespace" msgstr "Doit contenir un caractère autre qu'un espace." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Il semble y avoir une différence entre les fonctionnalités activées et les " "fonctionnalités du fichier que tu essayes d'ouvrir. Les migrations pour « " "%s » doivent être appliquées avant d'ouvrir le fichier." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "Le nom de la police de caractères ne peut contenir que des lettres, des " "chiffres, des espaces, des traits-d'union, des traits de soulignement et " "des points." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Une erreur est survenue." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Il y a eu une erreur avec le travailleur web en arrière-plan (web worker)." @@ -1510,7 +1641,7 @@ msgstr "Données invalides" msgid "errors.invalid-email" msgstr "Saisis une adresse courriel valide s'il te plaît" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "La confirmation de l'adresse courriel doit correspondre" @@ -1547,11 +1678,11 @@ msgstr "Cette invitation peut avoir été annulée ou est expirée." msgid "errors.ldap-disabled" msgstr "L'authentification LDAP est désactivée." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Tu as atteint le quota « %s ». Contacte le support technique." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Le nombre maximal (%s) de courriels invitables en une seule requête a été " @@ -1569,21 +1700,21 @@ msgstr "Le contenu de l'image ne correspond pas avec l'extension du fichier." msgid "errors.media-type-not-allowed" msgstr "Cette image n'est pas valide." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Le profil invité a tu ses courriels (selon les rapports de pourriels ou un " "haut taux de rebond)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migration en cours" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Seul le créateur de la version peut la vérouiller" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Seul le créateur de la version peut la déverrouiller" @@ -1595,7 +1726,7 @@ msgstr "Un problème est survenu en quittant l'organisation. Merci de réessayer msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "Le propriétaire de l'organisation ne peut pas la quitter." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "Cette organisation n'existe pas" @@ -1607,7 +1738,7 @@ msgstr "La confirmation du mot de passe doit correspondre" msgid "errors.password-too-short" msgstr "Le mot de passe doit contenir au moins 8 caractères" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Données invalide dans le presse-papiers" @@ -1615,7 +1746,7 @@ msgstr "Données invalide dans le presse-papiers" msgid "errors.profile-blocked" msgstr "Le profil est blocké" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Ton profil a éteint les courriels (rapports de pourriel ou haut taux de " @@ -1625,21 +1756,32 @@ msgstr "" msgid "errors.registration-disabled" msgstr "L'enregistrement est actuellement désactivé." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:700 +msgid "errors.save-failed" +msgstr "" +"La sauvegarde automatique ne fonctionne pas dû à une erreur. Contacter le " +"support pour la rapporter et rafraîchir pour continuer à partir de la " +"dernière version sauvegardée." + +#: src/app/main/data/persistence.cljs:447 +msgid "errors.save-retrying" +msgstr "Connexion interrompue. Nouvelle tentative de sauvegarde des modifications." + +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "Le SVG est invalide ou malformé" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Fonctionnalité incompatible détectée « %s »" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Il n'y a pas assez de membres pour quitter l'équipe. Tu veux probablement " "la supprimer." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Le membre que tu tentes d'assigner n'existe pas." @@ -1647,7 +1789,7 @@ msgstr "Le membre que tu tentes d'assigner n'existe pas." msgid "errors.team-leave.only-owner-can-delete" msgstr "Seul le propriétaire de l'organisation peut la supprimer." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "Le propriétaire ne peut pas quitter l'équipe. Tu dois assigner le rôle de " @@ -1663,13 +1805,13 @@ msgstr "" msgid "errors.token-set-already-exists" msgstr "Une collection du même nom existe déjà" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "Impossible de déplacer la collection. Une collection du même nom existe " "déjà à cet emplacement." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Un thème du même nom existe déjà" @@ -1681,13 +1823,13 @@ msgstr "Le thème fait référence à des ensembles non-existants : %s" msgid "errors.tokens.circular-reference" msgstr "Le token a une référence cirulaire" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "La hauteur de ligne dépend de la taille de police. Ajouter une taille de " "police pour obtenir la valeur calculée." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "Ce champ ne peut être vide" @@ -1707,13 +1849,13 @@ msgstr "Erreur d'importation :" msgid "errors.tokens.invalid-color" msgstr "Valeur de couleur invalide : %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "" "Valeur de token invalide : seuls les tokens de type famille de police " "peuvent être référés" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Valeur de graisse invalide : utiliser une valeur numérique (100-950) ou les " @@ -1735,43 +1877,43 @@ msgstr "" "Les noms de tokens doivent contenir seulement des lettres et des chiffres " "séparés par le caractère . et ne doivent pas commencer par le symbole $." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "" "Type d'ombre invalide : seuls « innerShadow » ou « dropShadow » sont " "acceptés" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Valeur de token invalid : seuls none, uppercase, lowercase ou capitalize " "sont acceptés" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "" "Valeur de token invalide : seuls none, underline ou strike-through sont " "acceptés" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "Valeur de token invalide : doit faire référence à un token d'ombre composé." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "" "Valeur de token invalide : doit faire référence à un token typographique " "composé." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Valeur de token invalide : %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Références de token manquantes : " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "" "Le nom de groupe %s entre en conflit avec un token du même nom dans un " @@ -1785,31 +1927,31 @@ msgstr "Aucun token, ensemble ou thème n'a été trouvé dans ce fichier." msgid "errors.tokens.number-too-large" msgstr "Valeur de token invalide. La valeur résultante est trop grande : %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "L'opacité doit être entre 0 et 100 % ou 0 et 1 (ex. 50 % ou 0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Le token s'autoréférence" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "Le flou de l'ombre doit être plus grand ou égal à 0." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "L'étalement de l'ombre doit être plus grand ou égal à 0." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "La largeur du contour doit être plus grande ou égale à 0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Valeur invalide : %s n'est pas permis." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Valeur invalide : les unités ne sont pas permises." @@ -1817,55 +1959,84 @@ msgstr "Valeur invalide : les unités ne sont pas permises." msgid "errors.unexpected-error" msgstr "Une erreur inattendue s'est produite." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Token inconnu" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Cette version est verrouillée" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Cette version est verrouillée et ne peut pas être supprimée par d'autres" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Le fichier est d'une version incompatible" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/auth/recovery.cljs:44, src/app/main/ui/auth/register.cljs:125, src/app/main/ui/settings/password.cljs:42 +msgid "errors.weak-password" +msgstr "Le mot de passe ne remplit pas les exigences" + +#: src/app/main/ui/auth/recovery.cljs:37, src/app/main/ui/auth/register.cljs:119, src/app/main/ui/settings/password.cljs:35 +#, backend +msgid "errors.weak-password.insufficient-digits" +msgstr "Au moins 1 chiffre" + +#: src/app/main/ui/auth/recovery.cljs:38, src/app/main/ui/auth/register.cljs:120, src/app/main/ui/settings/password.cljs:36 +#, backend +msgid "errors.weak-password.insufficient-lowercase" +msgstr "Au moins 1 lettre minuscule" + +#: src/app/main/ui/auth/recovery.cljs:40, src/app/main/ui/auth/register.cljs:122, src/app/main/ui/settings/password.cljs:38 +#, backend +msgid "errors.weak-password.insufficient-special" +msgstr "Au moins 1 caractère spécial" + +#: src/app/main/ui/auth/recovery.cljs:39, src/app/main/ui/auth/register.cljs:121, src/app/main/ui/settings/password.cljs:37 +#, backend +msgid "errors.weak-password.insufficient-uppercase" +msgstr "Au moins 1 lettre majuscule" + +#: src/app/main/ui/auth/recovery.cljs:36, src/app/main/ui/auth/register.cljs:118, src/app/main/ui/settings/password.cljs:34 +#, backend +msgid "errors.weak-password.too-short" +msgstr "Au moins 8 caractères" + +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL a arrêté de fonctionner. Recharger la page pour le réinitialiser" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Oups! Le contexte du canevas a été perdu" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Erreur de connexion, l'URL n'est pas atteignable" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "Erreur de validation de l'URL." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Erreur lors du dernier envoi." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Erreur de validation SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Délai d'attente dépassé" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Erreur innantendue de validation" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Statut inattendu %s" @@ -1885,17 +2056,17 @@ msgstr "Description" msgid "feedback.description-placeholder" msgstr "Raison du commentaire" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Nous sommes heureux que tu sois ici. Si tu as besoin d'aide, merci de " "chercher avant de poster." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Communauté Penpot" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Autres moyens de nous contacter" @@ -1917,11 +2088,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Contacte-nous" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Ici pour répondre à tes questions techniques." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Compte de support X" @@ -1941,15 +2112,39 @@ msgstr "Suggestion" msgid "feedback.type.issue" msgstr "Problème" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Télécharger les fichiers" +#: src/app/main/ui/exports/files.cljs:431 +msgid "files-export-modal.options.detach-libraries.message" +msgstr "Les atouts des bibliothèques liées ne seront pas inclus dans le fichier." + +#: src/app/main/ui/exports/files.cljs:425 +msgid "files-export-modal.options.detach-libraries.title" +msgstr "Traiter les atouts comme des objets de base" + +#: src/app/main/ui/exports/files.cljs:429 +msgid "files-export-modal.options.include-libraries.message" +msgstr "" +"Les fichiers avec des bibliothèques liées seront inclus dans l'export et " +"maintiendront leurs liens. " + +#: src/app/main/ui/exports/files.cljs:423 +msgid "files-export-modal.options.include-libraries.title" +msgstr "Exporter fichier + bibliothèques" + +#: src/app/main/ui/exports/files.cljs:432 +msgid "files-export-modal.options.link-later.message" +msgstr "" +"À l'import, il sera possible de lier des bibliothèques existantes qui ont " +"le même nom." + +#: src/app/main/ui/exports/files.cljs:426 +msgid "files-export-modal.options.link-later.title" +msgstr "Lier les bibliothèques correspondantes à l'import" #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "Une erreur s'est produite" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "Appliquer" @@ -1997,7 +2192,7 @@ msgstr "Aperçu de l'image de remplissage" msgid "inspect.attributes.shadow" msgstr "Ombre" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Taille et position" @@ -2005,43 +2200,31 @@ msgstr "Taille et position" msgid "inspect.attributes.stroke" msgstr "Contour" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centré" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Intérieur" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Extérieur" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Typographie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Famille de polices" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Taille de police" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Style de police" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Interlettrage" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Hauteur de ligne" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "Polices de caractères variées" @@ -2057,11 +2240,11 @@ msgstr "trait barré" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Souligné" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Première lettre en majuscule" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Minuscules" @@ -2073,7 +2256,7 @@ msgstr "Aucune" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Aucune" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Majuscules" @@ -2199,23 +2382,23 @@ msgstr "Valeur résultante :" msgid "inspect.tabs.styles.variants-panel" msgstr "Propriétés des variantes" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "Créer un nouveau token d'accès" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "Clique le bouton « Créer un nouveau token d'accès » pour en générer un." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "Aucun token." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "Tokens d'accès personnel" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "Les tokens d'accès personnel fonctionnent comme une alternative au système " @@ -2300,53 +2483,53 @@ msgstr "La clé MCP expire le %s" msgid "integrations.mcp-key.will-not-expire" msgstr "La clé MCP n'a pas de date d'expiration" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "Le serveur MCP de Penpot permet aux clients MCP d'interagir directement " "avec les fichiers design Penpot." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "Comment configurer les clients MCP" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "Ceci est l'URL serveur nécessaire pour configurer ton client MCP afin qu'il " "se connecte au serveur MCP Penpot." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "Regénérer la clé MCP" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "Clé MCP" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "La clé MCP est nécessaire à la configuration du client MCP" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Statut" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Désactivé" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Activé" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "La clé MCP utilisée pour se connecter au serveur MCP a expiré. Donc, une " "connexion n'a pu être établie." -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "" "Merci de regénérer la clé MCP et mettre ta configuration client à jour avec " @@ -2356,10 +2539,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "Serveur MCP" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "Bêta" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "Nom" @@ -2410,7 +2589,7 @@ msgstr "Régénérer la clé MCP" msgid "integrations.regenerate-mcp-key.title.created" msgstr "Clé MCP régénérée" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "Intégrations" @@ -2428,47 +2607,47 @@ msgstr "Ce token expire le %s" msgid "integrations.token.will-not-expire" msgstr "Ce token n'a pas de date d'expiration" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Tout marquer comme lu" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Raccourcis" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "À propos de Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Accepter" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Actif" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Ajouter" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Ajouter police de caractères" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Ajout en cours..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Admin" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Tous" @@ -2476,17 +2655,17 @@ msgstr "Tous" msgid "labels.and" msgstr "et" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Retour" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Merci d'attendre un peu et réessayer. Nous réalisons des travaux de " "maintenance des serveurs." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Passerelle invalide" @@ -2498,15 +2677,15 @@ msgstr "Flouter" msgid "labels.blur-effects" msgstr "Effets de flou" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Annuler" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Fermer" @@ -2514,7 +2693,7 @@ msgstr "Fermer" msgid "labels.code" msgstr "Code" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Réduire" @@ -2526,15 +2705,15 @@ msgstr "Couleur" msgid "labels.comment" msgstr "Commentaire" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Commentaires" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Communauté" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Communauté et contributions" @@ -2546,19 +2725,19 @@ msgstr "Calculé" msgid "labels.confirm-password" msgstr "Confirmer le mot de passe" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Contacter le support" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Contacte-nous" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continuer" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Tu peux continuer avec un compte Penpot" @@ -2570,23 +2749,23 @@ msgstr "Copier" msgid "labels.copy-color" msgstr "Copier couleur" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Copier lien" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "© Kaleidos, depuis 2019" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Créer" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Créer une nouvelle équipe" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Nom de l'équipe" @@ -2594,7 +2773,7 @@ msgstr "Nom de l'équipe" msgid "labels.current" msgstr "Valeur actuelle" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Survol" @@ -2602,7 +2781,7 @@ msgstr "Survol" msgid "labels.default" msgstr "Valeur par défaut" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Supprimer" @@ -2614,7 +2793,7 @@ msgstr "Supprimer commentaire" msgid "labels.delete-comment-thread" msgstr "Supprimer le fil" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Supprimer l'invitation" @@ -2622,39 +2801,35 @@ msgstr "Supprimer l'invitation" msgid "labels.delete-multi-files" msgstr "Supprimer %s fichiers" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "Vraiment supprimer la photo de profil?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "Supprimer la photo de profil" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Supprimés" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Développeur" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Rejeter" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "Télécharger %s" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "Télécharger" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "Téléchargement du fichier…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Brouillons" @@ -2662,19 +2837,19 @@ msgstr "Brouillons" msgid "labels.duplicate" msgstr "Dupliquer" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Modifier" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Éditeur" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Vide" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Erreur" @@ -2682,11 +2857,11 @@ msgstr "Erreur" msgid "labels.exit" msgstr "Quitter" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Expirée" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exporter" @@ -2698,7 +2873,7 @@ msgstr "Rétroactions désactivées" msgid "labels.feedback-sent" msgstr "Rétroaction envoyée" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2706,63 +2881,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Remplissage" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "Disposition flex" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Famille de polices de caractères" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Styles" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Police de caractères" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Répertoire Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Partage ton avis" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Retour" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Design graphique" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "Disposition en grille" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Centre d'aide" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Aide et formation" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Cacher" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Cacher les commentaires résolus" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importer" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inactif" @@ -2770,51 +2945,51 @@ msgstr "Inactif" msgid "labels.info" msgstr "Info" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Polices installées" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "Intégrations" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Une erreur s'est produite." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "Tu peux réessayer ou contacter le support pour rapporter l'erreur." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Erreur interne" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Invitations" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Langue" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Mise en page" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Centre de formation" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Bibliothèques et modèles" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Chargement…" @@ -2822,7 +2997,7 @@ msgstr "Chargement…" msgid "labels.lock" msgstr "Verrouiller" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Se connecter ou s'inscrire" @@ -2830,19 +3005,19 @@ msgstr "Se connecter ou s'inscrire" msgid "labels.login" msgstr "Se connecter" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Se déconnecter" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Membre" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Membres" @@ -2854,25 +3029,25 @@ msgstr "Mentionner" msgid "labels.new-password" msgstr "Nouveau mot de passe" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Suivant" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "" "Penpot est temporairement indisponible dû à une erreur système. Réessayer " "plus tard." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Tu es à jour! Les notifications de nouveaux commentaires apparaîtront ici." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Aucune invitation en attente." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Réunis ton monde et construisez de grandes choses ensemble." @@ -2880,27 +3055,27 @@ msgstr "Réunis ton monde et construisez de grandes choses ensemble." msgid "labels.no-matches" msgstr "Aucun résultat" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Oups!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notifications" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 fichier" msgstr[1] "%s fichiers" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 tableau" msgstr[1] "%s tableaux" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 projet" @@ -2910,43 +3085,43 @@ msgstr[1] "%s projets" msgid "labels.old-password" msgstr "Ancien mot de passe" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "Ouvrir" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Options" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Autre (spécifier)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Autre" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Propriétaire" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Mot de passe" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "En attente" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Journal des modifications de Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Centre Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Projets épinglés" @@ -2954,31 +3129,31 @@ msgstr "Projets épinglés" msgid "labels.post" msgstr "Envoyer" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Précédent" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Conception de produit ou UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Gestion de produit" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profile" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projets" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Récents" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Référence" @@ -2986,27 +3161,27 @@ msgstr "Référence" msgid "labels.refresh" msgstr "Rafraîchir" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Notes de version" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Recharger la page" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Retirer" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Supprimer le membre" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Renommer" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Renommer l'équipe" @@ -3030,11 +3205,11 @@ msgstr "nouvelle réponse" msgid "labels.reply.thread" msgstr "Répondre" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Renvoyer" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Renvoyer l'invitation" @@ -3042,43 +3217,43 @@ msgstr "Renvoyer l'invitation" msgid "labels.restore" msgstr "Restaurer" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Réessayer" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rôle" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Enregistrer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Rechercher" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Recherche une police" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Choisir une option" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Envoyer" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Envoi…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Nous sommes en période prévue de maintenance des systèmes." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Le service est indisponible" @@ -3086,55 +3261,55 @@ msgstr "Le service est indisponible" msgid "labels.sets" msgstr "Collections" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Paramètres" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Ombre" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Partager" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibliothèques" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Afficher" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Afficher tous les commentaires" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Afficher la liste des commentaires" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Afficher seulement tes mentions" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Afficher seulement tes commentaires" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Sources" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Démarrer" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Statut" @@ -3142,7 +3317,7 @@ msgstr "Statut" msgid "labels.stroke" msgstr "Contour" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Étudiant ou enseignant" @@ -3154,7 +3329,7 @@ msgstr "Styles" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Basculer" @@ -3162,19 +3337,19 @@ msgstr "Basculer" msgid "labels.text" msgstr "Texte" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Thèmes" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutoriels" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Typographie" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Erreur inconnue" @@ -3186,35 +3361,31 @@ msgstr "Déverrouiller" msgid "labels.unpublish-multi-files" msgstr "Dépublier %s fichiers" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Modifier" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Enregistrer le nom d'équipe" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Téléverser" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Téléverser des polices personnalisées" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Téléversement…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "Téléversement du fichier…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variant" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Notes de la version %s" @@ -3222,7 +3393,7 @@ msgstr "Notes de la version %s" msgid "labels.view-only" msgstr "Lecture seule" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Visionneur" @@ -3236,7 +3407,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s avertissement" msgstr[1] "%s avertissements" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Liens de rappel (webhooks)" @@ -3244,11 +3415,11 @@ msgstr "Liens de rappel (webhooks)" msgid "labels.write-new-comment" msgstr "Écrire un nouveau commentaire" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(toi)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Ton compte" @@ -3393,7 +3564,7 @@ msgstr "" "Tu es le seul membre de certaines équipes. Ces équipes, ainsi que leurs " "projets et fichiers, seront supprimées." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "AVANT DE QUITTER L'ORGANISATION" @@ -3401,80 +3572,80 @@ msgstr "AVANT DE QUITTER L'ORGANISATION" msgid "modals.before-leave-organization.warning" msgstr "Toute équipe dont tu es le seul membre sera supprimée." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Grande pichenotte" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Répéter le nouveau courriel" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Nous allons envoyer un courriel à l'adresse actuelle « %s » pour vérifier " "ton identité." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Nouveau courriel" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Changer l'adresse courriel" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Changer d'adresse courriel" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Créer le lien de rappel" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Créer un lien de rappel (webhook)" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL destinataire" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://exemple.com/receptionpost" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Annuler et conserver mon compte" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Oui, supprimer mon compte" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Si tu supprimes ton compte, tu vas perdre tous tes projets et fichiers." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "Cette action est irréversible. Ton compte, abonnement et organisations " "seront supprimés immédiatement. L'accès aux projets et fichiers courants " "sera perdu." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organisations qui seront supprimées" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s membres" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s équipes" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Vraiment supprimer ton compte?" @@ -3534,23 +3705,23 @@ msgstr "Vraiment supprimer %s fichiers?" msgid "modals.delete-file-multi-confirm.title" msgstr "Suppression de %s fichiers" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Vraiment supprimer ce style de police? Il ne chargera pas si il est utilité " "dans un fichier." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Supprimer un style de police" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Vraiment supprimer cette police? Elle ne chargera pas si elle est utilisée " "dans un fichier." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Suppression de la police" @@ -3558,19 +3729,19 @@ msgstr "Suppression de la police" msgid "modals.delete-organization-team-confirm.message" msgstr "Vraiment supprimer cette équipe qui fait partie de l'organisation %s?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Vraiment supprimer cette page?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Supprimer la page" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "Vraiment supprimer les pages sélectionnées ?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "Suppression de pages" @@ -3586,31 +3757,31 @@ msgstr "Vraiment supprimer ce projet?" msgid "modals.delete-project-confirm.title" msgstr "Supprimer le projet" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Supprimer le fichier" msgstr[1] "Supprimer les fichiers" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Activé dans aucun projet" msgstr[1] "Activés dans aucun projet" -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Cette bibliothèque est active ici : " msgstr[1] "Ces bibliothèques sont actives ici : " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Vraiment supprimer ce fichier?" msgstr[1] "Vraiment supprimer ces fichiers?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Suppression d'un fichier" @@ -3630,19 +3801,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Suppression de l'équipe" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Supprimer le membre" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Vraiment supprimer ce membre de l'équipe?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Supprimer le membre d'équipe" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3652,23 +3823,23 @@ msgstr[1] "" "Les atouts déjà en usage dans ces fichiers demeureront là (aucun design ne " "sera brisé)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Supprimer le lien de rappel" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Vraiment supprimer ce lien de rappel?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Supprimer un lien de rappel" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Modifier le lien de rappel" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Modifier un lien de rappel" @@ -3686,15 +3857,15 @@ msgstr "Importer des tokens" msgid "modals.import-library-tokens.title" msgstr "Importer des tokens depuis la bibliothèque?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Envoyer l'invitation" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Adresses courriel, séparées par des virgules" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Certains courriels font déjà partie de l'équipe. Nous inviterons les " @@ -3717,99 +3888,99 @@ msgstr "" "équipe et ils sont tous déjà membres. Pour plus d'informations, contacter " "le propriétaire de l'organisation." -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "Adresses qui ne recevront pas d'invitation" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "Annuler" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "Seuls les membres actuels de l'organisation peuvent être invités." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "Envoyer les invitations" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "Certaines invitations n'ont pu être envoyées" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "Certaines personnes de la liste ne recevront pas d'invitation." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Tu peux inviter des membre à l'équipe pour leur donner accès à ce fichier " "et à tous les fichiers de l'équipe." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Inviter des membres à l'équipe" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Comme tu es le seul membre de cette équipe, l'équipe sera supprimée ainsi " "que tous ses projets et fichiers." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Vraiment quitter l'équipe %s ?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Tu ne peux quitter l'équipe s'il n'y a aucun autre membre pour en devenir " "propriétaire. Tu peux toutefois supprimer l'équipe." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Tu es le propriétaire de cette équipe. Sélectionne un autre membre pour en " "devenir propriétaire avant de quitter." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promouvoir et quitter" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Choisir un membre à promouvoir" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Avant de quitter" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Quitter l'équipe" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Vraiment quitter cette équipe?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Quitter l'équipe" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Tu es propriétaire de certaines équipes de cette organisation. Sélectionner " "un autre membre afin de les promouvoir au rôle de propriétaire." -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Tu es membre unique de certaines équipes. Ces équipes ainsi que leurs " "projets et fichiers seront supprimés." -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "De plus, tu es propriétaire de certaines équipes de cette organisation. " @@ -3829,57 +4000,57 @@ msgstr "" msgid "modals.leave-organization-confirm.title" msgstr "Quitter l'organisation %s?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Déplacer" msgstr[1] "Déplacer" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Vraiment déplacer cette bibliothèque?" msgstr[1] "Vraiment déplacer ces bibliothèques?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Déplacer une bibliothèque" msgstr[1] "Déplacer des bibliothèques" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Taille de pichenotte" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Transférer la propriété" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Si tu transfères la propriété, ton rôle deviendra « Admin » et tu perdras " "des permissions sur cette équipe. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Tu es le propriétaire actuel de cette équipe. Vraiment transférer la " "propriété de l'équipe à « %s » ?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Nouveau propriétaire d'équipe" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publier" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Ta bibliothèque est vide. Vraiment la publier?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publier une bibliothèque vide" @@ -3901,23 +4072,23 @@ msgstr "Vraiment retirer l'équipe « %s » de l'organisation « %s »?" msgid "modals.remove-team-organization.title" msgstr "RETIRER L'ÉQUIPE DE L'ORGANISATION" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Petite pichenotte" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Dépublier" msgstr[1] "Dépublier" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Vraiment dépublier cette bibliothèque?" msgstr[1] "Vraiment dépublier ces bibliothèques?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Dépublier bibliothèque" @@ -3995,38 +4166,38 @@ msgstr "Activer" msgid "nitrate.code-activation.title" msgstr "Activer Nitrate" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "Contacter les ventes" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "" "Pour basculer à cet abonnement, merci de contacter l'équipe des ventes. " "Nous t'aiderons à mettre à jour l'abonnement et faire en sorte que tout " "soit bien configuré." -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Ton organisation sera supprimée." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "" "Le stockage total, l'historique des versions automatiques et la période de " "recouvrement des fichiers seront limités." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "" "Les équipes, projets et fichiers ne feront partie d'aucune organisation " "mais resteront disponibles." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "Lors de la mise à niveau inférieure :" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "Passer au forfait %s?" @@ -4044,15 +4215,15 @@ msgstr "Contacte-nous pour faire l'essai d'Enterprise pour 14 jours :" msgid "nitrate.form.contact-upgrade" msgstr "Contacte-nous pour mettre à niveau vers Enterprise :" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "Contacte-nous pour faire l'essai du forfait Entreprise pour 14 jours :" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "Contacte-nous pour une mise à niveau au forfait Entreprise :" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "" "Tu ne seras pas chargé maintenant. Le paiement sera prélevé à la fin de la " @@ -4074,7 +4245,7 @@ msgstr "" "La pleine liberté du code source libre, avec la gouvernance dont ton " "organisation a besoin" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "" "Tout ce dont les équipes ont besoin pour travailler de manière sécuritaire " @@ -4084,7 +4255,7 @@ msgstr "" msgid "nitrate.form.enterprise.price" msgstr "par membre par mois" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "Essai gratuit de 14 jours" @@ -4100,7 +4271,7 @@ msgstr "Démarrer avec Enterprise" msgid "nitrate.form.subscribe-with-code" msgstr "S'abonner avec un code d'activation" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "Déverrouille des fonctionnalités Enterprise" @@ -4112,7 +4283,7 @@ msgstr "Essai gratuit de 14 jours" msgid "nitrate.modal-success.title" msgstr "Bienvenue à Entreprise!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "Actif jusqu'au %s" @@ -4120,23 +4291,23 @@ msgstr "Actif jusqu'au %s" msgid "nitrate.subscription.renew-success" msgstr "Ton abonnement Nitrate a été renouvelé." -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "Annuler l'abonnement" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "Pour annuler l'abonnement Nitrate, merci de nous contacter au :" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "Renouveler avec code d'activation" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Cette page n'existe pas" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Erreur 404" @@ -4156,11 +4327,11 @@ msgstr "" "Vérifie tes courriels et clique sur le lien pour créer un nouveau mot de " "passe." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "S'inscrire gratuitement" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "Et commence à utiliser Penpot en quelques secondes!" @@ -4168,67 +4339,67 @@ msgstr "Et commence à utiliser Penpot en quelques secondes!" msgid "not-found.made-with-love" msgstr "Fait avec AMOUR et du code source libre" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Tu as déjà demandé l'accès à ce fichier." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Tu as déjà demandé l'accès à ce fichier ou d'autres fichiers ou projets de " "cette équipe." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Tu as déjà demandé l'accès à ce projet ou d'autres projets ou fichiers de " "cette équipe." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Tu as déjà demandé l'accès à ce projet." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "DEMANDER L'ACCÈS" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Souviens-toi que si le propriétaire le permet, tu joindras l'équipe." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Ta demande a bien été envoyée!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Tu n'as pas accès à ce fichier." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Aller à ton Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Si le propriétaire le permet, tu joindras l'équipe." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Fichier Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Tu n'as pas accès à ce projet." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJET" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Pour accéder à ce fichier, tu peux demander au propriétaire de l'équipe." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Pour accéder à ce projet, tu peux demander au propriétaire de l'équipe." @@ -4242,33 +4413,33 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Une nouvelle version est disponible." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "Cette invitation n'est plus disponible." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Invitation supprimée" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Invitation envoyée" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Lien d'invitation copié" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Tu ne peux pas supprimer ton profile. Réassigne tes équipe avant de " "continuer." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profile enregistré!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Courriel de vérification envoyé à %s. Va voir tes courriels!" @@ -4306,7 +4477,7 @@ msgstr "Nom de l'équipe" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Créer l'équipe" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Inviter des membres" @@ -4316,98 +4487,98 @@ msgstr "" "Dévelopeurs, designers, gestionnaires... tous sont bienvenus. Tu pourras " "inviter des gens plus tard." -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Inviter avec le rôle :" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Commençons!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Générer du vrai code à partir de designs" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Créer un système design" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototyper" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Concevoir le UI et UX d'une appli" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Maquettes en fil de fer" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Pour quoi utiliseras-tu Penpot ?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Parle-nous un peu de toi pour que nous puissions adapter Penpot à tes " "besoins. Tes réponses nous aident à prioriser les nouvelles fonctionnalités " "et t'orienter dans ton parcours d'utilisation." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Aide-nous à te connaître" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Lequel de ces outils utilises-tu le plus ?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Quel genre de travail fais-tu ?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Parle-nous de ta job" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Où aimerais-tu commencer ?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1 001 à 5 000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 à 500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 à 100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 à 1 000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "Seulement moi" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5 001+" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Éducation" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Personnel" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Travail" @@ -4489,11 +4660,11 @@ msgstr "Restaurer la configuration par défaut" msgid "restore-shortcuts.restore" msgstr "Restaurer" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Détacher" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Multiple" @@ -4505,35 +4676,35 @@ msgstr "Supprimer la couleur" msgid "settings.select-this-color" msgstr "Sélectionner les éléments avec ce style" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Commentaires" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Aligner en bas" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Aligner au centre horizontal" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Aligner à gauche" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Aligner à droite" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Aligner en haut" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Aligner au centre vertical" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Créer un tableau à partir de la sélection" @@ -4541,55 +4712,55 @@ msgstr "Créer un tableau à partir de la sélection" msgid "shortcuts.bold" msgstr "Basculer la graisse" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Soustraction booléenne" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Exclusion booléenne" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Intersection booléenne" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Union booléenne" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Envoyer à l'arrière-plan" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Envoyer vers l'arrière" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Emmener vers l'avant" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Emmener à l'avant-plan" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Vider l'historique d'actions" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copier" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Copier le lien" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Copier les propriétés" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Créer composant / variant" @@ -4597,55 +4768,55 @@ msgstr "Créer composant / variant" msgid "shortcuts.create-new-project" msgstr "Créer nouveau projet" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Couper" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Zoom arrière" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Supprimer" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Supprimer un nœud" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Détacher un composant" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "Désactiver raccourci" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "Désactivés" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "Raccourci désactivé" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "Flèche" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Courbe" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Ellipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Tableau" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "Ligne" @@ -4653,67 +4824,67 @@ msgstr "Ligne" msgid "shortcuts.draw-nodes" msgstr "Dessiner un chemin" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Chemin" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rectangle" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Texte" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Dupliquer" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "" "Cette combination sert à « %s ». Enregistrer celle-ci annulera le raccourci " "existant." -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "À l'enregistrement ce raccourci reviendra à sa valeur d'origine :" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "" "Cette combinaison de touches est disponible et peut être assignée sans " "conflits." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "Personaliser les raccourcis" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Annuler" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Exporter les éléments" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "Trouver" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "Rechercher et remplacer" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Zoom cadrant tout" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Retourner horizontalement" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Retourner verticalement" @@ -4737,27 +4908,27 @@ msgstr "Accéder aux bibliothèques partagées" msgid "shortcuts.go-to-search" msgstr "Recherche" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Grouper" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribuer horizontalement" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Afficher / cacher l'interface utilisateur" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "Importer/Exporter" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Zoom avant" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Insérer une image" @@ -4765,11 +4936,11 @@ msgstr "Insérer une image" msgid "shortcuts.italic" msgstr "Basculer l'italique" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Joindre des nœuds" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "Appuyer sur la combinaison de touches" @@ -4777,39 +4948,39 @@ msgstr "Appuyer sur la combinaison de touches" msgid "shortcuts.line-through" msgstr "Basculer la biffure" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Convertir en coin" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Convertir en courbe" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Masquer" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Fusionner des nœuds" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Déplacer" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Grand déplacement vers le bas" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Grand déplacement vers la gauche" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Grand déplacement vers la droite" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Grand déplacement vers le haut" @@ -4817,379 +4988,379 @@ msgstr "Grand déplacement vers le haut" msgid "shortcuts.move-nodes" msgstr "Déplacer un nœud" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Déplacement vers le bas" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Déplacement vers la gauche" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Déplacement vers la droite" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Déplacement vers le haut" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Tableau suivant" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "Aucun raccourci désactivé" -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "Aller à Tout pour personnaliser les raccourcis" -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "Aucun raccourci correspondant à ce terme" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Aucun raccourci trouvé" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Opacité 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Opacité 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Opacité 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Opacité 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Opacité 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Opacité 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Opacité 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Opacité 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Opacité 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Opacité 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Sélecteur de couleurs" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Accéder aux commentaires visionneur" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Accéder au tableau de bord" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Accéder à l'inspection visionneur" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Accéder aux intéractions visionneur" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Accéder au mode visionneur" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Accéder à l'espace de travail" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " ou " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "Ce raccourci a été réassigé à %s" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "Page des raccourcis" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Coller" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Coller les propriétés" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "Coller et remplacer" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "Personalisés" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Gestionnaire de plugiciels" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Tableau précédent" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Rétablir" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "" "Après avoir modifié un raccourci, recharger l'onglet Penpot pour appliquer " "les changements." -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Renommer" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "Rétablir à la valeur par défaut" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Rétablir le zoom" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Mettre à l'échelle" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "De base" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Tableau de bord" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Visionneuse" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Espace de travail" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Tout sélectionner" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Sélectionner le calque suivant" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Sélectionner le calque parent" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Sélectionner le calque précédent" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Séparer des nœuds" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "Afficher les mesures" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Afficher / masquer la grille pixel" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Afficher / masquer les raccourcis" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Accrochage de nœuds" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Accrochage à la grille pixel" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Commencer l'édition" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Alignement" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Édition" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Général" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu principal" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modification des calques" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panneaux" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Tracés" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formes" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Textes" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Outils" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Définir l'imagette" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Raccourcis clavier" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Basculer l'alignement dynamique" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Basculer les atouts" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Basculer la palette de couleurs" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "Basculer la visibilité des commentaires" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Basculer le mode focus" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Basculer plein écran" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Afficher / masquer les guides" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Basculer l'historique" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Basculer les calques" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Ajouter / retirer disposition flex" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Ajouter / retirer mise en page grille" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Verrouiller / déverrouiller" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Verrouiller les proportions" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Afficher / masquer les règles" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Accrochage aux guides" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "S'accrocher aux règles" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Basculer la palette de texte" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Changer de thème" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Afficher / masquer" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Basculer le style de zoom" @@ -5197,31 +5368,31 @@ msgstr "Basculer le style de zoom" msgid "shortcuts.underline" msgstr "Basculer le souligné" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Annuler" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Dégrouper" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Démasquer" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribuer verticalement" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Lentille zoom avant" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Lentille zoom arrière" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zoomer à la sélection" @@ -5240,7 +5411,7 @@ msgstr "Voir Enterprise à l'action" msgid "subscription.current-plan.enterprise" msgstr "Enterprise" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Enterprise" @@ -5252,7 +5423,7 @@ msgstr "Enterprise (Essai)" msgid "subscription.current-plan.professional" msgstr "Professionnel" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "Ton abonnement" @@ -5385,284 +5556,284 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Inviter des gens sur le forfait illimité" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "Le paiement ne s'est pas complété. Essayer de nouveau." -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "" "Le paiement n'a pas été complété. Essayer de nouveau. Si le problème " "persiste, contacte-nous : support@penpot.app." -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Abonnement" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "Saisir le code d'activation" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Ajoute une méthode de paiement pour continuer après la période d'essai" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "Console admin" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Tous les bénéfices du forfait professionnel et :" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Tous les bénéfices du forfait illimité et :" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "Tous les avantages du forfait Illimité, plus :" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s éditeurs)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Entreprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Entreprise (essai)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "Sauvegarde automatique des versions et recouvrement de fichiers de 90 jours" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Taux fixe mensuel" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "Contrôle avancé :" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "Sécurité Entreprise :" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Gestion multi-organisation :" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "" "SAML/SSO natif et journaux complets de piste d'audit pour le rapportage de " "compliance." -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "" "Liste d'autorisation granulaire des plugiciels, incluant notre serveur MCP " "pour la connectivité IA." -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "" "Compatibilité pour les équipes à hiérarchies complexes et " "sous-organisations séparées." -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Stockage illimité" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Gérer l'abonnement" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Ajouter les informations de paiement" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Ajoute tes infos de paiement maintenant pour conserver ton forfait après " "l'essai et continue d'encourager notre projet à code source ouvert. Tu ne " "seras pas facturé pour l'instant." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Sauter pour l'instant et commencer la période d'essai" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Aide-nous à croitre et à rendre ton essai plus facile" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Actuellement, tu as %s personne à travers tes équipes qui peuvent éditer." msgstr[1] "Actuellement, tu as %s personnes à travers tes équipes qui peuvent éditer." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Attention : changer vers un forfait inférieur signifie moins de stockage et " "une durée plus courte des copies de sécurité et de l'historique des " "versions." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Éditeurs" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "" "(Propriétaires, admins et éditeurs. Les visionneurs ne comptent pas comme " "éditeurs.)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "Tu ne peux pas réduire le nombre d'éditeurs. Dans les paramètres d'équipe, " "change le rôle (éditeur / admin à visionneur) pour les gens qui n'éditent " "pas les fichiers." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "Facturé après l'essai. Aucune carte de crédit n'est requise pour l'instant." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**%s $** / mois" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Activer le forfait %s pour tes équipes" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Astuce : tu peux augmenter le nombre de sièges pour devancer les " "invitations. Avec plus de 25 éditeurs à travers tes équipes, tu peux " "profiter d'un tarif fixe de 175 $ / mois." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Membre Penpot depuis : %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "En savoir plus" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "membre d'organisation / mois" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Autres forfaits Penpot" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "organisation par mois" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "usager / mois" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professionnel" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7 jours de sauvegarde de versions et recouvrement de fichier" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "" "Toutes les fonctionnalités de design et de prototypage, avec l'assistance " "de la communauté." -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "Contrôle complet sur les données et l'infrastructure." -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "Usagers, équipes et fichiers illimités." -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10 Go de stockage" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Équipes illimitées. Jusqu'à 8 éditeurs à travers tes équipes." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Recommandé" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Ton abonnement" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Commencer l'essai gratuit" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "S'abonner" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "Tu utilises le forfait %s depuis %s." -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Tu peux changer de forfait en tout temps à partir de la page « Abonnement » " "de ton compte." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Profite bien de ton forfait!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Merci d'avoir choisi le forfait Penpot %s!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Tu es %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Essaye gratuitement pour 14 jours" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Illimité" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Illimité (essai)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30 jours de sauvegarde de versions et recouvrement de fichier" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Tarif mensuel fixe de 175 $" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25 Go de stockage" @@ -5687,45 +5858,45 @@ msgstr "" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "Si tu aimerais augmenter cette limite, [bonifie ton forfait|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 invitation sélectionnée" msgstr[1] "%s invitations sélectionnées" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Fournisseurs de polices - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Polices - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projets - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Recherche - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Bibliothèques partagées - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Design libre pour les équipes" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Faire une rétroaction - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "Intégrations - Penpot" @@ -5733,7 +5904,7 @@ msgstr "Intégrations - Penpot" msgid "title.settings.notifications" msgstr "Notifications - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Paramètres - Penpot" @@ -5741,27 +5912,27 @@ msgstr "Paramètres - Penpot" msgid "title.settings.password" msgstr "Mot de passe - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profile - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Invitations - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Membres - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Paramètres - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Liens de rappel HTTP - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Mode visionnement - Penpot" @@ -5773,51 +5944,51 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Détacher du token" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Aucun tableau sur cette page." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Aucun tableau trouvé." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Commentaires (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Ne pas afficher les interactions" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Plein écran" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspecter (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interactions" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interactions (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Copier le lien" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Afficher les interactions" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Afficher les interactions au clic" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Plan de site" @@ -5854,67 +6025,67 @@ msgstr "Consulte notre " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "guide de dépannage WebGL" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "Rendu WebGL désactivé" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "Rendu WebGL activé" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "Le contexte WebGL a été perdu" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "Le contexte WebGL a été récupéré" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Le dernier envoi a réussi." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Aligner horizontalement au centre (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribuer l'espace horizontalement (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Aligner à gauche (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Aligner à droite (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Aligner en bas (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Aligner verticalement au centre (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribuer l'espace verticalement (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Aligner en haut (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Ajouter une bibliothèque" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Tous les atouts" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Couleurs" @@ -5926,7 +6097,7 @@ msgstr "Ajouter couleur" msgid "workspace.assets.component-group-options" msgstr "Options du groupe de composants" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Composants" @@ -5934,7 +6105,7 @@ msgstr "Composants" msgid "workspace.assets.components.add-component" msgstr "Ajouter un composant" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s variants" @@ -5942,13 +6113,13 @@ msgstr "%s variants" msgid "workspace.assets.create-group" msgstr "Créer un groupe" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Les éléments seront renommés « nom de groupe / nom d'élément » " "automatiquement" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Supprimer" @@ -5956,11 +6127,11 @@ msgstr "Supprimer" msgid "workspace.assets.delete-group" msgstr "Supprimer le groupe" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "Supprimer ces pages" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Dupliquer" @@ -5968,39 +6139,39 @@ msgstr "Dupliquer" msgid "workspace.assets.duplicate-main" msgstr "Dupliquer composant principal" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Éditer" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtrer" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Vue grille" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Grouper" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nom du groupe" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Vue liste" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "bibliothèque locale" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Gérer la bibliothèque" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Aucun atout trouvé" @@ -6008,7 +6179,7 @@ msgstr "Aucun atout trouvé" msgid "workspace.assets.open-library" msgstr "Ouvrir fichier bibliothèque" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Renommer" @@ -6016,11 +6187,11 @@ msgstr "Renommer" msgid "workspace.assets.rename-group" msgstr "Renommer le groupe" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Recherche d'atout" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Bibliothèque partagée" @@ -6030,43 +6201,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 composant" msgstr[1] "%s composants" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Ordonner" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Typographies" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Ajouter typographie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Taille" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Style de police" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Accéder au fichier de la bibliothèque du style pour éditer" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Interlettrage" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Interligne" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformation du texte" @@ -6074,15 +6245,15 @@ msgstr "Transformation du texte" msgid "workspace.assets.ungroup" msgstr "Dégrouper" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "Copie de l'image…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "Image copiée au presse-papiers" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "Erreur de copie de l'image" @@ -6090,85 +6261,85 @@ msgstr "Erreur de copie de l'image" msgid "workspace.colorpicker.color-tokens" msgstr "Tokens de couleur" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Les composants ne peuvent pas être imbriqués dans eux-mêmes." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "Certaines copies n'ont pu être interchangées. Les composants ne peuvent pas " "être imbriqués dans eux-mêmes." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Créer une zone" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "Copier les colonnes" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "Copier les rangées" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Créer un tableau" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Fusionner les cellules" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "Coller" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Ajouter 1 colonne à droite" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Ajouter 1 colonne à gauche" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Supprimer la colonne" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Supprimer la colonne et les formes" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Dupliquer la colonne" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Ajouter 1 colonne dessous" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Ajouter 1 colonne dessus" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Supprimer la rangée" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Supprimer la rangée et les formes" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Dupliquer la rangée" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "Couleur du guide" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "Supprimer le guide" @@ -6176,15 +6347,15 @@ msgstr "Supprimer le guide" msgid "workspace.debug.title" msgstr "Outils de débogage" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Mode focus" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Désactiver mode focus" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Activer mode focus" @@ -6196,243 +6367,243 @@ msgstr "Dégradé linéaire" msgid "workspace.gradients.radial" msgstr "Dégradé radial" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Désactiver l'alignement dynamique" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Désactiver la mise à l'échelle proportionnelle" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Désactiver l'accrochage aux guides" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Désactiver l'accrochage aux pixels" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Désactiver l'accrochage aux guides règles" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "Désactiver le rendu WebGL (bêta)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Activer l'alignement dynamique" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Activer la mise à l'échelle proportionnelle" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Accrochage aux guides" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Activer l'accrochage aux pixels" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Accrochage aux règles guides" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "Activer le rendu WebGL (bêta)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "Recherche" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "Rechercher et remplacer" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Masquer le nom des tableaux" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "Masquer les commentaires" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Masquer les guides" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Masquer la palette de couleurs" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Masquer la grille pixel" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Masquer les règles" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Masquer la palette de polices" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "Verrouiller les guides" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "Connecter" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "Déconnecter" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "Gérer (statut : désactivé)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "Gérer (statut : activé)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Éditer" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Fichier" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Aide et info" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "Serveur MCP" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Préférences" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Affichage" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Rétablir" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Tout sélectionner" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Afficher le nom des tableaux" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "Afficher les commentaires" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Afficher les guides" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Afficher la palette de couleurs" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Afficher la grille pixel" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Afficher les règles" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Afficher la palette des polices" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Basculer au mode sombre" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Basculer au mode clair" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Basculer au thème du système" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Défaire" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "Déverrouiller les guides" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Réinitialiser" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Erreur de sauvegarde" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Sauvegardé" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Sauvegarde en cours" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Partager" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Mode visionnement (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoom" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Remplir l'écran" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Ajuster aux dimensions" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Zoom cadrant tout" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Plein écran" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zoom à la sélection" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Éditer la grille" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Quitter" @@ -6440,7 +6611,7 @@ msgstr "Quitter" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Marge interne du bas" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Afficher la marge interne des 4 côtés" @@ -6476,37 +6647,37 @@ msgstr "Terminé" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Situer" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Situer la mise en page grille" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 couleur" msgstr[1] "%s couleurs" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "Ajouter couleur à la bibliothèque" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Aucun style de couleur dans la bibliothèque" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "Aucune couleur récente" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Aucun style de typographie dans la bibliothèque" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Bibliothèque du fichier" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Couleurs récentes" @@ -6518,75 +6689,75 @@ msgstr "RVBA" msgid "workspace.libraries.colors.save-color" msgstr "Enregistrer le style de couleur" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "Basculer la palette de couleurs" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 composant" msgstr[1] "%s composants" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Connecté à" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Ou ajoute celles-ci pour tester :" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "Aucune bibliothèque partagée dans ton équipe, tu peux chercher" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "des modèles ici" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Bibliothèque du fichier" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 graphisme" msgstr[1] "%s graphismes" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTHÈQUES DANS CE FICHIER" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTHÈQUES" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "MISES À JOUR DE BIBLIOTHÈQUES" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Chargement…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Aucune bibliothèque partagée ne requiert de mise à jour" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Aucun résultat trouvé pour « %s »" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Recherche des bibliothèques partagées" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "BIBLIOTHÈQUES PARTAGÉES" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Connecter une bibliothèque" @@ -6610,25 +6781,25 @@ msgstr "Délier tous les atouts" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "Détacher tous les tokens" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 typographie" msgstr[1] "%s typographies" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Déconnecter la bibliothèque" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Mettre à jour" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "voir tous les changements" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "MISES À JOUR" @@ -6694,117 +6865,117 @@ msgstr "Afficher/masquer plus d'options" msgid "workspace.options.canvas-background" msgstr "Arrière-plan du canevas" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Rogner le contenu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Composant" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Annotation" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Copier" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Créer une annotation" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Éditer l'annotation" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Principal" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Permuter composant" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Aucun atout dans cette bibliothèque" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Non-lié" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Localiser les variants en conflit" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Ce composant a des variants en conflit. Assure-toi que chaque variant " "possède des valeurs de propriétés uniques." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Localiser les doublons" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Certains variants ont des propriétés et valeurs identiques" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Ces variants ont des propriétés et valeurs identiques. Ajuster les valeurs " "pour qu'elles puissent être trouvées." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Ce variant a des propriétés et valeurs identiques à un autre variant. " "Ajuster les valeurs pour qu'elles puissent être trouvées." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Certains variants ont des propriétés et valeurs identiques. Ajuster les " "valeurs pour qu'elles puissent être trouvées." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Ce composant a des variants avec des noms invalides. Chaque variant doit " "suivre la structure correcte." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Localiser les variants invalides" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Certains variants ont des noms invalides" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Localiser les variants invalides" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Ces variants ont des noms invalides." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Ce variant a un nom invalide." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Certains variants ont des noms invalides." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[propriété]=[valeur], [propriété]=[valeur]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Utilise la structure suivante :" @@ -6842,7 +7013,7 @@ msgstr "Ont le même niveau de hiérarchie." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Les groupes, tableaux et mises en page sont considérés équivalents." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Comment les variants demeurent connectés" @@ -6890,11 +7061,11 @@ msgstr "Haut et bas" msgid "workspace.options.convert-to-typography" msgstr "Créer un style de typographie" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exporter" @@ -6902,37 +7073,37 @@ msgstr "Exporter" msgid "workspace.options.export-multiple" msgstr "Exporter la sélection" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Exporter 1 élément" msgstr[1] "Exporter %s éléments" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Ajouter une exportation" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Supprimer l'exportation" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Suffixe" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Exportation complétée" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "En cours d'exportation…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "L'exportation a échoué" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Exportation inopinément lente" @@ -6952,7 +7123,7 @@ msgstr "Supprimer le remplissage" msgid "workspace.options.fill.section" msgstr "Remplir la section" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Redimensionner le tableau au contenu" @@ -6976,67 +7147,67 @@ msgstr "Départs de parcours" msgid "workspace.options.grid.auto" msgstr "Auto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Colonnes" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Couleur" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Gouttière" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Hauteur" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Marge" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Définir par défaut" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Bas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centre" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Gauche" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Droite" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Étirer" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Haut" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Utiliser la valeur par défaut" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Largeur" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Rangées" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Carrée" @@ -7048,7 +7219,7 @@ msgstr "Remplissage du groupe" msgid "workspace.options.group-stroke" msgstr "Contour du groupe" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Ajouter un guide" @@ -7056,19 +7227,19 @@ msgstr "Ajouter un guide" msgid "workspace.options.guides.remove-guide" msgstr "Supprimer le guide" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Guides" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Basculer le guide" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Hauteur" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspection" @@ -7284,115 +7455,115 @@ msgstr "Interactions" msgid "workspace.options.interactions.add-interaction" msgstr "Ajouter une interaction" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Couleur" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Densité couleur +" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Densité couleur -" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Obscurcir" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Différence" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Exclusion" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Lumière crue" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Teinte" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Éclaircir" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminosité" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Produit" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Incrustation" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturation" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Superposition" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Lumière tamisée" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "Section du menu des calques" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Basculer la visibilité du calque" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Hauteur max." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Largeur max." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Hauteur min." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Largeur min." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Plus de couleurs" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Plus de couleurs de bibliothèque" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "Plus de tokens de couleur" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "Aucun taille prédéfinie correspondante" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacité" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototype" @@ -7428,23 +7599,23 @@ msgstr "Section du rayon du contour" msgid "workspace.options.radius.show-single-corners" msgstr "Afficher les rayons indépendants" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Récentes" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Relancer" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotation" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Recherche de police" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "Rechercher une taille prédéfinie" @@ -7454,11 +7625,11 @@ msgstr "" "Sélectionner une forme, un tableau ou un groupe pour ensuite glisser une " "connexion à un autre tableau." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Couleurs de la sélection" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "Section des couleurs de sélection" @@ -7526,55 +7697,55 @@ msgstr "Basculer l'ombre" msgid "workspace.options.show-fill-on-export" msgstr "Afficher dans l'exportation" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Afficher en mode visionnement" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Taille" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Tailles prédéfinies" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Verrouiller les proportions" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Déverrouiller les proportions" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Cercle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamant" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Flèche" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Aucun" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Arrondi" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Carré" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rectangle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triangle" @@ -7582,31 +7753,31 @@ msgstr "Triangle" msgid "workspace.options.stroke-color" msgstr "Couleur du contour" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "Trait" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "Gouttière" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Largeur du contour" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "Bas (block end)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "Gauche (inline start)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "Droite (inline end)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "Haut (block start)" @@ -7614,49 +7785,49 @@ msgstr "Haut (block start)" msgid "workspace.options.stroke.add-stroke" msgstr "Ajouter un contour" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centre" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Tirets" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Pointillés" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Intérieur" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Mixte" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Extérieur" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "Contour par côté" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "" "Les contours par côté sont seulement disponibles sous le nouveau moteur de " "rendu." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Supprimer le contour" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Plein" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "Basculer le contour" @@ -7692,11 +7863,11 @@ msgstr "Largeur automatique" msgid "workspace.options.text-options.grow-fixed" msgstr "Fixe" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Interlettrage" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Hauteur de ligne" @@ -7744,208 +7915,208 @@ msgstr "Souligné (%s)" msgid "workspace.options.use-play-button" msgstr "Utiliser le bouton de lecture dans le menu du haut pour voir le prototype." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Largeur" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Axe X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Axe Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Dessiner des nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Connecter les nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Convertir en coin (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Convertir en courbe (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Fusionner les nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Déplacer les nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Séparer les nœuds (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "S'accrocher aux nœuds (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Ouvrir" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Découvrir [d'autres plugiciels](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Aucun plugiciel installé" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Le manifeste du plugiciel est invalide." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Tu dois être un éditeur pour utiliser ce plugiciel" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Le plugiciel n'existe pas ou l'URL est invalide." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Installer" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Plugiciels installés" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Gestionnaire de plugiciels" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Plugiciels" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "MISE À JOUR DU PLUGICIEL" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Ce plugiciel a été modifié depuis sa dernière ouverture. Des accès " "supplémentaires sont demandés :" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Démarrer des téléchargements." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Stocker des données dans le navigateur." -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "Lire les contenus du presse-papiers." -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "Lire et écrire au presse-papiers." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Lire tes commentaires et réponses." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Lire et modifier tes commentaires et répondre en ton nom." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Lire le contenu de fichiers auxquels des usagers ont accès." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Lire et modifier le contenu de fichiers auxquels des usagers ont accès." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Noter que ce plugiciel a été créé par une tierce partie. S'assurer d'y " "faire confiance avant de permettre l'accès. La confidentialité des données " "est importante pour nous. Merci de contacter le support en cas de question." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Lire tes bibliothèques et atouts." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Lire et modifier tes bibliothèques et atouts." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "LE PLUGICIEL « %s » DEMANDE D'ACCÉDER À :" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Lire l'information de l'usager actuel." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Liste des plugiciels" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Supprimer le plugiciel" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Inscrire l'URL d'un plugiciel" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Plugiciels" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "PAS MAINTENANT" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Tu veux jetter un coup d'œil ? Ça ouvrira un nouveau brouillon dans ton " "équipe actuelle. (Autrement, tu peux accéder aux plugiciels installés dans " "n'importe quel fichier.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "LE PLUGICIEL « %s » EST INSTALLÉ POUR TON USAGER!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "ESSAYER LE PLUGICIEL" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Ajouter une disposition flex" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Ajouter une disposition en grille" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Ajouter une disposition" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Créer un variant" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Ajouter une propriété" @@ -7957,11 +8128,11 @@ msgstr "Envoyer à l'arrière-plan" msgid "workspace.shape.menu.backward" msgstr "Envoyer vers l'arrière" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "Effacer les guides des tableaux" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Combiner comme variants" @@ -7969,7 +8140,7 @@ msgstr "Combiner comme variants" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Les composants doivent être sur la même page" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Copier" @@ -8013,27 +8184,27 @@ msgstr "Copier en texte" msgid "workspace.shape.menu.create-annotation" msgstr "Créer une annotation" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Entableauter la sélection" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Créer un composant" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Créer plusieurs composants" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Couper" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Supprimer" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Supprimer le début de parcours" @@ -8045,7 +8216,7 @@ msgstr "Détacher l'instance" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Détacher les instances" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Différence" @@ -8053,15 +8224,15 @@ msgstr "Différence" msgid "workspace.shape.menu.duplicate" msgstr "Dupliquer" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Modifier" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Exclusion" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Aplatir" @@ -8073,7 +8244,7 @@ msgstr "Retourner horizontalement" msgid "workspace.shape.menu.flip-vertical" msgstr "Retourner verticalement" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Début de parcours" @@ -8085,31 +8256,31 @@ msgstr "Emmener vers l'avant" msgid "workspace.shape.menu.front" msgstr "Emmener à l'avant-plan" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Grouper" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Masquer" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Afficher / cacher l'interface utilisateur" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Intersection" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Verrouiller" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Utiliser comme masque" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Coller" @@ -8117,27 +8288,27 @@ msgstr "Coller" msgid "workspace.shape.menu.paste-props" msgstr "Coller les propriétés" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Opérations de chemin" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Supprimer la disposition flex" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Supprimer la disposition grille" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Supprimer la disposition" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Supprimer la propriété" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "Le variant doit avoir au moins une propriété" @@ -8161,11 +8332,11 @@ msgstr "Restaurer le variant" msgid "workspace.shape.menu.select-layer" msgstr "Sélectionner le calque" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Afficher" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Afficher dans le panneau d'atouts" @@ -8173,7 +8344,7 @@ msgstr "Afficher dans le panneau d'atouts" msgid "workspace.shape.menu.show-main" msgstr "Afficher le composant principal" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "Contour à chemin" @@ -8185,19 +8356,19 @@ msgstr "Supprimer l'imagette" msgid "workspace.shape.menu.thumbnail-set" msgstr "Définir en tant qu'imagette" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Dégroupper" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Union" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Déverrouiller" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Retirer le masque" @@ -8213,79 +8384,79 @@ msgstr "Réduire le panneau latéral" msgid "workspace.sidebar.expand" msgstr "Afficher le panneau latéral" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Historique" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Calques" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Composants" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Filtrer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Tableaux" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Groupes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Images" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Masques" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "Aucun résultat" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "Remplacer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "Tout remplacer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "Remplacer avec…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Recherche de calques" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "Rechercher et remplacer" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "Contenu texte" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "Noms des calques" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Formes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Textes" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Attributs SVG importés" @@ -8297,7 +8468,7 @@ msgstr "Pages" msgid "workspace.sidebar.sitemap.add-page" msgstr "Ajouter une page" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Plan du site" @@ -8325,7 +8496,7 @@ msgstr "Ajouter un ensemble à ce groupe" msgid "workspace.tokens.add-token" msgstr "Ajouter un token : %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Appliqué à" @@ -8345,7 +8516,7 @@ msgstr "Taille de police de base" msgid "workspace.tokens.base-font-size.error" msgstr "La taille de police de base doit être une valeur en pixels ou sans unité." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Couleur" @@ -8357,7 +8528,7 @@ msgstr "Copier le chemin du token" msgid "workspace.tokens.create-new-theme" msgstr "Crée le premier thème dès maintenant." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "En créer un." @@ -8381,7 +8552,7 @@ msgstr "Dupliquer le token" msgid "workspace.tokens.duplicate-group" msgstr "Dupliquer le groupe de tokens" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "copier" @@ -8405,7 +8576,7 @@ msgstr "Édition du token %s" msgid "workspace.tokens.enter-token-name" msgstr "Entrer le nom du token %s" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "" "Il n'est pas possible d'appliquer un token pendant l'édition du texte. " @@ -8435,7 +8606,7 @@ msgstr "Aperçu :" msgid "workspace.tokens.export.single-file" msgstr "Un seul fichier" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Taille de police ou {alias}" @@ -8445,7 +8616,7 @@ msgstr "" "Erreur de configuration de la graisse/style de la police. Ce style de " "police n'existe pas pour la police courante" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Graisse de police (300, gras italique, etc.) ou un {alias}" @@ -8485,7 +8656,7 @@ msgstr "" "En un fichier JSON seul. Les clés de premier niveau doivent être les noms " "des ensembles de tokens." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Importation de tokens" @@ -8493,17 +8664,17 @@ msgstr "Importation de tokens" msgid "workspace.tokens.import-warning" msgstr "Importer des tokens écrasera tous les tokens, ensembles et thèmes." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Inactif" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Cet ensemble n'est pas actif. Change de thème ou active cet ensemble pour " "voir les changements dans l'espace de travail" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Tokens individuels" @@ -8523,11 +8694,11 @@ msgstr "Thème" msgid "workspace.tokens.label.theme-placeholder" msgstr "Ajouter un thème (ex. Clair)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Interlettrage ou {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Hauteur de ligne (multiplicateur, px, %) ou {alias}" @@ -8543,11 +8714,11 @@ msgstr "Taille max." msgid "workspace.tokens.min-size" msgstr "Taille min." -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Référence manquante" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Clic droit pour voir les options" @@ -8563,7 +8734,7 @@ msgstr "Aucun thème actif" msgid "workspace.tokens.no-permisions-set" msgstr "Tu dois être un éditeur pour activer / désactiver des ensembles" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Tu dois être un éditeur pour utiliser les thèmes" @@ -8575,7 +8746,7 @@ msgstr "Aucun ensemble n'est défini. Crée-en un pour les assigner à un thème msgid "workspace.tokens.no-sets-yet" msgstr "Aucun ensemble n'existe." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Aucun thème n'existe." @@ -8591,7 +8762,7 @@ msgstr "Ne pas remapper" msgid "workspace.tokens.num-active-sets" msgstr "%s ensembles actifs" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Valeur d'origine : %s" @@ -8603,17 +8774,17 @@ msgstr "Marges intérieures" msgid "workspace.tokens.radius" msgstr "Rayon" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "" "La référence dans {%s} est invalide ou ne fait partie d'aucun ensemble " "actif." -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Entrer un alias de token typographique" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Entrer un alias de token d'ombre" @@ -8653,7 +8824,7 @@ msgstr "Renommer le groupe de tokens" msgid "workspace.tokens.rename-group-name-hint" msgstr "Les tokens seront automatiquement renommés à %s.(suffixe).(nomDeToken)" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Valeur calculée : %s" @@ -8691,27 +8862,27 @@ msgstr "" msgid "workspace.tokens.settings" msgstr "Paramètres des tokens" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Ajouter une ombre" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Flou" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Position de l'ombre" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Supprimer l'ombre" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Étalement" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "La valeur de flou ne peut être négative" @@ -8719,11 +8890,11 @@ msgstr "La valeur de flou ne peut être négative" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "La valeur d'étalement ne peut être négative" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -8731,11 +8902,11 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "Taille" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize ou {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through ou {alias}" @@ -8753,31 +8924,31 @@ msgstr "Liste des thèmes" msgid "workspace.tokens.token-description" msgstr "Description" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Sélectionner une famille de polices de caractères" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Famille de polices" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Famille de police ou liste de polices séparées par une virgule (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nom" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "Un token existe déjà au chemin : %s" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "Le nom doit être au moins 1 caractère" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " n'est pas un nom de token valide.\n" @@ -8788,11 +8959,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Valeur" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Entrer une valeur ou un alias avec {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENS - %s" @@ -8808,15 +8979,15 @@ msgstr "L'importation a réussi. Certains tokens n'ont pas été inclus." msgid "workspace.tokens.unknown-token-type-section" msgstr "Le type « %s » n'est pas supporté (%s)" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Utiliser une référence" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "La valeur n'est pas valide" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "Flèche (%s)" @@ -8824,75 +8995,71 @@ msgstr "Flèche (%s)" msgid "workspace.toolbar.assets" msgstr "Atouts" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Palette de couleurs (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Commentaires (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Courbe (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Ellipse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Tableau (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Image (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "Ligne (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "Se connecter ici" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP connecté" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Déplacer (%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "Barre de palettes" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Chemin (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Plugiciels (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rectangle (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Texte (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Typographies (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Basculer la barre d'outils" @@ -8913,11 +9080,11 @@ msgstr "Rendu indisponible. Recharger la page pour restaurer l'édition." msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "Recharger" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Il n'y a aucun changement à l'historique" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "par %s" @@ -9057,19 +9224,19 @@ msgstr "typographie" msgid "workspace.undo.entry.unknown" msgstr "Opération sur %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Ignorer" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Plus d'info" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Mises à jour dans les bibliothèques partagées" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Mettre à jour" @@ -9159,3 +9326,355 @@ msgstr "" #: src/app/main/ui/workspace/sidebar/versions.cljs:462 msgid "workspace.versions.warning.text" msgstr "Les versions auto-sauvegardées seront conservées pendant %s jours." + +#: src/app/main/ui/dashboard/sidebar.cljs:228 +msgid "dashboard.clear-search" +msgstr "Effacer la recherche" + +#: src/app/main/ui/dashboard/fonts.cljs:442, src/app/main/ui/dashboard/fonts.cljs:443 +msgid "dashboard.fonts.delete-variant" +msgstr "Supprimer le style de police" + +#: src/app/main/ui/dashboard/import.cljs:625 +msgid "dashboard.import.check.message" +msgstr "" +"Nous nous assurons que chaque fichier peut être importé. Ça ne prend qu'un " +"moment." + +#: src/app/main/ui/dashboard/import.cljs:624 +msgid "dashboard.import.check.title" +msgstr "Vérification des fichiers..." + +#: src/app/main/ui/dashboard/import.cljs:600 +msgid "dashboard.import.importing.done" +msgid_plural "dashboard.import.importing.done" +msgstr[0] "1 complété" +msgstr[1] "%s complétés" + +#: src/app/main/ui/dashboard/import.cljs:637 +msgid "dashboard.import.importing.message" +msgstr "Reconstruction des pages, composants et atouts de ton projet." + +#: src/app/main/ui/dashboard/import.cljs:598 +msgid "dashboard.import.importing.title" +msgid_plural "dashboard.import.importing.title" +msgstr[0] "Importation de 1 fichier" +msgstr[1] "Importation de %s fichiers" + +#: src/app/main/ui/dashboard/import.cljs:634 +msgid "dashboard.import.queued.message" +msgstr "" +"Les fichiers sont téléversés. L'importation commencera dès qu'un espace se " +"libère." + +#: src/app/main/ui/dashboard/import.cljs:633 +msgid "dashboard.import.queued.title" +msgstr "En attente de commencer..." + +#: src/app/main/ui/dashboard/import.cljs:628 +msgid "dashboard.import.ready.message" +msgstr "Les fichiers peuvent être importés. Continue de les téléverser." + +#: src/app/main/ui/dashboard/import.cljs:627 +msgid "dashboard.import.ready.title" +msgstr "Fichiers prêts" + +#: src/app/main/ui/dashboard/import.cljs:331 +msgid "dashboard.import.status.ready" +msgstr "Prêt" + +#: src/app/main/ui/dashboard/import.cljs:568 +msgid "dashboard.import.steps.check" +msgstr "Vérification" + +#: src/app/main/ui/dashboard/import.cljs:570 +msgid "dashboard.import.steps.import" +msgstr "Importation" + +#: src/app/main/ui/dashboard/import.cljs:573 +msgid "dashboard.import.steps.label" +msgstr "Étapes d'importation" + +#: src/app/main/ui/dashboard/import.cljs:569 +msgid "dashboard.import.steps.upload" +msgstr "Téléversement" + +#: src/app/main/ui/dashboard/import.cljs:1130 +msgid "dashboard.import.title" +msgstr "Importer un fichier" + +#: src/app/main/ui/dashboard/import.cljs:631 +msgid "dashboard.import.upload.message" +msgstr "" +"Envoi des fichiers. Garder cette boîte de dialogue ouverte jusqu'à ce que le " +"téléversement soit terminé." + +#: src/app/main/ui/dashboard/import.cljs:630 +msgid "dashboard.import.upload.title" +msgstr "Téléversement des fichiers..." + +#: src/app/main/data/exports/assets.cljs:64, src/app/main/data/exports/assets.cljs:93 +msgid "errors.export-failed" +msgstr "L'exportation a échoué. Réessaye de nouveau." + +#: src/app/main/data/exports/assets.cljs:372 +msgid "errors.export-too-large" +msgstr "Trop grand pour être exporté, essaye une échelle plus petite" + +#: src/app/main/errors.cljs:886 +msgid "errors.profile-props-too-large" +msgstr "" +"La configuration de ton profil est trop lourde pour sauvegader. Supprime des " +"plugiciels et essaye à nouveau." + +#: src/app/main/errors.cljs:691 +msgid "errors.save-still-retrying" +msgstr "" +"Les changements n'ont pu être sauvegardés car Penpot ne peut joindre le " +"serveur. Ils seront maintenus et Penpot tentera de les sauvegarder. " +"Maintiens cet onglet ouvert jusqu'à ce qu'ils soient sauvegardés." + +#: src/app/main/data/workspace/tokens/errors.cljs:62 +msgid "errors.tokens.division-by-zero" +msgstr "Valeur de token invalide : division par zéro" + +#: src/app/main/data/workspace/tokens/errors.cljs:74 +msgid "errors.tokens.function-argument-out-of-range" +msgstr "Valeur de token invalide : l'argument %s() est hors de portée" + +#: src/app/main/ui/jobs/progress.cljs:32 +msgid "jobs.progress.stage.manifest" +msgstr "Lecture de l'archive" + +#: src/app/main/data/workspace/tokens/errors.cljs:66 +msgid "errors.tokens.incompatible-units" +msgstr "Valeur de token invalide : les unités %s ne peuvent être combinées" + +#: src/app/main/data/workspace/tokens/errors.cljs:70 +msgid "errors.tokens.unknown-function" +msgstr "Valeur de token invalide : fonction inconnue %s" + +#: src/app/main/ui/exports/files.cljs:392 +msgid "files-download-modal.unsaved-changes" +msgstr "" +"Quelques-uns des derniers changements ne sont pas encore sauvegardés, alors " +"ce téléchargement ne les inclura pas." + +#: src/app/main/ui/exports/files.cljs:467 +msgid "files-export-modal.cancel-export" +msgstr "Annuler l'exportation" + +#: src/app/main/ui/exports/files.cljs:471 +msgid "files-export-modal.done" +msgstr "Terminé" + +#: src/app/main/ui/exports/files.cljs:210 +msgid "files-export-modal.export-error" +msgstr "Impossible d'exporter : %s" + +#: src/app/main/ui/exports/files.cljs:246 +msgid "files-export-modal.exported.message" +msgstr "Vérifier le dossier des téléchargements." + +#: src/app/main/ui/exports/files.cljs:239 +msgid "files-export-modal.exported.title" +msgid_plural "files-export-modal.exported.title" +msgstr[0] "1 fichier exporté" +msgstr[1] "%s fichiers exportés" + +#: src/app/main/ui/exports/files.cljs:245 +msgid "files-export-modal.exporting.message" +msgstr "Chaque fichier se télécharge dès qu'il est prêt." + +#: src/app/main/ui/exports/files.cljs:238 +msgid "files-export-modal.exporting.title" +msgid_plural "files-export-modal.exporting.title" +msgstr[0] "Exportation de 1 fichier" +msgstr[1] "Exportation de %s fichiers" + +#: src/app/main/ui/exports/files.cljs:430 +msgid "files-export-modal.options.merge-libraries.message" +msgstr "" +"Le fichier sera exporté avec toutes les bibliothèques d'atouts externes " +"intégrés au fichier." + +#: src/app/main/ui/exports/files.cljs:424 +msgid "files-export-modal.options.merge-libraries.title" +msgstr "Inclure les bibliothèques d'atouts dans le fichier" + +#: src/app/main/ui/exports/files.cljs:179 +msgid "files-export-modal.status.exported" +msgstr "Exporté" + +#: src/app/main/ui/exports/files.cljs:186 +msgid "files-export-modal.status.waiting" +msgstr "En attente" + +#: src/app/main/ui/exports/files.cljs:381 +msgid "files-export-modal.title" +msgstr "Exporter fichier" + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:113 +msgid "import-shortcuts.imported" +msgstr "Importé" + +#: src/app/main/ui/settings/shortcuts.cljs:511, src/app/main/ui/settings/shortcuts.cljs:513 +msgid "import-shortcuts.invalid-file" +msgstr "Ce fichier n'est pas une exportation de raccourcis valide." + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:153 +msgid "import-shortcuts.skipped-entries" +msgstr "" +"Ces entrées ne peuvent être importées : elles ne correspondent à aucun " +"raccourci Penpot ou combinaison de touches réservées." + +#: src/app/main/ui/inspect/code.cljs:322, src/app/main/ui/inspect/code.cljs:365 +msgid "inspect.code.expand-panel" +msgstr "Agrandir le panneau" + +#: src/app/main/ui/exports/files.cljs:223 +msgid "jobs.export-cancelled" +msgstr "Exportation annulée" + +#: src/app/main/data/imports.cljs:376 +msgid "jobs.import-cancelled" +msgstr "Importation annulée" + +#: src/app/main/ui/jobs/progress.cljs:26 +msgid "jobs.progress.stage.colors" +msgstr "Traitement des couleurs" + +#: src/app/main/ui/jobs/progress.cljs:25 +msgid "jobs.progress.stage.components" +msgstr "Traitement des composants" + +#: src/app/main/ui/jobs/progress.cljs:21 +msgid "jobs.progress.stage.files" +msgstr "Traitement des fichiers" + +#: src/app/main/ui/jobs/progress.cljs:23 +msgid "jobs.progress.stage.media" +msgstr "Traitement des médias" + +#: src/app/main/ui/jobs/progress.cljs:31 +msgid "jobs.progress.stage.metadata" +msgstr "Lecture des métadonnées" + +#: src/app/main/ui/jobs/progress.cljs:22 +msgid "jobs.progress.stage.pages" +msgstr "Traitement des pages" + +#: src/app/main/ui/jobs/progress.cljs:30 +msgid "jobs.progress.stage.relations" +msgstr "Traitement des relations de bibliothèque" + +#: src/app/main/ui/jobs/progress.cljs:29 +msgid "jobs.progress.stage.storage-objects" +msgstr "Traitement des objets de stockage" + +#: src/app/main/ui/jobs/progress.cljs:24 +msgid "jobs.progress.stage.thumbnails" +msgstr "Traitement des imagettes" + +#: src/app/main/ui/jobs/progress.cljs:28 +msgid "jobs.progress.stage.tokens" +msgstr "Traitement des tokens" + +#: src/app/main/ui/jobs/progress.cljs:27 +msgid "jobs.progress.stage.typographies" +msgstr "Traitement des polices de caractères" + +#: src/app/main/ui/jobs/progress.cljs:34 +msgid "jobs.progress.stage.unknown" +msgstr "Travail en cours" + +#: src/app/main/ui/jobs/progress.cljs:33 +msgid "jobs.progress.stage.upload" +msgstr "Téléversement du fichier…" + +#: src/app/main/ui/dashboard/import.cljs:328, src/app/main/ui/exports/files.cljs:103 +#, unused +msgid "jobs.queued" +msgstr "En attente d'un créneau de travail" + +#: src/app/main/ui/dashboard/sidebar.cljs:546 +msgid "labels.check-for-updates" +msgstr "Vérifier pour une mise à jour" + +#: src/app/main/ui/dashboard/sidebar.cljs:545 +msgid "labels.checking-for-updates" +msgstr "Vérification d'une mise à jour…" + +#: src/app/main/ui/viewer/thumbnails.cljs:96 +msgid "labels.expand" +msgstr "Agrandir" + +#: src/app/main/ui/static.cljs:367 +msgid "labels.nitrate-not-configured.desc-message" +msgstr "" +"Cette instance nécessite une étape de configuration incluse avec la dernière " +"version." + +#: src/app/main/ui/static.cljs:370 +msgid "labels.nitrate-not-configured.learn-more" +msgstr "Va voir nos" + +#: src/app/main/ui/static.cljs:365 +msgid "labels.nitrate-not-configured.main-message" +msgstr "Penpot ne peut pas encore démarrer." + +#: src/app/main/ui/static.cljs:375 +msgid "labels.nitrate-not-configured.technical-guide" +msgstr "guide technique" + +#: src/app/main/ui/dashboard/import.cljs:808 +msgid "labels.skip" +msgstr "Sauter" + +#: src/app/main/ui/static.cljs:562 +msgid "labels.sso-error.desc-message" +msgstr "" +"La connexion via le fournisseur d'identité n'a pu se compléter. Il est " +"possible que le fournisseur soit indisponible ou que le compte ne figure pas " +"encore au répertoire." + +#: src/app/main/ui/static.cljs:563 +msgid "labels.sso-error.retry" +msgstr "Réessayer" + +#: src/app/main/ui/static.cljs:561 +msgid "labels.sso-error.title" +msgstr "Connexion impossible %s" + +#: src/app/main/ui/dashboard/organization_team_switch.cljs:657 +msgid "labels.team-management" +msgstr "Gestion des équipes" + +#: src/app/main/ui/mcp_authorize.cljs:155 +msgid "mcp.oauth.access" +msgstr "" +"Ce client peut lire et modifier les designs via les connexions MCP établies " +"depuis Penpot." + +#: src/app/main/ui/mcp_authorize.cljs:153 +msgid "mcp.oauth.account" +msgstr "Tu es connecté en tant que %s." + +#: src/app/main/ui/mcp_authorize.cljs:162 +msgid "mcp.oauth.approve" +msgstr "Autoriser l'accès" + +#: src/app/main/ui/mcp_authorize.cljs:133 +msgid "mcp.oauth.cancelled" +msgstr "Connexion annulée" + +#: src/app/main/ui/mcp_authorize.cljs:157 +msgid "mcp.oauth.enable" +msgstr "" +"Autoriser l'accès permet à l'intégration MCP et crée un token MCP au besoin." + +#: src/app/main/ui/mcp_authorize.cljs:137 +msgid "mcp.oauth.failed" +msgstr "" +"Cette requête a expiré, a déjà été utilisée ou n'a pu être complétée. " +"Démarrer la connexion de nouveau à partir du client MCP." diff --git a/frontend/translations/gl.po b/frontend/translations/gl.po index e65047bed4..40041c532a 100644 --- a/frontend/translations/gl.po +++ b/frontend/translations/gl.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2026.10.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Xa tes unha conta?" @@ -29,7 +29,7 @@ msgstr "" "Este é un servizo de DEMOSTRACIÓN. NON O UTILICES para traballos reais, os " "proxectos eliminanse periódicamente." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Esqueciches o teu contrasinal?" @@ -37,27 +37,27 @@ msgstr "Esqueciches o teu contrasinal?" msgid "auth.fullname" msgstr "Nome completo" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Entra aquí" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "Gitlab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -77,7 +77,7 @@ msgstr "Perfil sen verificar, valida o perfil antes de continuar." msgid "auth.notifications.recovery-token-sent" msgstr "Enviouse ó teu correo electrónico un enlace co que recuperar o contrasinal." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Unícheste ao equipo" @@ -109,11 +109,11 @@ msgstr "Esqueceches o teu contrasinal?" msgid "auth.recovery-submit" msgstr "Cambiar o teu contrasinal" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Ainda non tes unha conta?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Crea unha conta" @@ -121,109 +121,109 @@ msgstr "Crea unha conta" msgid "auth.register-title" msgstr "Crea unha conta" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Condicións de servizo" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publicar" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Todas as persoas usuarias de Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Seguro que queres eliminar esta ligazón? Se o fas, non estará dispoñible " "para ninguén" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(actual)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Eliminar ligazón" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Obter ligazón" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "A ligazón copiouse correctamente" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Xestionar permisos" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 páxina compartida" msgstr[1] "% páxinas compartidas" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Poden comentar" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Poden ver o código" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Calquera persoa ca ligazón terá acceso" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Páxinas compartidas" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "A ligazón para compartir aparecerá aquí" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Só membros do equipo" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Compartir prototipos" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Seleccionar todas" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Cancelar publicación" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Xestión do equipo" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot está deseñado para equipos. Convida a persoas coas que trallar en " "proxectos e ficheiros" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Engadir como Biblioteca Compartida" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Cambiar correo electrónico" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copia)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Crear novo equipo" @@ -231,11 +231,11 @@ msgstr "Crear novo equipo" msgid "dashboard.default-team-name" msgstr "O teu Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Eliminar equipo" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Descargar ficheiro Penpot (.penpot)" @@ -251,59 +251,59 @@ msgstr "Duplicar % ficheiros" msgid "dashboard.export-binary-multi" msgstr "Descargar %s ficheiros Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exportar marcos a PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exportar a PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "% de % elementos seleccionados" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exportar" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Podes engadir axustes na exportación aos elementos dende as propiedades do " "deseño (parte inferior da barra lateral dereita)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Información sobre como configurar as exportacións en Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Non hai elementos con configuración de exportación." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Exportar selección" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Fonte eliminada" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Desbotar todas" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Aínda non instalaches as túas propas fontes." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "Engadiuse 1 fonte" msgstr[1] "Engadíronse % fontes" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -313,7 +313,7 @@ msgstr "" "Podes cargar fontes cos seguintes formatos: **TTF, OFT e WOFF** (só se " "precisa un)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -322,75 +322,47 @@ msgstr "" "[Condicións do servizo de Penpot](%s). Podes ler máis sobre [licenzas de " "fontes](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Cargar todas" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importar archivos de Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Vaia! Non se puido importar o ficheiro" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgstr "% ficheiros importáronse correctamente." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Algúns ficheiros contiñan obxectos non válidos que foron eliminados." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Procesando cores" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Procesando compoñentes" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Procesando medios" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Procesando páxina: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Procesando fontes" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Enviando datos ao servidor (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Enviando ficheiro: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Invitar ao equipo" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Abandonar o equipo" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliotecas e Modelos" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Explora máis e descubre como contribuír" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Ocorreu un problema ao importar o modelo. Non se importou o modelo." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliotecas compartidas" @@ -398,7 +370,7 @@ msgstr "Bibliotecas compartidas" msgid "dashboard.loading-files" msgstr "cargando os teus ficheiros …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "cargando as túas fontes …" @@ -414,35 +386,35 @@ msgstr "Mover % ficheiros a" msgid "dashboard.move-to-other-team" msgstr "Mover a outro equipo" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Novo ficheiro" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Novo ficheiro" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Novo proxecto" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Novo proxecto" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Non se atoparon resultados para “%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Os proxectos fixados aparecerán aquí" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Actualizouse a túa dirección de correo" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Verificouse a túa dirección de correo" @@ -450,7 +422,7 @@ msgstr "Verificouse a túa dirección de correo" msgid "dashboard.notifications.password-saved" msgstr "Contrasinal gardado correctamente!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s integrantes" @@ -458,7 +430,7 @@ msgstr "%s integrantes" msgid "dashboard.open-in-new-tab" msgstr "Abrir ficheiro nunha nova lapela" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opcións" @@ -466,35 +438,35 @@ msgstr "Opcións" msgid "dashboard.password-change" msgstr "Cambiar contrasinal" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Proxectos" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Queres borrar a túa conta?" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Gardar configuración" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Buscar…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Procurando “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Escolle idioma da interface" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Escolle un tema" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Mostra todos os ficheiros" @@ -514,7 +486,7 @@ msgstr "Duplicouse o ficheiro" msgid "dashboard.success-duplicate-project" msgstr "Duplicouse o proxecto" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Moveuse o ficheiro" @@ -526,47 +498,47 @@ msgstr "Movéronse os ficheiros" msgid "dashboard.success-move-project" msgstr "Moveuse o proxecto" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Información do equipo" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Integrantes do equipo" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Proxectos do equipo" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema da interface" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Resultados da procura" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Escribe algo para procurar" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Cancelar publicación da Biblioteca" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Actualizar configuración" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "A túa conta" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Correo electrónico" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Nome" @@ -582,7 +554,7 @@ msgstr "Atención" msgid "ds.component-subtitle" msgstr "Compoñentes para actualizar:" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Cancelar" @@ -634,18 +606,6 @@ msgstr "Sombra" msgid "inspect.attributes.stroke" msgstr "Bordo" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centro" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Interior" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Exterior" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografía" @@ -706,19 +666,19 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Texto" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Atallos do teclado" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Aceptar" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Administración" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Todo" @@ -726,15 +686,15 @@ msgstr "Todo" msgid "labels.and" msgstr "e" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Volver" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Cancelar" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Pechar" @@ -742,51 +702,51 @@ msgstr "Pechar" msgid "labels.code" msgstr "Código" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Comentarios" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continuar" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Crear" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Panel" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Eliminar" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Borradores" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Editar" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Editor" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Caducada" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exportar" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Estilos" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fontes" @@ -794,107 +754,107 @@ msgstr "Fontes" msgid "labels.info" msgstr "Información" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Invitacións" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Lingua" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Pechar sesión" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Integrante" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Integrantes" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Vaia!" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Contrasinal" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Pendente" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Perfil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Proxectos" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Retirar" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Mudar o nome" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Volver tentar" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rol" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Gardar" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Enviar" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Enviando…" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Configuración" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibliotecas" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Estado" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Titoriales" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Actualizar" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Cargar" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Cargando…" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Visor" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(ti)" @@ -906,51 +866,51 @@ msgstr "Actualizar" msgid "modals.update-remote-component.cancel" msgstr "Cancelar" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Comentarios" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copiar" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Cortar" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Eliminar" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Curva" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Taboleiro" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Ruta" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rectángulo" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Texto" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplicar" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Cancelar" @@ -958,149 +918,149 @@ msgstr "Cancelar" msgid "shortcuts.go-to-search" msgstr "Buscar" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Agrupar" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Máscara" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Mover" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Pegar" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Refacer" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Básicos" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Panel" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Visor" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Espazo de traballo" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Aliñamento" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Editar" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Xenérico" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navegación" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navegación" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navegación" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Paneis" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Ruta" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formas" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Ferramentas" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Desfacer" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Dispersar" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Quitar máscara" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interaccións" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Mapa do sitio" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Cores" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Compoñentes" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Eliminar" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplicar" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Editar" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Agrupar" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Mudar o nome" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Tipografías" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Tamaño" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" @@ -1108,27 +1068,27 @@ msgstr "Ag" msgid "workspace.assets.ungroup" msgstr "Dispersar" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Editar" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Ficheiro" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferencias" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Restablecer" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Gardado" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Gardando" @@ -1136,19 +1096,19 @@ msgstr "Gardando" msgid "workspace.libraries.colors.rgba" msgstr "RGBA" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTECAS" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Actualizar" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "Actualizacións" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Compoñente" @@ -1184,11 +1144,11 @@ msgstr "Enriba" msgid "workspace.options.constraints.topbottom" msgstr "Enriba e Embaixo" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Deseño" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exportar" @@ -1196,25 +1156,25 @@ msgstr "Exportar" msgid "workspace.options.export-multiple" msgstr "Exportar selección" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Exportar 1 elemento" msgstr[1] "Exportar %s elementos" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Sufixo" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Exportación completada" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Exportando…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Erro na exportación" @@ -1226,35 +1186,35 @@ msgstr "Recheo" msgid "workspace.options.grid.auto" msgstr "Automático" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Columna" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Cor" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Marxe" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Embaixo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Esquerda" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Dereita" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Enriba" @@ -1266,15 +1226,15 @@ msgstr "Ningún" msgid "workspace.options.interaction-pos-center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Ningún" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centro" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Editar" diff --git a/frontend/translations/ha.po b/frontend/translations/ha.po index 6382d0fa45..21ca5eb381 100644 --- a/frontend/translations/ha.po +++ b/frontend/translations/ha.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: Alejandro Alonso \n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Hausa " "\n" "Language: ha\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "ka na da taska?" @@ -29,7 +29,7 @@ msgstr "" "wannan aikin gwaji ne kawai,kar ka yi amfani da shi a aikin gaske,lokaci " "zuwa lokaci za ya ringa vacewa ne." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "ka manta lambar tsaro?" @@ -37,27 +37,27 @@ msgstr "ka manta lambar tsaro?" msgid "auth.fullname" msgstr "cikakken suna" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "shiga nan" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "matattarar manazarta" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "wurin nazari" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Gugul" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "shaidar buxewa" @@ -77,7 +77,7 @@ msgstr "ba a tantance bayananka ba,sai an tantance a ci gaba." msgid "auth.notifications.recovery-token-sent" msgstr "an aika maka da saqon matakan da za a bi domin dawo da lambar tsaronka." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "ka zama dan tawaga" @@ -105,109 +105,109 @@ msgstr "canza lambar tsaro" msgid "auth.register-title" msgstr "qirqiri sabon kundi" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "dokokin aiki" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "wallafawa" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "duk fenfot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "ka tabbata ka na son rufe wannan hanyar? idan ka rufe ba mai iya sake bi" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(yanzu)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "tarwatsa hanya" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "samun hanya" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "an samo kwafi" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "amincewar shugaba" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "tura shafi 1" msgstr[1] "%s an tura shafi" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "sharhi" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "iya bincka lamba" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "duk wanda ya mallaki mataki,za ya iya shiga" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "tura shafuka" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "hanya mai kyau za ta bayyana a nan" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "memba kaxai" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "fenfot" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "zavi duka" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "maras wallafa" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "shuwagabannin tawaga" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "fenfot ta menbobin tawaga ce. kirawo kowa domin yin aiki tarekundaye" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "haxin guiwa!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "tura tamkar fenfot" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "canza imel" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kwafi)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "yi sabuwar tawaga" @@ -215,11 +215,11 @@ msgstr "yi sabuwar tawaga" msgid "dashboard.default-team-name" msgstr "manhajar fenfot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "goge tawaga" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "sauke manhajar fenfot(.manhajar fenfot)" @@ -235,59 +235,59 @@ msgstr "kwafi %s kundaye" msgid "dashboard.export-binary-multi" msgstr "sauke %s kundayen manhajar fenfot(.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Allon fitarwa na PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Fitarwa a PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s of %s tubullan da aka zava" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Fitarwa" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "za ka iya daidaita kayan zanenka ta hanyar (amfani da madannin qasa sashen " "dama)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "bayanin yadda ake fitarwa daga fenfot" -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "ba wasu abubuwan da ake daidaitawa wajen tsara fitarwa." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Fitar da zavi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "rashin font" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "goge duka" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "fonts da ka xora nan za ya bayyana." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "an qara font 1" msgstr[1] "%s an qara fonts da yawa" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -296,7 +296,7 @@ msgstr "" "kasawa**gwaurayen font**. Za ka iya xora font ta waxannan hanyoyin: **TTF, " "OTF and WOFF** (xaya kawai ake buqata)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -305,11 +305,11 @@ msgstr "" "fpntaiki](https://fenfot.app/dokoki.html). Za ka so bayani game " "da[ffonts](https://www.rubutu.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "xora duka" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -319,73 +319,45 @@ msgstr "" "mun aminta da amfani da [taransifota](https://taransifota.org/) domin samo " "webfonts da adana kurakurai. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "shigo da kundin fenfot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "kash! mun gaza shigo da kundinka" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "an shigar kundi 1." msgstr[1] "%s kundaye sun shiga." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "wasu kundayen na dauke da abubuwan da ba su da amfani." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "aikin rini" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "aikin sassa" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "kammala aiki" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "fejin kasuwar duniya: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "kula da rubutu" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "xora bayani akan sabis (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "xora kundaye: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "neman mutane" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "fita tawaga" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "taska da shaidar kamfanoni" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "bincika su da kyau kasan ta yadda za ka bayar da gudunmawa" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "akwai matsala wurin shigo da fejin talla. fejin tallar ba ya xauko." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "taskoki" @@ -393,7 +365,7 @@ msgstr "taskoki" msgid "dashboard.loading-files" msgstr "aikin dora kundaye …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "xora abin adonka …" @@ -409,35 +381,35 @@ msgstr "tura %s kundaye a" msgid "dashboard.move-to-other-team" msgstr "tura ma sauran tawaga" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ sabon kundi" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "sabon kundi" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ sabon aiki" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "sabon aiki" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "ba wanda ya yi daidai da “%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "nan za a ga bayanin aiki" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "ka zamanantar da adireshinka na imel" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "an tantance adireshinka na imel" @@ -445,7 +417,7 @@ msgstr "an tantance adireshinka na imel" msgid "dashboard.notifications.password-saved" msgstr "an tsare maka shaidar tsaro!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s memba" @@ -453,7 +425,7 @@ msgstr "%s memba" msgid "dashboard.open-in-new-tab" msgstr "buxe kundi a sabon wurin buxewa" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "zavi" @@ -461,39 +433,39 @@ msgstr "zavi" msgid "dashboard.password-change" msgstr "sauya lambar tsaro" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "rufewa/buxewa" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "aiki" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "ka na son cire taskarka?" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "wurin ajiyar saiti" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "nema …" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "neman “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "zavavven harshen UI" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "zaven batu" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "duk kundaye" @@ -517,7 +489,7 @@ msgstr[1] "an kwafi kundayenka" msgid "dashboard.success-duplicate-project" msgstr "an kwafi aikinka" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "an tura aikinka" @@ -529,81 +501,81 @@ msgstr "an tura kundayenka" msgid "dashboard.success-move-project" msgstr "an tura aikinka" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "bayanin tawaga" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "mambobin tawaga" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "aiyukan tawaga" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "batun UI" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "neman sakamako" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "rubuta neman sakamako" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "wallafa taska" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "zamanartarwa wurin gyara" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "ya na amfani" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "idan an sami sauyi a nan ake kawo shi" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "irin ra'ayi" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "yin webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "an gina Webhook." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhooks hanyar sanar da manhajoji da addireshi intanet idan wani ya faru a " "Penpot. za a tura maka da saqon talla URLs ka samar da." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "danna qasa \"sanya webhook\" qara xaya." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "ba a gina wani webhooks ba." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "gidanka" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "imel" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "sunanka" @@ -619,7 +591,7 @@ msgstr "natsu" msgid "ds.component-subtitle" msgstr "zamanantar da sassa:" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "goge" @@ -635,15 +607,15 @@ msgstr "ka tabbata?" msgid "errors.auth-provider-not-configured" msgstr "manhajar tantancewar ba ta tsaru ba." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "lokacin ya qare ko ba a tantance ka ba." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "ba za a iya xora fonts %s ba" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "ba za a iya xora fonts %s ba" @@ -651,15 +623,15 @@ msgstr "ba za a iya xora fonts %s ba" msgid "errors.cannot-upload" msgstr "kasa xora xan aiken kundi." -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "manhajar binciken nan ba ta iya yin wannan aikin" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "an yi amfani da imel" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "an farfaxo da imel." @@ -667,25 +639,25 @@ msgstr "an farfaxo da imel." msgid "errors.email-as-password" msgstr "ba za ka iya amfani da imel ba a matsayin lambar tsaro ba" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "imel «%s» na da bayanan matsaloli na dindindin." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "saqonni marasa amfani na imel «%s»." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "tamkar ka buxe wani kundi da ke da muhimmanci '%s' bayar da dama qarin da " "ka yi ma fenfot xinka bai karbu ba ko ba zai yi aiki ba." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "sassa '%s' ba za su yi aiki ba." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "wata matsala ta faru." @@ -697,7 +669,7 @@ msgstr "launin bai yi ba" msgid "errors.invalid-email" msgstr "sanya imel mai amfani" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "tabbata imel xinka ya yi daidai" @@ -717,7 +689,7 @@ msgstr "za a iya soke gayyata ko ta ki aiki." msgid "errors.ldap-disabled" msgstr "LDAP ya gaza tantancewa." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "kammala adadin '%s' madogara. gyara." @@ -733,7 +705,7 @@ msgstr "hoton da aka sanya bai yi daidai da kundin da ake son faxaxawa ba." msgid "errors.media-type-not-allowed" msgstr "wannan hoton ba ya aiki." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "bayanan da ka nema imel din su ya suma (bayanan matsaloli)." @@ -749,7 +721,7 @@ msgstr "lambar tsaro dole ta kai yawan alamu 8" msgid "errors.profile-blocked" msgstr "bayanan a rufe suke" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "bayananka su na da imel maras motsi (baiyana matsaloli)." @@ -757,15 +729,15 @@ msgstr "bayananka su na da imel maras motsi (baiyana matsaloli)." msgid "errors.registration-disabled" msgstr "rigitar ba ta yi ba." -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "ba sauran masu fita daga tawaga, ba bu tabbacin gogewa." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "mamban da ka ke son sanyawa ba ya a ciki." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "mai abu ba ya barin tawaga, dole adubi matsayin mai abu." @@ -773,35 +745,35 @@ msgstr "mai abu ba ya barin tawaga, dole adubi matsayin mai abu." msgid "errors.unexpected-error" msgstr "afkuwar kuskuren da ba a zata ba." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "tukuicin da ba a san da shi ba" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "hadin bai yi ba, ba a iya samun URL" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL bai gyaru ba." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "saqon qarshe bai je ba." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "kuskure kan farfaxo da SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "dakatarwa" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "matsalar da ba zata ba lokacin farfaxowa" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "matsayin da ba zato %s" @@ -817,13 +789,13 @@ msgstr "tsohuwar lambar tsaro ba daidai ba ce" msgid "feedback.description" msgstr "bayani" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "mun yi farin ciki da samunka a nan. idan ka na da buqatar taimako sai ka " "tuntuvi na gaba da kai matsayi." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "dangin fenfot" @@ -837,11 +809,11 @@ msgstr "" "bayyana dalilin imel dinka, faiyace idan akwai matsala, an shawarwari ko " "hasashe. tawaga ko mamba za a kula da kai." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "tambayoyin da ke buqatar amsa." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "wurin karvar qorafin tiwita" @@ -873,7 +845,7 @@ msgstr "sauke hanyar hoto" msgid "inspect.attributes.shadow" msgstr "inuwa" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "wurin daxa girma" @@ -881,39 +853,27 @@ msgstr "wurin daxa girma" msgid "inspect.attributes.stroke" msgstr "yankewa" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "tsakiya" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "daga ciki" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "daga waje" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "tsara rubutu" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "gidan salo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "girman salo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "tsarin salo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "tazarar harafi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "tsawon layi" @@ -925,11 +885,11 @@ msgstr "babu" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "jan layi" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "yadda ake rubuta batu" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "qananan baqaqe" @@ -937,7 +897,7 @@ msgstr "qananan baqaqe" msgid "inspect.attributes.typography.text-transform.none" msgstr "babu" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "manyan baqaqe" @@ -997,31 +957,31 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "rubutu" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "yanke" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "karva" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "mai amfani" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "inganta font" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "shugaba" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "duk" @@ -1029,29 +989,29 @@ msgstr "duk" msgid "labels.and" msgstr "da" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "baya" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "ka jinkirta kaxan sannan ka qara gwadawa; mu na aiki daidai domin tattala " "aikinmu." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "akwai matsala" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "sokewa" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "rufewa" @@ -1059,11 +1019,11 @@ msgstr "rufewa" msgid "labels.code" msgstr "lamba" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "yabo" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "matattara" @@ -1071,35 +1031,35 @@ msgstr "matattara" msgid "labels.confirm-password" msgstr "tabbatar da lambar tsaro" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "ci gaba" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "za ka iya ci gaba a idanka na fenfot" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "kwafar hanya" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "yi" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "yi sabuwar tawaga" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "shigar da sabon sunan tawaga" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "dashbod" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "goge" @@ -1111,7 +1071,7 @@ msgstr "goge yabo" msgid "labels.delete-comment-thread" msgstr "goge rubutu" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "goge gayyata" @@ -1119,31 +1079,31 @@ msgstr "goge gayyata" msgid "labels.delete-multi-files" msgstr "goge %s kundaye" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "ma su qirqira" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "vatar" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "adana" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "gyara" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "maigyara" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "daina aiki" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "fitarwa" @@ -1155,43 +1115,43 @@ msgstr "kasa samun bayani" msgid "labels.feedback-sent" msgstr "aika bayani" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "ire-iren font" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "salo" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Font" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "taskar rubuce-rubuce" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "bada bayani" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "koma baya" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "sashen taimako" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "voye saqon da aka buxa" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "maras amfani" @@ -1199,47 +1159,47 @@ msgstr "maras amfani" msgid "labels.info" msgstr "bayani" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "sanya fenfot" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "kuskuren ciki" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "teburin aiki" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "gayyata" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "harshe" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "taskoki & allunan talla" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "yi ko shiga" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "fita" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "kasuwanci" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "mamba" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "mambobi" @@ -1247,35 +1207,35 @@ msgstr "mambobi" msgid "labels.new-password" msgstr "sabuwar lambar tsaro" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "na gaba" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "an dakatar da kai duka! Alamar sabon sharhi za ta fito nan." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "ba saqon da ba a buxa ba." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "alama!" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "kundi 1" msgstr[1] "kundaye %s" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "allo 1" msgstr[1] "alluna %s" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "aiki" @@ -1285,135 +1245,135 @@ msgstr[1] "aiyuka %s" msgid "labels.old-password" msgstr "tsohuwar lambar tsaro" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "idan akwai qari (bayyana)" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "mai" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "lambar tsaro" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "tukuna" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "can baya" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "shugaban aiki" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "kundi" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "aiyuka" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "bayar da bayani" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "cire" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "cire mamba" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "sake suna" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "sake suna tawaga" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "sake aika saqon gayyata" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "sake gwadawa" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "matsayi" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "ajiye" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "neman font" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "dauki wanda kake so" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "aika" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "aikawa…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "mu na cikin kula a tsarinka." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "ba sabis" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "gyara" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "taskoki" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "fito da yabo" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "jerin ire-iren yabo" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "bayyana na ka ra’ayin" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "zana" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "fara" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "daraja" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "malami ko dalibi" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "koyarwa" @@ -1421,23 +1381,23 @@ msgstr "koyarwa" msgid "labels.unpublish-multi-files" msgstr "kundayen da ba a wallafa ba %s" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "sabunta" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "sabunta tawaga" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "xorawa" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Upload custom fonts" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "ana dorawa…" @@ -1445,11 +1405,11 @@ msgstr "ana dorawa…" msgid "labels.view-only" msgstr "gani kaxai" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "ma su kallo" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -1457,11 +1417,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "rubuta s abon yabo" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(kai)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "fagenka" @@ -1489,61 +1449,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "qara “%s” xakin ajiya" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "jan hankali" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "tantance sabon imel" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "za mu aika maka da imel ta wannan imel xin “%s” domin tantance " "shaidarka." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "sabon imel" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "sake imel" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "sake imel xinka" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "qirqirar webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "qirqirar webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "farashin URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://misali.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "ajiye asusu da soke shi" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "haka, goge asusu" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "idan ka cire asusunka za ka iya rasa aikin da ka kammala." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "ka tabbata ka na son goge asusunka?" @@ -1591,31 +1551,31 @@ msgstr "ka tabbata kana son goge %s kundaye?" msgid "modals.delete-file-multi-confirm.title" msgstr "gogewa %s kundaye" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "ka tabbata kana son goge wannan salon font din? ba zai xoru ba idan an yi " "amfani da shi a kundi." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "goge salon font" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "ka tabbata kana son goge wannan font xin ? ba zai yi aiki ba idan an yi " "amfani da shi a kundi." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "goge font" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "ka tabbata kana son goge wannan shafin?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "goge shafi" @@ -1631,31 +1591,31 @@ msgstr "ka tabbata kana son goge wannan aikin?" msgid "modals.delete-project-confirm.title" msgstr "goge aikin" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "goge kundi" msgstr[1] "goge kundaye" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "ba zai yi aiki a kowane kundi ba." msgstr[1] "ba za su yi aiki a kowane kundi ba." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "za a iya buxe wannan taskirar a nan: " msgstr[1] "za a iya buxe taskirorin nan a nan: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "ka tabbata kana son goge wannan kundin?" msgstr[1] "ka tabbata kana son goge waxannan kundayen?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "goge kundi" @@ -1675,19 +1635,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "gogewar tawaga" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "goge memba" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "ka tabbata kana son cire wannan memban a wannan tawaar?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "cire memban tawaga" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -1697,141 +1657,141 @@ msgstr[1] "" "duk aikin da aka yi a waxancan kundayen, can za a same su (ba zane da za " "iya tsinkawa)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "goge webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "ka tabbata ka na son goge webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "gogewa webhook" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "gyara webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "gyara webhook" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "aika saqon" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "imel, rabawar waqafi" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "waxansu imel daga membobin qungiyar na yanzu. ba za a aikawa da gayyatarsu " "ba ." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "gayyato membobi zuwa ga tawaga" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "ka tabbata memban tawaga kaxai, za ya iya goge tawaga tare da aikinta da " "kundaye." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "ka tabbata ka na son fita daga %s tawaga?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Webhooks hanyar sanar da manhajoji da addireshi intanet idan wani ya faru a " "Penpot. za a tura maka da saqon talla URLs ka samar da." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "kai ne mai wannan tawagar. zabi wani memba da za ya iya inganta wa mai shi " "kafin ka fita." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "inganta sannan ka bari" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "zavi memba domin havakawa" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "kafin ka fita" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "barin tawaga" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "ka tabbata ka na son barin wannan tawagar?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "barin tawaga" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "adadin jan hankali" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "canza wurin mallaka" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "idan ka canza wurin mallaka, ba za ka iya sauya matsayin shugaba ba, " "gazawar wasu dokokin wannan tawaagar. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "kai ke da wannan tawagar yanzu. ka tabbata kana son yin %s sabon mai tawaga?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "mallakar tawaga" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "wallafa" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "ba komai a taskarka. ka na son wallafa ta?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "wallafa taska maras komai" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "qaramin jan hankali" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "maras wallafa" msgstr[1] "maras wallafa" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "ka tabbata ka na son rufe taskar nan?" msgstr[1] "ka tabbata ka na son rufe taskokin nan?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "rufe taska" @@ -1859,23 +1819,23 @@ msgstr "sabunta sashe a babbar taska" msgid "notifications.by-code.upgrade-version" msgstr "akwai sabon yayi." -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "an aika da saqon" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "kwafar hanyar gayya" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "ba za ka iya goge kundin ba. ka jira umarnin tawaga ka fin ka ci gaba." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "an ajiye bayanai!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "an aika da imel din tantancewa %s. bincika imel xinka!" @@ -1891,15 +1851,15 @@ msgstr "barka da zuwa fenfot!" msgid "onboarding.choice.team-up.create-team-desc" msgstr "bayan ka yi wa tawagarka suna, za ka iya gaiyato mutane ku hadu." -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "gayyato mambobi" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "gayyata tare da bayar da matsayi:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "za mu fara!" @@ -1945,43 +1905,43 @@ msgstr "fara tsarawa" msgid "profile.recovery.go-to-login" msgstr "je ka hanyar shiga" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "cire" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "gauraya" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Bayyana Ra'ayi" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "tsarin kasa" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "tsarin tsakiya ko ina" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "tsarin hagu" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "tsarin dama" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "tsarin sama" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "tsarin tsakiya a tsaye" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "kirkiri allo daga zabi" @@ -1989,43 +1949,43 @@ msgstr "kirkiri allo daga zabi" msgid "shortcuts.bold" msgstr "fito da shi barobaro" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "ma'auni mabanbanci" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "kebentaccan ma'auni" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "ma'aunin abubuwa daban daban" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "ma'auni hadaka" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "tura zuwa baya" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "komawa baya" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "tura gaba" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "kawo zuwa gaba" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "goge sake" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "kwafi" @@ -2033,35 +1993,35 @@ msgstr "kwafi" msgid "shortcuts.create-new-project" msgstr "samar da sabo abu" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "cire" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "fito da shi waje" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "goge" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "fita da ga cikin net wok" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "rarraba abubuwan da su ke a ware" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Ratse" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "siffar kwai" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Allo" @@ -2069,39 +2029,39 @@ msgstr "Allo" msgid "shortcuts.draw-nodes" msgstr "samar da hanya" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Hanya" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Ractangula" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "rubutaccan sako" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "maimaita" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "kubuta" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "samar da sabbabbin abubuwa" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "fitar da abubuwan da zai dace da ko wane abu" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "kifa shi dai dai" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "kifa ta tsaye" @@ -2125,23 +2085,23 @@ msgstr "ta fi zuwa rabbabben ma'ajiya" msgid "shortcuts.go-to-search" msgstr "gajeran sako" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "rukuni" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "raba ta tsaye" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "fito / boye UI" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "zuko ciki" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "sa hoto" @@ -2149,7 +2109,7 @@ msgstr "sa hoto" msgid "shortcuts.italic" msgstr "juya zuwa kwantaccen rubuyu" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "hada abubuwan" @@ -2157,39 +2117,39 @@ msgstr "hada abubuwan" msgid "shortcuts.line-through" msgstr "danna layin duk" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "kirkiri kwana" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "kirkiri lankwasa" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "takunkumi" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "hada da kauri" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "matsa" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Matsa kasa da sauri" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Matsa hagu da sauri" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "matsa dama da sauri" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Matsa sama da sauri" @@ -2197,293 +2157,293 @@ msgstr "Matsa sama da sauri" msgid "shortcuts.move-nodes" msgstr "matsa a hade" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Matsa da sashin kasa" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Matsa da sashin hagu" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Matsa da sashin dama" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Matsa da sashin samu" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "wani tsari" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "babu gajeriyar hanya" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Saita dishi dishi zuwa kashi 100" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "saita dishi dishi zuwa kashi 10" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Saita dishi dishi zuwa kashi 20" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Seta dashi dashi zuwa kashi 50" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Saita dishi dishi zuwa kashi 40" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Saita dishi dishi zuwa kashi 50" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Saita dishi dishi zuwa kashi 60" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Saita dishi dishi zuwa kashi 70" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "seta dishi dishin zuwa kashi 8o" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Seta dish dishi zuwa 90" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "abin daukan kala" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "tafi inda 'yan kallo za su bayyana ra'ayi" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "tafi gaban allon" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "tafi sashin da 'yan kallo za su duba" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "tafi sashin da masu kallo suke hulda" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "tafi sashin da masu kallo suke hulda" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "tafi fagen aiki" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " ko " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "manna" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "allon da ya gabata" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "gyara" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "zake zukowa" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "shikashikai" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "allon kallo" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Dankallo" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "fagen aiki" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "zabi duka" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "zabi wani shafi" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "zabi ainihin shafi" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "zabi shafin da ya gabata" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "raba kauri" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "nuna / boye akwatin pixel" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "nuna / boye yanken" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "yanke kauri" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "yanke zuwa ga akwatin pixel" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "fara gyarawa" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "kwaskwarima" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Tace" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "gamayya" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Babbar kumshiya" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "gyara shimfida" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "shawagi" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "shawagi" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "shawagi" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "allon sarrarfav naura" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Hanya" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Siffa" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Rubutu" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Kayan aiki" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zukowa" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zukowa" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "saita babban yatsa" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "allon harufa yanke" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Danna a jere" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Danna kadara" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Danna launukan kala" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Danna yanayin maida hankali" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Danna fuskar ta cika duka" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Danna tarihi" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Danna shimfida" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Tara/fitar da lankwasashhiyar shimfida" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Rufe/bude" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Rufe rabo" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Juya zuwa launukan rubutu" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Nuna/boye" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Danna salon zukowa" @@ -2491,67 +2451,67 @@ msgstr "Danna salon zukowa" msgid "shortcuts.underline" msgstr "Danna ta layi a kasa" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Cire" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Fita daka rukuni" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Cire takunkumi" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Rarraba ta tsaye" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Zuko raguwar ido" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Zuko karuwar ido" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zuko wanda aka zaba" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Tukunyar aje biro" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "samar da jerin harufa - %s - Mazubin biruka" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "jerin harufa - %s - Mazubin biruka" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "tsare tsare - %s - Mazubin biruka" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "nema - %s - Mazubin biruka" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Rababban dakin karatu- %s - Mazubin biruka" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Mazubin biruka - Tsara yanci ga tawaga" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "bada martani - Mazubin biruka" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Saiti - Mazubin biruka" @@ -2559,27 +2519,27 @@ msgstr "Saiti - Mazubin biruka" msgid "title.settings.password" msgstr "Nambobin sirri - Mazubin biruka" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Karin bayani - Mazubin biruka" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Gayyata - %s - Mazubin biruka" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Yan kungiya - %s - Mazubin biruka" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Saiti - %s - Mazubin biruka" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "gidan yanar gizo - %s - Mazubin biruka" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - duba kumburi - Mazubin biruka" @@ -2587,99 +2547,99 @@ msgstr "%s - duba kumburi - Mazubin biruka" msgid "title.workspace" msgstr "%s - Mazubin biruka" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Babu allon da aka samu a wannan fejin." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Ba'a sami allon ba." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Bayyana ra'ayi(%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Karka/ki nuna hulda" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Cika fuskar" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Duba (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Hulda" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Hulda (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Kwafi mahada" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Nuna hulda" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Nuna hulda da an danna" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "taswirar wuri" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Sakon karshe ya isa." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Daidaita tsakiya a kwance (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "rarraba filin kwance (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Daidaita hagu (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Daidaita dama (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Daidaita kasa (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Daidaita a kwance tsakiya (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "rarraba filin tsaye (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Daidaita sama(%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "duka kadara" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "kala" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Bangarori" @@ -2687,15 +2647,15 @@ msgstr "Bangarori" msgid "workspace.assets.create-group" msgstr "kirkiri rukuni" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "abubuwanka za a samu su suna nan take kamar \"sunan kungiya/ sunan abubuwan\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "goge" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "maimaita" @@ -2703,23 +2663,23 @@ msgstr "maimaita" msgid "workspace.assets.duplicate-main" msgstr "maimaita ainihin" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "tace" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "rukuni" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "sunan kungiya" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "dakin karatun gida" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "ba'a samu kadara ba" @@ -2727,7 +2687,7 @@ msgstr "ba'a samu kadara ba" msgid "workspace.assets.open-library" msgstr "bude fiyal din dakin karatu" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "sake suna" @@ -2735,43 +2695,43 @@ msgstr "sake suna" msgid "workspace.assets.rename-group" msgstr "sake sunan kungiyar" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "nemo kadarar" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "rababben dakin karatu" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "rubutun rubutu" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "girma" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "tsarin salo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "tafi zuwa salon dakin karatu don a tace" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "fili a tsakanin haruffa" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "tsawon layi" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "canja rubutu" @@ -2779,15 +2739,15 @@ msgstr "canja rubutu" msgid "workspace.assets.ungroup" msgstr "kashe daga kungiya" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "tsarin maida hankali" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "karka maida hankali" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "maida hankali" @@ -2799,151 +2759,151 @@ msgstr "a layi mikakke" msgid "workspace.gradients.radial" msgstr "a da'ira" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "kashe daidaitawa mai canjawa" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "kashe sikelin rabo" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "kashe tsinkewa zuwa mai jagora" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "kashe tsinkewa zuwa pixel" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "bada damar daidaitawa mai canjawa" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "bada damar sikelin rabo" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Tsinke zuwa mai jagora" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Bda damar tsinkewa zuwa akwatin pixel" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Boye allom suna" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "farantin launuka" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "boye pixel akwati" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "boye ma'auni" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "boye launukan yanayin tsarin rubutu" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Tace" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "fayil" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "taimako & bayani" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "fifiko" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "gani" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "sake" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "zabi duka" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Nuna sunayen allo" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "nuna launukan kala" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Nuna akwatin pixel" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Nuna ma'auni" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Nuna launukan tsarin rubutu" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Cire" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Kara saitawa" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "an samu kuskure wajen adanawa" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "An adana" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Adanawa" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "yanayin kallo (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zuko" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "cike - cika sikeli" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "dace - ja sikeli ya dace da shi" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "zuko yayi daidai da ko'ina" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Cika allon" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "zuko zababbe" @@ -2951,19 +2911,19 @@ msgstr "zuko zababbe" msgid "workspace.layout-grid.editor.title" msgstr "tace akwati" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Babu salon kaloli a ma'ajiya yanzu" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Babu rubutun rubutu a ma'ajiya yanzu" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "F" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "kalar yanzu" @@ -2975,47 +2935,47 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Adana salon kala" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Ma'adanar fiyal" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "Ma'adana a wanna fiyal" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "ma'adanai" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "sabunta ma'adana" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Babu rabben ma'adanai da suke bukatar sabuntawa" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Babu daidaituwa da aka samu na “%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Duba rabbaun ma'adanai" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "Rabban ma'adanai" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Sabuntawa" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "Aga duka canjin" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "sabuntawa" @@ -3035,31 +2995,31 @@ msgstr "zabin dishi dishi" msgid "workspace.options.canvas-background" msgstr "bayan zane" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Matse abun ciki" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Bangarori" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Yin sharhin rubutu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Kwafa" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Kirkiri sharhin rubuta" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Tace sharhin rubutu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Ainahin" @@ -3103,11 +3063,11 @@ msgstr "sama" msgid "workspace.options.constraints.topbottom" msgstr "sama & kasa" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "zane" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "fitarwa" @@ -3115,29 +3075,29 @@ msgstr "fitarwa" msgid "workspace.options.export-multiple" msgstr "Fitar da zababbun" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Fitarwa 1 Sashi" msgstr[1] "Fitarwa %s sashi-sashi" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Kari na bayan baki" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "An gama fitarwa" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Ana fitarwa" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "An gaza fitarwa" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Fitarwa ba tsammani ta sami tsaiko" @@ -3161,67 +3121,67 @@ msgstr "gudun farko" msgid "workspace.options.grid.auto" msgstr "da kanshi" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "shafi" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Kala" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "mahada" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "tsawo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "gefe" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "saita a tsoho" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Kasa" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Tsakiya" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Hagu" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Dama" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "mikewa" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "sama" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "yi amfani da tsoho" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "fadi" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "layuka" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "murabba'i" @@ -3233,11 +3193,11 @@ msgstr "Cika rukuni" msgid "workspace.options.group-stroke" msgstr "Ja layi a rukuni" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Tsawo" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Duba" @@ -3449,99 +3409,99 @@ msgstr "yayin dannawa" msgid "workspace.options.interactions" msgstr "yayin hulda" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "kala" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "kala konanniya" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "kala gudajjiya" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "kara masa duhu" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "banbanci" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "warewa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Haske mai muya" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Haske haske" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "kara masa haske" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "warewa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "sau" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "na kullum" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "mai rufi" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "jikewa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "allo" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "haske mai sauki" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "tsawo mafi yawa" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "fadi mafi yawa" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "tsawo mafi kankanta" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "fadi mafi kankanta" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "kaloli masu yawa" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Ma'ajiyar kaloli masu yawa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "dishi dishi" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "samfur" @@ -3565,19 +3525,19 @@ msgstr "saman hagu" msgid "workspace.options.radius-top-right" msgstr "saman dama" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "da dimi dimi" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "sake" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "juyawa" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "nemo jerin harufa" @@ -3585,7 +3545,7 @@ msgstr "nemo jerin harufa" msgid "workspace.options.select-a-shape" msgstr "zabi surar allo, ko rukuni ta hadu da daya allon." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "zababbabbin kaloli" @@ -3641,47 +3601,47 @@ msgstr "inuwar zabi" msgid "workspace.options.show-fill-on-export" msgstr "fito da shi a ga" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "fito da kaurin sosai" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "girma" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "yanayin girman yanayin" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "da'ira" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "mai daraja" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "mashi" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "ba komai" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "da'ira" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "murabba'i" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rectangle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triangle" @@ -3689,35 +3649,35 @@ msgstr "Triangle" msgid "workspace.options.stroke-color" msgstr "gigciye kalar" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "gigciye fadin" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "tsakiya" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "layin raba abu" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "digo digo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "ciki" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "gauraya" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "waje" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "mai tauri" @@ -3753,11 +3713,11 @@ msgstr "sarrafaffen fadi" msgid "workspace.options.text-options.grow-fixed" msgstr "dasa" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "tazarar harafi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "tsahon layi" @@ -3801,55 +3761,55 @@ msgstr "ja layi (%s)" msgid "workspace.options.use-play-button" msgstr "yi amfani da madanneta ta sama ki tafi da samfuri." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "fadi" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X layi" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y layi" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "zane da kauri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "hada kauri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "ta kwana (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Ta lankwasa (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "hade kauri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "tafi da kauri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "raba kauri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "tsinke kauri (%s)" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "kara sassaukan tsarit" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "kara akwatin tsari" @@ -3861,7 +3821,7 @@ msgstr "tura zuwa baya" msgid "workspace.shape.menu.backward" msgstr "tura zuwa baya" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "kwafi" @@ -3869,27 +3829,27 @@ msgstr "kwafi" msgid "workspace.shape.menu.create-annotation" msgstr "kirkiri hoto mai motsi" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "zababban allo" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "kirkiri abubuwa" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "kirkiri abubuwa da yawa" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "cire" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "goge" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "goge kwararar farko" @@ -3901,7 +3861,7 @@ msgstr "raba yanayin abin" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "raba yanayin abin" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "bambanci" @@ -3909,15 +3869,15 @@ msgstr "bambanci" msgid "workspace.shape.menu.duplicate" msgstr "maimaita" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "tace" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "kebe" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "mikad da abu" @@ -3929,7 +3889,7 @@ msgstr "kifa ta tsaye" msgid "workspace.shape.menu.flip-vertical" msgstr "kifa ta kwance" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "fara malala" @@ -3941,39 +3901,39 @@ msgstr "kawo ta gaba" msgid "workspace.shape.menu.front" msgstr "kawo zuwa gaba" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "rukuni" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "boye" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "nuna / boye UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "mahada" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "kulle" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "takunkumik" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "manna" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "hanya" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "cire sassaukan tsari" @@ -3989,11 +3949,11 @@ msgstr "saita ainihin wurin" msgid "workspace.shape.menu.select-layer" msgstr "zabi shimfida" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "nuna" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "nuna ta kusuwar kadara" @@ -4009,19 +3969,19 @@ msgstr "cire girman babban danyatsa" msgid "workspace.shape.menu.thumbnail-set" msgstr "kara kamar girman babban yatsa" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "raba rukunin" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "hadakan" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "bude" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "bude takunkumi" @@ -4037,47 +3997,47 @@ msgstr "ruguza a'ajiyar bayani" msgid "workspace.sidebar.expand" msgstr "kara yankin ma'agiyar bayani" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "labari" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "shafi" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "bangare" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "allo" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "rukuni" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "hoto" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "takunkumi" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "nemo shimfida" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Siffa" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "rubutu" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "shigo da shi SVG halaye" @@ -4085,7 +4045,7 @@ msgstr "shigo da shi SVG halaye" msgid "workspace.sidebar.sitemap" msgstr "shafi" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "taswirar wuri" @@ -4093,51 +4053,51 @@ msgstr "taswirar wuri" msgid "workspace.toolbar.assets" msgstr "kadara" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "farantin kala (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "ra'ayi (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "kwana(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "siffar kwai (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "allo (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "hoto (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "motsa (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "hanya (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rectangle (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "rubutu (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "rubutub rubutu (%s)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "babu labaran da su ka canja a yanzu" @@ -4277,18 +4237,18 @@ msgstr "rubutun rubuta kadara" msgid "workspace.undo.entry.unknown" msgstr "yanayi fiye da %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "watsar" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "bayanai masu yawa" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "a kwai na zamani a rababban ma'ajiya" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "sabintawas" diff --git a/frontend/translations/he.po b/frontend/translations/he.po index 38e7e83532..f038e49e7d 100644 --- a/frontend/translations/he.po +++ b/frontend/translations/he.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:13+0000\n" -"Last-Translator: Anonymous \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Hebrew " "\n" "Language: he\n" @@ -10,9 +10,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && " "n % 10 == 0) ? 2 : 3));\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "כבר יש לך חשבון?" @@ -32,7 +32,7 @@ msgstr "יצירת חשבון הדגמה" msgid "auth.demo-warning" msgstr "זה שירות ניסיוני, לא להשתמש בו לעבודה אמתית, המיזמים יימחקו מדי פעם בפעם." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "שכחת סיסמה?" @@ -40,35 +40,35 @@ msgstr "שכחת סיסמה?" msgid "auth.fullname" msgstr "שם מלא" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "כניסה לחשבון שלי" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "כניסה מכאן." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot הוא כלי בקוד פתוח לשיתוף עיצוב וקוד" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "‎OpenID Connect" @@ -76,7 +76,7 @@ msgstr "‎OpenID Connect" msgid "auth.new-password" msgstr "נא להקליד סיסמה חדשה" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "חברת אל %s" @@ -92,7 +92,7 @@ msgstr "הפרופיל לא עבר אימות, נא לאמת את הפרופיל msgid "auth.notifications.recovery-token-sent" msgstr "קישור לשחזור סיסמה נשלח לתיבת הדוא״ל הנכנס שלך." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "הצטרפת לצוות בהצלחה" @@ -124,7 +124,7 @@ msgstr "שכחת סיסמה?" msgid "auth.recovery-submit" msgstr "החלפת הסיסמה שלך" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "אין לך חשבון עדיין?" @@ -136,7 +136,7 @@ msgstr "איך נפנה אליך בלוח הבקרה ובהודעות בדוא״ msgid "auth.register-account-title" msgstr "השם שלך" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "יצירת חשבון" @@ -149,7 +149,7 @@ msgstr "יצירת חשבון" msgid "auth.terms-and-privacy-agreement" msgstr "[תנאי השירות](%s) ו[מדיניות הפרטיות](%s) מקובלים עליי." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "תנאי השירות" @@ -171,39 +171,39 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "לא נמצאו אנשים עבור ‎@%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "פרסום" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "כל משתמשי Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "להסיר את הקישור הזה? ביצוע פעולה זו תמנע מכולם לגשת אליו" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(נוכחי)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "השמדת קישור" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "קבלת קישור" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "הקישור הועתק בהצלחה" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "ניהול הרשאות" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "עמוד שותף" @@ -211,51 +211,51 @@ msgstr[1] "%s עמודים שותפו" msgstr[2] "%s עמודים שותפו" msgstr[3] "%s עמודים שותפו" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "הרשאות תגובה" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "הרשאות לחקירת הקוד" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "כל מי שיש לו את הקישור יכול לגשת" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "עמודים ששותפו" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "הקישור לשיתוף יופיע כאן" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "רק חברי צוות" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "שיתוף אבות טיפוס" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "בחירה בהכול" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "ביטול פרסום" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "ניהול צוות" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "Penpot מיועד לצוותים. אפשר להזמין חברים כדי לעבוד ביחד על מיזמים וקבצים" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "להתגבש כקבוצה!" @@ -271,47 +271,47 @@ msgstr "שחזור קובץ" msgid "dashboard.add-file" msgstr "הוספת קובץ" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "הוספת ספרייה משותפת" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "החלפת דוא״ל" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "העברת צוות" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "העברה אל:" -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "בחירת ארגון" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "החלפת ארגון הצוות" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "פינוי האשפה" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(עותק)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "יצירת ארגון חדש" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "יצירת צוות חדש" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "נעיצת הגרסה הזאת" @@ -319,7 +319,7 @@ msgstr "נעיצת הגרסה הזאת" msgid "dashboard.default-team-name" msgstr "ה־Penpot שלך" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "למחוק את כל המיזמים והקבצים שלך לצמיתות? זאת פעולה בלתי הפיכה." @@ -327,41 +327,41 @@ msgstr "למחוק את כל המיזמים והקבצים שלך לצמיתות msgid "dashboard.delete-file-forever-confirmation.description" msgstr "למחוק את %s לצמיתות? זאת פעולה בלתי הפיכה." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s קבצים נמחקו בהצלחה." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "מחיקה לצמיתות" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "מחיקת מיזם" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "למחוק את המיזם %s לצמיתות? הפעולה הזאת תמחק לצמיתות את כל הקבצים שבו. זאת " "פעולה בלתי הפיכה." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s נמחק בהצלחה." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "מחיקת צוות" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "האשפה שלך ריקה. קבצים ומיזמים שנמחקו יופיעו כאן." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "%s יימחק" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "הורדת קובץ Penpot‏ (‎.penpot)" @@ -373,19 +373,19 @@ msgstr "שכפול" msgid "dashboard.duplicate-multi" msgstr "שכפול %s קבצים" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "כשאחד או אחת מחברי הצוות יוצרים טיוטה, היא תוצג כאן." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "אין טיוטות עדיין." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "כשחבר או חברה במיזם יוצרים קובץ, הוא יופיע כאן." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "אין קבצים עדיין." @@ -432,23 +432,23 @@ msgstr "ייבוא קובץ ‎.penpot" msgid "dashboard.empty-project.start" msgstr "נתחיל לבנות דברים מדהימים" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "אירעה שגיאה במחיקת הקבצים." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "אירעה שגיאה במחיקת המיזם %s." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "אירעה שגיאה בשחזור הקובץ %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "אירעה שגיאה בשחזור הקבצים." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "אירעה שגיאה בשחזור המיזם %s עם הקבצים שלו." @@ -456,35 +456,35 @@ msgstr "אירעה שגיאה בשחזור המיזם %s עם הקבצים של msgid "dashboard.export-binary-multi" msgstr "הורדת %s קובצי Penpot‏ (‎.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "ייצוא לוחות אומנות כ־PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "ייצוא כ־PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "נבחרו %s מתוך %s רכיבים" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "ייצוא" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "אפשר להוסיף הגדרות ייצוא לרכיבים ממאפייני העיצוב (מתחתית הסרגל שמשמאל)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "מידע על הגדרות ייצוא ב־Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "אין רכיבים עם הגדרות ייצוא." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "ייצוא הבחירה" @@ -512,27 +512,27 @@ msgstr "תצוגת רשת" msgid "dashboard.files-layout.list" msgstr "תצוגת רשימה" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "הגופן נמחק" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "להתעלם מהכול" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "גופנים שהעלית יופיעו כאן." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "אין גופנים מותאמים אישית עדיין." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "לאחר העלאת גופן בהתאמה אישית על ידי חברים, הוא יוצג כאן." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "נוסף גופן" @@ -540,7 +540,7 @@ msgstr[1] "נוספו 2 גופנים" msgstr[2] "נוספו %s גופנים" msgstr[3] "נוספו %s גופנים" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -549,7 +549,7 @@ msgstr "" "גופנים יחידה**. ניתן להעלות גופנים מהסוגים הבאים: **TTF,‏ OTF ו־WOFF** (אחד " "הסוגים יספיק)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -557,11 +557,11 @@ msgstr "" "על כך מידע נוסף בסעיף זכויות התוכן של [תנאי השירות של Penpot](%s). אפשר גם " "לקרוא גם על [רישוי גופנים](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "להעלות הכול" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -570,15 +570,15 @@ msgstr "" "[זה]](https://vertical-metrics.netlify.app/). בנוסף, המלצתנו היא להשתמש " "ב־[Transfonter](https://transfonter.org/) כדי לייצר גופני רשת ולתקן שגיאות. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "מעבר למסוף הניהול" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "ייבוא קובצי Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "אופס! לא הצלחנו לייבא את הקובץ הזה" @@ -586,31 +586,31 @@ msgstr "אופס! לא הצלחנו לייבא את הקובץ הזה" msgid "dashboard.import.bad-url" msgstr "הייבוא נכשל. כתובת התבנית שגויה" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "לא הצלחנו לאמת את הקובץ הזה." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "נראה שהקובץ הזה פגום." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "לא כל הקבצים יובאו" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "בקבצים הבאים יש שגיאות:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "קבצים עם שגיאות לא יועברו." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "משהו השתבש בעת עיבוד הקובץ הזה." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "קובץ יובא בהצלחה." @@ -618,7 +618,7 @@ msgstr[1] "%s קבצים יובאו בהצלחה." msgstr[2] "%s קבצים יובאו בהצלחה." msgstr[3] "%s קבצים יובאו בהצלחה." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "חלק מהקבצים הכילו פריטים שגויים שהוסרו." @@ -626,55 +626,27 @@ msgstr "חלק מהקבצים הכילו פריטים שגויים שהוסרו. msgid "dashboard.import.no-perms" msgstr "אין לך הרשאה לייבא לצוות הזה" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "עיבוד צבעים" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "עיבוד רכיבים" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "עיבוד מדיה" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "עיבוד עמוד: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "עיבוד טיפוגרפיות" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "הנתונים נשלחים לשרת (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "נשלח קובץ: %s" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "תימחקנה ההזמנות שמיועדות אל:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "תישלחנה מחדש ההזמנות שמיועדות אל:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "מחיקת הזמנות" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "שליחת הזמנות מחדש" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "אין לך הרשאה להזמין אנשים להצטרף לצוות הזה או לערוך או למחוק הזמנות." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "הזמנת אנשים" @@ -686,27 +658,27 @@ msgstr "עזיבת הארגון" msgid "dashboard.leave-organization.toast" msgstr "הורחקת מהארגון %s." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "עזיבת הצוות" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "ספריות ותבניות" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "כאן יש כמה ספריות ותבניות שאפשר להוסיף למיזם שלך" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "עיון ביותר כאלה והסברים כיצד לתרום להן" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "אירעה שגיאה בייבוא התבנית והיא לא ייובאה." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "ספריות משותפות" @@ -714,7 +686,7 @@ msgstr "ספריות משותפות" msgid "dashboard.loading-files" msgstr "הקבצים שלך נטענים…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "הגופנים שלך נטענים…" @@ -734,59 +706,59 @@ msgstr "העברה של %s קבצים אל" msgid "dashboard.move-to-other-team" msgstr "העברה לצוות אחר" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ קובץ חדש" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "קובץ חדש" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ מיזם חדש" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "מיזם חדש" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "לא נמצאו תוצאות לחיפוש אחר „%s”" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "אין לך הרשאות להוסיף צוותים לאף אחד מהארגונים שלך." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "אסור לך ליצור צוותים בארגון %s. למידע נוסף, נא ליצור קשר עם בעלי הארגון." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "אסור לך למחוק צוותים שהם חלק מהארגון %s. למידע נוסף, נא ליצור קשר עם בעלי " "הארגון." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "אסור לך להעביר צוותים שהם חלק מהארגון %s. למידע נוסף, נא ליצור קשר עם בעלי " "הארגון." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "מיזמים נעוצים יופיעו כאן" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "התראות" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "כתובת הדוא״ל שלך עודכנה בהצלחה" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "כתובת הדוא״ל שלך אומתה בהצלחה" @@ -798,11 +770,11 @@ msgstr "הגדרות ההתראות עודכנו" msgid "dashboard.notifications.password-saved" msgstr "הסיסמה נשמרה בהצלחה!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "הצגת התראות" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s חברים" @@ -810,19 +782,19 @@ msgstr "%s חברים" msgid "dashboard.open-in-new-tab" msgstr "פתיחת קובץ בלשונית חדשה" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "אפשרויות" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "סידור לפי תפקיד" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "סידור לפי מצב" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "הארגון %s נמחק." @@ -846,7 +818,7 @@ msgstr "מונית לבעלי הצוות הזה." msgid "dashboard.permissions-change.viewer" msgstr "מונית לצפות בצוות הזה." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "נעיצה/שחרור" @@ -862,27 +834,27 @@ msgstr "לא ניתן לפענח את המניפסט/מצהר של התוסף" msgid "dashboard.plugins.try-plugin" msgstr "התנסות התוסף: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "קבצים נמחקים…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "קבצים משוחזרים…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "המחיקה איטית להחריד" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "השחזור איטי להחריד" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "מיזמים" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "להסיר את החשבון שלך?" @@ -890,99 +862,99 @@ msgstr "להסיר את החשבון שלך?" msgid "dashboard.removed-from-team" msgstr "נגרעת מהצוות „%s”." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "הפעולה הזאת תשחזר את כל המיזמים והקבצים שלך. זאת פעולה שיכולה לארוך זמן מה." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "שחזור כל המיזמים והקבצים" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "לשחזר הכול" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s קבצים שוחזרו בהצלחה." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "שחזור מיזם" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "הפעולה הזאת תשחזר את המיזם %s ואת כל הקבצים שבו." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "שחזור מיזם" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s שוחזר בהצלחה." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "שמירת ההגדרות" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "חיפוש…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "מתבצע חיפוש אחר „%s”…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "ארגונים" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "צוותים" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "הוספה לארגון" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "בחירת ארגון:" -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "הזמנות ממתינות למשתמשים חיצוניים תבוטלנה." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "כאן אפשר למצוא את כל הארגונים שלך שבהם מותר לך ליצור או להוסיף צוותים." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "בחירת ארגון" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "הוספת צוות לארגון" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "בחירת שפת מנשק משתמש" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "בחירת ערכת עיצוב" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot כהה (ברירת מחדל)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot בהיר" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "סגנון המערכת" @@ -1046,11 +1018,11 @@ msgstr "עדכון הגדרות" msgid "dashboard.settings.notifications.title" msgstr "התראות" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "הצגת כל הקבצים" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "היסטוריית גרסאות" @@ -1078,7 +1050,7 @@ msgstr[3] "הקבצים שלך שוכפלו בהצלחה" msgid "dashboard.success-duplicate-project" msgstr "המיזם שלך שוכפל בהצלחה" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "הקובץ שלך הועבר בהצלחה" @@ -1090,179 +1062,179 @@ msgstr "הקבצים שלך הועברו בהצלחה" msgid "dashboard.success-move-project" msgstr "המיזם שלך הועבר בהצלחה" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s שייך מעתה ל־%s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "פרטי הצוות" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "חברי הצוות" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s לא שייך יותר ל־%s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "ארגון הצוות" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "הוספה לארגון" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "אין לך הרשאה להוסיף את הצוות הזה לאף אחד מהארגונים שלך." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "החלפת ארגון הצוות" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "אפשר להזמין רק אנשים מהארגון שלך." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "הצוות הזה אינו חלק מאף ארגון" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "הסרת צוות מארגון" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "מיזמי הצוות" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "הוספה למיזם שלך" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "ערכת עיצוב מנשק משתמש" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "תוצאות חיפוש" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "קבצים שנמחקו יישארו באשפה למשך" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s ימים. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "לאחר מכן, הם יימחקו לצמיתות." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "אם התחרטת, אפשר לשחזר אותם או למחוק אותם לצמיתות מהתפריט של כל אחד מהקבצים." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "נא להקליד כדי לחפש" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "ביטול פרסום ספרייה" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "עדכון הגדרות" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "נושלת מחברות בארגון %s" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "בטא" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "תיצוג WebGL יכול לשפר את הביצועים אבל הוא בשלב בטא (התנסות) ועלול להיות " "פחות יציב. חלק מההבדלים החזותיים עלולים להופיע בין משטח הציור, החומרים " "המיוצאים (SVG/PDF) ומצב התצוגה." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "כבוי" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "פעיל" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "שליחת משוב" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "מצב" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "תיצוג עם WebGL" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "פעילה" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "כאשר ההתליה הזאת מופעלת פרטי האירוע יישלחו" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "אפשר למחוק או לשנות את ההתליות שיצרת." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "סוג תוכן" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "יצירת התליית רשת" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "התליית הרשת נוצרה בהצלחה." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "התליות רשת הן דרך פשוטה לאפשר לאתרים אחרים לקבל הודעות כשאירועים מסוימים " "מתרחשים ב־Penpot. אנו נשלח בקשת POST לכל אחת מהכתובות שתצויינה." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "לחיצה על הכפתור „הוספת התליית רשת” תוסיף אחת." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "לא נוצרו התליות רשת עדיין." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "החשבון שלך" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "דוא״ל" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "שמך" @@ -1278,11 +1250,11 @@ msgstr "תשומת לב" msgid "ds.component-subtitle" msgstr "רכיבים לעדכון:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "לאפשר" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "ביטול" @@ -1302,7 +1274,7 @@ msgstr "אין אסימונים תקפים בערכות או בערכות העי msgid "ds.inputs.numeric-input.no-matches" msgstr "לא נמצאו תוצאות." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "פתיחת רשימת אסימונים" @@ -1318,15 +1290,15 @@ msgstr "ספק האימות לא מורשה לפרופיל הזה" msgid "errors.auth-provider-not-configured" msgstr "לא מוגדר ספק אימות." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "נראה שלא עברת אימות או שתוקף ההפעלה פג." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "לא ניתן לטעון את הגופן %s" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "לא ניתן לטעון את הגופנים %s" @@ -1338,57 +1310,57 @@ msgstr "לא ניתן להעלות את קובץ המדיה." msgid "errors.character-limit-exceeded" msgstr "הגעת למגבלת כמות התווים" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "אי אפשר להוסיף ספרייה: תיווצר תלות מעגלית בעקבות זאת" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "ה־API של לוח הגזירים לא זמין. יש לארח את Penpot דרך HTTPS כדי להפעיל גישה " "ללוח גזירים" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "הדפדפן שלך לא יכול לבצע את הפעולה הזאת" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "הגישה ללוח הגזירים נדחתה. נא לספק הרשאה לוח הגזירים בדפדפן שלך כדי להדביק " "תוכן" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "אירעה שגיאה עם ההערה" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "זה קובץ ישן שמשתמש בסוג של משאבי Penpot שאינם נתמכים עוד ואי אפשר לפתוח " "אותו, מתנצלים." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "כדי שנוכל לעזור לך." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "למרות ש־Penpot לא תומך יותר בסוג המשאבים הזה, אפשר" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "ליצור איתנו קשר" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "לא ניתן לגרור סדרת הורה לתוך נתיב צאצא של עצמה." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "כתובת הדוא״ל כבר בשימוש" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "כתובת הדוא״ל כבר אומתה." @@ -1400,29 +1372,29 @@ msgstr "אין לך אפשרות להשתמש בכתובת הדוא״ל שלך msgid "errors.email-does-not-match-invitation" msgstr "כתובת הדוא״ל לא תואמת את ההזמנה." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "שם התחום לא מורשה" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "לכתובת הדוא״ל „%s” יש יותר מדי דוחות החזרה קבועים." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "כתובת הדוא״ל „%s” דווחה כספאם או שההודעות תוקפצנה לצמיתות." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "נראה שניסית לפתוח קובץ בו פעילה היכולת ‚%s’ אבל מנשק ה־Penpot שלך לא תומך " "בה או שהיא מושבתת." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "אין תמיכה ביכולת ‚%s’." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "חייב להכיל עד %s תווים." @@ -1430,7 +1402,7 @@ msgstr "חייב להכיל עד %s תווים." msgid "errors.field-min-length" msgstr "חייב להיות באורך של תו אחד לפחות." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "שדה ריק" @@ -1438,23 +1410,23 @@ msgstr "שדה ריק" msgid "errors.field-not-all-whitespace" msgstr "השם חייב לכלול כמה אותיות למעט רווח." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "נראה שאין התאמ בין היכולות הפעילות לבין הקובץ שניסית לפתוח. יש להחיל הסבות " "עבור ‚%s’ לפני שיתאפשר לפתוח את הקובץ הזה." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "שם משפחת הגופן יכולה להכיל רק אותיות, מספרים, רווחים, מינוסים, קווים תחתיים " "ונקודות." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "קרה משהו לא טוב." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "משהו השתבש עם מנגנון הדפדפן." @@ -1470,7 +1442,7 @@ msgstr "נתונים שגויים" msgid "errors.invalid-email" msgstr "נא למלא כתובת דוא״ל תקפה בבקשה" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "כתובת הדוא״ל לאימות חייבת להיות תואמת" @@ -1504,11 +1476,11 @@ msgstr "ההזמנה כנראה בוטלה או שתוקפה פג." msgid "errors.ldap-disabled" msgstr "אימות מול LDAP הושבת." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "הגעת למיכסת ה־‚%s’. נא ליצור קשר עם התמיכה." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "הגעת למספר הכתובות המרבי (%s) שאפשר להזמין בבקשה אחת" @@ -1524,19 +1496,19 @@ msgstr "נראה כי תוכן התמונה לא תואם לסיומת הקוב msgid "errors.media-type-not-allowed" msgstr "נראה כי זאת תמונה שגויה." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "הודעות הדוא״ל לפרופיל שהזמנת מושתקות (דיווחים על דואר זבל או הרבה החזרות)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "מתבצעת הסבה" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "רק יוצר או יוצרת הגרסה יכולים לנעול אותה" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "רק יוצר או יוצרת הגרסה יכולים לשחרר את הנעילה שלה" @@ -1548,7 +1520,7 @@ msgstr "אירעה תקלה בעזיבת הארגון. נא לנסות שוב." msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "בעלי הארגון לא יכולים לעזוב את הארגון." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "הארגון הזה לא קיים" @@ -1560,7 +1532,7 @@ msgstr "סיסמת האימות חייבת להיות תואמת" msgid "errors.password-too-short" msgstr "הסיסמה חייבת להיות באורך 8 תווים לפחות" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "נתונים שגויים בלוח הגזירים" @@ -1568,7 +1540,7 @@ msgstr "נתונים שגויים בלוח הגזירים" msgid "errors.profile-blocked" msgstr "הפרופיל חסום" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "הודעות הדוא״ל לפרופיל שלך מושתקות (דיווחי דואר זבל או הרבה החזרות)." @@ -1576,19 +1548,19 @@ msgstr "הודעות הדוא״ל לפרופיל שלך מושתקות (דיוו msgid "errors.registration-disabled" msgstr "ההרשמה מושבתת כרגע." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "ה־SVG שגוי או פגום" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "התגלתה יכולת לא תואמת ‚%s’" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "אין מספיק חברים כדי לעזוב את הצוות, כנראה יהיה עליך למחוק אותו." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "החבר שניסית להקצות לא קיים." @@ -1596,7 +1568,7 @@ msgstr "החבר שניסית להקצות לא קיים." msgid "errors.team-leave.only-owner-can-delete" msgstr "רק בעלי הצוות יכולים למחוק אותו." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "הבעלים לא יכולים לעזוב את הקבוצה, עליך להעביר את תפקיד הבעלות." @@ -1608,11 +1580,11 @@ msgstr "שם הצוות לא יכול להכיל אף אחת מהאותיות ה msgid "errors.token-set-already-exists" msgstr "כבר קיימת סדרה בשם הזה" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "לא ניתן להשלים את החיתוך, כבר קיימת סדרה באותו השם בנתיב." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "כבר קיימת אפשרות ערכת עיצוב באותו השם" @@ -1624,11 +1596,11 @@ msgstr "ערכת העיצוב מתייחס לכל מיני ערכות שאינן msgid "errors.tokens.circular-reference" msgstr "לאסימון יש הפניה מעגלית" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "גובה השורה תלוי בגודל הגופן. יש להוסיף גודל גופן כדי לקבל את הערך הפתור." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "השדה הזה לא יכול להישאר ריק" @@ -1648,11 +1620,11 @@ msgstr "שגיאת ייבוא:" msgid "errors.tokens.invalid-color" msgstr "ערך צבע שגוי: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "ערך אסימון שגוי: אפשר להפנות לאסימון font-family (משפחת גופנים) בלבד" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "ערך משקל גופן שגוי: יש להשתמש בערכים מספריים (100-‏950) או שמות תקניים " @@ -1674,31 +1646,31 @@ msgstr "" "שמות האסימונים יכולים להכיל אותיות וספרות מופרדים בתווי . ואסור שיתחילו " "בדולר ($)." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "סוג הצללה שגוי: רק ‚innerShadow’ או ‚dropShadow’ מורשים" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "ערך אסימון שגוי: רק none,‏ Uppercase,‏ Lowercase או Capitalize מורשים" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "ערך אסימון שגוי: מותר רק none,‏ underline ו־strike-through" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "ערך שגוי: יש להפנות לאסימון הצללה מורכבת." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "ערך שגוי: חייב להפנות לאסימון טיפוגרפיה מרוכב." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "ערך אסימון שגוי: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "חסרות הפניות אסימונים: " @@ -1710,31 +1682,31 @@ msgstr "לא נמצאו אסימונים, סדרות או ערכות עיצוב msgid "errors.tokens.number-too-large" msgstr "ערך אסימון שגוי. הערך הפתור גדול מדי: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "שקיפות צריכה להיות בין 0 ל־100% או 0 ו־1 (כלומר 50% או 0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "לאסימון יש הפניה עצמית" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "טשטוש הצל חייב להיות גדול או שווה ל־0." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "התפרסות ההצללה חייב להיות גדולה או שווה ל־0." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "עובי הקו חייב להיות גדול או שווה ל־0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "ערך שגוי: אסור %." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "ערך שגוי: אסור יחידות." @@ -1742,55 +1714,55 @@ msgstr "ערך שגוי: אסור יחידות." msgid "errors.unexpected-error" msgstr "אירעה שגיאה בלתי צפויה." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "אסימון בלתי ידוע" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "הגרסה הזאת כבר נעולה" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "הגרסה הזאת נעולה ואחרים לא יכולים למחוק אותה" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "מספר גרסת הקובץ לא תואם" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL הפסיק לעבוד. נא לרענן את העמוד כדי לאפס אותו" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "אופס! הקשר לוח הציור אבד" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "שגיאת תקשורת, הכתובת אינה נגישה" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "הכתובת לא מעבירה תיקוף." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "ההעברה האחרונה לא הצליחה." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "שגיאה בתיקוף SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "תום זמן המתנה" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "שגיאה לא צפויה בעת תיקוף" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "המצב %s לא צפוי" @@ -1810,15 +1782,15 @@ msgstr "תיאור" msgid "feedback.description-placeholder" msgstr "נא לתאר את הסיבה למשוב שלך" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "אנחנו שמחים שהצטרפת אלינו. לקבלת עזרה, נא לחפש בטרם פרסום הודעה." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "קהילת Penpot" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "דרכים אחרות ליצור איתנו קשר" @@ -1840,11 +1812,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "ליצור איתנו קשר" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "כאן כדי לסייע בסוגיות טכניות." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "חשבון תמיכה ב־X" @@ -1864,10 +1836,6 @@ msgstr "רעיון" msgid "feedback.type.issue" msgstr "בעיה" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "הורדת קבצים" - #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "אירעה שגיאה" @@ -1904,7 +1872,7 @@ msgstr "תצוגה מקדימה של תמונת מילוי הצורה" msgid "inspect.attributes.shadow" msgstr "צל" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "גודל ומיקום" @@ -1912,43 +1880,31 @@ msgstr "גודל ומיקום" msgid "inspect.attributes.stroke" msgstr "מתאר" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "מרכז" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "בפנים" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "בחוץ" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "טיפוגרפיה" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "משפחת גופנים" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "גודל גופן" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "סגנון גופן" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "ריווח תווים" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "גובה שורה" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "משפחות גופנים מעורבבות" @@ -1960,11 +1916,11 @@ msgstr "ללא" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "קו תחתי" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "ראשונות גדולות" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "אותיות קטנות" @@ -1976,7 +1932,7 @@ msgstr "ללא" msgid "inspect.attributes.typography.text-transform.unset" msgstr "ביטול הגדרה" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "אותיות גדולות" @@ -2096,23 +2052,23 @@ msgstr "ערך פתור:" msgid "inspect.tabs.styles.variants-panel" msgstr "מאפייני הגוון" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "יצירת אסימון גישה חדש" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "לחיצה על הכפתור „יצירת אסימון גישה חדש” תיצור אחד." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "אין לך אסימון נכון לעכשיו." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "אסימוני גישה אישית" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "אסימוני גישה אישית מתנהגים כמו חלופה למערכת האימות באמצעות שם משתמש/סיסמה " @@ -2194,45 +2150,45 @@ msgstr "מפתח ה־MCP יפוג ב־%s" msgid "integrations.mcp-key.will-not-expire" msgstr "למפתח ה־MCP אין תאריך תפוגה" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "שרת ה־MCP של Penpot מפעיל לקוחות MCP לתפעול ישיר של קובצי עיצוב של Penpot." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "איך להגדיר לקוחות MCP" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "זאת כתובת השרת שצריך להגדיר בלקוח ה־MCP שלך כדי שיוכל להתחבר לשרת ה־MCP של " "Penpot." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "יצירת מפתח MCP מחדש" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "מפתח MCP" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "מפתח ה־MCP נחוץ להקמת לקוח ה־MCP" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "מצב" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "כבוי" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "פעיל" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "התוקף של מפתח MCP המשמש לחיבור לשרת MCP פג. לכן, לא ניתן לתקשר." @@ -2240,51 +2196,47 @@ msgstr "התוקף של מפתח MCP המשמש לחיבור לשרת MCP פג. msgid "integrations.mcp-server.title" msgstr "שרת MCP" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "בטא" - -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "לסמן הכול כנקרא" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "קיצורי דרך" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "על Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "מקובל" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "פעיל" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "הוספה" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "הוספת גופן משלך" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "מתווסף…" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "ניהול" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "הכול" @@ -2292,17 +2244,17 @@ msgstr "הכול" msgid "labels.and" msgstr "וגם" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "חזרה" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "נראה כאילו עליך להמתין מעט ולנסות שוב, אנו מבצעים עבודות תחזוקה פשוטות " "בשרתים שלנו." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "שער גישה שגוי" @@ -2310,15 +2262,15 @@ msgstr "שער גישה שגוי" msgid "labels.blur" msgstr "טשטוש" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "ביטול" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "סגירה" @@ -2326,7 +2278,7 @@ msgstr "סגירה" msgid "labels.code" msgstr "קוד" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "צמצום" @@ -2338,15 +2290,15 @@ msgstr "צבע" msgid "labels.comment" msgstr "הערה" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "הערות" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "קהילה" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "קהילה ותרומות" @@ -2358,19 +2310,19 @@ msgstr "מחושב" msgid "labels.confirm-password" msgstr "אישור סיסמה" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "יצירת קשר עם התמיכה" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "יצירת קשר איתנו" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "להמשיך" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "אפשר להמשיך עם חשבון Penpot" @@ -2382,31 +2334,31 @@ msgstr "העתקה" msgid "labels.copy-color" msgstr "העתקת צבע" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "העתקת קישור" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos ‏© 2019-עכשיו" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "יצירה" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "יצירת צוות חדש" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "נא למלא שם לצוות החדש" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "לוח בקרה" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "מחיקה" @@ -2418,7 +2370,7 @@ msgstr "מחיקת הערה" msgid "labels.delete-comment-thread" msgstr "מחיקת שרשור" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "מחיקת הזמנה" @@ -2426,23 +2378,23 @@ msgstr "מחיקת הזמנה" msgid "labels.delete-multi-files" msgstr "מחיקת %s קבצים" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "נמחקה" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "פיתוח" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "התעלמות" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "הורדת %s" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "טיוטות" @@ -2450,27 +2402,27 @@ msgstr "טיוטות" msgid "labels.duplicate" msgstr "שכפול" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "עריכה" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "עורך" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "ריקה" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "שגיאה" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "התוקף פג" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "ייצוא" @@ -2482,7 +2434,7 @@ msgstr "המשוב הושבת" msgid "labels.feedback-sent" msgstr "המשוב נשלח" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2490,55 +2442,55 @@ msgstr "Figma" msgid "labels.fill" msgstr "מילוי" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "משפחת גופנים" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "סגנונות" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "גופנים" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "מאגר GitHub" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "הגשת משוב" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "חזרה" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "עיצוב גרפי" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "מרכז העזרה" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "עזרה ולמידה" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "הסתרה" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "הסתרת הערות שנפתרו" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "ייבוא" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "בלתי פעיל" @@ -2546,51 +2498,51 @@ msgstr "בלתי פעיל" msgid "labels.info" msgstr "פירוט" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "גופנים מותקנים" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "שילובים" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "משהו גרוע התרחש." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "אפשר לנסות לבצע את הפעולה שוב או ליצור קשר עם התמיכה כדי לדווח על השגיאה." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "שגיאה פנימית" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "הזמנות" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "שפה" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "פריסה" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "מרכז הלמידה" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "ספריות ותבניות" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "בטעינה…" @@ -2598,7 +2550,7 @@ msgstr "בטעינה…" msgid "labels.lock" msgstr "נעילה" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "כניסה או הרשמה" @@ -2606,19 +2558,19 @@ msgstr "כניסה או הרשמה" msgid "labels.login" msgstr "כניסה" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "יציאה" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "שיווק" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "חבר" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "חברים" @@ -2630,23 +2582,23 @@ msgstr "אזכור" msgid "labels.new-password" msgstr "סיסמה חדשה" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "הבאה" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "Penpot לא זמין באופן זמני עקב תקלת מערכת. נא לנסות שוב בהקדם." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "עברת על הכול! התראות על הערות חדשות תופענה כאן." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "אין הזמנות ממתינות." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "אוספים יחד את האנשים ויוצרים דברים מדהימים." @@ -2654,15 +2606,15 @@ msgstr "אוספים יחד את האנשים ויוצרים דברים מדהי msgid "labels.no-matches" msgstr "אין תוצאות" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "אופס!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "התראות" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "קובץ" @@ -2670,7 +2622,7 @@ msgstr[1] "%s קבצים" msgstr[2] "%s קבצים" msgstr[3] "%s קבצים" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "לוח" @@ -2678,7 +2630,7 @@ msgstr[1] "%s לוחות" msgstr[2] "%s לוחות" msgstr[3] "%s לוחות" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "מיזם" @@ -2690,43 +2642,43 @@ msgstr[3] "%s מיזמים" msgid "labels.old-password" msgstr "סיסמה ישנה" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "פתוח" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "אפשרויות" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "אחר (נא לפרט)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "אחר" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "בעלים" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "סיסמה" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "בהמתנה" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "יומן השינויים של Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "המרכז של Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "מיזמים נעוצים" @@ -2734,31 +2686,31 @@ msgstr "מיזמים נעוצים" msgid "labels.post" msgstr "רשומה" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "הקודמת" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "עיצוב מוצר או חוויית לקוח" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "ניהול מוצר" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "פרופיל" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "מיזמים" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "אחרונות" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "הפניה" @@ -2766,27 +2718,27 @@ msgstr "הפניה" msgid "labels.refresh" msgstr "ריענון" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "הודעות מהדורה" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "ריענון העמוד" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "הסרה" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "הסרת חבר" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "שינוי שם" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "שינוי שם לצוות" @@ -2810,11 +2762,11 @@ msgstr "תגובה חדשה" msgid "labels.reply.thread" msgstr "תגובה" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "שליחה מחדש" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "שליחת ההזמנה מחדש" @@ -2822,43 +2774,43 @@ msgstr "שליחת ההזמנה מחדש" msgid "labels.restore" msgstr "שחזור" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "ניסיון חוזר" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "תפקיד" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "שמירה" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "חיפוש" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "חיפוש גופן" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "בחירת אפשרות" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "שליחה" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "מתבצעת שליחה…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "אנחנו בהפוגת תחזוקה מתוכננת של המערכות שלנו." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "השירות אינו זמין" @@ -2866,55 +2818,55 @@ msgstr "השירות אינו זמין" msgid "labels.sets" msgstr "סדרות" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "הגדרות" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "צל" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "שיתוף" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "ספריות משותפות" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "הצגה" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "הצגת כל ההערות" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "הצגת רשימת הערות" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "הצגת האזכורים שלך בלבד" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "הצגת ההערות שלך בלבד" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "מקורות" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "התחלה" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "מצב" @@ -2922,7 +2874,7 @@ msgstr "מצב" msgid "labels.stroke" msgstr "קו מתאר" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "סטודנט/ית או מרצה" @@ -2934,7 +2886,7 @@ msgstr "סגנונות" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "החלפה" @@ -2942,19 +2894,19 @@ msgstr "החלפה" msgid "labels.text" msgstr "טקסט" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "ערכות עיצוב" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "מדריכים" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "טיפוגרפיה" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "שגיאה לא ידועה" @@ -2966,35 +2918,31 @@ msgstr "שחרור נעילה" msgid "labels.unpublish-multi-files" msgstr "ביטול פרסום קובצי %s" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "עדכון" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "עדכון צוות" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "העלאה" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "העלאת גופנים משלך" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "מתבצעת העלאה…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "קובץ נשלח…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "הגוון" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "הערות לגרסה %s" @@ -3002,7 +2950,7 @@ msgstr "הערות לגרסה %s" msgid "labels.view-only" msgstr "תצוגה בלבד" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "מציג" @@ -3018,7 +2966,7 @@ msgstr[1] "שתי אזהרות" msgstr[2] "%s אזהרות" msgstr[3] "%s אזהרות" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "התליות" @@ -3026,11 +2974,11 @@ msgstr "התליות" msgid "labels.write-new-comment" msgstr "כתיבת הערה חדשה" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(אני)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "החשבון שלך" @@ -3170,7 +3118,7 @@ msgstr "" "בחלק מהצוותים אין עוד חברים מלבדך. הצוותים האלה יימחקו על המיזמים והקבצים " "שלהם." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "לפני עזיבת הארגון" @@ -3178,77 +3126,77 @@ msgstr "לפני עזיבת הארגון" msgid "modals.before-leave-organization.warning" msgstr "כל צוות שבו אין עוד חברים מלבדך יימחק." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "הינד גדול" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "אישור כתובת דוא״ל חדשה" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "נשלח הודעה לכתובת הדוא״ל הנוכחית שלך „%s” כדי לאמת את הזהות שלך." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "כתובת דוא״ל חדשה" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "החלפת כתובת דוא״ל" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "החלפת כתובת הדוא״ל שלך" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "יצירת התליה" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "יצירת התליה" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "כתובת מטען" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "ביטול ושמירה על החשבון שלי" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "כן, למחוק את החשבון שלי" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "הסרת החשבון שלך תוביל לאיבוד כל המיזמים והארכיונים הקיימים שלך." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "זאת פעולה בלתי הפיכה. החשבון, המינויים והארגונים שלך יימחקו מיידית. גם " "הגישה למיזמים ולקבצים הנוכחיים שלך תאבד." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "ארגונים שיימחקו" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s חברים" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s צוותים" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "למחוק את החשבון שלך?" @@ -3300,19 +3248,19 @@ msgstr "למחוק %s קבצים?" msgid "modals.delete-file-multi-confirm.title" msgstr "%s קבצים נמחקים" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "למחוק את סגנון הגופן הזה? הוא לא ייטען אם נעשה בו שימוש בקובץ." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "סגנון גופן נמחק" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "למחוק את הגופן הזה? הוא לא ייטען אם נעשה בו שימוש בקובץ." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "גופן נמחק" @@ -3320,19 +3268,19 @@ msgstr "גופן נמחק" msgid "modals.delete-organization-team-confirm.message" msgstr "למחוק את הצוות הזה שהוא חלק מהארגון %s?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "למחוק את העמוד הזה?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "מחיקת עמוד" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "למחוק את העמודים הנבחרים?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "מחיקת עמודים" @@ -3348,7 +3296,7 @@ msgstr "למחוק את המיזם הזה?" msgid "modals.delete-project-confirm.title" msgstr "מחיקת מיזם" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "מחיקת קובץ" @@ -3356,7 +3304,7 @@ msgstr[1] "מחיקת קבצים" msgstr[2] "מחיקת קבצים" msgstr[3] "מחיקת קבצים" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "לא מופעל באף קובץ." @@ -3364,7 +3312,7 @@ msgstr[1] "לא מופעלים באף קובץ." msgstr[2] "לא מופעלים באף קובץ." msgstr[3] "לא מופעלים באף קובץ." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "הספרייה הזאת מופעלת כאן: " @@ -3372,7 +3320,7 @@ msgstr[1] "הספריות האלו מופעלות כאן: " msgstr[2] "הספריות האלו מופעלות כאן: " msgstr[3] "הספריות האלו מופעלות כאן: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "למחוק את הקובץ?" @@ -3380,7 +3328,7 @@ msgstr[1] "למחוק את הקבצים?" msgstr[2] "למחוק את הקבצים?" msgstr[3] "למחוק את הקבצים?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "מחיקת קובץ" @@ -3400,19 +3348,19 @@ msgstr "למחוק את הצוות הזה? כל המיזמים והקבצים ש msgid "modals.delete-team-confirm.title" msgstr "צוות נמחק" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "מחיקת חבר" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "למחוק את החבר הזה מהצוות?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "למחוק חבר בצוות" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "נכסים שכבר נעשה בהם שימוש בקובץ הזה יישארו שם (אף עיצוב לא ייפגע)." @@ -3420,23 +3368,23 @@ msgstr[1] "נכסים שכבר נעשה בהם שימוש בקבצים האלה msgstr[2] "נכסים שכבר נעשה בהם שימוש בקבצים האלה יישארו שם (אף עיצוב לא ייפגע)." msgstr[3] "נכסים שכבר נעשה בהם שימוש בקבצים האלה יישארו שם (אף עיצוב לא ייפגע)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "מחיקת התליה" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "למחוק את ההתליה?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "מחיקת התליה" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "עריכת התליה" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "עריכת התליה" @@ -3452,15 +3400,15 @@ msgstr "ייבוא אסימונים" msgid "modals.import-library-tokens.title" msgstr "לייבא אסימונים מהספרייה?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "שליחת הזמנה" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "כתובות דוא״ל, מופרדות בפסיקים" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "חלק מהחברים כבר נמצאים בצוות. נזמין את השאר." @@ -3468,83 +3416,83 @@ msgstr "חלק מהחברים כבר נמצאים בצוות. נזמין את ה msgid "modals.invite-restricted-members.all-blocked-title" msgstr "לא נשלחו הזמנות" -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "כתובות שלא יקבלו הזמנה" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "ביטול" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "אפשר להזמין רק חברים קיימים מהארגון הזה." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "שליחת הזמנות" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "חלק מההזמנות לא נשלחו" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "חלק מהאנשים ברשימה שלך לא יקבלו את ההזמנות." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "אפשר להזמין חברים לצוות כדי שיוכלו לגשת לקובץ הזה ולהיחשף לכל שאר קובצי " "הצוות." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "הזמנת חברים לצוות" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "כיוון שאין עוד חברים בצוות הזה מלבדך, הצוות יימחק על כל המיזמים והקבצים שלו." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "ברצונך לעזוב את הצוות %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "אי אפשר לעזוב צוות אם אין חברים שאפשר לקדם לבעלות עליה. אולי עדיף למחוק את " "הצוות." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "הבעלות על הצוות הזה בידיך. נא לבחור מישהו כדי לקידום לבעלות בטרם עזיבתך." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "קידום ועזיבה" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "נא לבחור חבר לקידום" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "בטרם עזיבתך" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "עזיבת צוות" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "לעזוב את הצוות הזה?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "עזיבת הצוות" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "העברה" @@ -3552,7 +3500,7 @@ msgstr[1] "העברה" msgstr[2] "העברה" msgstr[3] "העברה" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "להעביר את הספרייה הזאת?" @@ -3560,7 +3508,7 @@ msgstr[1] "להעביר את הספריות האלו?" msgstr[2] "להעביר את הספריות האלו?" msgstr[3] "להעביר את הספריות האלו?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "העברת ספרייה" @@ -3568,45 +3516,45 @@ msgstr[1] "העברת ספריות" msgstr[2] "העברת ספריות" msgstr[3] "העברת ספריות" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "כמות ההינד" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "העברת בעלות" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "העברת בעלות תשנה את תפקידך לחלק מההנהלה תוך אובדן חלק מההרשאות שלך על הצוות " "הזה. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "הצוות הזה הוא כרגע בבעלותך. להפוך את %s לבעלים החדשים של הצוות?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "בעלים חדשים לצוות" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "פרסום" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "הספרייה שלך ריקה. לפרסם אותה בכל זאת?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "פרסום ספרייה ריקה" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "הינד קטן" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "ביטול פרסום" @@ -3614,7 +3562,7 @@ msgstr[1] "ביטול פרסום" msgstr[2] "ביטול פרסום" msgstr[3] "ביטול פרסום" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "לבטל את פרסום הספרייה הזאת?" @@ -3622,7 +3570,7 @@ msgstr[1] "לבטל את פרסום הספריות האלו?" msgstr[2] "לבטל את פרסום הספריות האלו?" msgstr[3] "לבטל את פרסום הספריות האלו?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "ביטול פרסום ספרייה" @@ -3648,23 +3596,23 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "עדכון רכיב בספריה משותפת" -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "הארגון שלך יימחק." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "סך כל האחסון שלך, היסטוריית גרסאות אוטומטיות ותקופת שחזור קבצים יוגבלו." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "הצוותים, המיזמים והקבצים לא יהיו עוד חלק מאף ארגון אך יישארו זמינים." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "מתי לשנמך:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "לעבור לתוכנית %s?" @@ -3672,11 +3620,11 @@ msgstr "לעבור לתוכנית %s?" msgid "nitrate.form.cancel-anytime" msgstr "התשלום יעובד בסוף תקופת הניסיון. אפשר לבטל בכל עת." -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "העמוד לא קיים" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "שגיאת 404" @@ -3692,11 +3640,11 @@ msgstr "שלחנו הודעת שחזור בדוא״ל אל" msgid "not-found.login.sent-recovery-check" msgstr "נא לבדוק בתיבת הדוא״ל שלך וללחוץ על הקישור כדי ליצור סיסמה חדשה." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "הרשמה בחינם" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "ולהתחיל להשתמש ב־Penpot תוך שניות!" @@ -3704,63 +3652,63 @@ msgstr "ולהתחיל להשתמש ב־Penpot תוך שניות!" msgid "not-found.made-with-love" msgstr "נוצר באהבה ובקוד פתוח" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "כבר ביקשת גישה לקובץ הזה." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "כבר ביקשת לגשת לקובץ הזה או לקבצים או מיזמים אחרים של הצוות הזה." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "כבר ביקשת לגשת למיזם הזה או למיזמים או קבצים אחרים של הצוות הזה." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "כבר ביקשת לגשת למיזם הזה." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "בקשת גישה" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "חשוב לזכור שכתלות בהרשאת הבעלים תישלח אליך הזמנה לצוות." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "הבקשה שלך נשלחה כראוי!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "אין לך גישה לקובץ הזה." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "מעבר ל־Penpot שלך" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "כתלות בהרשאת הבעלים, תישלח אליך הזמנה לצוות." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "קובץ Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "אין לך גישה למיזם הזה." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "מיזם" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "כדי לגשת לקובץ הזה, אפשר לבקש מבעלי הקבוצה." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "כדי לגשת למיזם הזה, אפשר לבקש מבעלי הצוות." @@ -3772,33 +3720,33 @@ msgstr "הפסקת תחזוקה: המערכת תושבת לעבודת תחזוק msgid "notifications.by-code.upgrade-version" msgstr "יש גרסה חדשה." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "ההזמנה הזאת לא זמינה יותר." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "ההזמנה נמחקה בהצלחה" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "ההזמנה נשלחה בהצלחה" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "קישור ההזמנה הועתק" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "אי אפשר למחוק את הפרופיל שלך. יש להקצות את הצוותים שלך מחדש בטרם המשך " "בתהליך." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "הפרופיל נשמר בהצלחה!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "הודעת האימות נשלחה בדוא״ל אל %s. נא לבדוק את הדוא״ל שלך!" @@ -3826,78 +3774,78 @@ msgstr "לאחר מתן שם לצוות שלך, יתאפשר לך להזמין msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "ליצור צוות" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "הזמנת חברים" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "הזמנה עם התפקיד:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "מתחילים!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "מייצר קוד אמיתי מעיצובים" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "יצירת מערכות עיצוב" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "יצירת אבטיפוס" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "עיצוב ממשק/חוויית משתמש של יישום" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "ציור קווי מתאר" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "לאיזו מטרה ישמש אותך Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "נשמח לדעת לאילו מטרות משמש אותך Penpot כדי שנוכל להמשיך ולשפר אותו למענך. " "התשובות שלך יסייעו לנו לתעדף את היכולות החדשות ולהכווין אותך לדרך שהכי נכון " "לך להתחיל בה." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "נשמח להכיר אותך" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "איזה מהכלים האלה הכי משמש אותך?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "מה אופי העבודה שלך?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "מה גודל החברה שלך?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "איפה כדאי להתחיל?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "חינוכי" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "אישי" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "עבודה" @@ -3947,11 +3895,11 @@ msgstr "להתחיל לעצב" msgid "profile.recovery.go-to-login" msgstr "מעבר למסך הכניסה" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "ניתוק" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "מעורב" @@ -3963,35 +3911,35 @@ msgstr "הסרת צבע" msgid "settings.select-this-color" msgstr "בחירת פריטים שמשתמשים בסגנון הזה" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "הערות" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "יישור לתחתית" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "יישור למרכז אופקית" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "יישור שמאלה" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "יישור ימינה" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "יישור לראש" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "יישור למרכז אנכית" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "יצירת לוח מהבחירה" @@ -3999,55 +3947,55 @@ msgstr "יצירת לוח מהבחירה" msgid "shortcuts.bold" msgstr "החלפת מצב מודגש" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "הבדל בוליאני" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "החרגה בוליאנית" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "הצלבה בוליאנית" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "איחוד בוליאני" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "שליחה אחורה" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "הרחקה לאחור" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "קידום" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "הבאה לחזית" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "פינוי ביטול" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "העתקה" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "העתקת קישור ללוח הגזירים" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "העתקת מאפיינים" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "יצירת רכיב / הגוון" @@ -4055,35 +4003,35 @@ msgstr "יצירת רכיב / הגוון" msgid "shortcuts.create-new-project" msgstr "יצירת חדש" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "גזירה" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "התרחקות" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "מחיקה" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "מחיקת מפרק" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "ניתוק רכיב" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "עיקול" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "אליפסה" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "לוח" @@ -4091,39 +4039,39 @@ msgstr "לוח" msgid "shortcuts.draw-nodes" msgstr "ציור נתיב" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "נתיב" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "מרובע" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "טקסט" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "שכפול" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "ביטול" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "ייצוא צורות" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "כיוון תקריב כדי להציג הכול" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "היפוך אופקי" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "היפוך אנכי" @@ -4147,23 +4095,23 @@ msgstr "מעבר לספריות המשותפות" msgid "shortcuts.go-to-search" msgstr "חיפוש" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "קבוצה" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "פיזור אופקית" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "הצגת/הסתרת מנשק משתמש" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "התקרבות" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "הוספת תמונה" @@ -4171,7 +4119,7 @@ msgstr "הוספת תמונה" msgid "shortcuts.italic" msgstr "החלפת מצב נטוי" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "צירוף מפרקים" @@ -4179,39 +4127,39 @@ msgstr "צירוף מפרקים" msgid "shortcuts.line-through" msgstr "החלפת מצב קו חוצה" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "הפיכה לפינה" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "הפיכה לעיקול" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "מסכה" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "מיזוג מפרקים" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "העברה" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "העברה למטה מהר" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "העברה שמאלה מהר" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "העברה ימינה מהר" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "העברה למעלה מהר" @@ -4219,333 +4167,333 @@ msgstr "העברה למעלה מהר" msgid "shortcuts.move-nodes" msgstr "העברת מפרק" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "העברה למטה" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "העברה שמאלה" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "העברה ימינה" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "העברה למעלה" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "הלוח הבא" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "לא נמצאו קיצורי דרך" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "הגדרת אטימות ל־100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "הגדרת אטימות ל־10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "הגדרת אטימות ל־20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "הגדרת אטימות ל־30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "הגדרת אטימות ל־40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "הגדרת אטימות ל־50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "הגדרת אטימות ל־60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "הגדרת אטימות ל־70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "הגדרת אטימות ל־80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "הגדרת אטימות ל־90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "בוחר צבעים" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "מעבר לסעיף הערות צופים" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "מעבר ללוח בקרה" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "מעבר לסעיף חקירת צופה" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "מעבר לסעיף תפעול משתמש" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "מעבר לסעיף תפעול צופה" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "מעבר לסביבת עבודה" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " או " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "הדבקה" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "הדבקת מאפיינים" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "מנהל תוספים" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "לוח קודם" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "ביצוע חוזר" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "שינוי שם" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "איפוס תקריב" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "התאמת גודל" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "יסודות" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "לוח בקרה" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "מציג" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "סביבת עבודה" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "בחירה בהכול" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "בחירת השכבה הבאה" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "בחירת שכבת הורה" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "בחירת השכבה הקודמת" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "הפרדת מפרקים" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "הצגת/הסתרת רשת פיקסלים" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "הצגת/הסתרת קיצורי דרך" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "הצמדה למפרקים" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "הצמדה לרשת פיקסלים" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "התחלת עריכה" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "יישור" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "עריכה" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "כללי" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "תפריט ראשי" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "שינוי שכבות" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "ניווט" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "ניווט" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "ניווט" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "לוחות" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "נתיבים" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "צורות" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "טקסטים" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "כלים" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "תקריב" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "תקריב" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "הגדרת תמונות ממוזערות" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "קיצורי מקלדת" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "החלפת מצב יישור דינמי" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "החלפת מצב נכסים" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "החלפת מצב לוח צבעים" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "החלפת מצב מיקוד" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "החלפת מילוי מסך" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "הצגת / הסתרת קווים מנחים" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "החלפת הצגת היסטוריה" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "החלפת הצגת שכבות" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "הוספת/הסרת פריסת flex" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "הוספת/הסרת פריסת רשת" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "נעילת הנבחרים" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "נעילת יחסים" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "הצגת / הסתרת סרגלים" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "הצמדה לקווים מנחים" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "הצמדה לקווים מנחים סרגלים" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "החלפת לוח טקסט" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "החלפת ערכת עיצוב" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "החלפת מצב הצגה" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "החלפת סגנון תקריב" @@ -4553,31 +4501,31 @@ msgstr "החלפת סגנון תקריב" msgid "shortcuts.underline" msgstr "החלפת מצב קו תחתי" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "ביטול" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "פירוק קבוצה" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "ביטול מסכה" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "פיזור אנכי" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "הקטנת עדשת תקריב" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "הגדלת עדשת תקריב" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "התמקדות על הנבחר" @@ -4665,70 +4613,70 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "הזמנת אנשים תוך השתתפות בתוכנית הבלתי מוגבלת" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "מינוי" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "נא להוסיף שיטת תשלום כדי להמשיך להשתמש אחרי תקופת הניסיון" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "כל היתרונות של תוכנית מקצועית וגם:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "כל יתרונות התוכנית הבלתי מוגבלת וגם:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(× %s עורכים)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "מסחרי" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "תאגידית (ניסיונית)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90 ימי שמירת גרסאות אוטומטית ושחזור קבצים" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "חשבונית חודשית מוגבלת" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "אחסון בלתי מוגבל" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "ניהול המינוי שלך" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "הוספת פרטי תשלום" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "אפשר להוסיף את פרטי התשלום שלך היית כדי להשאיר את המינוי שלך פעיל ונטול " "תקלות גם אחרי תקופת הניסיון וגם כדי להמשיך לתמוך במיזם הקוד הפתוח שלנו. לא " "יתבצע שום חיוב עדיין." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "לדלג לבינתיים ולהתחיל בהתנסות" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "נשמח לסיוע בצמיחה ולהפוך את ההתנסות שלך לפשוטה יותר" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "נכון לעכשיו, יש משתמש/ת %s על פני הצוותים שלך שיכולים לערוך." @@ -4736,130 +4684,130 @@ msgstr[1] "נכון לעכשיו, יש %s משתמשים/ות על פני הצו msgstr[2] "נכון לעכשיו, יש %s משתמשים/ות על פני הצוותים שלך שיכולים לערוך." msgstr[3] "נכון לעכשיו, יש %s משתמשים/ות על פני הצוותים שלך שיכולים לערוך." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "חשוב לציין: שנמוך לתוכנית נמוכה יותר תפחית את מקום האחסון ותקצר את " "היסטוריית הגיבויים והגרסאות." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "עורכים" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "[בעלים, מנהלים ועורכים. צופים לא נספרים כעורכים)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "אי אפשר להגדיר פחות עורכים ממה שיש לך כרגע. יש לשנות את התפקיד (עריכה/הנהלה " "לצפייה) לאנשים שלא עורכים קבצים בפועל דרך הגדרות הצוות." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "יחויב אחרי הניסיון. לא צריך כרגע כרטיס אשראי." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s** לחודש" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "החלת %s על הצוותים שלך" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "עצה: אפשר להגדיל את כמות המושבים שלך כעת ולהקדים את ההזמנות. עם יותר מ־25 " "עורכים על פני הצוותים, אנו מציעים 175$ לחודש בלי חיובים נוספים." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "חברות ב־Penpot מאז: %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "פרטים נוספים" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "תוכניות אחרות ב־Penpot" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "ארגון לחודש" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "מקצועי" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "גרסאות שנשמרות אוטומטית 7 ימים אחורה ושחזור קבצים" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10 ג״ב אחסון" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "צוותים ללא הגבלה. עד 7 עורכים על פני הצוותים בבעלותך." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "מומלץ" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "המינוי שלך" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "התחלת ניסיון בחינם" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "הרשמה למינוי" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "אפשר לערוך את המינוי שלך בכל עת מעמוד ‚מינוי’ בפרטי החשבון שלך." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "מאחלים לך הנאה מהתוכנית שלך!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "תודה על הבחירה בתוכנית %s של Penpot!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "התוכנית שלך היא %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "להתנסות בחינם למשך 14 ימים" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "ללא הגבלה" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "ללא הגבלה (התנסות)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "גרסאות שנשמרות אוטומטית 30 ימים אחורה ושחזור קבצים" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "חשבון חודשי מוגבל" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25 ג״ב אחסון" @@ -4882,7 +4830,7 @@ msgstr "כדי להגדיל את מסגרת המגבלה, נא ליצור קשר msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "כדי להגדיר את מסגרת המגבלה הזאת, נא [לשדרג את התוכנית שלך|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "הזמנה נבחרה" @@ -4890,35 +4838,35 @@ msgstr[1] "שתי הזמנות נבחרו" msgstr[2] "%s הזמנות נבחרו" msgstr[3] "%s הזמנות נבחרו" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s‏ - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "ספקי גופנים - %s‏ - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "גופנים - %s‏ - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "מיזמים - %s‏ - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "חיפוש - %s‏ - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "ספריות משותפות - %s‏ - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - חופש עיצובי לצוותים" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "הגשת משוב - Penpot" @@ -4926,7 +4874,7 @@ msgstr "הגשת משוב - Penpot" msgid "title.settings.notifications" msgstr "התראות - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "הגדרות - Penpot" @@ -4934,27 +4882,27 @@ msgstr "הגדרות - Penpot" msgid "title.settings.password" msgstr "סיסמה - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "פרופיל - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "הזמנות - %s -‏ Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "חברים - %s‏ - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "הגדרות - %s‏ - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "התליות - %s‏ - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - מצב תצוגה - Penpot" @@ -4966,99 +4914,99 @@ msgstr "%s‏ - Penpot" msgid "token-actions.detach-token" msgstr "ניתוק אסימון" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "לא נמצאו לוחות בעמוד." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "הלוח לא נמצא." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "הערות (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "לא להציג אינטראקציות" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "מסך מלא" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "חקירה (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "אינטראקציות" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "אינטראקציות (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "העתקת קישור" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "הצגת פעילויות" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "הצגת פעילויות בקליק" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "מפת אתר" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "ההעברה האחרונה הצליחה." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "(%s) יישור למרכז האופקי" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "(%s) פיזור ריווח אופקי" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "(%s) יישור שמאלה" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "(%s) יישור ימינה" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "(%s) יישור למטה" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "(%s) יישור למרכז אנכית" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "(%s) פיזור בריווח אנכי" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "(%s) יישור למעלה" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "הוספת ספרייה" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "כל המשאבים" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "צבעים" @@ -5070,7 +5018,7 @@ msgstr "הוספת צבע" msgid "workspace.assets.component-group-options" msgstr "אפשרויות קבוצת רכיבים" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "רכיבים" @@ -5078,7 +5026,7 @@ msgstr "רכיבים" msgid "workspace.assets.components.add-component" msgstr "הוספת רכיב" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s הגוונים" @@ -5086,15 +5034,15 @@ msgstr "%s הגוונים" msgid "workspace.assets.create-group" msgstr "יצירת קבוצה" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "הפריטים שלך יקבלו אוטומטית שם בסגנון „שם קבוצה / שם פריט”" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "מחיקה" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "שכפול" @@ -5102,39 +5050,39 @@ msgstr "שכפול" msgid "workspace.assets.duplicate-main" msgstr "שכפול הראשי" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "עריכה" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "סינון" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "תצוגת טבלה" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "קבוצה" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "שם קבוצה" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "תצוגת רשימה" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "ספרייה מקומית" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "ניהול ספרייה" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "לא נמצאו משאבים" @@ -5142,7 +5090,7 @@ msgstr "לא נמצאו משאבים" msgid "workspace.assets.open-library" msgstr "פתיחת קובץ ספרייה" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "שינוי שם" @@ -5150,11 +5098,11 @@ msgstr "שינוי שם" msgid "workspace.assets.rename-group" msgstr "שינוי שם קבוצה" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "חיפוש במשאבים" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "ספרייה משותפת" @@ -5166,43 +5114,43 @@ msgstr[1] "שני רכיבים" msgstr[2] "%s רכיבים" msgstr[3] "%s רכיבים" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "מיון" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "טיפוגרפיות" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "הוספת טיפוגרפיה" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "גודל" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "סגנון גופן" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "מעבר לקובץ ספריית סגנון כדי לערוך" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "ריווח תווים" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "גובה שורה" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "שצ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "התמרת טקסט" @@ -5214,63 +5162,63 @@ msgstr "פירוק קבוצה" msgid "workspace.colorpicker.color-tokens" msgstr "אסימוני צבע" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "אי אפשר לקנן את הרכיבים בתוך עצמם." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "לא ניתן לכבות/להדליק חלק מהעותקים. אי אפשר לקנן את הרכיבים בתוך עצמם." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "יצירת שטח" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "יצירת לוח" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "מיזוג תאים" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "הוספת עמודה מימין" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "הוספת עמודה משמאל" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "מחיקת עמודה" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "מחיקת עמודה וצורות" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "שכפול עמודה" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "הוספת שורה מתחת" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "הוספת שורה למעלה" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "מחיקת שורה" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "מחיקת שורה וצורות" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "שכפול שורה" @@ -5278,15 +5226,15 @@ msgstr "שכפול שורה" msgid "workspace.debug.title" msgstr "כלי ניפוי שגיאות" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "מצב מיקוד" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "מיקוד כבוי" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "מיקוד פעיל" @@ -5298,191 +5246,191 @@ msgstr "מדרג קווי" msgid "workspace.gradients.radial" msgstr "מדרג מעגלי" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "השבתת יישור דינמי" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "השבתת קנה מידה יחסי" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "השבתת הצמדה לקווים המנחים" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "השבתת הצמדה לפיקסל" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "השבתת הצמדה לקווים מנחים סרגלים" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "הפעלת יישור דינמי" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "הפעלת קנה מידה יחסי" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "הצמדה לקווים מנחים" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "הפעלת הצמדה לפיקסל" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "הצמדה לקווים מנחים סרגלים" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "הסתרת שמות לוחות" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "הסתרת קווים מנחים" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "הסתרת ערכת צבעים" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "הסתרת רשת פיקסלים" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "הסתרת סרגלים" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "הסתרת לוח גופנים" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "עריכה" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "קובץ" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "עזרה ומידע" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "העדפות" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "תצוגה" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "ביצוע מחדש" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "לבחור הכול" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "הצגת שמות לוחות" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "הצגת קווים מנחים" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "הצגת ערכת צבעים" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "הצגת רשת פיקסלים" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "הצגת סרגלים" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "הצגת לוח גופנים" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "החלפה לערכת עיצוב כהה" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "החלפה לערכת עיצוב בהירה" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "החלפה לערכת עיצוב כמו המערכת" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "החזרה" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "איפוס" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "שגיאה בשמירה" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "נשמר" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "בשמירה" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "שיתוף" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "מצב תצוגה (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "תקריב" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "מילוי - שינוי גודל כדי למלא" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "התאמה - שינוי גודל כדי להתאים" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "כיוון תקריב כדי להציג הכול" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "מסך מלא" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "התמקדות על הנבחר" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "עריכת רשת" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "יציאה מהמערכת" @@ -5490,7 +5438,7 @@ msgstr "יציאה מהמערכת" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "ריפוד מלמטה" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "הצגת אפשרויות ריפוד עם 4 צדדים" @@ -5526,11 +5474,11 @@ msgstr "בוצע" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "איתור" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "איתור פריסת הרשת" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "צבע" @@ -5538,19 +5486,19 @@ msgstr[1] "שני צבעים" msgstr[2] "%s צבעים" msgstr[3] "%s צבעים" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "אין עדיין סגנונות צבע בספרייה שלך" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "עדיין אין סוגי טיפוגרפיה בספרייה שלך" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "ספריית קבצים" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "צבעים אחרונים" @@ -5562,7 +5510,7 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "שמירת סגנון צבע" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "רכיב" @@ -5570,27 +5518,27 @@ msgstr[1] "שני רכיבים" msgstr[2] "%s רכיבים" msgstr[3] "%s רכיבים" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "מחובר אל" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "או להוסיף כמה מאלה לניסיון:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "אין ספריות משותפות בצוות שלך, אפשר לחפש אחר" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "כל מיני תבניות כאן" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "ספריית קבצים" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "פריט גרפיקה" @@ -5598,43 +5546,43 @@ msgstr[1] "שני פריטי גרפיקה" msgstr[2] "%s פריטי גרפיקה" msgstr[3] "%s פריטי גרפיקה" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "ספריות בקובץ הזה" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "ספריות" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "עדכוני ספרייה" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "בטעינה…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "אין ספריות משותפות שדורשות עדכון" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "לא נמצאו תוצאות לחיפוש אחר „%s”" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "חיפוש בספריות המשותפות" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "ספריות משותפות" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "חיבור ספרייה" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "טיפוגרפיה אחת" @@ -5642,19 +5590,19 @@ msgstr[1] "שתי טיפוגרפיות" msgstr[2] "%s טיפוגרפיות" msgstr[3] "%s טיפוגרפיות" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "ניתוק ספרייה" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "עדכון" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "הצגת כל השינויים" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "עדכונים" @@ -5690,111 +5638,111 @@ msgstr "החלת/הסרת טשטוש" msgid "workspace.options.canvas-background" msgstr "רקע משטח ציור" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "חיתוך התוכן" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "רכיב" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "הסבר" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "העתקה" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "יצירת הסבר" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "עריכת הסבר" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "ראשי" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "החלפת רכיב" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "עדיין אין נכסים בתיקייה הזאת" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "לא מקושר" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "מעבר לרכיב הראשי" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "לרכיב הזה יש הגוונים סותרים. נא לוודא שלכל אחד מההגוונים יש סדרה ייחודית של " "ערכי מאפיינים." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "איתור הגוונים כפולים" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "לחלק מההגוונים יש מאפיינים וערכים זהים" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "להגוונים האלה יש מאפיינים וערכים זהים." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "להגוון הזה יש מאפיינים וערכים זהים להגוון אחר." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "לחלק מההגוונים האלה יש מאפיינים וערכים זהים." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "לרכיב הזה יש הגוונים עם שמות שגויים. נא לוודא שכל אחד מההגוונים מיושר עם " "המבנה הנכון." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "איתור הגוונים שגויים" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "לחלק מההגוונים יש שמות שגויים" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "איתור הגוונים שגויים" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "להגוונים האלה יש שמות שגויים." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "להגוון הזה יש שם שגוי." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "לחלק מההגוונים האלה יש שם שגוי." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[מאפיין]=[ערך], [מאפיין]=[ערך]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "לנסות להשתמש במבנה הבא:" @@ -5828,7 +5776,7 @@ msgstr "יש את אותה רמת ההיררכיה." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "קבוצות, לוחות ופריסות נחשבים לשווי ערך." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "איך הגוונים נשארים מחוברים" @@ -5872,11 +5820,11 @@ msgstr "עליון" msgid "workspace.options.constraints.topbottom" msgstr "עליון ותחתון" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "עיצוב" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "ייצוא" @@ -5884,7 +5832,7 @@ msgstr "ייצוא" msgid "workspace.options.export-multiple" msgstr "ייצוא הבחירה" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "ייצוא רכיב" @@ -5892,31 +5840,31 @@ msgstr[1] "ייצוא %s רכיבים" msgstr[2] "ייצוא %s רכיבים" msgstr[3] "ייצוא %s רכיבים" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "הוספת ייצוא" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "הסרת ייצוא" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "סיומת" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "הייצוא הושלם" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "מתבצע ייצוא…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "הייצוא נכשל" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "הייצוא אטי בהגזמה" @@ -5932,7 +5880,7 @@ msgstr "הוספת מילוי" msgid "workspace.options.fill.remove-fill" msgstr "הסרת מילוי" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "שינוי גודל הלוח כדי שיתאים לתוכן" @@ -5956,67 +5904,67 @@ msgstr "הזרימה מתחילה" msgid "workspace.options.grid.auto" msgstr "אוטומטי" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "עמודות" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "צבע" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "מרזב" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "גובה" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "שול" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "הגדרה כבררת מחדל" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "תחתון" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "מרכז" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "שמאל" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "ימין" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "מתיחה" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "עליון" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "להשתמש בבררת המחדל" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "רוחב" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "שורות" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "ריבוע" @@ -6028,7 +5976,7 @@ msgstr "מילוי קבוצה" msgid "workspace.options.group-stroke" msgstr "מתאר קבוצה" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "הוספת קו מנחה" @@ -6036,19 +5984,19 @@ msgstr "הוספת קו מנחה" msgid "workspace.options.guides.remove-guide" msgstr "הסרת קו מנחה" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "קווים מנחים" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "הצגת/הסתרת קו מנחה" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "גובה" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "חקירה" @@ -6264,107 +6212,107 @@ msgstr "אינטראקציות" msgid "workspace.options.interactions.add-interaction" msgstr "הוספת אינטראקציה" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "צבע" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "צריבת צבע" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "בריחת צבע" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "החשכה" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "הבדל" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "הדרה" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "אור קשיח" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "גוון" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "הבהרה" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "תאורה" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "הכפלה" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "רגיל" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "שכבת על" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "רוויה" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "מסך" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "אור רך" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "הצגת/הסתרת שכבה" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "גובה מרבי" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "רוחב מרבי" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "גובה מזערי" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "רוחב מזערי" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "צבעים נוספים" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "צבעי ספרייה נוספים" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "עוד אסימוני צבע" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "אטימות" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "אבטיפוס" @@ -6396,19 +6344,19 @@ msgstr "צמצום רדיוס עצמאי" msgid "workspace.options.radius.show-single-corners" msgstr "הצגת רדיוס עצמאי" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "אחרונים" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "לנסות שוב" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "סיבוב" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "חיפוש גופן" @@ -6416,7 +6364,7 @@ msgstr "חיפוש גופן" msgid "workspace.options.select-a-shape" msgstr "נא לבחור צורה, לוח או קבוצה כדי לגרור חיבור ללוח אחר." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "צבעים נבחרים" @@ -6484,55 +6432,55 @@ msgstr "הצגת/הסתרת הצללה" msgid "workspace.options.show-fill-on-export" msgstr "הצגה בייצואים" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "להציג במצב הצגה" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "גודל" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "תבניות גודל" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "נעילת יחס" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "שחרור יחס" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "עיגול" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "יהלום" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "חץ" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "ללא" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "עגול" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "ריבוע" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "מרובע" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "משולש" @@ -6540,7 +6488,7 @@ msgstr "משולש" msgid "workspace.options.stroke-color" msgstr "צבע מתאר" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "עובי מתאר" @@ -6548,35 +6496,35 @@ msgstr "עובי מתאר" msgid "workspace.options.stroke.add-stroke" msgstr "הוספת צבע מתאר" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "מרכז" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "מקווקוו" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "מנוקד" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "בפנים" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "מעורב" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "בחוץ" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "הסרת מתאר" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "אחיד" @@ -6612,11 +6560,11 @@ msgstr "רוחב אוטומטי" msgid "workspace.options.text-options.grow-fixed" msgstr "קבוע" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "ריווח תווים" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "גובה שורה" @@ -6660,197 +6608,197 @@ msgstr "קו תחתי (%s)" msgid "workspace.options.use-play-button" msgstr "ניתן להשתמש בכפתור הנגינה שבכותרת כדי להריץ את תצוגת האבטיפוס." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "עובי" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "ציר X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "ציר Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "ציור מפרקים (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "צירוף מפרקים (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "לפינה (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "לעיקול (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "מיזוג מפרקים (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "העברת מפרקים (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "הפרדת מפרקים (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "הצמדת מפרקים (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "פתיחה" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "חשיפת [תוספים נוספים](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "עדיין לא מותקן אף תוסף" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "המצהר/מניפסט של התוסף שגוי." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "צריך הרשאות עריכה כדי להשתמש בתוסף הזה" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "התוסף לא קיים או שהכתובת שגויה." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "התקנה" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "תוספים מותקנים" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "מנהל תוספים" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "תוספים" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "לעדכן את התוסף הזה" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "התוסף הזה השתנה מאז שפתחת אותו. עכשיו הוא רוצה גם לגשת אל:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "התחלת הורדות קבצים." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "אחסון נתונים בדפדפן." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "קריאת התגובות שלך ומתן מענה עליהן." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "לקרוא ולשנות את ההערות שלך ולהגיב בשמך." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "קריאת התוכן של קבצים שלמשתמשים יש גישה אליהם." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "קריאה ושינוי התוכן של קבצים שלמשתמשים יש גישה אליהם." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "נא לשים לב שהתוסף הזה נוצר על ידי גוף חיצוני, לכן כדאי לוודא שהוא אמין לפי " "שמעניקים לו גישה. פרטיות ואבטחת המידע שלך חשובים לנו. במקרה של ספק נא ליצור " "קשר עם התמיכה." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "קריאת הספריות והמשאבים שלך." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "קריאה ושינוי הספריות והמשאבים שלך." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "התוסף ‚%s’ רוצה לגשת אל:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "קריאת פרטי הפרופיל של המשתמש הנוכחי." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "רשימת תוספים" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "הסרת תוסף" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "נא למלא כתובת תוסף" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "תוספים" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "לא עכשיו" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "מעניין אותך להציץ? בחירה בזה תיפתח בטיוטה חדשה לצוות הנוכחי שלך. (אם לא, " "תמיד אפשר למצוא אותו בתוספים המותקנים של כל קובץ שהוא.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "תוסף ‚%s’ מותקן למשתמש שלך!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "התנסות בתוסף" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "הוספת פריסת flex" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "הוספת פריסת רשת" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "הוספת פריסה" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "יצירת הגוון" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "הוספת מאפיין חדש" @@ -6862,7 +6810,7 @@ msgstr "הרחקה" msgid "workspace.shape.menu.backward" msgstr "הרחקה לאחור" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "שילוב כהגוון" @@ -6870,7 +6818,7 @@ msgstr "שילוב כהגוון" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "הרכיבים צריכים להיות באותו העמוד" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "העתקה" @@ -6906,27 +6854,27 @@ msgstr "העתקה כטקסט" msgid "workspace.shape.menu.create-annotation" msgstr "יצירת הסבר" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "בחירה ללוח" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "יצירת רכיב" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "יצירת מגוון רכיבים" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "גזירה" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "מחיקה" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "מחיקת התחלת זרימה" @@ -6938,7 +6886,7 @@ msgstr "ניתוק מופע" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "הפרדת מופעים" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "הבדל" @@ -6946,15 +6894,15 @@ msgstr "הבדל" msgid "workspace.shape.menu.duplicate" msgstr "שכפול" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "עריכה" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "החרגה" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "שיטוח" @@ -6966,7 +6914,7 @@ msgstr "היפוך אופקי" msgid "workspace.shape.menu.flip-vertical" msgstr "היפוך אנכי" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "התחלת זרימה" @@ -6978,31 +6926,31 @@ msgstr "קידום" msgid "workspace.shape.menu.front" msgstr "קידום לחזית" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "קבוצה" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "הסתרה" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "הצגת/הסתרת מנשק משתמש" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "הצלבה" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "נעילה" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "מסכה" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "הדבקה" @@ -7010,27 +6958,27 @@ msgstr "הדבקה" msgid "workspace.shape.menu.paste-props" msgstr "הדבקת מאפיינים" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "נתיב" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "הסרת פריסת flex" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "הסרת פריסת רשת" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "הסרת פריסה" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "הסרת מאפיין" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "להגוון חייב להיות מאפיין אחד לפחות" @@ -7054,11 +7002,11 @@ msgstr "שחזור הגוון" msgid "workspace.shape.menu.select-layer" msgstr "בחירת שכבה" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "הצגה" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "הצגה בלוח משאבים" @@ -7074,19 +7022,19 @@ msgstr "הסרת התמונה הממוזערת" msgid "workspace.shape.menu.thumbnail-set" msgstr "הגדרה כתמונה ממוזערת" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "פירוק קבוצה" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "איחוד" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "שחרור נעילה" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "ביטול מסכה" @@ -7102,51 +7050,51 @@ msgstr "צמצום סרגל צד" msgid "workspace.sidebar.expand" msgstr "הרחבת סרגל צד" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "היסטוריה" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "שכבות" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "רכיבים" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "מסנן" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "לוחות" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "קבוצות" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "תמונות" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "מסכות" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "איתור…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "צורות" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "טקסטים" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "מאפייני SVG יובאו" @@ -7158,7 +7106,7 @@ msgstr "עמודים" msgid "workspace.sidebar.sitemap.add-page" msgstr "הוספת עמוד" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "מפת אתר" @@ -7178,7 +7126,7 @@ msgstr "הוספת סדרה לקבוצה הזאת" msgid "workspace.tokens.add-token" msgstr "הוספת אסימון: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "חל על" @@ -7198,7 +7146,7 @@ msgstr "גודל כתב בסיס" msgid "workspace.tokens.base-font-size.error" msgstr "גודל הכתב הבסיסי חייב להיות ערך בפיקסלים או ללא יחידות." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "צבע" @@ -7206,7 +7154,7 @@ msgstr "צבע" msgid "workspace.tokens.create-new-theme" msgstr "אפשר ליצור את ערכת העיצוב הראשונה שלך עכשיו." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "ליצור אחד." @@ -7226,7 +7174,7 @@ msgstr "מחיקת ערכת עיצוב" msgid "workspace.tokens.duplicate" msgstr "שכפול אסימון" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "עותק" @@ -7274,7 +7222,7 @@ msgstr "תצוגה מקדימה:" msgid "workspace.tokens.export.single-file" msgstr "קובץ בודד" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "גודל גופן או {כינוי}" @@ -7282,7 +7230,7 @@ msgstr "גודל גופן או {כינוי}" msgid "workspace.tokens.font-variant-not-found" msgstr "שגיאה בהגדרת משקל/סגנון גופן. סגנון הגופן הזה לא קיים בגופן הנוכחי" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "משקל גופן (300, מודגש נטוי…) או {כינוי}" @@ -7318,7 +7266,7 @@ msgstr "בריבוי קבצים, שם / נתיב הקובץ הם השמות המ msgid "workspace.tokens.import-single-file" msgstr "בקובץ JSON יחיד, המפתחות ברמה העליונה חייבים להיות שמות הסדרה של האסימונים." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "ייבוא אסימונים" @@ -7326,17 +7274,17 @@ msgstr "ייבוא אסימונים" msgid "workspace.tokens.import-warning" msgstr "ייבוא אסימונים ידרוס את כל האסימונים הנוכחיים, הסדרות וערכות העיצוב שלך." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "לא פעילה" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "הסדרה הזאת לא פעילה. יש להחליף את ערכת העיצוב או להפעיל את הסדרה הזאת כדי " "לראות את השינויים באשנב" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "להשתמש באסימונים עצמאיים" @@ -7356,11 +7304,11 @@ msgstr "ערכת עיצוב" msgid "workspace.tokens.label.theme-placeholder" msgstr "הוספת ערכת עיצוב (למשל: בהירה)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "ריווח תווים או {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "גובה שורה (מכפיל, פיקסלים, %) או {כינוי}" @@ -7376,11 +7324,11 @@ msgstr "גודל מרבי" msgid "workspace.tokens.min-size" msgstr "גודל מזערי" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "הפניה חסרה" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "אפשר לראות את האפשרויות בלחיצה על כפתור העכבר הימני" @@ -7396,7 +7344,7 @@ msgstr "אין ערכת עיצוב פעילה" msgid "workspace.tokens.no-permisions-set" msgstr "נדרשות הרשאות עריכה כדי להפעיל / להשבית סדרות" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "נדרשות הרשאות עריכה כדי להשתמש בערכות עיצוב" @@ -7408,7 +7356,7 @@ msgstr "עדיין לא מוגדרות סדרות. נא ליצור אחת קוד msgid "workspace.tokens.no-sets-yet" msgstr "אין סדרות עדיין." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "אין ערכות עיצוב." @@ -7424,7 +7372,7 @@ msgstr "לא למפות מחדש" msgid "workspace.tokens.num-active-sets" msgstr "%s סדרות פעילות" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "ערך מקורי: %s" @@ -7436,11 +7384,11 @@ msgstr "ריפודים" msgid "workspace.tokens.radius" msgstr "רדיוס" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "נא למלא כינוי לטיפוגרפיית אסימון" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "נא למלא כינוי הצללת אסימון" @@ -7456,7 +7404,7 @@ msgstr "למפות את כל האסימונים שמשתמשים ב־`%s` ל־`% msgid "workspace.tokens.remap-warning-time" msgstr "הפעולה הזאת עלולה לארוך זמן מה." -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "ערך פתור: %s" @@ -7488,27 +7436,27 @@ msgstr "כאן אפשר להגדיר את גודל הכתב הבסיסי, שמג msgid "workspace.tokens.settings" msgstr "הגדרות אסימונים" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "הוספת הצללה" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "טשטוש" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "כיווץ פנימה" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "הסרת הצללה" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "התפרסות" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "ערך הטשטוש לא יכול להיות שלילי" @@ -7516,11 +7464,11 @@ msgstr "ערך הטשטוש לא יכול להיות שלילי" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "ערך הפריסה לא יכול להיות שלילי" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -7542,31 +7490,31 @@ msgstr "רשימת ערכות עיצוב" msgid "workspace.tokens.token-description" msgstr "תיאור" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "בחירת משפחת גופנים" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "משפחת גופנים" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "משפחת גופנים או רשימת גופנים מופרדת בפסיקים (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "שם" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "כבר קיים אסימון בנתיב: %s או בקידומת תואמת." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "אורך השם חייב להיות תו אחד לפחות" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " הוא לא שם תקף לאסימון.\n" @@ -7577,11 +7525,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "ערך" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "נא למלא ערך או כינוי עם {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "אסימונים - %s" @@ -7599,11 +7547,11 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "הסוג ‚%s’ לא נתמך (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "להשתמש בהפניה" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "הערך לא תקף" @@ -7611,55 +7559,55 @@ msgstr "הערך לא תקף" msgid "workspace.toolbar.assets" msgstr "משאבים" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "ערכת צבעים (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "הערות (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "עיקול (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "אליפסה (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "לוח (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "תמונה (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "העברה (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "נתיב (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "תוספים (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "ריבוע (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "טקסט (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "טיפוגרפיות (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "הצגת/הסתרת סרגל כלים" @@ -7672,7 +7620,7 @@ msgstr "בוצע" msgid "workspace.top-bar.view-only" msgstr "**חקירת קוד** (צפייה בלבד)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "אין שינויים היסטוריים עד כה" @@ -7812,19 +7760,19 @@ msgstr "משאב טיפוגרפי" msgid "workspace.undo.entry.unknown" msgstr "פעולה על %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "התעלמות" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "מידע נוסף" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "יש עדכונים בספריות המשותפות" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "עדכון" diff --git a/frontend/translations/hi.po b/frontend/translations/hi.po index 7d0d01d99d..96f4dba300 100644 --- a/frontend/translations/hi.po +++ b/frontend/translations/hi.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:14+0000\n" -"Last-Translator: VKing9 \n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Hindi " "\n" "Language: hi\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "पहले से खाता है?" @@ -33,7 +33,7 @@ msgstr "" "यह डेमो सेवा है, असली काम के लिए न इस्तेमाल करें, प्रोजेक्ट्स समय-समय पर " "हटाए जाएंगे।" -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "पासवर्ड भूल गए?" @@ -41,35 +41,35 @@ msgstr "पासवर्ड भूल गए?" msgid "auth.fullname" msgstr "पूरा नाम" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "मेरे खाते में लॉग इन करें" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "यहाँ लॉग इन करें।" -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "पेनपॉट एक मुफ़्त ओपन-सोर्स डिज़ाइन टूल है जो डिज़ाइन और कोड सहयोग के लिए है" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "openID" @@ -89,7 +89,7 @@ msgstr "प्रोफ़ाइल अभी सत्यापित नही msgid "auth.notifications.recovery-token-sent" msgstr "पासवर्ड रिकवरी लिंक आपके इनबॉक्स में भेज दिया गया है।" -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "टीम में सफलतापूर्वक शामिल हो गए" @@ -121,7 +121,7 @@ msgstr "पासवर्ड भूल गए?" msgid "auth.recovery-submit" msgstr "अपना पासवर्ड बदलें" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "अभी तक कोई खाता नहीं?" @@ -133,7 +133,7 @@ msgstr "हमें बताएं कि आपको डैशबोर् msgid "auth.register-account-title" msgstr "आपका नाम" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "खाता बनाएं" @@ -146,7 +146,7 @@ msgstr "खाता बनाएं" msgid "auth.terms-and-privacy-agreement" msgstr "मैं [सेवा की शर्तें](%s) और [गोपनीयता नीति](%s) से सहमत हूँ।" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "सेवा की शर्तें" @@ -166,93 +166,93 @@ msgstr "कोई रंग tokens उपलब्ध नहीं है। स msgid "comments.mentions.not-found" msgstr "@%s के लिए कोई व्यक्ति नहीं मिला" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "प्रकाशित" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "सभी पेनपॉट उपयोगकर्ता" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "क्या आप वाकई इस लिंक को हटाना चाहते हैं? अगर आप इसे हटाते हैं, तो यह किसी " "के लिए भी उपलब्ध नहीं रहेगा" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(वर्तमान)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "लिंक नष्ट करें" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "लिंक प्राप्त करें" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "लिंक सफलतापूर्वक कॉपी किया गया" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "अनुमतियाँ सेट करें" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 पृष्ठ साझा किया गया" msgstr[1] "%s पृष्ठ साझा किए गए" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "टिप्पणी कर सकते हैं" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "कोड देख सकते हैं" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "लिंक रखने वाला कोई भी व्यक्ति पहुंच सकता है" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "साझा किए गए पृष्ठ" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "साझा करने योग्य लिंक यहां दिखाई देगा" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "केवल टीम के सदस्य" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "प्रोटोटाइप्स साझा करें" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "सभी चुनें" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "अप्रकाशित" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "टीम प्रबंधन" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot टीमों के लिए है। परियोजनाओं और फ़ाइलों पर साथ काम करने के लिए " "सदस्यों को आमंत्रित करें" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "टीम बनाओ!" @@ -268,27 +268,27 @@ msgstr "फ़ाइल पुनर्स्थापित करें" msgid "dashboard.add-file" msgstr "फ़ाइल जोड़ें" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "साझी लाइब्रेरी के रूप में जोड़ें" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "ईमेल बदलें" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "कचरा साफ़ करें" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(कॉपी)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "नई टीम बनाएं" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "इस संस्करण को पिन करें" @@ -296,7 +296,7 @@ msgstr "इस संस्करण को पिन करें" msgid "dashboard.default-team-name" msgstr "आपका पेनपॉट" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "क्या आप वाकई अपने सभी डिलीट किए गए प्रोजेक्ट और फाइलों को हमेशा के लिए " @@ -308,42 +308,42 @@ msgstr "" "क्या आप वाकई %s को हमेशा के लिए हटाना चाहते हैं? यह एक अपरिवर्तनीय कार्रवाई " "है।" -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "% फ़ाइलों को सफलतापूर्वक हटा दिया गया है।।" -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "हमेशा के लिए हटाएं" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "प्रोजेक्ट हटाएँ" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "क्या आप वाकई %s प्रोजेक्ट को हमेशा के लिए हटाना चाहते हैं? आप इसे और इसमें " "मौजूद सभी फाइलों को हमेशा के लिए हटाने जा रहे हैं। यह एक अपरिवर्तनीय " "प्रक्रिया है।" -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s सफलतापूर्वक हटा दिया गया है।" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "टीम हटाएं" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "आपका ट्रैश खाली है। डिलीट की गई फ़ाइलें और प्रोजेक्ट यहाँ दिखाई देंगे।" -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "% हटा दिया जाएगा" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "पेनपोट फ़ाइल (.penpot) डाउनलोड करें" @@ -355,19 +355,19 @@ msgstr "नकल करें" msgid "dashboard.duplicate-multi" msgstr "%s फ़ाइलों की नकल करें" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "जैसे ही एक परियोजना सदस्य एक ड्राफ्ट बनाता है, वह यहाँ प्रदर्शित होगा।" -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "अभी तक कोई ड्राफ्ट नहीं है।" -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "जैसे ही एक परियोजना सदस्य एक फ़ाइल बनाता है, वह यहाँ प्रदर्शित होगी।" -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "अभी तक कोई फ़ाइल नहीं।" @@ -415,15 +415,15 @@ msgstr "एक .penpot फ़ाइल आयात करें" msgid "dashboard.empty-project.start" msgstr "शानदार चीज़ें बनाना शुरू करें" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "फ़ाइलों को हटाने के दौरान त्रुटि हुई।" -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "प्रोजेक्ट %s को डिलीट करते समय एक त्रुटि हुई।" -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "फ़ाइल %s को रिस्टोर करते समय एक त्रुटि हुई।" @@ -431,37 +431,37 @@ msgstr "फ़ाइल %s को रिस्टोर करते समय msgid "dashboard.export-binary-multi" msgstr "%s Penpot फ़ाइलें (.penpot) डाउनलोड करें" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "बोर्ड्स को PDF के रूप में निर्यात करें" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "PDF के रूप में निर्यात करें" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s में से %s तत्व चयनित किए गए" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "निर्यात" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "आप डिज़ाइन गुणों (दाएँ साइडबार के नीचे) से तत्वों में निर्यात सेटिंग्स जोड़ " "सकते हैं।" -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "पेनपोट पर निर्यात सेट करने की जानकारी।" -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "निर्यात सेटिंग वाले कोई तत्व नहीं हैं।" -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "निर्यात चयन" @@ -471,35 +471,35 @@ msgid_plural "dashboard.file-menu.delete-files-permanently-option" msgstr[0] "फ़ाइल हटाएँ" msgstr[1] "फ़ाइलें हटाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "फ़ॉन्ट गायब है" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "सभी हटा दें" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "आप जो कस्टम फ़ॉन्ट्स अपलोड करेंगे, वे यहाँ दिखाई देंगे।" -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "अभी तक कोई कस्टम फ़ॉन्ट नहीं।" -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "जब कोई परियोजना सदस्य कोई कस्टम फ़ॉन्ट अपलोड करेगा, तो वह यहां प्रदर्शित " "किया जाएगा।" -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 फ़ॉन्ट जोड़ा गया" msgstr[1] "%s फ़ॉन्ट जोड़े गए" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -509,7 +509,7 @@ msgstr "" "जाएगा। आप निम्नलिखित प्रारूपों में फ़ॉन्ट्स अपलोड कर सकते हैं: **TTF, OTF " "और WOFF** (एक ही प्रारूप पर्याप्त होगा)।" -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -519,11 +519,11 @@ msgstr "" "लाइसेंसिंग](https://www.typography.com/faq) के बारे में भी पढ़ना चाह सकते " "हैं।" -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "सभी अपलोड करें" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -534,15 +534,15 @@ msgstr "" "[Transfonter](https://transfonter.org/) का उपयोग करके वेबफ़ॉन्ट्स जनरेट " "करने और त्रुटियों को ठीक करने की सलाह देते हैं। " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "एडमिन कंसोल पर जाएं" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "पेनपॉट फ़ाइलें आयात करें" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "उफ्फ! हम इस फ़ाइल को आयात नहीं कर पाए" @@ -550,25 +550,25 @@ msgstr "उफ्फ! हम इस फ़ाइल को आयात नह msgid "dashboard.import.bad-url" msgstr "आयात विफल हुआ. टेम्पलेट URL ग़लत है" -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "सभी फ़ाइलें आयात नहीं की गई हैं" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "निम्नलिखित फ़ाइलों में त्रुटियाँ हैं:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "त्रुटिपूर्ण फ़ाइलें अपलोड नहीं की जाएंगी।" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 फ़ाइल सफलतापूर्वक आयात की गई।" msgstr[1] "%s फ़ाइलें सफलतापूर्वक आयात की गईं।" -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "कुछ फ़ाइलों में अवैध ऑब्जेक्ट्स थे जिन्हें हटा दिया गया है।" @@ -576,77 +576,49 @@ msgstr "कुछ फ़ाइलों में अवैध ऑब्जे msgid "dashboard.import.no-perms" msgstr "आपको इस टीम में आयात करने की अनुमति नहीं है" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "रंगों की प्रोसेसिंग हो रही है" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "कंपोनेंट्स की प्रोसेसिंग हो रही है" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "मीडिया की प्रोसेसिंग हो रही है" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "पृष्ठ प्रोसेस हो रहा है: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "टाइपोग्राफ़ी की प्रोसेसिंग हो रही है" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "सर्वर पर डेटा अपलोड किया जा रहा है (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "फ़ाइल अपलोड हो रही है: %s" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "आप निम्न आमंत्रणों को हटाने जा रहे हैं:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "आप निम्नलिखित को पुनः निमंत्रण भेजने जा रहे हैं:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "निमंत्रण हटाएँ" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "निमंत्रण पुनः भेजें" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "लोगों को आमंत्रित करें" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "टीम छोड़ें" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "लाइब्रेरी और टेम्पलेट्स" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "यहाँ कुछ लाइब्रेरीज़ और टेम्पलेट्स हैं जिन्हें आप अपने प्रोजेक्ट में जोड़ " "सकते हैं" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "इनमें से और अधिक एक्सप्लोर करें और जानें कि आप कैसे योगदान दे सकते हैं" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "टेम्पलेट आयात करने में समस्या आई। टेम्पलेट आयात नहीं किया गया।" -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "लाइब्रेरीज़" @@ -654,7 +626,7 @@ msgstr "लाइब्रेरीज़" msgid "dashboard.loading-files" msgstr "आपकी फ़ाइलें लोड हो रही हैं…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "आपके फ़ॉन्ट लोड हो रहे हैं…" @@ -674,39 +646,39 @@ msgstr "%s फ़ाइलों को स्थानांतरित कर msgid "dashboard.move-to-other-team" msgstr "दूसरी टीम में स्थानांतरित करें" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ नई फ़ाइल" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "नई फ़ाइल" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ नया प्रोजेक्ट" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "नया प्रोजेक्ट" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "“%s“ के लिए कोई मेल नहीं मिला" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "पिन किए गए प्रोजेक्ट यहां दिखाई देंगे" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "सूचनाएँ" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "आपका ईमेल पता सफलतापूर्वक अपडेट कर दिया गया है" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "आपका ईमेल पता सफलतापूर्वक सत्यापित हो गया है" @@ -718,11 +690,11 @@ msgstr "सूचना सेटिंग्स अपडेट कर दी msgid "dashboard.notifications.password-saved" msgstr "पासवर्ड सफलतापूर्वक सहेजा गया!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "सूचनाएं देखें" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s सदस्य" @@ -730,15 +702,15 @@ msgstr "%s सदस्य" msgid "dashboard.open-in-new-tab" msgstr "फ़ाइल को नए टैब में खोलें" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "विकल्प" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "भूमिका के अनुसार क्रमबद्ध करें" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "स्थिति के अनुसार क्रमबद्ध करें" @@ -762,7 +734,7 @@ msgstr "आप अब इस टीम के मालिक (Owner) बन ग msgid "dashboard.permissions-change.viewer" msgstr "आप अब इस टीम के दृश्यकर्ता (Viewer) बन गए हैं।" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "पिन/अनपिन" @@ -778,11 +750,11 @@ msgstr "प्लगइन मैनिफेस्ट को पार्स msgid "dashboard.plugins.try-plugin" msgstr "प्लगइन प्रयास करें: " -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "प्रोजेक्ट्स" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "क्या आप अपना खाता हटाना चाहते हैं?" @@ -790,35 +762,35 @@ msgstr "क्या आप अपना खाता हटाना चाह msgid "dashboard.removed-from-team" msgstr "अब आप टीम “%s“ का हिस्सा नहीं हैं।" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "सेटिंग्स सहेजें" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "खोजना…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "“%s“ की खोज की जा रही है…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "UI भाषा चुनें" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "थीम चुनें" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "पेनपॉट डार्क (डिफ़ॉल्ट)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "पेनपॉट लाइट" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "सिस्टम थीम" @@ -882,11 +854,11 @@ msgstr "सेटिंग्स अपडेट करें" msgid "dashboard.settings.notifications.title" msgstr "सूचनाएं" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "सभी फ़ाइलें दिखाएँ" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "संस्करण इतिहास" @@ -910,7 +882,7 @@ msgstr[1] "आपकी फ़ाइलों को सफलतापूर् msgid "dashboard.success-duplicate-project" msgstr "आपका प्रोजेक्ट सफलतापूर्वक डुप्लिकेट किया गया है" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "आपकी फ़ाइल सफलतापूर्वक स्थानांतरित कर दी गई है" @@ -922,90 +894,90 @@ msgstr "आपकी फ़ाइलें सफलतापूर्वक स msgid "dashboard.success-move-project" msgstr "आपका प्रोजेक्ट सफलतापूर्वक स्थानांतरित कर दिया गया है" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "टीम जानकारी" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "टीम के सदस्यगण" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "टीम प्रोजेक्ट्स" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "अपने प्रोजेक्ट में जोड़ें" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "UI थीम" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "खोज के परिणाम" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "परिणाम खोजने के लिए टाइप करें" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "लाइब्रेरी अप्रकाशित करें" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "सेटिंग्स अपडेट करें" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "सक्रिय है" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "जब यह हुक ट्रिगर होगा, तो घटना विवरण भेजा जाएगा" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "आप केवल वेबहुक्स को हटा या संशोधित कर सकते हैं जो आपने बनाए हैं।" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "सामग्री प्रकार" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "वेबहुक बनाएं" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "वेबहुक सफलतापूर्वक बनाया गया।" -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "वेबहुक्स एक सरल तरीका हैं जिससे अन्य वेबसाइटें और ऐप्स Penpot में कुछ " "घटनाएँ होने पर सूचित हो सकें। हम आपके द्वारा दिए गए प्रत्येक URL पर POST " "अनुरोध भेजेंगे।" -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "\"वेबहुक जोड़ें\" बटन दबाएं एक जोड़ने के लिए।" -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "अभी तक कोई वेबहुक नहीं बनाया गया है।" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "आपका खाता" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "ईमेल" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "आपका नाम" @@ -1021,11 +993,11 @@ msgstr "ध्यान दें" msgid "ds.component-subtitle" msgstr "अपडेट करने के लिए घटक:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "अनुमति देना" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "रद्द करें" @@ -1045,7 +1017,7 @@ msgstr "सक्रिय सेट या थीम में कोई ला msgid "ds.inputs.numeric-input.no-matches" msgstr "कोई मेल नहीं मिले।" -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "token सूची खोलें" @@ -1061,15 +1033,15 @@ msgstr "इस प्रोफाइल के लिए ऑथ प्रोव msgid "errors.auth-provider-not-configured" msgstr "प्रमाणीकरण प्रदाता कॉन्फ़िगर नहीं किया गया है।" -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "ऐसा लगता है कि आप प्रमाणित नहीं हैं या सत्र की अवधि समाप्त हो गई है।" -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "फ़ॉन्ट %s लोड नहीं किया जा सका" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "फ़ॉन्ट %s लोड नहीं हो सके" @@ -1081,41 +1053,41 @@ msgstr "मीडिया फ़ाइल अपलोड नहीं की msgid "errors.character-limit-exceeded" msgstr "अक्षर सीमा पार हो गई है" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "आपका ब्राउज़र यह कार्य नहीं कर सकता" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "टिप्पणी में एक त्रुटि हुई है" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "माफ़ कीजिए! यह एक पुरानी फ़ाइल है जो पेनपॉट की अप्रचलित संपत्ति प्रकार का " "उपयोग करती है और आप इसे खोल नहीं सकते।" -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "ताकि हम आपकी सहायता कर सकें।" -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "हालाँकि पेनपॉट अब इस प्रकार की संपत्तियों का समर्थन नहीं करता, आप फिर भी" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "हमसे संपर्क करें" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "किसी पैरेंट को उसकी स्वयं की चाइल्ड पथ पर ड्रॉप नहीं किया जा सकता।" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "ईमेल पहले से उपयोग में है" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "ईमेल पहले ही सत्यापित किया जा चुका है।" @@ -1127,32 +1099,32 @@ msgstr "आप अपना ईमेल पासवर्ड के रूप msgid "errors.email-does-not-match-invitation" msgstr "ईमेल आमंत्रण से मेल नहीं खाता।" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "डोमेन की अनुमति नहीं है" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "ईमेल «%s» के कई स्थायी बाउंस रिपोर्ट्स हैं।" -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "" "ईमेल «%s» को स्पैम के रूप में रिपोर्ट किया गया है या स्थायी रूप से बाउंस " "हुआ है।" -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "ऐसा लगता है कि आप एक ऐसी फ़ाइल खोल रहे हैं जिसमें '%s' सुविधा सक्षम है, " "लेकिन वर्तमान Penpot संस्करण इसे समर्थन नहीं करता या इसे अक्षम कर दिया गया " "है।" -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "फीचर '%s' समर्थित नहीं है।" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "इसमें अधिकतम %s वर्ण होने चाहिए।" @@ -1160,7 +1132,7 @@ msgstr "इसमें अधिकतम %s वर्ण होने चा msgid "errors.field-min-length" msgstr "इसमें कम से कम 1 अक्षर अवश्य होना चाहिए।" -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "रिक्त क्षेत्र" @@ -1168,18 +1140,18 @@ msgstr "रिक्त क्षेत्र" msgid "errors.field-not-all-whitespace" msgstr "नाम में केवल खाली स्थान के अलावा कोई अन्य अक्षर होना चाहिए।" -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "ऐसा लगता है कि सक्षम सुविधाओं और उस फ़ाइल की सुविधाओं के बीच असंगति है जिसे " "आप खोलने का प्रयास कर रहे हैं। फ़ाइल खोलने से पहले '%s' के लिए माइग्रेशन " "लागू करना आवश्यक है।" -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "कुछ ग़लत हुआ है।" -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "वेब वर्कर के साथ कुछ गलत हो गया है।" @@ -1195,7 +1167,7 @@ msgstr "अमान्य डेटा" msgid "errors.invalid-email" msgstr "कृपया एक मान्य ईमेल दर्ज करें" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "पुष्टिकरण ईमेल मेल खाना चाहिए" @@ -1219,11 +1191,11 @@ msgstr "यह आमंत्रण रद्द हो सकता है य msgid "errors.ldap-disabled" msgstr "LDAP प्रमाणीकरण अक्षम है।" -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "आप '%s' सीमा तक पहुंच चुके हैं। सहायता से संपर्क करें।" -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "एक ही अनुरोध में निमंत्रित किए जा सकने वाले अधिकतम (%s) ईमेल की संख्या " @@ -1241,21 +1213,21 @@ msgstr "ऐसा लगता है कि छवि की सामग्र msgid "errors.media-type-not-allowed" msgstr "ऐसा लगता है कि यह एक मान्य छवि नहीं है।" -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "जिस प्रोफाइल को आप निमंत्रित कर रहे हैं उसके ईमेल म्यूट किए गए हैं (स्पैम " "रिपोर्ट्स या उच्च बाउंस के कारण)।" -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "माइग्रेशन प्रगति पर है" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "केवल संस्करण निर्माता ही इसे लॉक कर सकता है" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "केवल संस्करण निर्माता ही इसे अनलॉक कर सकता है" @@ -1267,7 +1239,7 @@ msgstr "पुष्टिकरण पासवर्ड मेल खाना msgid "errors.password-too-short" msgstr "पासवर्ड कम से कम 8 अक्षरों का होना चाहिए" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "क्लिपबोर्ड में अमान्य डेटा है" @@ -1275,7 +1247,7 @@ msgstr "क्लिपबोर्ड में अमान्य डेटा msgid "errors.profile-blocked" msgstr "प्रोफ़ाइल अवरुद्ध है" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "आपकी प्रोफ़ाइल के ईमेल म्यूट किए गए हैं (स्पैम रिपोर्ट्स या उच्च बाउंस के " @@ -1285,25 +1257,25 @@ msgstr "" msgid "errors.registration-disabled" msgstr "पंजीकरण वर्तमान में अक्षम है।" -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG अमान्य या त्रुटिपूर्ण है" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "असंगत सुविधा '%s' का पता चला" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "टीम छोड़ने के लिए सदस्यों की संख्या अपर्याप्त है, शायद आप इसे हटाना चाहते " "हैं।" -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "जिस सदस्य को आप असाइन करने की कोशिश कर रहे हैं वह मौजूद नहीं है।" -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "मालिक टीम नहीं छोड़ सकता, आपको मालिक की भूमिका पुनः असाइन करनी होगी।" @@ -1311,15 +1283,15 @@ msgstr "मालिक टीम नहीं छोड़ सकता, आप msgid "errors.token-set-already-exists" msgstr "एक समान नाम वाला सेट पहले से मौजूद है" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "ड्रॉप पूरा नहीं किया जा सकता, उसी नाम का सेट पथ पर पहले से मौजूद है।" -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "समान नाम वाला थीम विकल्प मौजूद है" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "पंक्ति की ऊँचाई फ़ॉन्ट आकार पर निर्भर करती है। हल किया गया मान प्राप्त करने " @@ -1341,11 +1313,11 @@ msgstr "आयात त्रुटि:" msgid "errors.tokens.invalid-color" msgstr "अमान्य रंग मान: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "Invalid token value: आप केवल फ़ॉन्ट-परिवार token का संदर्भ ले सकते हैं" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "अमान्य फ़ॉन्ट भार मान: संख्यात्मक मान (100-950) या मानक नाम (पतला, हल्का, " @@ -1366,35 +1338,35 @@ msgstr "" "टोकन नामों में केवल अक्षर और अंक होने चाहिए, जो डॉट (.) से अलग किए गए हों, " "और $ चिह्न से शुरू नहीं होने चाहिए।" -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "अमान्य छाया प्रकार: केवल 'innerShadow' या 'dropShadow' स्वीकार किए जाते हैं" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "अमान्य token मान: केवल कोई नहीं, अपरकेस, लोअरकेस या कैपिटलाइज़ स्वीकार किए " "जाते हैं" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "" "अमान्य token मान: केवल कोई नहीं, रेखांकित और स्ट्राइक-थ्रू स्वीकार किए जाते " "हैं" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "अमान्य मूल्य: एक समग्र छाया टोकन का संदर्भ देना चाहिए।।" -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "अमान्य मान: एक संयुक्त टाइपोग्राफी token का संदर्भ होना चाहिए।" -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "अमान्य टोकन मान: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "गुम टोकन संदर्भ: " @@ -1406,31 +1378,31 @@ msgstr "इस फ़ाइल में कोई टोकन, सेट य msgid "errors.tokens.number-too-large" msgstr "अमान्य टोकन मान. हल किया गया मान बहुत बड़ा है: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "अपारदर्शिता 0 और 100% या 0 और 1 (जैसे 50% या 0.5) के बीच होनी चाहिए।" -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "टोकन में स्व-संदर्भ होता है" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "छाया धुंधलापन 0 से अधिक या उसके बराबर होना चाहिए।" -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "छाया प्रसार 0 से अधिक या उसके बराबर होना चाहिए।" -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "स्ट्रोक की चौड़ाई 0 से बड़ी या उसके बराबर होनी चाहिए।" -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "अमान्य मान: % की अनुमति नहीं है।" -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "अमान्य मान: इकाइयाँ अनुमति नहीं हैं।" @@ -1438,47 +1410,47 @@ msgstr "अमान्य मान: इकाइयाँ अनुमति msgid "errors.unexpected-error" msgstr "एक अप्रत्याशित त्रुटि हुई है।" -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "अज्ञात टोकन" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "यह संस्करण पहले से ही लॉक है" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "यह संस्करण लॉक है और इसे अन्य लोग हटा नहीं सकते" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "फ़ाइल का संस्करण संख्या असंगत है" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "कनेक्शन त्रुटि, URL उपलब्ध नहीं" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL सत्यापन पास नहीं करता है।" -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "अंतिम डिलीवरी सफल नहीं रही।" -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "SSL सत्यापन में त्रुटि हुई है।" -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "समय समाप्त" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "सत्यापन के दौरान अप्रत्याशित त्रुटि हुई" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "अप्रत्याशित स्थिति %s" @@ -1498,17 +1470,17 @@ msgstr "वर्णन" msgid "feedback.description-placeholder" msgstr "कृपया अपनी प्रतिक्रिया का कारण बताएँ" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "हमें खुशी है कि आप यहाँ हैं। यदि आपको सहायता की आवश्यकता हो, तो कृपया पोस्ट " "करने से पहले खोजें।" -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "पेनपॉट समुदाय" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "हमसे संपर्क करने के अन्य तरीके" @@ -1532,11 +1504,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "हमसे संपर्क करें" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "यहाँ आपकी तकनीकी प्रश्नों में मदद के लिए।" -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X समर्थन खाता" @@ -1592,7 +1564,7 @@ msgstr "आकृति की भरण छवि का पूर्वाव msgid "inspect.attributes.shadow" msgstr "छाया" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "आकार और स्थिति" @@ -1600,39 +1572,27 @@ msgstr "आकार और स्थिति" msgid "inspect.attributes.stroke" msgstr "स्ट्रोक" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "केंद्र" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "अंदर" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "बाहर की ओर" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "टाइपोग्राफी" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "फ़ॉन्ट परिवार" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "फ़ॉन्ट आकार" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "फ़ॉन्ट शैली" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "अक्षरांतर" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "लाइन ऊँचाई" @@ -1648,11 +1608,11 @@ msgstr "" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "रेखांकित (अंडरलाइन)" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "प्रमुख अक्षर करना" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "छोटे अक्षर" @@ -1664,7 +1624,7 @@ msgstr "कोई नहीं" msgid "inspect.attributes.typography.text-transform.unset" msgstr "अप्रयुक्त करें" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "बड़े अक्षर" @@ -1790,47 +1750,47 @@ msgstr "हल किया गया मान:" msgid "inspect.tabs.styles.variants-panel" msgstr "भिन्न गुण" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "सभी को पढ़ा हुआ मार्क करें" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "शॉर्टकट्स" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "पेनपोट के बारे में" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "स्वीकार करें" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "सक्रिय" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "जोड़ें" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "कस्टम फ़ॉन्ट जोड़ें" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "जोड़ा जा रहा है..।" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "प्रशासक" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "एडोब XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "सभी" @@ -1838,17 +1798,17 @@ msgstr "सभी" msgid "labels.and" msgstr "तथा" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "पीछे" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "ऐसा लगता है कि आपको थोड़ी देर प्रतीक्षा करनी होगी और फिर से प्रयास करना " "होगा; हम अपने सर्वरों का छोटा रखरखाव कर रहे हैं।" -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "खराब गेटवे" @@ -1856,15 +1816,15 @@ msgstr "खराब गेटवे" msgid "labels.blur" msgstr "धुंधला" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "रद्द करें" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "बंद करें" @@ -1872,7 +1832,7 @@ msgstr "बंद करें" msgid "labels.code" msgstr "कोड" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "संकुचित करें" @@ -1884,15 +1844,15 @@ msgstr "रंग" msgid "labels.comment" msgstr "टिप्पणी" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "टिप्पणियाँ" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "समुदाय" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "समुदाय & योगदान" @@ -1904,19 +1864,19 @@ msgstr "परिकलित" msgid "labels.confirm-password" msgstr "पासवर्ड पुष्टि करें" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "समर्थन से संपर्क करें" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "हमसे संपर्क करें" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "जारी रखें" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "आप पेनपॉट खाते के साथ जारी रख सकते हैं" @@ -1928,31 +1888,31 @@ msgstr "कापी" msgid "labels.copy-color" msgstr "रंग कॉपी करें" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "लिंक कॉपी" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "कैलिडोस © 2019-वर्तमान" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "निर्माण" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "नई टीम निर्माण" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "नई टीम का नाम दर्ज करें" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "डैशबोर्ड" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "मिटाना" @@ -1964,7 +1924,7 @@ msgstr "टिप्पणी हटाएं" msgid "labels.delete-comment-thread" msgstr "थ्रेड हटाएं" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "निमंत्रण हटाएं" @@ -1972,19 +1932,19 @@ msgstr "निमंत्रण हटाएं" msgid "labels.delete-multi-files" msgstr "%s फ़ाइलें हटाएं" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "विकास" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "छोड़ना" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "%s डाउनलोड करें" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "ड्राफ्ट्स" @@ -1992,27 +1952,27 @@ msgstr "ड्राफ्ट्स" msgid "labels.duplicate" msgstr "नक़ल" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "संपादन करें" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "संपादक" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "रिक्त" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "त्रुटि" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "अवधि समाप्त" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "निर्यात" @@ -2024,7 +1984,7 @@ msgstr "प्रतिक्रिया अक्षम की गई है" msgid "labels.feedback-sent" msgstr "प्रतिक्रिया भेजी गई" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2032,55 +1992,55 @@ msgstr "Figma" msgid "labels.fill" msgstr "भरना" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "फ़ॉन्ट परिवार" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "शैलियाँ" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "फ़ॉन्ट्स" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "GitHub रिपॉज़िटरी" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "प्रतिक्रिया दें" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "वापस जाएँ" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "ग्राफ़िक डिज़ाइन" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "सहायता केंद्र" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "मदद & सीखना" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "छुपाएँ" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "सुलझाए गए टिप्पणियाँ छुपाएँ" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "आयात" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "निष्क्रिय" @@ -2088,49 +2048,49 @@ msgstr "निष्क्रिय" msgid "labels.info" msgstr "जानकारी" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "स्थापित फ़ॉन्ट" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "कुछ बुरा हुआ।" -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "" "आप ऑपरेशन पुनः प्रयास कर सकते हैं या त्रुटि की रिपोर्ट करने के लिए समर्थन " "से संपर्क कर सकते हैं।" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "आंतरिक त्रुटि" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision(इंविज़न)" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "आमंत्रण" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "भाषा" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "लेआउट" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "अध्ययन केन्द्र" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "लाइब्रेरी और टेम्पलेट्स" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "लोड हो रहा है…" @@ -2138,7 +2098,7 @@ msgstr "लोड हो रहा है…" msgid "labels.lock" msgstr "ताला" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "लॉग इन या साइन अप" @@ -2146,19 +2106,19 @@ msgstr "लॉग इन या साइन अप" msgid "labels.login" msgstr "लॉग इन करें" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "लॉग आउट" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "मार्केटिंग" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "सदस्य" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "सदस्यों" @@ -2170,43 +2130,43 @@ msgstr "उल्लेख" msgid "labels.new-password" msgstr "नया पासवर्ड" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "अगला" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "आप पूरी तरह से अपडेट हैं! नई टिप्पणी सूचनाएँ यहाँ दिखेंगी।" -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "कोई लंबित निमंत्रण नहीं है।" -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "अपने लोगों को इकट्ठा करें और साथ मिलकर महान चीजें बनाएं।" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "उफ!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "सूचनाएं" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 फ़ाइल" msgstr[1] "%s फ़ाइलें" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 बोर्ड" msgstr[1] "%s बोर्ड्स" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 परियोजना" @@ -2216,39 +2176,39 @@ msgstr[1] "%s परियोजनाएँ" msgid "labels.old-password" msgstr "पुराना पासवर्ड" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "विकल्प" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "अन्य (स्पष्ट करें)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "अन्य" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "स्वामी" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "पासवर्ड" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "लंबित" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "पेनपॉट चेंजलॉग" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "पेनपॉट हब" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "पिन किए गए प्रोजेक्ट" @@ -2256,27 +2216,27 @@ msgstr "पिन किए गए प्रोजेक्ट" msgid "labels.post" msgstr "डाक" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "पिछला" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "उत्पाद या UX डिज़ाइन" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "उत्पाद प्रबंधन" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "प्रोफ़ाइल" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "परियोजनाओं" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "संदर्भ" @@ -2284,23 +2244,23 @@ msgstr "संदर्भ" msgid "labels.refresh" msgstr "ताज़ा करें" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "रिलीज़ नोट्स" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "हटाना" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "सदस्य निकालें" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "नाम बदलें" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "टीम का नाम बदलें" @@ -2324,11 +2284,11 @@ msgstr "नया उत्तर" msgid "labels.reply.thread" msgstr "उत्तर" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "पुन: भेजें" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "आमंत्रण पुनः भेजें" @@ -2336,43 +2296,43 @@ msgstr "आमंत्रण पुनः भेजें" msgid "labels.restore" msgstr "पुनः स्थापित करें" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "पुन: प्रयास करें" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "भूमिका" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "सहेजें" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "खोज" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "फ़ॉन्ट खोजें" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "विकल्प चुनें" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "भेजना" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "भेजा जा रहा है…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "हमारे सिस्टम्स की निर्धारित रखरखाव प्रक्रिया चल रही है।" -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "सेवा अनुपलब्ध है" @@ -2380,55 +2340,55 @@ msgstr "सेवा अनुपलब्ध है" msgid "labels.sets" msgstr "सेट्स" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "सेटिंग्स" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "छाया" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "साझा करें" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "लाइब्रेरीज़" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "दिखाएँ" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "सभी टिप्पणियाँ दिखाएँ" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "टिप्पणियाँ सूची दिखाएँ" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "सिर्फ आपके उल्लेख दिखाएँ" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "सिर्फ आपकी टिप्पणियाँ दिखाएँ" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "रेखाचित्र" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "स्त्रोत" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "प्रारंभ" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "स्थिति" @@ -2436,7 +2396,7 @@ msgstr "स्थिति" msgid "labels.stroke" msgstr "स्ट्रोक" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "छात्र या शिक्षक" @@ -2448,7 +2408,7 @@ msgstr "शैलियों" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "बदलना" @@ -2456,19 +2416,19 @@ msgstr "बदलना" msgid "labels.text" msgstr "मूलपाठ" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "थीम्स" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "ट्यूटोरियल्स" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "अक्षर विन्यास" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "अज्ञात त्रुटि" @@ -2480,31 +2440,31 @@ msgstr "अनलॉक" msgid "labels.unpublish-multi-files" msgstr "%s फ़ाइलें अप्रकाशित करें" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "अद्यतन" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "टीम को अद्यतन करें" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "अपलोड करें" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "कस्टम फ़ॉन्ट्स अपलोड करें" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "अपलोड हो रहा है…" -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "रूपांतर" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "संस्करण %s नोट्स" @@ -2512,7 +2472,7 @@ msgstr "संस्करण %s नोट्स" msgid "labels.view-only" msgstr "केवल देखें" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "दर्शक" @@ -2520,7 +2480,7 @@ msgstr "दर्शक" msgid "labels.visibility" msgstr "दृश्यता" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "वेबहुक" @@ -2528,11 +2488,11 @@ msgstr "वेबहुक" msgid "labels.write-new-comment" msgstr "नई टिप्पणी लिखें" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(आप)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "आपका खाता" @@ -2669,61 +2629,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "\"%s\" को साझा लाइब्रेरी के रूप में जोड़ें" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "बड़ा धक्का" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "नया ईमेल सत्यापित करें" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "हम आपकी पहचान सत्यापित करने के लिए आपके वर्तमान ईमेल “%s” पर एक ईमेल " "भेजेंगे।" -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "नया ईमेल" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "ईमेल बदले" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "अपना ईमेल बदलें" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "वेबहुक बनाएं" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "वेबहुक बनाएं" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "पेलोड URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "रद्द करें और मेरा खाता रखें" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "हाँ, मेरा खाता हटाएं" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "अपने खाते को हटाने पर आप अपनी सभी वर्तमान परियोजनाएँ और अभिलेख खो देंगे।" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "\"क्या आप वाकई अपना खाता हटाना चाहते हैं?" @@ -2773,31 +2733,31 @@ msgstr "क्या आप वाकई %s फ़ाइलें हटान msgid "modals.delete-file-multi-confirm.title" msgstr "%s फ़ाइलें हटाई जा रही हैं" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "क्या आप वाकई इस फ़ॉन्ट स्टाइल को हटाना चाहते हैं? यदि यह किसी फ़ाइल में " "उपयोग हो रहा है तो यह लोड नहीं होगा।" -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "फ़ॉन्ट शैली हटाना" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "\"क्या आप वाकई इस फ़ॉन्ट को हटाना चाहते हैं? यदि यह किसी फ़ाइल में उपयोग हो " "रहा है तो यह लोड नहीं होगा।" -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "फ़ॉन्ट हटाया जा रहा है" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "क्या आप वाकई इस पृष्ठ को हटाना चाहते हैं?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "पृष्ठ हटाएँ" @@ -2813,31 +2773,31 @@ msgstr "क्या आप वाकई इस परियोजना को msgid "modals.delete-project-confirm.title" msgstr "परियोजना हटाएँ" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "फ़ाइल हटाएँ" msgstr[1] "फ़ाइलें हटाएँ" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "यह किसी भी फ़ाइल में सक्रिय नहीं है।" msgstr[1] "वे किसी भी फ़ाइल में सक्रिय नहीं हैं।" -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "यह लाइब्रेरी यहाँ सक्रिय है: " msgstr[1] "ये लाइब्रेरियाँ यहाँ सक्रिय हैं: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "क्या आप वाकई इस फ़ाइल को हटाना चाहते हैं?" msgstr[1] "क्या आप वाकई इन फ़ाइलों को हटाना चाहते हैं?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "फ़ाइल हटाई जा रही है" @@ -2857,19 +2817,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "टीम हटाई जा रही है" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "सदस्य हटाएँ" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "क्या आप वाकई इस सदस्य को टीम से हटाना चाहते हैं?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "टीम सदस्य हटाएँ" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -2879,165 +2839,165 @@ msgstr[1] "" "उन फ़ाइलों में पहले से उपयोग किए गए एसेट्स वहीं रहेंगे (कोई डिज़ाइन टूटेगा " "नहीं)।" -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "वेबहुक हटाएँ" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "क्या आप वाकई इस वेबहुक को हटाना चाहते हैं?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "वेबहुक हटाया जा रहा है" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "वेबहुक संपादित करें" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "वेबहुक संपादित करें" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "निमंत्रण भेजना" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "ईमेल, अल्पविराम से अलग किए गए" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "कुछ ईमेल वर्तमान टीम के सदस्यों के हैं। उनकी निमंत्रण नहीं भेजी जाएगी।" -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "आप टीम के सदस्यों को आमंत्रित कर सकते हैं ताकि वे इस फ़ाइल और सभी टीम " "फ़ाइलों तक पहुंच सकें।" -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "सदस्यों को टीम में आमंत्रित करें" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "चूंकि आप इस टीम के एकमात्र सदस्य हैं, टीम अपने परियोजनाओं और फ़ाइलों के साथ " "हटा दी जाएगी।" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "क्या आप वाकई %s टीम छोड़ना चाहते हैं?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "यदि टीम में मालिक बनाने के लिए कोई अन्य सदस्य नहीं है, तो आप टीम नहीं छोड़ " "सकते। आप टीम को हटाना चाह सकते हैं।" -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "आप इस टीम के मालिक हैं। कृपया टीम छोड़ने से पहले किसी अन्य सदस्य को मालिक " "बनाएं।" -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "पदोन्नत करें और छोड़ें" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "पदोन्नति के लिए सदस्य चुनें" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "छोड़ने से पहले" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "टीम छोड़ें" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "क्या आप सचमुच इस टीम को छोड़ना चाहते हैं?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "टीम छोड़ रहे हैं" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "स्थानांतरित करें" msgstr[1] "स्थानांतरित करें" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "क्या आप वाकई इस लाइब्रेरी को स्थानांतरित करना चाहते हैं?" msgstr[1] "क्या आप वाकई इन लाइब्रेरियों को स्थानांतरित करना चाहते हैं?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "लाइब्रेरी स्थानांतरित करें" msgstr[1] "लाइब्रेरियाँ स्थानांतरित करें" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "झटका मात्रा" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "स्थानांतरण स्वामित्व" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "यदि आप स्वामित्व स्थानांतरित करते हैं, तो आपकी भूमिका एडमिन में बदल जाएगी, " "और इस टीम पर कुछ अनुमतियाँ आप खो देंगे। " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "आप इस टीम के वर्तमान स्वामी हैं। क्या आप वाकई %s को टीम का नया स्वामी बनाना " "चाहते हैं?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "नई टीम स्वामी" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "प्रकाशित" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "आपकी लाइब्रेरी खाली है। क्या आप वाकई इसे प्रकाशित करना चाहते हैं?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "खाली लाइब्रेरी प्रकाशित करें" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "छोटा धक्का" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "अप्रकाशित करें" msgstr[1] "अप्रकाशित करें" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "क्या आप वाकई इस लाइब्रेरी को अप्रकाशित करना चाहते हैं?" msgstr[1] "क्या आप वाकई इन लाइब्रेरियों को अप्रकाशित करना चाहते हैं?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "लाइब्रेरी अप्रकाशित करें" @@ -3061,11 +3021,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "साझा लाइब्रेरी में एक घटक अपडेट करें" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "यह पृष्ठ मौजूद नहीं है" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 त्रुटि" @@ -3083,11 +3043,11 @@ msgstr "हमने एक पुनर्प्राप्ति ईमेल msgid "not-found.login.sent-recovery-check" msgstr "अपना ईमेल जांचें और नया पासवर्ड बनाने के लिए लिंक पर क्लिक करें।" -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "मुफ्त में साइन अप" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "और सेकंडों में पेनपॉट का उपयोग शुरू करें!" @@ -3095,69 +3055,69 @@ msgstr "और सेकंडों में पेनपॉट का उप msgid "not-found.made-with-love" msgstr "प्यार और ओपन सोर्स के साथ बनाया गया" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "आपने पहले ही इस फ़ाइल तक पहुँच का अनुरोध किया है।" -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "आपने इस फ़ाइल या इस टीम की अन्य फ़ाइलों या परियोजनाओं तक पहुँच का पहले ही " "अनुरोध किया है।" -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "आपने इस परियोजना या इस टीम की अन्य परियोजनाओं या फ़ाइलों तक पहुँच का पहले " "ही अनुरोध किया है।" -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "आपने इस परियोजना तक पहुँच का पहले ही अनुरोध किया है।" -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "पहुँच का अनुरोध करें" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "" "ध्यान रखें कि, यदि स्वामी अनुमति देता है, तो आपको टीम में आमंत्रित किया " "जाएगा।" -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "आपका अनुरोध सफलतापूर्वक भेज दिया गया है!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "आपके पास इस फ़ाइल तक पहुँच नहीं है।" -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "अपने पेनपॉट पर जाएं" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "यदि स्वामी अनुमति देता है, तो आपको टीम में आमंत्रित किया जाएगा।" -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "पेनपॉट फ़ाइल" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "आपके पास इस परियोजना तक पहुँच नहीं है।" -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "परियोजना" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "इस फ़ाइल तक पहुँचने के लिए, आप टीम के स्वामी से पूछ सकते हैं।" -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "इस परियोजना तक पहुँचने के लिए, आप टीम के स्वामी से पूछ सकते हैं।" @@ -3169,29 +3129,29 @@ msgstr "रखरखाव विराम: हम 5 मिनट के भी msgid "notifications.by-code.upgrade-version" msgstr "एक नया संस्करण उपलब्ध है ।" -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "आमंत्रण सफलतापूर्वक हटा दिया गया" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "आमंत्रण सफलतापूर्वक भेज दिया गया" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "आमंत्रण लिंक कॉपी किया गया" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "आप अपनी प्रोफ़ाइल हटा नहीं सकते। आगे बढ़ने से पहले अपनी टीमों को पुनः असाइन " "करें।" -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "प्रोफ़ाइल सफलतापूर्वक सहेज ली गई!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "%s को सत्यापन ईमेल भेजा गया है। अपना ईमेल जांचें!" @@ -3221,78 +3181,78 @@ msgstr "" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "टीम बनाएं" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "सदस्यों को आमंत्रित करो" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "भूमिका के साथ आमंत्रित करें:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "चलिए शुरू करते हैं!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "डिज़ाइन से वास्तविक कोड उत्पन्न करना" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "डिज़ाइन सिस्टम बनाना" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "प्रोटोटाइपिंग" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "किसी ऐप का UI/UX डिज़ाइन करें" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "वायरफ़्रेमिंग" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "आप पेनपोट का उपयोग किस लिए करेंगे?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "कृपया हमें अपने बारे में थोड़ा बताएं ताकि हम पेनपोट को आपके लिए बेहतर बना " "सकें। आपके जवाब हमें नए फीचर्स की प्राथमिकता तय करने और आपको सही दिशा में " "मार्गदर्शन करने में मदद करेंगे।" -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "हमें आपको जानने में मदद करें" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "इन उपकरणों में से आप सबसे अधिक किसका उपयोग करते हैं?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "आप किस तरह का काम करते हैं?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "आपकी कंपनी का आकार क्या है?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "आप कहां से शुरुआत करना चाहेंगे?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "शिक्षा" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "निजी" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "काम" @@ -3342,11 +3302,11 @@ msgstr "डिजाइनिंग शुरू करें" msgid "profile.recovery.go-to-login" msgstr "लॉगइन पर जाएं" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "अलग करें" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "मिश्रित" @@ -3358,35 +3318,35 @@ msgstr "रंग हटाओ" msgid "settings.select-this-color" msgstr "इस शैली का उपयोग करके आइटम चुनें" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "टिप्पणियाँ" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "नीचे संरेखित करें" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "क्षैतिज रूप से केंद्र संरेखित करें" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "बाएँ संरेखित करें" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "दाएँ संरेखित करें" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "ऊपर संरेखित करें" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "ऊर्ध्वाधर रूप से केंद्र संरेखित करें" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "चयन से बोर्ड बनाएं" @@ -3394,55 +3354,55 @@ msgstr "चयन से बोर्ड बनाएं" msgid "shortcuts.bold" msgstr "बोल्ड टॉगल करें" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "बूलियन अंतर" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "बूलियन बहिष्कृत" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "बूलियन चौराहा" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "बूलियन संघ" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "पीछे भेजो" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "पीछे भेजें" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "आगे लाओ" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "सामने लाओ" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "पूर्ववत साफ़ करें" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "कापी" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "लिंक कॉपी करें" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "गुण कॉपी करें" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "घटक/संस्करण बनाएं" @@ -3450,35 +3410,35 @@ msgstr "घटक/संस्करण बनाएं" msgid "shortcuts.create-new-project" msgstr "नया निर्माण" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "काटना" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "ज़ूम आउट" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "मिटाना" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "नोड हटाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "घटक अलग करें" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "वक्र" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "अंडाकार" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "बोर्ड" @@ -3486,39 +3446,39 @@ msgstr "बोर्ड" msgid "shortcuts.draw-nodes" msgstr "पथ बनाएं" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "पथ" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "आयत" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "पाठ" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "नकल करें" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "रद्द करें" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "आकृतियाँ निर्यात करें" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "सभी को फिट करने के लिए ज़ूम करें" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "क्षैतिज फ्लिप" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "लंबवत पलटें" @@ -3542,23 +3502,23 @@ msgstr "साझा लाइब्रेरीज़ पर जाएँ" msgid "shortcuts.go-to-search" msgstr "खोज" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "समूह" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "क्षैतिज रूप से वितरित करें" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "UI दिखाएँ/छिपाएँ" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "ज़ूम इन करें" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "चित्र डालें" @@ -3566,7 +3526,7 @@ msgstr "चित्र डालें" msgid "shortcuts.italic" msgstr "इटैलिक टॉगल करें" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "नोड्स जोड़ें" @@ -3574,39 +3534,39 @@ msgstr "नोड्स जोड़ें" msgid "shortcuts.line-through" msgstr "लाइन को टॉगल करें" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "कोना बनाओ" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "वक्र बनाओ" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "मास्क" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "नोड्स को जोड़ें" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "हिलाएँ" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "तेज़ी से नीचे ले जाएँ" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "तेज़ी से बाएँ ले जाएँ" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "तेज़ी से दाएँ ले जाएँ" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "तेज़ी से ऊपर ले जाएँ" @@ -3614,333 +3574,333 @@ msgstr "तेज़ी से ऊपर ले जाएँ" msgid "shortcuts.move-nodes" msgstr "नोड को स्थानांतरित करें" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "नीचे ले जाएँ" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "बाएँ ले जाएँ" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "दाएँ ले जाएँ" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "ऊपर ले जाएँ" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "अगला बोर्ड" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "कोई शॉर्टकट नहीं मिला" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "अपारदर्शिता 100% करें" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "अपारदर्शिता 10% करे" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "अपारदर्शिता 20% करें" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "अपारदर्शिता 30% करें" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "अपारदर्शिता 40% करें" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "अपारदर्शिता 50% करें" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "अपारदर्शिता 60% करें" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "अपारदर्शिता 70% करें" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "अपारदर्शिता 80% करें" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "अपारदर्शिता 90% करें" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "रंग चयनकर्ता" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "दर्शक टिप्पणी अनुभाग पर जाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "डैशबोर्ड पर जाएँ" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "दर्शक निरीक्षण अनुभाग पर जाएँ" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "दर्शक इंटरैक्शन अनुभाग पर जाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "दर्शक इंटरैक्शन अनुभाग में जाएँ" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "वर्कस्पेस पर जाएँ" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " अथवा " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "चिपकाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "गुण चिपकाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "प्लगइन्स प्रबंधक" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "पिछला बोर्ड" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "फिर से करें" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "नाम बदलें" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "ज़ूम रीसेट करें" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "आकार बदलना" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "बुनियादी बातें" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "डैशबोर्ड" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "दर्शक" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "कार्यस्थान" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "सभी चुनें" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "अगली परत चुनें" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "मुख्य परत चुनें" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "पिछली परत का चयन करें" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "अलग नोड्स" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "पिक्सेल ग्रिड दिखाएँ / छिपाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "शॉर्टकट दिखाएँ / छुपाएँ" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "नोड्स से चिपकाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "पिक्सेल ग्रिड से चिपकाएँ" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "संपादन शुरू करें" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "संतुलन" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "संपादन" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "सार्वभौमिक" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "मुख्य मेन्यू" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "परतें संशोधित करें" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "मार्गदर्शन" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "मार्गदर्शन" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "मार्गदर्शन" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "पैनल्स" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "रास्ते" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "आकृतियाँ" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "टेक्स्ट" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "औजार" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "ज़ूम" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "ज़ूम" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "थंबनेल सेट करें" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "कीबोर्ड शॉर्टकट्स" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "डायनामिक अलाइनमेंट टॉगल करें" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "एसेट्स टॉगल करें" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "रंग पैलेट टॉगल करें" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "फ़ोकस मोड टॉगल करें" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "संपूर्ण स्क्रीन टॉगल करें" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "गाइड दिखाएँ/छिपाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "इतिहास टॉगल करें" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "लेयर्स टॉगल करें" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "फ्लेक्स लेआउट जोड़ें / हटाएं" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "ग्रिड लेआउट जोड़ें/हटाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "ताला लगाएं / ताला खोलें" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "अनुपात लॉक करें" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "रूलर दिखाएँ / छिपाएँ" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "गाइड्स से चिपकाएं" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "रूलर गाइड्स से चिपकाएं" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "टेक्स्ट पैलेट चालू/बंद करें" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "थीम बदलें" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "दिखाएं / छिपाएं" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "ज़ूम शैली टॉगल करें" @@ -3948,31 +3908,31 @@ msgstr "ज़ूम शैली टॉगल करें" msgid "shortcuts.underline" msgstr "रेखांकन चालू/बंद करें" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "पूर्ववत" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "असमूहीकृत" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "मास्क हटाएं" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "ऊर्ध्वाधर समान वितरण करें" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "ज़ूम लेंस घटाएं" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "ज़ूम लेंस बढ़ाएं" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "चयनित पर ज़ूम करें" @@ -4065,203 +4025,203 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "असीमित योजना के दौरान लोगों को आमंत्रित करना" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "सदस्यता" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "ट्रायल के बाद जारी रखने के लिए भुगतान का तरीका जोड़ें" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "सभी प्रोफेशनल प्लान लाभ और:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "सभी अनलिमिटेड प्लान लाभ और:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s संपादक)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "उद्यम" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "एंटरप्राइज़ (परीक्षण)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90-दिन के ऑटोसेव संस्करण और फ़ाइल पुनर्प्राप्ति" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "फ्लैट मासिक बिल" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "असीमित भंडारण" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "अपनी सदस्यता प्रबंधित करें" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "भुगतान विवरण जोड़ें" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "परीक्षण अवधि के बाद अपनी सदस्यता को सुचारू रूप से जारी रखने और हमारे " "ओपन-सोर्स प्रोजेक्ट का समर्थन जारी रखने के लिए अभी अपनी भुगतान जानकारी " "जोड़ें। आपसे अभी कोई शुल्क नहीं लिया जाएगा।" -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "अभी छोड़ें और परीक्षण शुरू करें" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "हमें आगे बढ़ने में मदद करें और अपने परीक्षण को आसान बनाएं" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "वर्तमान में, आपकी टीम में %s व्यक्ति हैं जो संपादन कर सकते हैं।" msgstr[1] "वर्तमान में, आपकी टीम में %s लोग हैं जो संपादन कर सकते हैं।" -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "ध्यान दें: कम प्लान पर स्विच करने का मतलब कम संग्रहण और कम अवधि के बैकअप " "तथा संस्करण इतिहास है।" -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "संपादनकर्ता" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(स्वामी, व्यवस्थापक और संपादक। दर्शकों को संपादक नहीं माना जाएगा)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "आप मौजूदा संपादकों की संख्या से कम संपादक नहीं सेट कर सकते। टीम सेटिंग में " "उन लोगों की भूमिका (संपादक/व्यवस्थापक से दर्शक) बदलें जो वास्तव में फ़ाइलें " "संपादित नहीं करते हैं।" -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "परीक्षण के बाद शुल्क लिया जाएगा। अभी क्रेडिट कार्ड की आवश्यकता नहीं है।" -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "$7 प्रति संपादक/महीना x %s" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "%s को अपनी टीमों पर लागू करें" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "सुझाव: आमंत्रणों से आगे रहने के लिए आप अभी अपनी सीटों की संख्या बढ़ा सकते " "हैं। 25+ संपादकों वाली टीमों में, आपको प्रति माह ₹175 का एकमुश्त शुल्क " "मिलेगा।" -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "%s से Penpot सदस्य" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "अधिक जानकारी" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "अन्य पेनपॉट योजनाएँ" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "प्रति माह संगठन" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "प्रोफेशनल" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7-दिन का स्वतः सहेजा गया संस्करण और फ़ाइल पुनर्प्राप्ति" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10GB स्टोरेज" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "असीमित टीमें। आपकी स्वामित्व वाली टीमों में अधिकतम 8 संपादक।" -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "अनुशंसित" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "आपकी सदस्यता" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "नि:शुल्क ट्रायल शुरू करें" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "सदस्यता लें" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "आप अपने खाते के विवरण में 'सदस्यता' पृष्ठ से कभी भी अपनी सदस्यता संपादित कर " "सकते हैं।" -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "अपनी योजना का आनंद लें!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "पेनपोट %s योजना चुनने के लिए धन्यवाद!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "आप %s हैं!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "14 दिनों का मुफ्त परीक्षण करें" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "असीमित" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "असीमित (परीक्षण)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30-दिन का स्वतः सहेजा गया संस्करण और फ़ाइल पुनर्प्राप्ति" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "मासिक बिल की सीमा" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GB स्टोरेज" @@ -4288,41 +4248,41 @@ msgstr "" "यदि आप इस सीमा को बढ़ाना चाहते हैं, तो [अपना प्लान अपग्रेड " "करें|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 आमंत्रण चयनित" msgstr[1] "%s आमंत्रण चयनित" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - पेनपॉट" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "फ़ॉन्ट प्रदाता - %s - पेनपॉट" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "फ़ॉन्ट्स - %s - पेनपॉट" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "परियोजनाएँ - %s - पेनपॉट" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "खोज - %s - पेनपॉट" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "साझा लाइब्रेरी - %s - पेनपॉट" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "पेनपॉट - टीमों के लिए डिज़ाइन की स्वतंत्रता" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "प्रतिक्रिया दें - पेनपॉट" @@ -4330,7 +4290,7 @@ msgstr "प्रतिक्रिया दें - पेनपॉट" msgid "title.settings.notifications" msgstr "सूचनाएं - पेनपॉट" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "सेटिंग्स - पेनपॉट" @@ -4338,27 +4298,27 @@ msgstr "सेटिंग्स - पेनपॉट" msgid "title.settings.password" msgstr "पासवर्ड - पेनपॉट" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "प्रोफ़ाइल - पेनपॉट" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "निमंत्रण - %s - पेनपॉट" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "सदस्य - %s - पेनपॉट" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "सेटिंग्स - %s - पेनपॉट" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "वेबहुक - %s - पेनपोट" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - दृश्य मोड - पेनपोट" @@ -4370,99 +4330,99 @@ msgstr "%s - पेनपोट" msgid "token-actions.detach-token" msgstr "token अलग करें" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "पृष्ठ पर कोई बोर्ड नहीं मिला।" -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "बोर्ड नहीं मिला।" -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "टिप्पणियाँ (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "इंटरैक्शन न दिखाएं" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "पूर्ण स्क्रीन" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "निरीक्षण करें (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "इंटरैक्शन" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "अंतर्क्रियाएँ (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "लिंक कॉपी करें" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "इंटरैक्शन दिखाएँ" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "क्लिक पर इंटरैक्शन दिखाएं" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "साइटमैप" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "पिछली डिलीवरी सफल रही।" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "क्षैतिज केंद्र पर संरेखित करें (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "क्षैतिज अंतराल वितरित करें (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "बाएँ संरेखित करें (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "दाएँ संरेखित करें (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "नीचे संरेखित करें (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "ऊर्ध्वाधर केंद्र पर संरेखित करें (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "ऊर्ध्वाधर अंतराल वितरित करें (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "ऊपर संरेखित करें (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "लाइब्रेरी जोड़ें" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "सभी एसेट्स" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "रंगों" @@ -4474,7 +4434,7 @@ msgstr "रंग जोड़ें" msgid "workspace.assets.component-group-options" msgstr "घटक समूह विकल्प" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "घटकों" @@ -4482,7 +4442,7 @@ msgstr "घटकों" msgid "workspace.assets.components.add-component" msgstr "घटक जोड़ें" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s प्रकार" @@ -4490,15 +4450,15 @@ msgstr "%s प्रकार" msgid "workspace.assets.create-group" msgstr "एक समूह बनाएं" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "आपके आइटम अपने आप “ग्रुप नाम / आइटम नाम” के रूप में नामित किए जाएंगे" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "मिटाना" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "नकल करें" @@ -4506,39 +4466,39 @@ msgstr "नकल करें" msgid "workspace.assets.duplicate-main" msgstr "मुख्य की नकल करें" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "संपादन" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "फिल्टर" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "जाल दृश्य" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "समूह" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "समूह नाम" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "सूची दृश्य" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "स्थानीय लाइब्रेरी" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "लाइब्रेरी मैनेज करें" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "कोई एसेट्स नहीं मिले" @@ -4546,7 +4506,7 @@ msgstr "कोई एसेट्स नहीं मिले" msgid "workspace.assets.open-library" msgstr "लाइब्रेरी फ़ाइल खोलें" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "नाम बदलें" @@ -4554,11 +4514,11 @@ msgstr "नाम बदलें" msgid "workspace.assets.rename-group" msgstr "समूह का नाम बदलें" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "एसेट्स खोजें" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "साझा लाइब्रेरी" @@ -4568,43 +4528,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 घटक" msgstr[1] "%s घटकों" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "क्रमबद्ध" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "टाइपोग्राफ़ी" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "टाइपोग्राफी जोड़ें" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "आकार" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "फ़ॉन्ट शैली" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "संपादन के लिए स्टाइल लाइब्रेरी फ़ाइल पर जाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "पत्र अंतराल" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "रेखा ऊँचाई" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "पाठ रूपांतरण" @@ -4616,77 +4576,77 @@ msgstr "असमूहीकृत" msgid "workspace.colorpicker.color-tokens" msgstr "रंग टोकन" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "घटकों को अपने अंदर नहीं रखा जा सकता।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "कुछ प्रतियों को स्विच नहीं किया जा सका। घटकों को आपस में नेस्ट नहीं किया जा " "सकता।" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "क्षेत्र बनाएं" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "बोर्ड बनाएं" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "कोष्ठक जोड़ें" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "दाईं ओर 1 कॉलम जोड़ें" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "बाईं ओर 1 कॉलम जोड़ें" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "कॉलम हटाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "स्तंभ और आकृतियाँ हटाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "डुप्लिकेट कॉलम" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "नीचे 1 पंक्ति जोड़ें" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "ऊपर 1 पंक्ति जोड़ें" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "पंक्ति हटाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "पंक्ति और आकृतियाँ हटाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "डुप्लिकेट पंक्ति" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "फोकस मोड" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "ध्यान हटाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "ध्यान केंद्रित करें" @@ -4698,191 +4658,191 @@ msgstr "रैखिक ग्रेडिएंट" msgid "workspace.gradients.radial" msgstr "वृत्तीय ग्रेडिएंट" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "गतिशील संरेखण अक्षम करें" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "अनुपातिक स्केल अक्षम करें" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "मार्गदर्शकों से स्नैप बंद करें" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "पिक्सेल से स्नैप अक्षम करें" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "रूलर गाइड से स्नैप अक्षम करें" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "डायनामिक संरेखण सक्षम करें" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "आनुपातिक पैमाना सक्षम करें" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "गाइड्स से स्नैप" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "पिक्सेल पर स्नैप सक्षम करें" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "रूलर गाइड पर स्नैप करें" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "बोर्ड के नाम छिपाएँ" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "गाइड छिपाएँ" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "रंग पैलेट छिपाएँ" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "पिक्सेल ग्रिड छिपाएँ" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "रूलर छुपाएं" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "फ़ॉन्ट पैलेट छुपाएं" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "संपादन" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "फ़ाइल" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "सहायता और जानकारी" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "प्राथमिकताएँ" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "देखना" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "फिर से करें" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "सबका चयन करें" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "बोर्डों के नाम दिखाएँ" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "गाइड दिखाएँ" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "रंग पैलेट दिखाएँ" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "पिक्सेल ग्रिड दिखाएँ" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "रूलर दिखाएं" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "फ़ॉन्ट पैलेट दिखाएं" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "डार्क थीम पर स्विच करें" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "लाइट थीम पर स्विच करें" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "सिस्टम थीम पर स्विच करें" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "पूर्ववत" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "रीसेट करें" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "सहेजने में त्रुटि" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "सहेजा गया" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "सहेजा जा रहा है" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "शेयर" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "दृश्य मोड (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "ज़ूम" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "भरें - भरने के लिए पैमाना" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "फिट - फिट करने के लिए स्केल डाउन करें" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "सभी को फिट करने के लिए ज़ूम करें" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "पूर्ण स्क्रीन" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "चयनित पर ज़ूम करें" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "ग्रिड संपादित करें" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "प्रस्थान" @@ -4890,7 +4850,7 @@ msgstr "प्रस्थान" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "नीचे पैडिंग" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "4 ओर के पैडिंग विकल्प दिखाएं" @@ -4926,29 +4886,29 @@ msgstr "पूर्ण" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "स्थान खोजें" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "ग्रिड लेआउट का स्थान खोजें" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 रंग" msgstr[1] "%s रंग" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "आपकी लाइब्रेरी में अभी कोई रंग शैली नहीं है" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "आपकी लाइब्रेरी में अभी कोई टाइपोग्राफी शैली नहीं है" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "फ़ाइल लाइब्रेरी" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "हाल के रंग" @@ -4960,93 +4920,93 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "रंग शैली सहेजें" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 घटक" msgstr[1] "%s घटक" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "से जुड़ा" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "या इनमें से कुछ को जोड़कर देखें:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "आपकी टीम में कोई साझा लाइब्रेरी नहीं है, आप खोज सकते हैं" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "यहाँ कुछ टेम्पलेट्स हैं" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "फ़ाइल लाइब्रेरी" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 ग्राफ़िक" msgstr[1] "%s ग्राफ़िक्स" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "इस फ़ाइल में लाइब्रेरीज़" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "लाइब्रेरीज़" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "लाइब्रेरी अपडेट" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "लोड हो रहा है…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "ऐसी कोई साझा लाइब्रेरी नहीं है जिसे अद्यतन करने की आवश्यकता हो" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "“%s“ के लिए कोई मिलान नहीं मिला" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "साझा लाइब्रेरी खोजें" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "साझा लाइब्रेरीज़" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "लाइब्रेरी कनेक्ट करें" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 अक्षर विन्यास" msgstr[1] "%s अक्षर विन्यास" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "लाइब्रेरी डिस्कनेक्ट करें" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "अद्यतन" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "सभी परिवर्तन देखें" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "अपडेट" @@ -5082,117 +5042,117 @@ msgstr "धुंधलापन टॉगल करें" msgid "workspace.options.canvas-background" msgstr "कैनवास पृष्ठभूमि" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "क्लिप सामग्री" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "घटक" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "व्याख्या" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "कापी" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "एक व्याख्या बनाएं" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "व्याख्या संपादित करें" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "मुख्य" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "स्वैप घटक" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "इस लाइब्रेरी में अभी तक कोई संपत्ति नहीं है" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "अनलिंक" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "परस्पर विरोधी वेरिएंट का पता लगाएं" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "इस घटक में परस्पर विरोधी वैरिएंट हैं। सुनिश्चित करें कि प्रत्येक वैरिएंट " "में गुण मानों का एक विशिष्ट सेट हो।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "डुप्लिकेट वेरिएंट का पता लगाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "कुछ वेरिएंट में समान गुण और मूल्य होते हैं" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "इन वेरिएंट के गुण और मान समान हैं। मानों को समायोजित करें ताकि उन्हें " "पुनर्प्राप्त किया जा सके।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "इस संस्करण के गुण और मान दूसरे संस्करण के समान हैं। मानों को समायोजित करें " "ताकि उन्हें पुनर्प्राप्त किया जा सके।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "इनमें से कुछ वेरिएंट के गुण और मान समान हैं। मानों को समायोजित करें ताकि " "उन्हें पुनर्प्राप्त किया जा सके।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "इस घटक के कुछ वेरिएंट अमान्य नामों वाले हैं। सुनिश्चित करें कि प्रत्येक " "वेरिएंट सही संरचना का पालन कर रहा है।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "अमान्य वेरिएंट का पता लगाएं" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "कुछ वेरिएंट के नाम अमान्य हैं" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "अमान्य वेरिएंट का पता लगाएं" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "इन वेरिएंट के नाम अमान्य हैं।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "इस संस्करण का नाम अमान्य है।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "इनमें से कुछ रूपों के नाम अमान्य हैं।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[संपत्ति]=[मूल्य], [संपत्ति]=[मूल्य]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "निम्नलिखित संरचना का उपयोग करने का प्रयास करें:" @@ -5228,7 +5188,7 @@ msgstr "समान पदानुक्रम स्तर रखें।" msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "समूह, बोर्ड और लेआउट को समतुल्य माना जाता है।" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "वेरिएंट कैसे जुड़े रहते हैं" @@ -5272,11 +5232,11 @@ msgstr "शीर्ष" msgid "workspace.options.constraints.topbottom" msgstr "शीर्ष और तल" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "डिज़ाइन" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "निर्यात" @@ -5284,37 +5244,37 @@ msgstr "निर्यात" msgid "workspace.options.export-multiple" msgstr "निर्यात चयन" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "1 तत्व निर्यात करें" msgstr[1] "%s तत्व निर्यात करें" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "निर्यात जोड़ें" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "निर्यात हटाएँ" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "प्रत्यय" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "निर्यात पूर्ण" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "निर्यात हो रहा है…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "निर्यात विफल" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "निर्यात अप्रत्याशित रूप से धीमा" @@ -5330,7 +5290,7 @@ msgstr "भरण जोड़ें" msgid "workspace.options.fill.remove-fill" msgstr "भराव हटाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "सामग्री को फिट करने के लिए बोर्ड का आकार बदलें" @@ -5354,67 +5314,67 @@ msgstr "प्रवाह प्रारंभ हो जाता है" msgid "workspace.options.grid.auto" msgstr "स्वत:" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "कॉलम" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "रंग" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "मध्य अंतराल" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "ऊंचाई" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "मार्जिन" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "डिफ़ॉल्ट के रूप में सेट करें" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "तल" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "केंद्र" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "बाएं" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "दाहिने" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "खींचना" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "शीर्ष" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "डिफ़ॉल्ट उपयोग करें" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "चौड़ाई" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "पंक्तियाँ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "चौकोर" @@ -5426,7 +5386,7 @@ msgstr "समूह भरण" msgid "workspace.options.group-stroke" msgstr "समूह स्ट्रोक" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "मार्गदर्शक जोड़ें" @@ -5434,19 +5394,19 @@ msgstr "मार्गदर्शक जोड़ें" msgid "workspace.options.guides.remove-guide" msgstr "मार्गदर्शक हटाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "मार्गदर्शक" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "मार्गदर्शक टॉगल करें" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "ऊंचाई" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "निरीक्षण करें" @@ -5662,107 +5622,107 @@ msgstr "इंटरैक्शन" msgid "workspace.options.interactions.add-interaction" msgstr "इंटरैक्शन जोड़ें" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "रंग" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "रंग जलना" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "रंग डॉज" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "अंधेरा करना" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "भेद" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "बहिष्करण" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "हार्ड लाइट" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Hue" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "हल्का" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "प्रकाशीयता" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "गुणा करना" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "सामान्य" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "ओवरले" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "संतृप्ति" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "स्क्रीन" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "कोमल प्रकाश" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "परत दृश्यता टॉगल करें" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "अधिकतम.ऊंचाई" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "अधिकतम.चौड़ाई" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "न्यूनतम.ऊंचाई" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "न्यूनतम.चौड़ाई" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "और अधिक रंग" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "और लाइब्रेरी रंग" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "अधिक रंग के टोकन" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "ओपेसिटी" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "प्रोटोटाइप" @@ -5794,19 +5754,19 @@ msgstr "स्वतंत्र त्रिज्या को संक्ष msgid "workspace.options.radius.show-single-corners" msgstr "स्वतंत्र त्रिज्या दिखाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "हाल ही का" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "पुन: प्रयास करें" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "चक्कर" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "फ़ॉन्ट खोजें" @@ -5814,7 +5774,7 @@ msgstr "फ़ॉन्ट खोजें" msgid "workspace.options.select-a-shape" msgstr "किसी कनेक्शन को दूसरे बोर्ड तक ले जाने के लिए एक शेप, बोर्ड या ग्रुप चुनें।" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "चयनित रंग" @@ -5882,55 +5842,55 @@ msgstr "छाया टॉगल करें" msgid "workspace.options.show-fill-on-export" msgstr "निर्यात में दिखाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "दृश्य मोड में दिखाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "आकार" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "आकार प्रीसेट" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "लॉक अनुपात" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "अनलॉक अनुपात" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "चक्र" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "हीरा" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "तीर" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "कोई नहीं" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "गोल" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "चौकोर" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "आयत" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "त्रिकोण" @@ -5938,7 +5898,7 @@ msgstr "त्रिकोण" msgid "workspace.options.stroke-color" msgstr "स्ट्रोक का रंग" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "स्ट्रोक की चौड़ाई" @@ -5946,35 +5906,35 @@ msgstr "स्ट्रोक की चौड़ाई" msgid "workspace.options.stroke.add-stroke" msgstr "स्ट्रोक रंग जोड़ें" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "केंद्र" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "टूटी रेखा" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "बिंदुदार" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "अंदर" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "मिश्रित" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "बाहर की ओर" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "स्ट्रोक हटाओ" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "ठोस" @@ -6010,11 +5970,11 @@ msgstr "ऑटो चौड़ाई" msgid "workspace.options.text-options.grow-fixed" msgstr "निश्चित" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "अक्षरांतर" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "रेखा ऊँचाई" @@ -6058,128 +6018,128 @@ msgstr "अंडरलाइन (%s)" msgid "workspace.options.use-play-button" msgstr "प्रोटोटाइप व्यू चलाने के लिए हेडर में प्ले बटन का उपयोग करें।" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "चौड़ाई" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X अक्ष" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y अक्ष" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "नोड ड्रा करें (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "नोड्स जोड़ें (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "कोने तक (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "वक्र बनाएं (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "नोड्स मर्ज करें (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "नोड्स ले जाएँ (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "अलग नोड्स (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "स्नैप नोड्स (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "खुला" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "खोजें [अधिक प्लगइन्स](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "अभी तक कोई प्लगइन्स इंस्टॉल नहीं है" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "प्लगइन मैनिफेस्ट गलत है।" -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "इस प्लगइन का उपयोग करने के लिए आपको एक संपादक होना आवश्यक है" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "प्लगइन मौजूद नहीं है या URL सही नहीं है।" -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "इंस्टॉल" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "इंस्टॉल किए गए प्लगइन्स" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "प्लगइन्स मैनेजर" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "प्लग-इन" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "इस प्लगइन को अद्यतन करें" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "आपके द्वारा पिछली बार खोलने के बाद से इस प्लगइन में बदलाव किए गए हैं। अब यह " "निम्न तक पहुँच प्राप्त करना चाहता है:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "फ़ाइल डाउनलोड प्रारंभ करें।" -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "ब्राउज़र में डेटा संग्रहीत करें।" -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "अपनी टिप्पणियाँ और उत्तर पढ़ें।" -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "आपकी टिप्पणियाँ पढ़ें और उनमें संशोधन करें, और आपके नाम से उत्तर दें।" -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "उन फ़ाइलों की सामग्री पढ़ें जिन तक उपयोगकर्ताओं को पहुँच प्राप्त है।" -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "" "उन फ़ाइलों की सामग्री पढ़ें और संशोधित करें जिन तक उपयोगकर्ताओं को पहुँच " "प्राप्त है।" -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "कृपया ध्यान दें कि यह प्लगइन किसी बाहरी पक्ष द्वारा बनाया गया है, इसलिए " @@ -6187,74 +6147,74 @@ msgstr "" "आपकी डेटा गोपनीयता और सुरक्षा हमारे लिए महत्वपूर्ण है। यदि आपको कोई चिंता " "हो, तो कृपया सहायता केंद्र से संपर्क करें।" -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "आपकी लाइब्रेरी और संसाधनों को पढ़ें।" -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "आपकी लाइब्रेरी और संसाधनों को पढ़ें और संशोधित करें।" -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "'%s' प्लगइन को निम्न तक पहुँच चाहिए:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "वर्तमान उपयोगकर्ता की प्रोफ़ाइल जानकारी पढ़ें।" -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "प्लगइन्स सूची" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "प्लगइन हटाएँ" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "एक प्लगइन URLलिखें" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "प्लग-इन" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "अभी नहीं" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "देखना चाहेंगे? यह आपकी वर्तमान टीम के लिए एक नए ड्राफ्ट में खुलेगा। (यदि " "नहीं, तो आप इसे किसी भी फ़ाइल के इंस्टॉल किए गए प्लगइन्स में हमेशा पा सकते " "हैं।)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "%s' प्लगइन आपके उपयोगकर्ता के लिए इंस्टॉल हो चुका है!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "प्लगइन आज़माएँ" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "फ्लेक्स लेआउट जोड़ें" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "ग्रिड लेआउट जोड़ें" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "लेआउट जोड़ें" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "वैरिएंट बनाएं" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "नई संपत्ति जोड़ें" @@ -6266,7 +6226,7 @@ msgstr "वापस भेजो" msgid "workspace.shape.menu.backward" msgstr "पीछे भेजें" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "वैरिएंट के रूप में संयोजित करें" @@ -6274,7 +6234,7 @@ msgstr "वैरिएंट के रूप में संयोजित msgid "workspace.shape.menu.combine-as-variants-error" msgstr "घटकों को एक ही पृष्ठ पर होना आवश्यक है" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "कापी" @@ -6310,27 +6270,27 @@ msgstr "पाठ के रूप में कॉपी करें" msgid "workspace.shape.menu.create-annotation" msgstr "एनोटेशन बनाएं" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "बोर्ड के लिए चयन" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "घटक बनाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "अनेक घटक बनाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "काटना" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "मिटाना" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "प्रवाह प्रारंभ हटाएँ" @@ -6342,7 +6302,7 @@ msgstr "उदाहरण अलग करें" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "उदाहरणों को अलग करें" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "भेद" @@ -6350,15 +6310,15 @@ msgstr "भेद" msgid "workspace.shape.menu.duplicate" msgstr "नकल करें" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "संपादन" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "बहिष्कृत" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "समतल" @@ -6370,7 +6330,7 @@ msgstr "क्षैतिज पलटें" msgid "workspace.shape.menu.flip-vertical" msgstr "ऊर्ध्वाधर पलटना" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "प्रवाह प्रारंभ" @@ -6382,31 +6342,31 @@ msgstr "आगे लाओ" msgid "workspace.shape.menu.front" msgstr "सामने लाना" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "समूह" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "छुपाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "UI दिखाएँ / छिपाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "चौराहा" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "ताला" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "मास्क" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "चिपकाएँ" @@ -6414,27 +6374,27 @@ msgstr "चिपकाएँ" msgid "workspace.shape.menu.paste-props" msgstr "गुण चिपकाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "पथ" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "फ्लेक्स लेआउट हटाएँ" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "ग्रिड लेआउट हटाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "लेआउट हटाएँ" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "संपत्ति हटाएं" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "वैरिएंट में कम से कम एक प्रॉपर्टी होनी चाहिए" @@ -6458,11 +6418,11 @@ msgstr "वैरिएंट पुनर्स्थापित करें" msgid "workspace.shape.menu.select-layer" msgstr "परत चुनें" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "दिखाएँ" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "संपत्ति पैनल में दिखाएँ" @@ -6478,19 +6438,19 @@ msgstr "थंबनेल निकालें" msgid "workspace.shape.menu.thumbnail-set" msgstr "थंबनेल के रूप में सेट करें" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "असमूहीकृत" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "संघ" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "अनलॉक" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "मास्क हटाएं" @@ -6506,47 +6466,47 @@ msgstr "साइडबार को संक्षिप्त करें" msgid "workspace.sidebar.expand" msgstr "साइडबार का विस्तार करें" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "इतिहास" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "परतें" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "घटकों" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "बोर्ड्स" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "कई समूह" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "छवियाँ" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "मास्क्स" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "खोजो…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "आकृतियाँ" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "टेक्स्ट" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "आयातित SVG गुण" @@ -6558,7 +6518,7 @@ msgstr "पृष्ठों" msgid "workspace.sidebar.sitemap.add-page" msgstr "पृष्ठ जोड़ें" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "साइटमैप" @@ -6578,7 +6538,7 @@ msgstr "इस समूह में सेट जोड़ें" msgid "workspace.tokens.add-token" msgstr "टोकन जोड़ें: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "के लिए आवेदन किया" @@ -6598,7 +6558,7 @@ msgstr "आधार फ़ॉन्ट आकार" msgid "workspace.tokens.base-font-size.error" msgstr "बेस फ़ॉन्ट का आकार पिक्सेल या यूनिट रहित होना चाहिए।" -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "रंग" @@ -6606,7 +6566,7 @@ msgstr "रंग" msgid "workspace.tokens.create-new-theme" msgstr "अभी अपनी पहली थीम बनाएं।" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "एक बनाएं।" @@ -6626,7 +6586,7 @@ msgstr "थीम हटाएँ" msgid "workspace.tokens.duplicate" msgstr "डुप्लीकेट टोकन" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "कापी" @@ -6674,7 +6634,7 @@ msgstr "पूर्व दर्शन:" msgid "workspace.tokens.export.single-file" msgstr "एक फाइल" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "फ़ॉन्ट आकार या {उपनाम}" @@ -6684,7 +6644,7 @@ msgstr "" "फ़ॉन्ट वज़न/शैली सेट करते समय त्रुटि हुई। यह फ़ॉन्ट शैली वर्तमान फ़ॉन्ट में " "मौजूद नहीं है" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "फ़ॉन्ट वज़न (300, बोल्ड इटैलिक...) या {उपनाम}" @@ -6720,7 +6680,7 @@ msgstr "एकाधिक फ़ाइलों में, फ़ाइल न msgid "workspace.tokens.import-single-file" msgstr "एकल JSON फ़ाइल में, प्रथम-स्तरीय कुंजियाँ टोकन सेट के नाम होने चाहिएं।" -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "टोकन आयात करें" @@ -6728,17 +6688,17 @@ msgstr "टोकन आयात करें" msgid "workspace.tokens.import-warning" msgstr "टोकन आयात करने से आपके सभी वर्तमान टोकन, सेट और थीम ओवरराइट हो जाएँगे।" -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "निष्क्रिय" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "यह सेट सक्रिय नहीं है। व्यूपोर्ट में परिवर्तन देखने के लिए थीम बदलें या इस " "सेट को सक्रिय करें" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "व्यक्तिगत टोकन का प्रयोग करें" @@ -6758,11 +6718,11 @@ msgstr "थिम" msgid "workspace.tokens.label.theme-placeholder" msgstr "एक थीम जोड़ें (अर्थात् प्रकाश)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "अक्षर रिक्ति या {उपनाम}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "पंक्ति ऊँचाई (गुणक, पिक्सेल, %) या {उपनाम}" @@ -6778,7 +6738,7 @@ msgstr "अधिकतम. आकार" msgid "workspace.tokens.min-size" msgstr "न्यूनतम. आकार" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "विकल्प देखने के लिए राइट क्लिक करें" @@ -6794,7 +6754,7 @@ msgstr "कोई थीम सक्रिय नहीं" msgid "workspace.tokens.no-permisions-set" msgstr "सेट्स को सक्रिय या निष्क्रिय करने के लिए आपको एक संपादक होना आवश्यक है" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "थीम्स का उपयोग करने के लिए आपको एक संपादक होना आवश्यक है" @@ -6806,7 +6766,7 @@ msgstr "अभी तक कोई सेट परिभाषित नही msgid "workspace.tokens.no-sets-yet" msgstr "अभी तक कोई सेट नहीं है।" -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "कोई थीम नहीं हैं।" @@ -6818,7 +6778,7 @@ msgstr "आपके पास वर्तमान में कोई थी msgid "workspace.tokens.num-active-sets" msgstr "%s सक्रिय सेट" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "मूल मान: %s" @@ -6830,15 +6790,15 @@ msgstr "पैडिंग्स" msgid "workspace.tokens.radius" msgstr "त्रिज्या" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "token टाइपोग्राफी उपनाम दर्ज करें" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "एक token छाया आलिया दर्ज करें" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "समाधानित मान: %s" @@ -6874,31 +6834,31 @@ msgstr "" msgid "workspace.tokens.settings" msgstr "टोकन सेटिंग्स" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "छाया जोड़ें" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "धुंधला" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "अंतर्भूत" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "छाया हटाएँ" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "फैलाना" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -6906,11 +6866,11 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "आकार" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "कोई नहीं | अपरकेस | लोअरकेस | कैपिटलाइज़ या {उपनाम}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "कोई नहीं | रेखांकित | स्ट्राइक-थ्रू या {उपनाम}" @@ -6928,31 +6888,31 @@ msgstr "थीम्स सूची" msgid "workspace.tokens.token-description" msgstr "विवरण" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "फ़ॉन्ट परिवार चुनें" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "फॉन्ट परिवार" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "फ़ॉन्ट परिवार या अल्पविराम (,) द्वारा अलग किए गए फ़ॉन्ट की सूची" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "नाम" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "पथ पर एक token पहले से मौजूद है: %s" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "नाम कम से कम 1 अक्षर का होना चाहिए" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " एक मान्य टोकन नाम नहीं है।\n" @@ -6963,11 +6923,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "मान" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "{alias} के साथ एक मान या उपनाम दर्ज करें" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "टोकन - %s" @@ -6985,11 +6945,11 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "टाइप '%s' समर्थित नहीं है (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "एक संदर्भ का प्रयोग करें" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "मान मान्य नहीं है" @@ -6997,55 +6957,55 @@ msgstr "मान मान्य नहीं है" msgid "workspace.toolbar.assets" msgstr "एसेट्स" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "रंग पैलेट (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "टिप्पणियाँ (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "वक्र (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "दीर्घवृत्त (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "बोर्ड (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "छवि (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "स्थानांतरित करें (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "पथ (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "प्लगइन्स (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "आयत (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "पाठ (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "टाइपोग्राफी (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "टूलबार टॉगल करें" @@ -7058,7 +7018,7 @@ msgstr "पूर्ण" msgid "workspace.top-bar.view-only" msgstr "**निरीक्षण कोड** (केवल देखें)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "अभी तक इतिहास में कोई बदलाव नहीं हुआ है" @@ -7198,19 +7158,19 @@ msgstr "टाइपोग्राफी संपत्ति" msgid "workspace.undo.entry.unknown" msgstr "% से अधिक ऑपरेशन" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "नकार देना" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "और जानकारी" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "साझा लाइब्रेरीज़ में अपडेट्स हैं" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "अद्यतन" diff --git a/frontend/translations/hr.po b/frontend/translations/hr.po index 93081b3c64..69f679cb48 100644 --- a/frontend/translations/hr.po +++ b/frontend/translations/hr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: al0cam \n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Croatian " "\n" "Language: hr\n" @@ -10,9 +10,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Već imaš račun?" @@ -30,7 +30,7 @@ msgstr "" "Ovo je DEMO usluga. NEMOJ KORISTITI za pravi rad. Projekti će se povremeno " "brisati." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Zaboravljena lozinka?" @@ -38,35 +38,35 @@ msgstr "Zaboravljena lozinka?" msgid "auth.fullname" msgstr "Puno ime" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Prijavite se na svoj račun" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Prijavi se ovdje." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot je besplatan alat otvorenog koda za dizajn i suradnju na kodu" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -86,7 +86,7 @@ msgstr "Profil nije potvrđen, potvrdi profil prije nastavka." msgid "auth.notifications.recovery-token-sent" msgstr "Veza za oporavak lozinke poslana je u tvoj inbox." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Uspješno pridružen/a timu" @@ -118,7 +118,7 @@ msgstr "Zaboravljena lozinka?" msgid "auth.recovery-submit" msgstr "Promjeni lozinku" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Još nemaš račun?" @@ -130,7 +130,7 @@ msgstr "Recite nam kako bismo Vas trebali zvati na nadzornoj ploči i u e-poruka msgid "auth.register-account-title" msgstr "Vaše ime" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Stvori račun" @@ -143,7 +143,7 @@ msgstr "Stvori račun" msgid "auth.terms-and-privacy-agreement" msgstr "Slažem se s [uvjetima usluge](%s) i [pravilima privatnosti](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Uvjeti pružanja usluge" @@ -155,94 +155,94 @@ msgstr "Poslovna e-pošta" msgid "comments.mentions.not-found" msgstr "Nisu pronađene osobe za @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Objavi" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Svi Penpot korisnici" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Jesi li siguran/na da želiš ukloniti ovu vezu? Ako to učiniš, više nikome " "neće biti dostupno" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(postojeći)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Poništi poveznicu" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Izradi poveznicu" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Poveznica uspješno kopirana" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Upravljanje dopuštenjima" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 stranica podijeljena" msgstr[1] "%s stranica podijeljeno" msgstr[2] "%s stranica podijeljeno" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Dopušten komentar" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Dopušteno provjeriti kod" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Svatko sa poveznicom imat će pristup" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Stranice podijeljene" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Ovdje će se pojaviti poveznica za dijeljenje" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Samo članovi tima" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Podijeli prototip" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Označi sve" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Poništi objavu" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Upravljanje timom" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot je namijenjen timovima. Pozovi članove da zajedno rade na projektima " "i datotekama" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Udruži se!" @@ -250,23 +250,23 @@ msgstr "Udruži se!" msgid "dashboard.add-file" msgstr "Dodaj datoteku" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Dodaj kao zajedničku biblioteku" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Promijeni e-mail" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopiraj)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "+ Kreiraj novi tim" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Prikvači ovu verziju" @@ -274,11 +274,11 @@ msgstr "Prikvači ovu verziju" msgid "dashboard.default-team-name" msgstr "Tvoj Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Obriši tim" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Preuzmi Penpot datoteku (.penpot)" @@ -290,19 +290,19 @@ msgstr "Kopija" msgid "dashboard.duplicate-multi" msgstr "Kopiraj %s datoteka" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Nakon što član projekta izradi nacrt, on će se prikazati ovdje." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Još nema nacrta." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Nakon što član projekta stvori datoteku, ona će se prikazati ovdje." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Još nema datoteka." @@ -326,68 +326,68 @@ msgstr "Još nema biblioteka." msgid "dashboard.export-binary-multi" msgstr "Preuzmi %s Penpot datoteke (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Izvezi artboard u PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Izvezi u PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "% od % elementa označeno" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Izvezi" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Postavke izvoza možeš dodati elementima iz svojstava dizajna (na dnu desne " "bočne trake)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informacije kako postaviti izvoz na Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Nema elemenata s postavkama izvoza." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Izvezi odabir" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Font izbrisan" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Odbaci sve" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Još uvijek nemaš instalirane custom fontove." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Još nema prilagođenih fontova." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "Nakon što član projekta prenese prilagođeni font, on će se prikazati ovdje." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 font dodan" msgstr[1] "%s fontova dodano" msgstr[2] "%s fontova dodano" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -396,7 +396,7 @@ msgstr "" "će grupirani kao **jedan font**. Možeš učitati fontove sa sljedećim " "formatima: **TTF, OTF i WOFF** (biti će potreban samo jedan)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -405,11 +405,11 @@ msgstr "" "pružanja usluge](%s). Također možeš pročitati o [licenciranju " "fontova](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Učitaj sve" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -420,11 +420,11 @@ msgstr "" "korištenje [Transfonter](https://transfonter.org/) za generiranje " "web-fontova i ispravljanje pogrešaka. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Uvezi Penpot datoteke" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Ups! Nismo mogli uvesti ovu datoteku" @@ -432,14 +432,14 @@ msgstr "Ups! Nismo mogli uvesti ovu datoteku" msgid "dashboard.import.bad-url" msgstr "Uvoz nije uspio. URL predloška nije točan" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "Datoteka je uspješno uvezena." msgstr[1] "%s datoteke su uspješno uvezene." msgstr[2] "%s datoteke su uspješno uvezene." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Neke su datoteke sadržavale nevažeće objekte koji su uklonjeni." @@ -447,55 +447,27 @@ msgstr "Neke su datoteke sadržavale nevažeće objekte koji su uklonjeni." msgid "dashboard.import.no-perms" msgstr "Nemate dopuštenje za uvoz u ovaj tim" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Obrada boja" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Obrada komponenti" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Obrada medija" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Obrada stranice: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Obrada tipografija" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Prijenos podataka na poslužitelj (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Prijenos datoteke: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Pozovi u tim" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Napusti tim" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Biblioteke i predlošci" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Istraži više njih i saznaj kako doprinijeti" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Došlo je do problema prilikom uvoza predloška. Predložak nije uvezen." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Biblioteke" @@ -503,7 +475,7 @@ msgstr "Biblioteke" msgid "dashboard.loading-files" msgstr "učitavanje tvojih datoteka…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "učitavanje tvojih fontova…" @@ -519,39 +491,39 @@ msgstr "Premijesti %s datoteke u" msgid "dashboard.move-to-other-team" msgstr "Premijesti u drugi tim" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nova datoteka" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Nova datoteka" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Novi projekt" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Novi projekt" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Nisu pronađeni rezultati za “%s”" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Prikvačeni projekti pojavit će se ovdje" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Obavijesti" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Tvoja e-mail adresa je uspješno ažurirana" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Tvoja e-mail adresa je uspješno potvrđena" @@ -563,11 +535,11 @@ msgstr "Postavke obavijesti ažurirane" msgid "dashboard.notifications.password-saved" msgstr "Lozinka je uspješno spremljena!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Pregledajte obavijesti" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s članova" @@ -575,7 +547,7 @@ msgstr "%s članova" msgid "dashboard.open-in-new-tab" msgstr "Otvori datoteku u novoj kartici" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Mogućnosti" @@ -599,7 +571,7 @@ msgstr "Sada ste vlasnik ovog tima." msgid "dashboard.permissions-change.viewer" msgstr "Sada ste gledatelj u ovom timu." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Prikvači/Otkvači" @@ -615,11 +587,11 @@ msgstr "Nije moguće analizirati manifest dodatka" msgid "dashboard.plugins.try-plugin" msgstr "Isprobajte dodatak: " -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projekti" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Želiš li ukloniti svoj račun?" @@ -627,23 +599,23 @@ msgstr "Želiš li ukloniti svoj račun?" msgid "dashboard.removed-from-team" msgstr "Niste više dio tima \"%s\"." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Spremi postavke" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Pretraži…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Pretraga \"%s\"…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Odaberite jezik korisničkog sučelja" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Odaberi temu" @@ -707,11 +679,11 @@ msgstr "Ažurirajte postavke" msgid "dashboard.settings.notifications.title" msgstr "Obavijesti" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Prikaži sve datoteke" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Povijest verzija" @@ -737,7 +709,7 @@ msgstr[2] "Tvoje datoteke su uspješno duplicirane" msgid "dashboard.success-duplicate-project" msgstr "Tvoj projekt je uspješno dupliciran" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Tvoja datoteka je uspješno premještena" @@ -749,86 +721,86 @@ msgstr "Tvoje datoteke su uspješno premještene" msgid "dashboard.success-move-project" msgstr "Tvoj projekt je uspješno premješten" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Informacije tima" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Članovi tima" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Projekti tima" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema korisničkog sučelja" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Pretraži rezultate" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Upiši za rezultate pretraživanja" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Poništi objavu biblioteke" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Ažuriraj postavke" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Aktivno" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Kada se ovaj webhook pokrene, bit će isporučene pojedinosti o događaju" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Možete samo brisati ili mijenjati webhookove koje ste sami izradili." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Vrsta sadržaja" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Stvorite webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook je uspješno stvoren." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhookovi su jednostavan način da se drugim web stranicama i aplikacijama " "omogući da budu obaviješteni kada se određeni događaji dogode na Penpotu. " "Poslat ćemo POST zahtjev na svaki URL koji navedete." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Pritisnite gumb \"Dodaj webhook\" da biste ga dodali." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Do sada nije izrađen nijedan webhook." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Tvoj korisnički račun" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-mail" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Ime" @@ -844,11 +816,11 @@ msgstr "Pažnja" msgid "ds.component-subtitle" msgstr "Komponente za ažuriranje:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Dopusti" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Poništi" @@ -868,15 +840,15 @@ msgstr "Davatelj autentifikacije nije dopušten za ovaj profil" msgid "errors.auth-provider-not-configured" msgstr "Pružatelj autentifikacije nije konfiguriran." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Čini se da nisi autentificiran/a ili je sesija istekla." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Font %s nije moguće učitati" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Fontovi %s nisu mogli biti učitani" @@ -884,15 +856,15 @@ msgstr "Fontovi %s nisu mogli biti učitani" msgid "errors.cannot-upload" msgstr "Nije moguće učitati medijsku datoteku." -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Tvoj preglednik ne može izvršiti ovu operaciju" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E-mail se već koristi" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-mail je već potvrđen." @@ -900,25 +872,25 @@ msgstr "E-mail je već potvrđen." msgid "errors.email-as-password" msgstr "Ne možeš koristiti svoj e-mail kao lozinku" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domena nije dopuštena" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "E-pmail «%s» ima mnogo trajnih izvješća o odbijanju." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "E-mail «%s» je prijavljen kao neželjena pošta ili je trajno odbijen." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Čini se da otvarate datoteku koja ima omogućenu značajku '%s', ali je " "trenutna verzija penpota ne podržava ili je onemogućena." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Značajka '%s' nije podržana." @@ -930,14 +902,14 @@ msgstr "Mora sadržavati najmanje 1 znak." msgid "errors.field-not-all-whitespace" msgstr "Ime mora sadržavati neki znak osim razmaka." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Čini se da postoji neusklađenost između omogućenih značajki i značajki " "datoteke koju pokušavate otvoriti. Prije otvaranja datoteke potrebno je " "primijeniti migracije za '%s'." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Dogodilo se nešto loše." @@ -949,7 +921,7 @@ msgstr "Pogrešna boja" msgid "errors.invalid-email" msgstr "Unesite valjanu e-poštu" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "E-mail za potvrdu mora odgovarati" @@ -969,11 +941,11 @@ msgstr "Ova pozivnica je možda otkazana ili je istekla." msgid "errors.ldap-disabled" msgstr "Onemogućena je LDAP provjera autentičnosti." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Dosegli ste kvotu '%s'. Kontaktirajte podršku." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "Dosegnut je najveći (%s) broj e-pošta koje se mogu pozvati u jednom zahtjevu" @@ -989,13 +961,13 @@ msgstr "Čini se da sadržaj slike ne odgovara ekstenziji datoteke." msgid "errors.media-type-not-allowed" msgstr "Čini se da ovo nije važeća slika." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Profil koji pozivaš ima isključen e-email (izvješća o neželjenoj pošti ili " "veliki broj odbijanja)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migracija u tijeku" @@ -1007,7 +979,7 @@ msgstr "Lozinka za potvrdu mora odgovarati" msgid "errors.password-too-short" msgstr "Lozinka mora sadržavati najmanje 8 znakova" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Nevažeći podaci u međuspremniku" @@ -1015,7 +987,7 @@ msgstr "Nevažeći podaci u međuspremniku" msgid "errors.profile-blocked" msgstr "Profil je blokiran" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Tvoj profil ima isključen e-mail (izvješća o neželjenoj pošti ili veliki " @@ -1025,19 +997,19 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Registracija je trenutno onemogućena." -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Otkrivena nekompatibilna značajka '%s'" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "Nedovoljno članova za napuštanje tima, vjerojatno ga želiš izbrisati." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Član kojeg pokušavaš dodijeliti ne postoji." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Vlasnik ne može napustiti tim, moraš ponovno dodijeliti ulogu vlasnika." @@ -1045,39 +1017,39 @@ msgstr "Vlasnik ne može napustiti tim, moraš ponovno dodijeliti ulogu vlasnika msgid "errors.unexpected-error" msgstr "Došlo je do neočekivane pogreške." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Nepoznati token" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Datoteka ima nekompatibilan broj verzije" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Pogreška veze, URL nije dostupan" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL ne prolazi provjeru valjanosti." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Zadnja dostava nije bila uspješna." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Pogreška pri SSL provjeri valjanosti." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Istek vremena" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Neočekivana pogreška prilikom provjere" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Neočekivani status %s" @@ -1093,11 +1065,11 @@ msgstr "Stara lozinka je netočna" msgid "feedback.description" msgstr "Opis" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "Sretni smo što si ovdje. Ako trebaš pomoć, pretraži prije objavljivanja." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot zajednica" @@ -1111,11 +1083,11 @@ msgstr "" "Opiši razlog svojeg e-maila, navedi radi li se o problemu, ideji ili " "nedoumici. Član našeg tima će odgovoriti u najkraćem mogućem roku." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Ovdje za pomoć za tvoje tehničke upite." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X korisnički račun za podršku" @@ -1147,7 +1119,7 @@ msgstr "Preuzmi izvornu sliku" msgid "inspect.attributes.shadow" msgstr "Sjena" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Veličina i položaj" @@ -1155,39 +1127,27 @@ msgstr "Veličina i položaj" msgid "inspect.attributes.stroke" msgstr "Potez" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Sredina" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Unutra" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Vani" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografija" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Font" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Veličina fonta" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Stil fonta" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Razmak između slova" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Visina linije" @@ -1199,11 +1159,11 @@ msgstr "Nikakav" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Podcrtano" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Velika i mala slova" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Mala slova" @@ -1215,7 +1175,7 @@ msgstr "Nikakav" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Nepostavljeno" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Velika slova" @@ -1277,39 +1237,39 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Tekst" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Prečaci" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Prihvati" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Aktivan" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Dodati" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Dodajte custom font" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Dodavanje..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Administrator" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Sve" @@ -1317,29 +1277,29 @@ msgstr "Sve" msgid "labels.and" msgstr "i" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Povratak" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Čini se da moraš malo pričekati i pokušati ponovno; vršimo mala održavanja " "naših servera." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Loš Gateway" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Odbaci" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Zatvori" @@ -1347,7 +1307,7 @@ msgstr "Zatvori" msgid "labels.code" msgstr "Kod" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Sažmi" @@ -1355,11 +1315,11 @@ msgstr "Sažmi" msgid "labels.comment" msgstr "Komentar" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Komentari" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Zajenica" @@ -1367,35 +1327,35 @@ msgstr "Zajenica" msgid "labels.confirm-password" msgstr "Potvrdi lozinku" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Nastavi" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Možeš nastaviti s Penpot računom" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Kopiraj vezu" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Kreiraj" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Kreiraj novi tim" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Unesi ime novog tima" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Nadzorna ploča" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Izbriši" @@ -1407,7 +1367,7 @@ msgstr "Izbriši komentar" msgid "labels.delete-comment-thread" msgstr "Izbriši thread" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Izbriši pozivnicu" @@ -1415,31 +1375,31 @@ msgstr "Izbriši pozivnicu" msgid "labels.delete-multi-files" msgstr "Izbriši %s datoteka" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Razvoj" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Odbaciti" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Nacrti" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Uredi" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Urednik" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Isteklo" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Izvezi" @@ -1451,51 +1411,51 @@ msgstr "Povratne informacije onemogućene" msgid "labels.feedback-sent" msgstr "Povratne informacije poslane" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Font" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Stilovi" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fontovi" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Github repozitorij" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Daj povratnu informaciju" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Povratak" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Grafički dizajn" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Centar za pomoć" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Sakrij riješene komentare" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Uvoz" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Neaktivan" @@ -1503,35 +1463,35 @@ msgstr "Neaktivan" msgid "labels.info" msgstr "Informacija" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Instalirani fontovi" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Interna pogreška" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Pozivnice" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Jezik" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Biblioteke i predlošci" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Učitavanje…" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Prijava ili registracija" @@ -1539,19 +1499,19 @@ msgstr "Prijava ili registracija" msgid "labels.login" msgstr "Prijava" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Odjava" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Član" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Članovi" @@ -1559,41 +1519,41 @@ msgstr "Članovi" msgid "labels.new-password" msgstr "Nova lozinka" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Sljedeći" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Nemaš obavijesti o komentarima na čekanju." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Nema pozivnica." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Ups!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Obavijesti" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 datoteka" msgstr[1] "%s datoteka" msgstr[2] "%s datoteka" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 ploča" msgstr[1] "%s ploča" msgstr[2] "%s ploča" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 projekt" @@ -1604,27 +1564,27 @@ msgstr[2] "%s projekata" msgid "labels.old-password" msgstr "Stara lozinka" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Mogućnosti" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Ostalo (navesti)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Ostalo" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Vlasnik" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Lozinka" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "U tijeku" @@ -1632,43 +1592,43 @@ msgstr "U tijeku" msgid "labels.post" msgstr "Objaviti" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Prethodno" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Dizajn proizvoda ili UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Upravljanje proizvodima" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projekti" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Release notes" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Ukloni" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Ukloni člana" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Preimenuj" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Preimenuj tim" @@ -1692,7 +1652,7 @@ msgstr "novi odgovor" msgid "labels.reply.thread" msgstr "Odgovor" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Ponovno pošalji pozivnicu" @@ -1700,43 +1660,43 @@ msgstr "Ponovno pošalji pozivnicu" msgid "labels.restore" msgstr "Vratiti" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Pokušaj ponovo" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Uloga" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Spremi" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Pretraživanje" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Pretraži font" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Odaberite opciju" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Pošalji" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Slanje…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "U programiranom smo održavanju naših sustava." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Usluga je nedostupna" @@ -1744,55 +1704,55 @@ msgstr "Usluga je nedostupna" msgid "labels.sets" msgstr "Skupovi" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Postavke" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Dijeliti" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibiloteke" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Prikaži sve komentare" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Prikaži listu komentara" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Prikaži samo svoja spominjanja" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Prikaži samo tvoje komentare" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Skica" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Start" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Učenik ili učitelj" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Teme" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Upute" @@ -1800,23 +1760,23 @@ msgstr "Upute" msgid "labels.unpublish-multi-files" msgstr "Poništi objavu datoteka %s" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Ažuriraj" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Ažuriraj tim" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Prenesi" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Prenesi custom fontove" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Prijenos…" @@ -1824,11 +1784,11 @@ msgstr "Prijenos…" msgid "labels.view-only" msgstr "Samo pogled" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Promatrač" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhookovi" @@ -1836,11 +1796,11 @@ msgstr "Webhookovi" msgid "labels.write-new-comment" msgstr "Napiši novi komentar" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(ti)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Tvoj korisnički račun" @@ -1893,61 +1853,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Dodajte “%s” kao zajedničku biblioteku" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Big nudge" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Potvrdite novi e-mail" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Poslat ćemo e-mail na tvoj trenutni e-mail \"%s\" kako bismo potvrdili tvoj " "identitet." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Novi e-mail" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Promijeni e-mail" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Promijeni svoj e-mail" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Izradi webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Izradi webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL tereta" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://primjer.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Otkaži i zadrži moj račun" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Da, obriši moj račun" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Uklanjanjem računa izgubit ćeš sve svoje trenutne projekte i arhive." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Jeste li siguran/na da želiš izbrisati svoj račun?" @@ -1997,31 +1957,31 @@ msgstr "Jesi li siguran/na da želiš izbrisati %s datoteke?" msgid "modals.delete-file-multi-confirm.title" msgstr "Brisanje %s datoteka" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Jesi li siguran/na da želiš izbrisati ovaj stil fonta? Neće se učitati ako " "se koristi u datoteci." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Brisanje fonta" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Jesi li siguran/na da želiš izbrisati ovaj font? Neće se učitati ako se " "koristi u datoteci." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Brisanje fonta" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Jesi li siguran/na da želiš izbrisati ovu stranicu?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Brisanje stranice" @@ -2037,35 +1997,35 @@ msgstr "Jesi li siguran/na da želiš izbrisati ovaj projekt?" msgid "modals.delete-project-confirm.title" msgstr "Brisanje projekta" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Izbriši datoteku" msgstr[1] "Izbriši datoteke" msgstr[2] "Izbriši datoteke" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Nije aktivirana ni u jednoj datoteci." msgstr[1] "Nisu aktivirane ni u jednoj datoteci." msgstr[2] "Nisu aktivirane ni u jednoj datoteci." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Ova biblioteka je aktivirana ovdje: " msgstr[1] "Ove biblioteke su aktivirana ovdje: " msgstr[2] "Ove biblioteke su aktivirana ovdje: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Jesi li siguran/na da želiš izbrisati ovu datoteku?" msgstr[1] "Jesi li siguran/na da želiš izbrisati ove datoteke?" msgstr[2] "Jesi li siguran/na da želiš izbrisati ove datoteke?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Brisanje datoteke" @@ -2086,19 +2046,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Brisanje tima" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Brisanje člana" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Jesi li siguran/na da želiš izbrisati ovog člana iz tima?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Brisanje člana tima" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -2111,172 +2071,172 @@ msgstr[2] "" "Sredstva koja su već korištena u tim datotekama ostat će ondje (nijedan " "dizajn neće biti pokvaren)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Izbriši webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Jeste li sigurni da želite izbrisati ovaj webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Brisanje webhooka" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Uredi webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Uredi webhook" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Pošalji pozivnicu" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-mail, odvojeno zarezom" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Neke e-adrese su od trenutačnih članova tima. Njima pozivnice neće biti " "poslane." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Možete pozvati članove u tim kako bi mogli pristupiti ovoj datoteci i svim " "datotekama tima." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Pozovi članove u tim" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Budući da si jedini član ovog tima, tim će biti izbrisan zajedno sa svojim " "projektima i datotekama." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Jesi li siguran/na da želiš napustiti tim %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Ne možete napustiti tim ako nema drugog člana kojeg možete promovirati u " "vlasnika. Možda želite izbrisati tim." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Ti si vlasnik ovog tima. Prije odlaska odaberi drugog člana kojeg želiš " "promovirati u vlasnika." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promoviraj i napusti" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Odaberi člana za promociju" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Prije nego odeš" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Napusti tim" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Jesi li siguran/na da želiš napustiti ovaj tim?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Napuštanje tima" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Premjestiti" msgstr[1] "Premjestiti" msgstr[2] "Premjestiti" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Jeste li sigurni da želite premjestiti ovu biblioteku?" msgstr[1] "Jeste li sigurni da želite premjestiti ove biblioteke?" msgstr[2] "Jeste li sigurni da želite premjestiti ove biblioteke?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Premjesti biblioteku" msgstr[1] "Premjesti biblioteke" msgstr[2] "Premjesti biblioteke" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Pomakni za" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Prenesi vlasništvo" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Ako preneseš vlasništvo, promijenit ćeš svoju ulogu u Administrator, čime " "ćeš izgubiti neka dopuštenja za ovaj tim. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Ti si trenutni vlasnik ovog tima. Jesi li siguran/na da %s želiš postaviti " "za novog vlasnika tima?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Novi vlasnik tima" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Objavi" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Vaša biblioteka je prazna. Jeste li sigurni da je želite objaviti?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Objavi praznu biblioteku" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Mali pomak" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Poništi objavu" msgstr[1] "Poništi objave" msgstr[2] "Poništi objave" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Jesi li siguran/na da želiš poništiti objavu ove biblioteke?" msgstr[1] "Jesi li siguran/na da želiš poništiti objavu ovih biblioteka?" msgstr[2] "Jesi li siguran/na da želiš poništiti objavu ovih biblioteka?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Poništi objavu biblioteke" @@ -2301,11 +2261,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Ažuriraj komponentu u zajedničkoj biblioteci" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Ova stranica ne postoji" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 greška" @@ -2323,11 +2283,11 @@ msgstr "Poslali smo e-poštu za oporavak na" msgid "not-found.login.sent-recovery-check" msgstr "Provjerite svoju e-poštu i kliknite na vezu za stvaranje nove lozinke." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Prijavite se besplatno" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "I počnite koristiti Penpot za nekoliko sekundi!" @@ -2335,67 +2295,67 @@ msgstr "I počnite koristiti Penpot za nekoliko sekundi!" msgid "not-found.made-with-love" msgstr "Napravljeno s LJUBAVLJU i otvorenim kodom" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Već ste zatražili pristup ovoj datoteci." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Već ste zatražili pristup ovoj datoteci ili drugim datotekama ili " "projektima ovog tima." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Već ste zatražili pristup ovom projektu ili drugim projektima ili " "datotekama ovog tima." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Već ste zatražili pristup ovom projektu." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "ZATRAŽITE PRISTUP" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Zapamtite da ćete biti pozvani u tim ako vlasnik to dopusti." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Vaš zahtjev je ispravno poslan!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Nemate pristup ovoj datoteci." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Idite na svoj Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Ako vlasnik to dopusti, bit ćete pozvani u tim." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot datoteka" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Nemate pristup ovom projektu." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJEKT" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Za pristup ovoj datoteci možete pitati vlasnika tima." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Za pristup ovom projektu možete pitati vlasnika tima." @@ -2409,25 +2369,25 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Dostupna je nova verzija." -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Pozivnica je uspješno poslana" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Veza pozivnice je kopirana" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Ne možeš izbrisati svoj profil. Ponovno dodijeli svoje timove prije " "nastavka." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil je uspješno spremljen!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "E-mail za potvrdu poslana je na %s. Provjeri e-email!" @@ -2455,78 +2415,78 @@ msgstr "Nakon što imenuješ svoj tim, moći ćeš pozvati ljude da se pridruže msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Stvorite tim" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Pozovi članove" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Pozovi s ulogom:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Započnimo!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Generiranje stvarnog koda iz dizajna" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Stvaranje sustava dizajna" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototipiranje" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Dizajnirajte UI/UX aplikacije" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Za što ćete koristiti Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Recite nam nešto o sebi kako biste nam pomogli da Penpot radi za Vas. Vaši " "odgovori pomoći će nam da odredimo prioritet novih značajki i uputit će Vas " "u pravom smjeru za početak." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Pomozite nam da Vas upoznamo" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Koji od ovih alata najviše koristite?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Kakvim se poslom bavite?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Recite nam nešto o svom poslu" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Gdje biste željeli započeti?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Obrazovanje" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Osobno" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Posao" @@ -2576,11 +2536,11 @@ msgstr "Počni dizajnirati" msgid "profile.recovery.go-to-login" msgstr "Idi na prijavu" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Odvojiti" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Izmješano" @@ -2592,35 +2552,35 @@ msgstr "Uklonite boju" msgid "settings.select-this-color" msgstr "Odaberite stavke koristeći ovaj stil" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Komentari" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Poravnaj dno" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Poravnaj sredinu vodoravno" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Poravnaj lijevo" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Poravnaj desno" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Poravnaj vrh" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Poravnaj sredinu okomito" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Kreiraj ploču iz odabira" @@ -2628,47 +2588,47 @@ msgstr "Kreiraj ploču iz odabira" msgid "shortcuts.bold" msgstr "Uključi/isključi podebljano" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Boolean razlika" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Boolean isključenje" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Boolean presijek" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Boolean unija" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Premijesti u pozadinu" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Premiijesti nazad" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Premijesti naprijed" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Premijesti skroz naprijed" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Obriši povrat" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Kopiraj" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Kopiraj vezu u međuspremnik" @@ -2676,35 +2636,35 @@ msgstr "Kopiraj vezu u međuspremnik" msgid "shortcuts.create-new-project" msgstr "Kreiraj novo" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Izreži" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Zoom out" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Izbriši" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Izbriši čvor" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Odvoji komponentu" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Krivulja" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipsa" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Panel" @@ -2712,39 +2672,39 @@ msgstr "Panel" msgid "shortcuts.draw-nodes" msgstr "Ucrtaj put" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Put" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Pravokutnik" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Tekst" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Dupliciraj" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Poništi" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Izvezi oblike" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Zumiraj da stane sve" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Okreni vodoravno" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Okreni okomito" @@ -2768,23 +2728,23 @@ msgstr "Idi na zajedničke biblioteke" msgid "shortcuts.go-to-search" msgstr "Traži" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Grupiraj" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribuiraj vodoravno" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Prikaži/sakrij UI" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Zoom in" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Umetni sliku" @@ -2792,7 +2752,7 @@ msgstr "Umetni sliku" msgid "shortcuts.italic" msgstr "Prebaci kurziv" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Spoji čvorove" @@ -2800,39 +2760,39 @@ msgstr "Spoji čvorove" msgid "shortcuts.line-through" msgstr "Uključi precrtavanje" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Izradi rub" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Izradi krivulju" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Maskiraj" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Spoji čvorove" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Premijesti" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Premijesti brzo dolje" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Premijesti brzo lijevo" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Premijesti brzo desno" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Premijesti brzo gore" @@ -2840,325 +2800,325 @@ msgstr "Premijesti brzo gore" msgid "shortcuts.move-nodes" msgstr "Premijesti čvor" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Premijesti dolje" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Premijesti lijevo" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Premijesti desno" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Premijesti gore" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Slijedeća ploča" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Nema pronađenih prečaca" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Postavi neprozirnost na 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Postavi neprozirnost na 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Postavi neprozirnost na 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Postavi neprozirnost na 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Postavi neprozirnost na 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Postavi neprozirnost na 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Postavi neprozirnost na 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Postavi neprozirnost na 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Postavi neprozirnost na 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Postavi neprozirnost na 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Birač boja" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Idi na odjeljak s komentarima" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Idi na nadzornu ploču" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Idite na odjeljak pregleda preglednika" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Idi na odjeljak interakcija" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Idi na odjeljak interakcija gledatelja" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Idi na radni prostor" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " ili " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Zaljepi" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Prethodna ploča" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Ponovi" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Preimenujte" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Resetiraj zoom" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Skalirajte" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Osnove" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Nadzorna ploča" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Gledatelj" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Radni prostor" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Označi sve" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Odaberite sljedeći sloj" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Odaberite nadređeni sloj" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Odaberite prethodni sloj" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Posebni čvorovi" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Prikaži/sakrij \"pixel grid\"" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Prikaži/sakrij prečace" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Priključi na čvorove" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Priključi na \"pixel grid\"" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Počni uređivanje" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Poravnanje" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Uredi" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Generičko" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Glavni meni" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Izmijeni layere" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigacija" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigacija" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigacija" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Paneli" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Putanje" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Oblici" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Tekstovi" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Alati" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Postavi sličice" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Prečaci na tipkovnici" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Promijena dinamičkog poravnanja" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Promijena stavaka" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Promijena palete boja" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Promijena fokus moda" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Promijeni cijeli zaslon" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Prikaži / Sakrij vodiče" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Promijena povijesti" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Promijena layera" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Dodavanje/uklanjanje flex rasporeda" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Dodaj/ukloni grid raspored" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Zaključaj odabrano" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Zaključaj proporcije" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Prikaži / Sakrij ravnala" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Pričvrstite na vodilice" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Pričvrstite na vodilice ravnala" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Promijeni paletu teksta" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Promjena teme" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Promijeni vidljivost" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Promijeni stil zooma" @@ -3166,63 +3126,63 @@ msgstr "Promijeni stil zooma" msgid "shortcuts.underline" msgstr "Uključi podcrtavanje" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Poništi" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Razgrupiraj" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Makni masku" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribuiraj okomito" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Smanjenje zuma objektiva" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Povećanje zuma objektiva" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zoomiraj na selektirano" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Dobavljači fontova - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Fontovi - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projekti - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Traži - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Zajedničke biblioteke - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Sloboda dizajna za timove" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Pošalji povratnu informaciju - Penpot" @@ -3230,7 +3190,7 @@ msgstr "Pošalji povratnu informaciju - Penpot" msgid "title.settings.notifications" msgstr "Obavijesti - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Postavke - Penpot" @@ -3238,27 +3198,27 @@ msgstr "Postavke - Penpot" msgid "title.settings.password" msgstr "Lozinka - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Pozivnice - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Članovi - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Postavke - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhookovi - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Način prikaza - Penpot" @@ -3266,99 +3226,99 @@ msgstr "%s - Način prikaza - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Na stranici nisu pronađene ploče." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Ploča nije pronađena." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Komentari (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Ne prikazuj interakcije" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Cijeli zaslon" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Pregledaj (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interakcije" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interakcije (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Kopiraj poveznicu" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Prikaži interakcije" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Prikaži interakcije na klik" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Sitemap" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Zadnja isporuka bila je uspješna." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Poravnaj vodoravno u sredinu (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribuiraj vodoravni razmak (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Poravnaj lijevo (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Poravnaj desno (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Poravnaj dolje (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Poravnaj okomito u sredinu (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribuiraj okomiti razmak (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Poravnaj gore (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Dodaj biblioteku" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Sve stavke" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Boje" @@ -3366,7 +3326,7 @@ msgstr "Boje" msgid "workspace.assets.colors.add-color" msgstr "Dodajte boju" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Komponente" @@ -3378,15 +3338,15 @@ msgstr "Dodajte komponentu" msgid "workspace.assets.create-group" msgstr "Kreiraj grupu" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Tvoje stavke će se automatski imenovati kao \"naziv grupe / naziv stavke\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Izbriši" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Dupliciraj" @@ -3394,35 +3354,35 @@ msgstr "Dupliciraj" msgid "workspace.assets.duplicate-main" msgstr "Dupliciraj glavni" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Uredi" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filter" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Grid prikaz" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Grupa" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Ime grupe" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Popisni prikaz" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "lokalna biblioteka" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Nisu pronađene stavke" @@ -3430,7 +3390,7 @@ msgstr "Nisu pronađene stavke" msgid "workspace.assets.open-library" msgstr "Otvori datoteku biblioteke" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Preimenuj" @@ -3438,11 +3398,11 @@ msgstr "Preimenuj" msgid "workspace.assets.rename-group" msgstr "Preimenuj grupu" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Pretraži stavke" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Zajednička biblioteka" @@ -3453,43 +3413,43 @@ msgstr[0] "1 komponenta" msgstr[1] "%s komponente" msgstr[2] "%s komponente" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Sortiraj" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Tipografija" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Dodajte tipografiju" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Veličina" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Stil fonta" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Idi na datoteku biblioteke stilova za uređivanje" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Razmak između slova" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Visina linije" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformiraj tekst" @@ -3497,67 +3457,67 @@ msgstr "Transformiraj tekst" msgid "workspace.assets.ungroup" msgstr "Razgrupiraj" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Stvorite područje" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Napravi ploču" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Spoji ćelije" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Dodajte 1 stupac desno" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Dodajte 1 stupac lijevo" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Izbriši stupac" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Izbriši stupac i oblike" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Dupliciraj stupac" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Dodajte 1 red ispod" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Dodajte 1 red iznad" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Izbriši red" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Izbrišite red i oblike" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Dupliciraj redak" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Fokus mode" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Fokus isključen" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Fokus uključen" @@ -3569,191 +3529,191 @@ msgstr "Linearni gradijent" msgid "workspace.gradients.radial" msgstr "Radijalni gradijent" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Onemogući dinamičko poravnanje" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Onemogući proporcionalno mjerilo" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Onemogući \"snap to guides\"" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Onemogući \"snap to pixel\"" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Onemogući spajanje na vodilice ravnala" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Omogući dinamičko poravnanje" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Omogući proporcionalno mjerilo" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Pričvrstite na vodilice" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Omogući \"snap to pixel\"" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Pričvrstite na vodilice ravnala" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Sakrij nazive ploča" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Sakrij vodiče" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Sakrij paletu boja" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Sakrij \"pixel grid\"" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Sakrij \"rules\"" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Sakrij paletu boja" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Uredi" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Datoteka" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Pomoć i informacije" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferencije" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Pregled" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Ponovi" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Odaberi sve" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Prikaži nazive ploča" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Prikaži vodiče" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Prikaži paletu boja" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Prikaži \"pixel grid\"" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Prikaži \"rules\"" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Prikaži paletu fontova" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Prebacite se na tamnu temu" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Prebaci na svijetlu temu" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Poništi" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Resetiraj" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Greška kod spremanja" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Spremljeno" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Spremanje" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Podijeli" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Način prikaza (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zum" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Ispuna - Skaliraj za popunjavanje" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Prilagodi - Smanji veličinu da pristane" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Zumiraj da stane sve" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Cijeli ekran" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zumiraj na odabrano" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Uredi grid" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Izlaz" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Prikaži 4-strane opcije podstava" @@ -3769,23 +3729,23 @@ msgstr "Gotovo" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Pronađi" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Pronađite grid raspored" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "U vašoj biblioteci još nema stilova boja" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "U vašoj biblioteci još nema stilova tipografije" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Biblioteka datoteka" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Nedavno korištene boje" @@ -3797,71 +3757,71 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Spremi u stil boja" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Ili dodajte neke od ovih za isprobavanje:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "U vašem timu nema dijeljenih biblioteka, možete ih potražiti" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "neki predlošci ovdje" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Biblioteka datoteka" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTEKE U OVOJ DATOTECI" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTEKE" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "AŽURIRANJE KNJIŽNICE" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Učitavanje…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Ne postoje zajedničke biblioteke koje je potrebno ažurirati" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Nisu pronađeni rezultati za “%s”" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Pretraži zajedničke biblioteke" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "ZAJEDNIČKE BIBLIOTEKE" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Povežite biblioteku" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Odspojite biblioteku" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Ažuriranje" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "vidjeti sve promjene" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "AŽURIRANJA" @@ -3893,39 +3853,39 @@ msgstr "Uključi/isključi zamućenje" msgid "workspace.options.canvas-background" msgstr "Pozadina canvasa" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Isjeci sadržaj" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Komponenta" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Bilješka" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Kopirati" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Napravite bilješku" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Uredite bilješku" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Glavni" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Zamijenite komponentu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "U ovoj biblioteci još nema sredstava" @@ -3969,11 +3929,11 @@ msgstr "Vrh" msgid "workspace.options.constraints.topbottom" msgstr "Vrh i dno" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Dizajn" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Izvoz" @@ -3981,38 +3941,38 @@ msgstr "Izvoz" msgid "workspace.options.export-multiple" msgstr "Izvezi selektirano" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Izvezi 1 element" msgstr[1] "Izvezi %s elemenata" msgstr[2] "Izvezi %s elemenata" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Dodaj izvoz" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Ukloni izvoz" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Sufiks" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Izvoz završen" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Izvoz…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Izvoz nije uspio" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Izvoz neočekivano spor" @@ -4028,7 +3988,7 @@ msgstr "Dodajte ispunu" msgid "workspace.options.fill.remove-fill" msgstr "Ukloni ispunu" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Promijenite veličinu ploče da odgovara sadržaju" @@ -4052,67 +4012,67 @@ msgstr "Flow započinje" msgid "workspace.options.grid.auto" msgstr "Auto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Stupci" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Boja" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Oluk" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Visina" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margina" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Postavi kao zadano" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Dno" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Sredina" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Lijevo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Desno" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Razvuci" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Vrh" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Koristi zadano" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Širina" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Redovi" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Kvadrat" @@ -4124,7 +4084,7 @@ msgstr "Popunjavanje grupe" msgid "workspace.options.group-stroke" msgstr "Grupni potez" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Dodajte vodilicu" @@ -4132,19 +4092,19 @@ msgstr "Dodajte vodilicu" msgid "workspace.options.guides.remove-guide" msgstr "Uklonite vodilicu" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Vodilice" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Uključi/isključi vodilicu" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Visina" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Pregledati" @@ -4360,103 +4320,103 @@ msgstr "Interakcije" msgid "workspace.options.interactions.add-interaction" msgstr "Dodajte interakciju" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Boja" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Izgaranje boje" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Izbjegavanje boja" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Zatamni" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Razlika" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Ekskluzija" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Tvrdo svjetlo" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Nijansa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Posvijetli" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Svjetlost" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Umnoži" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normalno" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Preklapanje" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturacija" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Ekran" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Nježno svjetlo" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Uključi/isključi vidljivost sloja" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Max.visina" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Max.širina" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Min.visina" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Min.širina" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Više boja" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Više boja iz biblioteke" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Neprozirnost" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototip" @@ -4488,19 +4448,19 @@ msgstr "Sažmi neovisni radijus" msgid "workspace.options.radius.show-single-corners" msgstr "Prikaži neovisni radijus" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Nedavni" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Pokušaj ponovo" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotacija" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Pretraži font" @@ -4508,7 +4468,7 @@ msgstr "Pretraži font" msgid "workspace.options.select-a-shape" msgstr "Odaberi oblik, ploču ili grupu za povlačenje veze na drugu ploču." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Odabrane boje" @@ -4576,47 +4536,47 @@ msgstr "Uključi/isključi sjenu" msgid "workspace.options.show-fill-on-export" msgstr "Prikaži u izvozu" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Prikaži u načinu pregleda" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Veličina" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Unaprijed postavljena veličina" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Krug" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Dijamant" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Strelica" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Nijedan" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Okruglo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Kvadrat" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Pravokutnik" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Trokut" @@ -4624,7 +4584,7 @@ msgstr "Trokut" msgid "workspace.options.stroke-color" msgstr "Boja poteza" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Širina poteza" @@ -4632,35 +4592,35 @@ msgstr "Širina poteza" msgid "workspace.options.stroke.add-stroke" msgstr "Dodajte boju poteza" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Sredina" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Crtkano" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Točkasto" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Unutra" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Miješano" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Vani" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Uklonite potez" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Čvrsto" @@ -4696,11 +4656,11 @@ msgstr "Automatska širina" msgid "workspace.options.text-options.grow-fixed" msgstr "Popravljeno" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Razmak između slova" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Visina linije" @@ -4744,122 +4704,122 @@ msgstr "Podcrtano (%s)" msgid "workspace.options.use-play-button" msgstr "Upotrijebi play gumb u zaglavlju za pokretanje prikaza prototipa." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Širina" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X os" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Crtaj čvorove (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Spoji čvorove (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Do kuta (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Do krivulje (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Spoji čvorove (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Premijesti čvorove (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Odvoji čvorove(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Priključi čvorove (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Otvoriti" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Otkrijte [više dodataka](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Još nema instaliranih dodataka" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Manifest dodatka nije točan." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Morate biti urednik da biste koristili ovaj dodatak" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Dodatak ne postoji ili URL nije točan." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Instalirati" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Instalirani dodaci" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Upravitelj dodataka" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Dodaci" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "AŽURIRAJTE OVAJ DODATAK" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Dodatak je izmijenjen otkad ste ga zadnji put otvorili. Sada također želi " "pristupiti:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Pokrenite preuzimanje datoteka." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Pročitajte svoje komentare i odgovore." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Pročitajte i izmijenite svoje komentare i odgovorite u svoje ime." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Pročitajte sadržaj datoteka kojima korisnici imaju pristup." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Čitajte i mijenjajte sadržaj datoteka kojima korisnici imaju pristup." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Imajte na umu da je ovaj dodatak izradila vanjska strana, stoga provjerite " @@ -4867,65 +4827,65 @@ msgstr "" "vaših podataka važni su nam. Ako imate bilo kakvih nedoumica, obratite se " "podršci." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Čitajte svoje biblioteke i stavke." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Čitajte i mijenjajte svoje biblioteke i stavke." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "'%s' DODATAK ŽELI PRISTUP:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Pročitajte informacije o profilu trenutnog korisnika." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Popis dodataka" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Ukloni dodatak" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Napišite URL dodatka" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Dodaci" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "NE SADA" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Želite li pogledati? Otvorit će se u novom nacrtu za vaš trenutni tim. (Ako " "ne, uvijek ga možete pronaći u instaliranim dodacima bilo koje datoteke.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "'%s' PLUGIN JE INSTALIRAN ZA VAŠEG KORISNIKA!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "PROBAJTE DODATAK" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Dodajte flex raspored" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Dodajte grid raspored" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Dodajte raspored" @@ -4937,7 +4897,7 @@ msgstr "Pošalji natrag" msgid "workspace.shape.menu.backward" msgstr "Pošalji u pozadinu" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Kopiraj" @@ -4969,27 +4929,27 @@ msgstr "Kopiraj kao tekst" msgid "workspace.shape.menu.create-annotation" msgstr "Napravite bilješku" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Odabir na ploču" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Kreiraj komponentu" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Stvorite više komponenti" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Izreži" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Izbriši" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Izbriši početak flowa" @@ -5001,7 +4961,7 @@ msgstr "Odvoji instancu" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Odvoji instance" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Razlika" @@ -5009,15 +4969,15 @@ msgstr "Razlika" msgid "workspace.shape.menu.duplicate" msgstr "Dupliciraj" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Uredi" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Izuzmi" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Spljošti" @@ -5029,7 +4989,7 @@ msgstr "Okreni vodoravno" msgid "workspace.shape.menu.flip-vertical" msgstr "Okreni okomito" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Početak flowa" @@ -5041,31 +5001,31 @@ msgstr "Postavi ispred" msgid "workspace.shape.menu.front" msgstr "Postavi naprijed" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Grupiraj" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Sakrij" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Prikaži/sakrij korisničko sučelje" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Presjek" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Zaključaj" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Maskiraj" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Zalijepi" @@ -5073,19 +5033,19 @@ msgstr "Zalijepi" msgid "workspace.shape.menu.paste-props" msgstr "Svojstva lijepljenja" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Putanja" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Uklonite flex raspored" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Uklonite grid raspored" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Uklonite raspored" @@ -5105,11 +5065,11 @@ msgstr "Vrati glavnu komponentu" msgid "workspace.shape.menu.select-layer" msgstr "Označi layer" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Prikaži" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Prikaži na ploči stavki" @@ -5125,19 +5085,19 @@ msgstr "Ukloni sličicu" msgid "workspace.shape.menu.thumbnail-set" msgstr "Postavi kao sličicu" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Razgrupiraj" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Unija" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Otključaj" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Ukloni masku" @@ -5153,47 +5113,47 @@ msgstr "Sažmi bočnu traku" msgid "workspace.sidebar.expand" msgstr "Proširi bočnu traku" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Povijest" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Layeri" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Komponente" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Ploče" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Grupe" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Slike" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Maske" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Traži layere" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Oblici" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Tekstovi" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Uvezeni SVG atributi" @@ -5205,7 +5165,7 @@ msgstr "Stranice" msgid "workspace.sidebar.sitemap.add-page" msgstr "Dodaj stranicu" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Sitemap" @@ -5213,7 +5173,7 @@ msgstr "Sitemap" msgid "workspace.tokens.active-themes" msgstr "%s aktivnih tema" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Primijenjeno na" @@ -5225,7 +5185,7 @@ msgstr "Povratak na popis tema" msgid "workspace.tokens.create-new-theme" msgstr "Stvorite svoju prvu temu sada." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Stvorite jedan." @@ -5277,7 +5237,7 @@ msgstr "Nema aktivnih tema" msgid "workspace.tokens.no-permisions-set" msgstr "Za aktiviranje/deaktiviranje skupova morate biti urednik" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Morate biti urednik da biste koristili teme" @@ -5289,7 +5249,7 @@ msgstr "Još nema definiranih skupova. Prvo stvorite jedan." msgid "workspace.tokens.no-sets-yet" msgstr "Još nema skupova." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Nema tema." @@ -5297,11 +5257,11 @@ msgstr "Nema tema." msgid "workspace.tokens.no-themes-currently" msgstr "Trenutno nemate nijednu temu." -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Izvorna vrijednost: %s" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Riješena vrijednost: %s" @@ -5323,11 +5283,11 @@ msgstr "" msgid "workspace.tokens.token-description" msgstr "Opis" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Ime" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " nije važeće ime tokena.\n" @@ -5338,7 +5298,7 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Vrijednost" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENI - %s" @@ -5346,7 +5306,7 @@ msgstr "TOKENI - %s" msgid "workspace.tokens.tools" msgstr "Alati" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Vrijednost nije važeća" @@ -5354,55 +5314,55 @@ msgstr "Vrijednost nije važeća" msgid "workspace.toolbar.assets" msgstr "Stavke" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Paleta boja (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Komentari (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Krivulja (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elipsa (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Ploča (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Slika (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Premijesti (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Putanja (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Dodaci (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Pravokutnik (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Tekst (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Tipografija (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Uključi/isključi alatnu traku" @@ -5415,7 +5375,7 @@ msgstr "Gotovo" msgid "workspace.top-bar.view-only" msgstr "**Provjera koda** (samo pregled)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Do sada nema promjena povijesti" @@ -5555,19 +5515,19 @@ msgstr "tipografska stavka" msgid "workspace.undo.entry.unknown" msgstr "Operacija izvršena %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Odbaci" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Više informacija" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Postoje ažuriranja u zajedničkim bibliotekama" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Ažuriraj" diff --git a/frontend/translations/id.po b/frontend/translations/id.po index 250ff26b5f..86528cdc1a 100644 --- a/frontend/translations/id.po +++ b/frontend/translations/id.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: Linerly \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Indonesian " "\n" "Language: id\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Sudah memiliki akun?" @@ -29,7 +29,7 @@ msgstr "" "Ini layanan DEMO, JANGAN GUNAKAN untuk pekerjaan nyata, proyek-proyek ini " "akan di hapus secara berkala." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Lupa kata sandi?" @@ -37,37 +37,37 @@ msgstr "Lupa kata sandi?" msgid "auth.fullname" msgstr "Nama Lengkap" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Masuk ke akun saya" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Masuk di sini." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot adalah peralatan desain bebas & bersumber terbuka untuk kolaborasi " "Desain dan Kode" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID Connect" @@ -87,7 +87,7 @@ msgstr "Profil belum terverifikasi, harap verifikasi profil sebelum melanjutkan. msgid "auth.notifications.recovery-token-sent" msgstr "Tautan pemulihan kata sandi telah dikirim ke kotak masuk Anda." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Berhasil bergabung ke tim" @@ -119,7 +119,7 @@ msgstr "Lupa kata sandi?" msgid "auth.recovery-submit" msgstr "Ubah kata sandi Anda" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Belum punya akun?" @@ -133,7 +133,7 @@ msgstr "" msgid "auth.register-account-title" msgstr "Nama Anda" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Buat akun baru" @@ -146,7 +146,7 @@ msgstr "Buat akun baru" msgid "auth.terms-and-privacy-agreement" msgstr "Saya menyetujui [persyaratan layanan](%s) dan [kebijakan privasi](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Kebijakan layanan" @@ -158,92 +158,92 @@ msgstr "Surel kerja" msgid "comments.mentions.not-found" msgstr "Tidak ada orang yang ditemukan untuk @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Terbitkan" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Semua pengguna Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Apakah Anda yakin ingin menghapus tautan ini? Jika Anda menghapusnya, maka " "tautan tersebut tidak akan lagi tersedia bagi siapa pun" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(saat ini)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Hancurkan tautan" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Dapatkan tautan" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Tautan berhasil disalin" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Atur perizinan" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "%s halaman dibagikan" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Dapat berkomentar" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Dapat menginspeksi kode" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Siapapun yang memiliki tautan dapat mengakses" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Halaman yang dibagikan" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Tautan yang dapat dibagikan akan muncul di sini" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Hanya anggota tim" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Bagikan prototipe" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Pilih Semua" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Batalkan penerbitan" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Pengelolaan tim" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot dimaksud untuk tim. Undang anggota untuk bekerja bersama pada " "berbagai proyek dan berkas" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Bersekutu!" @@ -251,23 +251,23 @@ msgstr "Bersekutu!" msgid "dashboard.add-file" msgstr "Tambahkan berkas" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Tambahkan sebagai Pustaka Bersama" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Ubah surel" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(salin)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Buat tim baru" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Sematkan versi ini" @@ -275,11 +275,11 @@ msgstr "Sematkan versi ini" msgid "dashboard.default-team-name" msgstr "Penpot Anda" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Hapus tim" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Unduh berkas Penpot (.penpot)" @@ -291,19 +291,19 @@ msgstr "Duplikasi" msgid "dashboard.duplicate-multi" msgstr "Gandakan % berkas" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Ketika anggota proyek membuat draf, itu akan ditampilkan di sini." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Belum ada draf." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Ketika anggota proyek membuat berkas, itu akan ditampilkan di sini." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Belum ada berkas." @@ -355,66 +355,66 @@ msgstr "Mulai membuat hal-hal yang hebat" msgid "dashboard.export-binary-multi" msgstr "Unduh %s berkas Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Ekspor papan sebagai PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Ekspor sebagai PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s dari %s elemen dipilih" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Ekspor" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Anda dapat menambahkan pengaturan pengeksporan ke elemen dari properti " "desain (di bawah dari bilah sisi kanan)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Info bagaimana menetapkan pengeksporan di Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Tidak ada elemen dengan pengaturan pengeksporan." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Ekspor pilihan" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Fon dihapus" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Abaikan semua" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Fon khusus yang Anda unggah akan muncul di sini." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Belum ada fon kustom." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "Ketika anggota proyek mengunggah fon kustom, itu akan ditampilkan di sini." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "%s fon ditambahkan" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -424,7 +424,7 @@ msgstr "" "fon tunggal**. Anda dapat mengunggah fon dengan format berikut: **TTF, OTF, " "dan WOFF** (hanya satu yang diperlukan)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -433,11 +433,11 @@ msgstr "" "konten (Content rights) dari [Ketentuan Layanan Penpot](%s). Anda mungkin " "juga ingin membaca tentang [lisensi fon](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Unggah semua" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -448,11 +448,11 @@ msgstr "" "menyarankan menggunakan [Transfonter](https://transfonter.org/) untuk " "membuat fon web dan memperbaiki kesalahan. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Impor berkas Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Aduh! Kami tidak dapat mengimpor berkas ini" @@ -460,11 +460,11 @@ msgstr "Aduh! Kami tidak dapat mengimpor berkas ini" msgid "dashboard.import.bad-url" msgstr "Pengimporan gagal. URL templat salah" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgstr "%s berkas telah berhasil diimpor." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Beberapa berkas berisi objek yang tidak valid yang telah dihapus." @@ -472,61 +472,33 @@ msgstr "Beberapa berkas berisi objek yang tidak valid yang telah dihapus." msgid "dashboard.import.no-perms" msgstr "Anda tidak memiliki perizinan untuk mengimpor ke tim ini" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Memproses warna" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Memproses komponen" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Memproses media" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Memproses halaman: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Memproses tipografi" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Mengunggah data ke server (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Mengunggah berkas: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Undang orang" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Tinggalkan tim" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Pustaka & Template" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Di sini Anda memiliki Pustaka dan templat yang dapat ditambahkan ke proyek " "Anda" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Jelajahi lebih banyak dan pelajari cara berkontribusi" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Terjadi masalah saat mengimpor template. Template tidak diimpor." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Pustaka" @@ -534,7 +506,7 @@ msgstr "Pustaka" msgid "dashboard.loading-files" msgstr "memuat berkas Anda …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "memuat fon Anda …" @@ -554,39 +526,39 @@ msgstr "Pindahkan %s berkas ke" msgid "dashboard.move-to-other-team" msgstr "Pindahkan ke tim lain" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Buat Berkas" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Berkas Baru" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Buat Proyek" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Proyek Baru" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Tidak ada hasil yang ditemukan untuk “%s”" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Proyek yang disemat akan muncul di sini" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notifikasi" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Alamat surel Anda berhasil diperbarui" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Alamat surel Anda berhasil diverifikasi" @@ -598,11 +570,11 @@ msgstr "Pengaturan notifikasi diperbarui" msgid "dashboard.notifications.password-saved" msgstr "Berhasil menyimpan kata sandi!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Lihat notifikasi" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s anggota" @@ -610,7 +582,7 @@ msgstr "%s anggota" msgid "dashboard.open-in-new-tab" msgstr "Buka berkas di tab baru" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opsi" @@ -634,7 +606,7 @@ msgstr "Anda sekarang seorang pemilik di tim ini." msgid "dashboard.permissions-change.viewer" msgstr "Anda sekarang seorang peninjau di tim ini." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Semat/Lepas semat" @@ -650,11 +622,11 @@ msgstr "Tidak dapat mengurai manifes plugin" msgid "dashboard.plugins.try-plugin" msgstr "Coba plugin: " -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Proyek" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Ingin menghapus akun Anda?" @@ -662,23 +634,23 @@ msgstr "Ingin menghapus akun Anda?" msgid "dashboard.removed-from-team" msgstr "Anda bukan merupakan bagian dari tim “%s” lagi." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Simpan pengaturan" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Cari…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Mencari “%s”…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Pilih bahasa antarmuka" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Pilih tema" @@ -742,11 +714,11 @@ msgstr "Perbarui pengaturan" msgid "dashboard.settings.notifications.title" msgstr "Notifikasi" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Tampilkan semua berkas" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Riwayat versi" @@ -768,7 +740,7 @@ msgstr[0] "Berkas Anda berhasil digandakan" msgid "dashboard.success-duplicate-project" msgstr "Proyek Anda berhasil digandakan" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Berkas Anda berhasil dipindah" @@ -780,67 +752,67 @@ msgstr "Berkas Anda berhasil dipindah" msgid "dashboard.success-move-project" msgstr "Proyek Anda berhasil dipindah" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Info tim" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Anggota tim" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Proyek tim" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Tambahkan ke proyek Anda" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema antarmuka" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Hasil pencarian" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Ketik untuk mencari hasil" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Batal Penerbitan Pustaka" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Perbarui pengaturan" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Aktif" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Ketika kaitan ini dipicu detail peristiwa akan dikirim" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Anda hanya dapat menghapus atau mengubah webhook yang dibuat oleh Anda." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Jenis konten" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Buat kaitan web" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Kaitan web berhasil dibuat." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Kaitan web (webhook) adalah cara yang sederhana untuk memperbolehkan situs " @@ -848,23 +820,23 @@ msgstr "" "di Penpot. Kami mengirim permintaan POST untuk setiap URL yang Anda " "sediakan." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Klik tombol \"Tambahkan kaitan web\" untuk menambahkan." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Tidak ada kaitan web yang dibuat sejauh ini." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Akun Anda" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Surel" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Nama Anda" @@ -880,11 +852,11 @@ msgstr "Peringatan" msgid "ds.component-subtitle" msgstr "Komponen untuk diperbarui:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Perbolehkan" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Batal" @@ -904,15 +876,15 @@ msgstr "Penyedia autentikasi tidak diperbolehkan untuk profil ini" msgid "errors.auth-provider-not-configured" msgstr "Penyedia autentikasi tidak dikonfigurasi." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Sepertinya Anda tidak terautentikasi atau sesi kedaluwarsa." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Fon %s tidak dapat dimuat" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Fon %s tidak dapat dimuat" @@ -924,19 +896,19 @@ msgstr "Tidak dapat mengunggah berkas media." msgid "errors.character-limit-exceeded" msgstr "Melebihi batas karakter" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Peramban Anda tidak dapat melakukan operasi ini" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Tidak dapat melepaskan set induk ke jalur anak sendiri." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Surel sudah digunakan" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Surel sudah divalidasi." @@ -944,25 +916,25 @@ msgstr "Surel sudah divalidasi." msgid "errors.email-as-password" msgstr "Anda tidak dapat menggunakan surel Anda sebagai kata sandi" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domain tidak diperbolehkan" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Surel “%s” memiliki banyak laporan lompatan permanen." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "Surel “%s” telah dilaporkan sebagai spam atau lompatan permanen." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Sepertinya Anda membuka berkas yang memiliki fitur '%s' aktif tetapi " "frontend Penpot Anda tidak mendukungnya atau menonaktifkannya." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Fitur '%s' tidak didukung." @@ -970,7 +942,7 @@ msgstr "Fitur '%s' tidak didukung." msgid "errors.field-min-length" msgstr "Harus berisi setidaknya 1 karakter." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Kolom kosong" @@ -978,14 +950,14 @@ msgstr "Kolom kosong" msgid "errors.field-not-all-whitespace" msgstr "Nama harus berisi karakter selain spasi." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Sepertinya ada ketidakcocokan antara fitur yang diaktifkan dengan fitur " "berkas yang sedang Anda buka. Migrasi untuk '%s' harus diterapkan sebelum " "berkasnya dapat dibuka." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Sesuatu yang salah terjadi." @@ -1001,7 +973,7 @@ msgstr "Data tidak valid" msgid "errors.invalid-email" msgstr "Silakan menyediakan surel yang valid" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Surel konfirmasi harus cocok" @@ -1025,11 +997,11 @@ msgstr "Undangan ini mungkin dibatalkan atau telah kedaluwarsa." msgid "errors.ldap-disabled" msgstr "Autentikasi LDAP dinonaktifkan." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Anda telah meraih kutipan '%s'. Hubungi dukungan." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Jumlah email maksimum (%s) yang dapat diundang dalam satu permintaan telah " @@ -1047,13 +1019,13 @@ msgstr "Serpertinya konten gambar tidak cocok dengan ekstensi berkas." msgid "errors.media-type-not-allowed" msgstr "Sepertinya ini bukan gambar yang valid." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Profil yang Anda undang membisukan surel (laporan spam atau lompatan " "tinggi)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migrasi sedang berlangsung" @@ -1065,7 +1037,7 @@ msgstr "Kata sandi konfirmasi harus cocok" msgid "errors.password-too-short" msgstr "Kata sandi setidaknya 8 karakter" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Data tidak valid dalam papan klip" @@ -1073,7 +1045,7 @@ msgstr "Data tidak valid dalam papan klip" msgid "errors.profile-blocked" msgstr "Profil diblokir" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "Profil Anda membisukan surel (laporan spam atau lompatan tinggi)." @@ -1081,21 +1053,21 @@ msgstr "Profil Anda membisukan surel (laporan spam atau lompatan tinggi)." msgid "errors.registration-disabled" msgstr "Pendaftaran saat ini dinonaktifkan." -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Fitur '%s' tidak kompatibel terdeteksi" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Anggota tidak cukup untuk meninggalkan tim, Anda mungkin ingin menghapusnya " "saja." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Anggota yang Anda mencoba untuk memberikan peran tidak ada." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "Pemilik tidak dapat meninggalkan tim, Anda harus memberikan ulang peran " @@ -1105,13 +1077,13 @@ msgstr "" msgid "errors.token-set-already-exists" msgstr "Set dengan nama yang sama sudah ada" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "Tidak dapat menyelesaikan drop, set dengan nama yang sama sudah ada di " "jalur ini." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Opsi Tema dengan nama yang sama sudah ada" @@ -1131,11 +1103,11 @@ msgstr "Nilai warna tidak valid: %s" msgid "errors.tokens.invalid-json" msgstr "Kesalahan pengimporan: Data token tidak valid dalam JSON." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Nilai token tidak valid: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Referensi token hilang: " @@ -1143,11 +1115,11 @@ msgstr "Referensi token hilang: " msgid "errors.tokens.number-too-large" msgstr "Nilai token tidak valid. Nilai terurai terlalu besar: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "Opasitas harus antara 0 dan 100% atau 0 dan 1 (mis. 50% atau 0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Token memiliki referensi diri" @@ -1155,39 +1127,39 @@ msgstr "Token memiliki referensi diri" msgid "errors.unexpected-error" msgstr "Sebuah kesalahan tidak terduga terjadi." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Token tidak diketahui" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Berkas memiliki nomor versi tidak kompatibel" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Kesalahan koneksi, URL tidak dapat diraih" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL tidak melewati validasi." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Pengiriman terakhir tidak berhasil." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Terjadi kesalahan pada validasi SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Waktu habis" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Terjadi kesalahan tidak terduga saat memvalidasi" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Status %s tidak diduga" @@ -1203,13 +1175,13 @@ msgstr "Kata sandi lama tidak benar" msgid "feedback.description" msgstr "Deskripsi" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Kami senang memiliki Anda di sini. Jika Anda membutuhkan bantuan, silakan " "mencari sebelum Anda mengirim." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Komunitas Penpot" @@ -1224,11 +1196,11 @@ msgstr "" "masalah, sebuah ide, atau sebuah keluhan. Salah satu anggota dari tim kami " "akan menjawab sesegera mungkin." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Di sini untuk membantu dengan kueri teknis Anda." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Akun dukungan X" @@ -1260,7 +1232,7 @@ msgstr "Unduh gambar sumber" msgid "inspect.attributes.shadow" msgstr "Bayangan" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Ukuran dan posisi" @@ -1268,39 +1240,27 @@ msgstr "Ukuran dan posisi" msgid "inspect.attributes.stroke" msgstr "Sapuan" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Tengah" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Dalam" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Luar" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Keluarga Fon" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Ukuran Fon" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Gaya Fon" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Spasi Huruf" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Tinggi Garis" @@ -1312,11 +1272,11 @@ msgstr "Tidak ada" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Garis bawah" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Huruf Judul" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Huruf Kecil" @@ -1328,7 +1288,7 @@ msgstr "Tidak ada" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Tidak ditetapkan" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Huruf Besar" @@ -1402,43 +1362,43 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Teks" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Tandai semua sebagai terbaca" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Pintasan" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Terima" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Aktif" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Tambahkan" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Tambahkan fon khusus" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Menambahkan..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Admin" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Semua" @@ -1446,29 +1406,29 @@ msgstr "Semua" msgid "labels.and" msgstr "dan" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Kembali" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Sepertinya Anda harus menunggu sebentar dan mencoba lagi; kami melakukan " "pemeliharaan kecil pada peladen kami." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Gerbang Jalur Buruk" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Batal" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Tutup" @@ -1476,7 +1436,7 @@ msgstr "Tutup" msgid "labels.code" msgstr "Kode" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Tutup" @@ -1484,11 +1444,11 @@ msgstr "Tutup" msgid "labels.comment" msgstr "Komentar" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Komentar" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Komunitas" @@ -1496,11 +1456,11 @@ msgstr "Komunitas" msgid "labels.confirm-password" msgstr "Konfirmasi kata sandi" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Lanjutkan" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Anda dapat melanjutkan dengan sebuah akun Penpot" @@ -1512,27 +1472,27 @@ msgstr "Salin" msgid "labels.copy-color" msgstr "Salin warna" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Salin tautan" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Buat" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Buat tim baru" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Masukkan nama tim baru" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Dasbor" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Hapus" @@ -1544,7 +1504,7 @@ msgstr "Hapus komentar" msgid "labels.delete-comment-thread" msgstr "Hapus utasan" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Hapus undangan" @@ -1552,31 +1512,31 @@ msgstr "Hapus undangan" msgid "labels.delete-multi-files" msgstr "Hapus %s berkas" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Pengembang" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Abaikan" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Draf" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Sunting" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Penyunting" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Kedaluwarsa" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Ekspor" @@ -1588,55 +1548,55 @@ msgstr "Masukan dinonaktifkan" msgid "labels.feedback-sent" msgstr "Masukan dikirim" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Keluarga Fon" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Gaya" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fon" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Repositori GitHub" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Berikan masukan" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Kembali" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Desain grafis" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Pusat Bantuan" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Sembunyikan" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Sembunyikan komentar yang telah diselesaikan" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Impor" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Tidak Aktif" @@ -1644,35 +1604,35 @@ msgstr "Tidak Aktif" msgid "labels.info" msgstr "Info" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Fon terpasang" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Kesalahan Internal" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Undangan" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Bahasa" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Pustaka & Templat" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Memuat…" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Masuk atau daftar" @@ -1680,19 +1640,19 @@ msgstr "Masuk atau daftar" msgid "labels.login" msgstr "Masuk" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Keluar" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Pemasaran" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Anggota" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Anggota" @@ -1704,37 +1664,37 @@ msgstr "Sebutkan" msgid "labels.new-password" msgstr "Kata sandi baru" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Berikutnya" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Anda telah melihat semuanya! Notifikasi komentar baru akan muncul di sini." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Tidak ada undangan yang menunggu." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Aduh!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notifikasi" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "%s berkas" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "%s papan" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "%s proyek" @@ -1743,27 +1703,27 @@ msgstr[0] "%s proyek" msgid "labels.old-password" msgstr "Kata sandi lama" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Opsi" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Lainnya (jelaskan)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Lainnya" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Pemilik" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Kata sandi" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Ditunda" @@ -1771,23 +1731,23 @@ msgstr "Ditunda" msgid "labels.post" msgstr "Kiriman" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Sebelumnya" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Desain produk atau UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Pengelolaan Produk" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Proyek" @@ -1795,23 +1755,23 @@ msgstr "Proyek" msgid "labels.refresh" msgstr "Muat ulang" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Catatan rilis" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Hapus" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Keluarkan anggota" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Ubah nama" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Ubah nama tim" @@ -1835,7 +1795,7 @@ msgstr "balasan baru" msgid "labels.reply.thread" msgstr "Balas" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Kirim ulang undangan" @@ -1843,43 +1803,43 @@ msgstr "Kirim ulang undangan" msgid "labels.restore" msgstr "Pulihkan" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Coba lagi" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Peran" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Simpan" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Cari" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Cari fon" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Pilih opsi" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Kirim" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Mengirim…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Kami dalam pemeliharaan yang telah diprogram untuk sistem kami." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Layanan Tidak Tersedia" @@ -1887,63 +1847,63 @@ msgstr "Layanan Tidak Tersedia" msgid "labels.sets" msgstr "Set" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Pengaturan" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Bagikan" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Pustaka" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Tampilkan" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Tampilkan semua komentar" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Tampilkan daftar komentar" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Hanya tampilkan sebutan Anda" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Hanya tampilkan komentar Anda" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Mulai" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Siswa atau Guru" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Tema" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutorial" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Kesalahan tidak diketahui" @@ -1951,23 +1911,23 @@ msgstr "Kesalahan tidak diketahui" msgid "labels.unpublish-multi-files" msgstr "Batalkan publikasi %s berkas" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Perbarui" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Perbarui tim" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Unggah" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Unggah fon khusus" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Mengunggah…" @@ -1975,11 +1935,11 @@ msgstr "Mengunggah…" msgid "labels.view-only" msgstr "TAMPILAN SAJA" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Pelihat" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Kaitan web" @@ -1987,11 +1947,11 @@ msgstr "Kaitan web" msgid "labels.write-new-comment" msgstr "Tulis komentar baru" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(Anda)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Akun Anda" @@ -2126,63 +2086,63 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Tambahkan “%s” sebagai Pustaka Terbagi" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Dorongan besar" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Verifikasi surel baru" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Kami akan mengirim Anda sebuah surel ke surel Anda “%s” saat ini untuk " "memverifikasi identitas Anda." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Surel baru" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Ubah surel" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Ubah surel Anda" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Buat kaitan web" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Buat kaitan web" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL Muatan" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://contoh.co.id/terimapos" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Batalkan dan jaga akun saya" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Ya, hapus akun saya" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "Dengan menghapus akun Anda akan kehilangan semua proyek dan arsip Anda saat " "ini." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Apakah Anda yakin ingin menghapus akun Anda?" @@ -2232,31 +2192,31 @@ msgstr "Apakah Anda yakin ingin menghapus %s berkas?" msgid "modals.delete-file-multi-confirm.title" msgstr "Menghapus %s berkas" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Apakah Anda yakin ingin menghapus gaya fon ini? Ini tidak akan muat jika " "digunakan dalam sebuah berkas." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Menghapus gaya fon" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Apakah Anda yakin ingin menghapus fon ini? Ini tidak akan muat jika " "digunakan dalam sebuah berkas." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Menghapus fon" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Apakah Anda yakin ingin menghapus halaman ini?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Hapus halaman" @@ -2272,27 +2232,27 @@ msgstr "Apakah Anda yakin ingin menghapus proyek ini?" msgid "modals.delete-project-confirm.title" msgstr "Hapus proyek" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Hapus berkas" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Tidak diaktifkan dalam berkas mana pun." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Pustaka ini diaktifkan di sini: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Apakah Anda yakin ingin menghapus berkas ini?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Menghapus berkas" @@ -2311,181 +2271,181 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Menghapus tim" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Keluarkan anggota" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Apakah Anda yakin ingin mengeluarkan anggota ini dari tim?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Keluarkan anggota tim" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" "Aset yang telah digunakan dalam berkas akan tetap di sana (tidak ada desain " "yang akan rusak)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Hapus kaitan web" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Apakah Anda yakin ingin menghapus kaitan web ini?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Menghapus kaitan web" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Sunting kaitan web" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Sunting kaitan web" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Kirim undangan" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Surel, dipisah dengan koma" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Beberapa surel berasal dari anggota tim saat ini. Undangan mereka tidak " "akan dikirim." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Anda dapat mengundang anggota ke tim supaya mereka dapat mengakses berkas " "ini dan semua berkas tim." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Undang anggota ke tim" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Karena Anda anggota satu-satunya dari tim ini, tim akan dihapus beserta " "proyek dan berkasnya." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Apakah Anda yakin ingin meninggalkan tim %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Anda tidak dapat tinggalkan tim jika tidak ada anggota lain untuk " "dipromosikan sebagai pemilik. Anda mungkin ingin menghapus tim ini." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Anda adalah pemilik dari tim ini. Silakan memilih anggota lain untuk " "mempromosikan sebagai pemilik sebelum Anda tinggal." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promosikan dan tinggal" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Pilih seorang anggota untuk dipromosikan" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Sebelum Anda tinggal" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Tinggalkan tim" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Apakah Anda ingin meninggalkan tim ini?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Meninggalkan tim" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Pindahkan" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Apakah Anda yakin ingin memindahkan pustaka ini?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Pindahkan pustaka" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Jumlah dorongan" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Transfer kemilikan" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Jika Anda mentransfer kemilikan, Anda akan mengubah peran Anda ke Admin, " "dan kehilangan beberapa izin pada tim ini. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Anda saat ini adalah pemilik dari tim ini. Apakah Anda yakin ingin membuat " "%s sebagai pemilik tim ini?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Pemilik tim baru" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Terbitkan" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Pustaka Anda sedang kosong. Apakah Anda ingin menerbitkannya?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Terbitkan pustaka kosong" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Dorongan kecil" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Batalkan penerbitan" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Apakah Anda yakin ingin membatalkan penerbitan pustaka ini?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Batalkan penerbitan pustaka" @@ -2508,11 +2468,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Perbarui sebuah komponen dalam sebuah pustaka terbagi" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Laman ini tidak ada" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Kesalahan 404" @@ -2530,11 +2490,11 @@ msgstr "Kami telah mengirim surel pemulihan ke" msgid "not-found.login.sent-recovery-check" msgstr "Periksa surel Anda dan klik tautan untuk membuat kata sandi baru." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Daftar secara gratis" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "Dan mulai menggunakan Penpot dalam hitungan detik!" @@ -2542,67 +2502,67 @@ msgstr "Dan mulai menggunakan Penpot dalam hitungan detik!" msgid "not-found.made-with-love" msgstr "Dibuat dengan CINTA dan Sumber Terbuka" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Anda sudah meminta akses ke berkas ini." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Anda sudah meminta akses ke berkas ini atau berkas lain atau proyek dari " "tim ini." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Anda sudah meminta akses ke proyek ini atau proyek lain atau berkas dari " "tim ini." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Anda sudah meminta akses ke proyek ini." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "MINTA AKSES" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Perlu diingat, jika pemilik mengizinkan, Anda akan diundang ke tim." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Permintaan Anda telah dikirim dengan benar!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Anda tidak memiliki akses ke berkas ini." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Pergi ke Penpot Anda" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Jika pemilik mengizinkan, Anda akan diundang ke tim." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Berkas Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Anda tidak memiliki akses ke proyek ini." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROYEK" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Untuk mengakses berkas ini, Anda dapat menanyakan kepada pemilik tim." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Untuk mengakses proyek ini, Anda dapat menanyakan kepada pemilik tim." @@ -2616,25 +2576,25 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Versi baru sudah tersedia." -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Undangan berhasil dikirim" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Tautan undangan disalin" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Anda tidak dapat menghapus profil Anda. Ubah peran pada tim Anda sebelum " "menlanjutkan." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil berhasil disimpan!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Surel verifikasi dikirim ke %s. Periksa surel Anda!" @@ -2664,78 +2624,78 @@ msgstr "" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Buat tim" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Undang anggota" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Undang dengan peran:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Mari kita mulai!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Membuat kode asli dari desain" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Membuat Sistem Desain" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototipe" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Desain UI/UX aplikasi" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Kerangka Kerja" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Untuk apa Anda akan menggunakan Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Beri tahu kami sedikit tentang Anda untuk membantu kami membuat Penpot " "berfungsi bagi Anda. Jawaban Anda akan membantu kami memprioritaskan fitur " "baru dan mengarahkan Anda ke arah yang tepat untuk memulai." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Bantu kami mengenal Anda" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Alat manakah yang paling sering Anda gunakan?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Jenis pekerjaan apa yang Anda lakukan?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Ceritakan tentang pekerjaan Anda" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Di mana Anda ingin mulai?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Edukasi" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Pribadi" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Kerja" @@ -2785,11 +2745,11 @@ msgstr "Mulai mendesain" msgid "profile.recovery.go-to-login" msgstr "Pergi ke log masuk" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Copot" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Tercampur" @@ -2801,35 +2761,35 @@ msgstr "Hapus warna" msgid "settings.select-this-color" msgstr "Pilih item menggunakan gaya ini" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Komentar" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Sesuaikan ke bawah" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Sesuaikan ke tengah secara horizontal" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Sesuaikan ke kiri" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Sesuaikan ke kanan" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Sesuaikan ke atas" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Sesuaikan ke tengah secara vertikal" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Buat papan dari seleksi" @@ -2837,51 +2797,51 @@ msgstr "Buat papan dari seleksi" msgid "shortcuts.bold" msgstr "Tebal" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Perbedaan boolean" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Pengecualian boolean" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Berpotongan boolean" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Serikat boolean" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Turunkan ke paling belakang" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Turunkan ke belakang" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Naikkan ke atas" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Naikkan ke paling atas" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Hapus urungan" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Salin" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Salin tautan ke papan klip" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Salin properti" @@ -2889,35 +2849,35 @@ msgstr "Salin properti" msgid "shortcuts.create-new-project" msgstr "Buat baru" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Potong" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Perkecil" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Hapus" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Hapus simpul" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Lepaskan komponen" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Kurva" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipss" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Papan" @@ -2925,39 +2885,39 @@ msgstr "Papan" msgid "shortcuts.draw-nodes" msgstr "Gambar jalur" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Jalur" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Persegi Panjang" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Teks" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Gandakan" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Batal" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Ekspor bentuk" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Zum untuk muat semua" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Balikkan secara horizontal" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Balikkan secara vertikal" @@ -2981,23 +2941,23 @@ msgstr "Pergi ke pustaka terbagi" msgid "shortcuts.go-to-search" msgstr "Cari" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Kelompok" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribusikan secara horizontal" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Tampilkan/sembunyikan antarmuka" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Perbesar" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Sematkan gambar" @@ -3005,7 +2965,7 @@ msgstr "Sematkan gambar" msgid "shortcuts.italic" msgstr "Miring" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Gabungkan simpul" @@ -3013,39 +2973,39 @@ msgstr "Gabungkan simpul" msgid "shortcuts.line-through" msgstr "Coret" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Buat sudut" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Buat kurva" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Topeng" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Gabungkan simpul" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Pindah" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Pindah ke bawah secara cepat" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Pindah ke kiri secara cepat" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Pindah ke kanan secara cepat" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Pindah ke atas secara cepat" @@ -3053,333 +3013,333 @@ msgstr "Pindah ke atas secara cepat" msgid "shortcuts.move-nodes" msgstr "Pindahkan simpul" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Pindah ke bawah" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Pindah ke kiri" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Pindah ke kanan" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Pindah ke atas" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Papan berikutnya" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Tidak ada pintasan yang ditemukan" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Tetapkan opasitas ke 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Tetapkan opasitas ke 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Tetapkan opasitas ke 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Tetapkan opasitas ke 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Tetapkan opasitas ke 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Tetapkan opasitas ke 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Tetapkan opasitas ke 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Tetapkan opasitas ke 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Tetapkan opasitas ke 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Tetapkan opasitas ke 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Pemilih warna" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Pergi ke bagian komentar pelihat" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Pergi ke dasbor" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Pergi ke bagian inspeksi penampil" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Pergi ke bagian interaksi pelihat" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Pergi ke bagian interaksi pelihat" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Pergi ke ruang kerja" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " atau " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Tempelkan" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Tempelkan properti" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Pengelola plugin" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Papan sebelumnya" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Ulangi" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Ubah nama" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Atur ulang zum" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Skala" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Dasar" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Dasbor" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Pelihat" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Ruang Kerja" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Pilih semua" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Pilih lapisan berikutnya" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Pilih lapisan induk" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Pilih lapisan sebelumnya" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Pisahkan simpul" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Tampilkan/sembunyikan kisi piksel" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Tampilkan/sembunyikan pintasan" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Tancap ke simpul" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Tancap ke kisi piksel" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Mulai menyunting" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Penyesuaian" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Sunting" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Generik" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu utama" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Ubah lapisan" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigasi" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigasi" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigasi" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panel" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Jejak" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Bentuk" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Teks" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Peralatan" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zum" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zum" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Tetapkan gambar kecil" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Pintasan papan ketik" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Alih penyesuaian dinamis" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Alih aset" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Alih palet warna" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Alih mode fokus" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Alih layar penuh" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Tampilkan / Sembunyikan panduan" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Alih riwayat" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Alih lapisan" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Tambahkan/hapus tata letak flex" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Tambahkan/hapus tata letak kisi" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Kunci dipilih" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Kunci proporsi" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Tampilkan / Sembunyikan penggaris" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Pasang ke panduan" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Pasang ke panduan penggaris" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Alih palet teks" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Ubah tema" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Alih keterlihatan" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Alih gaya zum" @@ -3387,63 +3347,63 @@ msgstr "Alih gaya zum" msgid "shortcuts.underline" msgstr "Garis bawah" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Urungkan" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Lepaskan kelompok" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Lepaskan topeng" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribusikan secara vertikal" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Kurangi lensa zum" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Tambahkan lensa zum" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zum ke terpilih" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Penyedia Fon - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Fon - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Proyek - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Cari - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Pustaka Terbagi - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Kebebasan Desain untuk Tim" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Berikan masukan - Penpot" @@ -3451,7 +3411,7 @@ msgstr "Berikan masukan - Penpot" msgid "title.settings.notifications" msgstr "Notifikasi - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Pengaturan - Penpot" @@ -3459,27 +3419,27 @@ msgstr "Pengaturan - Penpot" msgid "title.settings.password" msgstr "Kata sandi - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Undangan - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Anggota - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Pengaturan - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Kaitan Web - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Mode penampilan - Penpot" @@ -3487,99 +3447,99 @@ msgstr "%s - Mode penampilan - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Tidak ada papan yang ditemukan di halaman." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Papan tidak ditemukan." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Komentar (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Jangan tampilkan interaksi" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Layar Penuh" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspeksi (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interaksi" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interaksi (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Salin tautan" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Tampilkan interaksi" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Tampilkan interaksi pada klik" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Peta Situs" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Pengiriman terakhir berhasil." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Sesuaikan tengah horizontal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribusikan spasi horizontal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Sesuaikan ke kiri (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Sesuaikan ke kanan (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Sesuaikan ke bawah (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Sesuaikan ke tengah vertikal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribusikan spasi vertikal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Sesuaikan ke atas (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Tambahkan pustaka" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Semua aset" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Warna" @@ -3587,7 +3547,7 @@ msgstr "Warna" msgid "workspace.assets.colors.add-color" msgstr "Tambahkan warna" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Komponen" @@ -3599,15 +3559,15 @@ msgstr "Tambahkan komponen" msgid "workspace.assets.create-group" msgstr "Buat sebuah kelompok" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Butir Anda akan dinamakan \"nama kelompok / nama butir\" secara otomatis" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Hapus" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Gandakan" @@ -3615,39 +3575,39 @@ msgstr "Gandakan" msgid "workspace.assets.duplicate-main" msgstr "Gandakan utama" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Sunting" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Penyaring" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Tampilan kisi" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Kelompok" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nama kelompok" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Tampilan daftar" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "pustaka lokal" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Kelola pustaka" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Tidak ada aset yang ditemukan" @@ -3655,7 +3615,7 @@ msgstr "Tidak ada aset yang ditemukan" msgid "workspace.assets.open-library" msgstr "Buka berkas pustaka" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Ubah nama" @@ -3663,11 +3623,11 @@ msgstr "Ubah nama" msgid "workspace.assets.rename-group" msgstr "Ubah nama kelompok" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Cari aset" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Pustaka terbagi" @@ -3676,43 +3636,43 @@ msgid "workspace.assets.sidebar.components" msgid_plural "workspace.assets.sidebar.components" msgstr[0] "%s komponen" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Urutkan" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Tipografi" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Tambahkan tipografi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Ukuran" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Gaya Fon" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Pergi ke berkas pustaka untuk menyunting" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Spasi Huruf" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Ketinggian Garis" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformasi Teks" @@ -3720,67 +3680,67 @@ msgstr "Transformasi Teks" msgid "workspace.assets.ungroup" msgstr "Lepas kelompok" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Buat area" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Buat papan" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Gabungkan sel" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Tambah 1 kolom ke kanan" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Tambah 1 kolom ke kiri" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Hapus kolom" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Hapus kolom dan bentuk" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Gandakan kolom" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Tambah 1 baris di bawah" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Tambah 1 baris di atas" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Hapus baris" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Hapus baris dan bentuk" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Gandakan baris" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Mode fokus" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Fokus nonaktif" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Fokus aktif" @@ -3792,187 +3752,187 @@ msgstr "Gradien linear" msgid "workspace.gradients.radial" msgstr "Gradien radial" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Nonaktifkan penyesuaian dinamis" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Nonaktifkan skala proporsional" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Nonaktifkan tancapan ke pemandu" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Nonaktifkan tancapan ke piksel" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Nonaktifkan pemasangan ke panduan penggaris" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Aktifkan penyesuaian dinamis" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Aktifkan skala proporsional" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Tancapkan ke pemandu" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Aktifkan tancapkan ke piksel" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Pasang ke panduan penggaris" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Sembunyikan nama papan" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Sembunyikan panduan" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Sembunyikan palet warna" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Sembunyikan kisi piksel" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Sembunyikan penggaris" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Sembunyikan palet fon" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Sunting" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Berkas" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Bantuan & info" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferensi" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Tampilan" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Ulangi" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Pilih semua" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Tampilkan nama papan" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Tampilkan panduan" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Tampilkan palet warna" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Tampilkan kisi piksel" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Tampilkan penggaris" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Tampilkan palet fon" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Ubah ke tema gelap" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Ubah ke tema terang" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Urungkan" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Atur ulang" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Kesalahan saat menyimpan" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Disimpan" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Menyimpan" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Bagikan" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Mode penampilan (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zum" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Penuhi - Ubah ukuran untuk memenuhi" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Pas - Ubah ukuran supaya pas" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Zum supaya pas semua" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Layar penuh" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zum ke terpilih" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Sunting kisi" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Keluar" @@ -3980,7 +3940,7 @@ msgstr "Keluar" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Padding bawah" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Tampilkan opsi bantalan 4 sisi" @@ -4016,23 +3976,23 @@ msgstr "Selesai" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Cari lokasi" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Lokasi tata letak kisi" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Belum ada gaya warna dalam pustaka Anda" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Belum ada gaya tipografi dalam pustaka Anda" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Pustaka berkas" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Warna terkini" @@ -4044,71 +4004,71 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Simpan gaya warna" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Atau tambahkan beberapa dari sini untuk mencoba:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "Tidak ada Pustaka Terbagi dalam tim Anda, Anda dapat melihat" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "beberapa templat di sini" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Pustaka berkas" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "PUSTAKA DALAM BERKAS INI" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "PUSTAKA" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "PEMBARUAN PUSTAKA" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Memuat…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Tidak ada Pustaka Terbagi yang membutuhkan pembaruan" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Tidak ada yang cocok dengan “%s”" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Cari pustaka terbagi" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "PUSTAKA TERBAGI" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Hubungkan pustaka" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Putuskan pustaka" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Perbarui" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "lihat semua perubahan" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "PEMBARUAN" @@ -4140,39 +4100,39 @@ msgstr "Sakelar buram" msgid "workspace.options.canvas-background" msgstr "Latar belakang kanvas" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Klip konten" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Komponen" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Anotasi" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Salin" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Buat anotasi" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Sunting anotasi" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Utama" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Ganti komponen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Belum ada aset dalam pustaka ini" @@ -4216,11 +4176,11 @@ msgstr "Atas" msgid "workspace.options.constraints.topbottom" msgstr "Atas & Bawah" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Desain" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Ekspor" @@ -4228,36 +4188,36 @@ msgstr "Ekspor" msgid "workspace.options.export-multiple" msgstr "Ekspor seleksi" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Ekspor %s elemen" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Tambahkan ekspor" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Hapus ekspor" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Akhiran" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Pengeksporan selesai" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Mengekspor…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Pengeksporan gagal" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Pengeksporan secara tidak terduga lambat" @@ -4273,7 +4233,7 @@ msgstr "Tambahkan warna isian" msgid "workspace.options.fill.remove-fill" msgstr "Hapus isian" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Ubah ukuran papan untuk memuat konten" @@ -4297,67 +4257,67 @@ msgstr "Aluran dimulai" msgid "workspace.options.grid.auto" msgstr "Otomatis" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Kolom" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Warna" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Selokan" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Tinggi" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margin" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Tetapkan sebagai bawaan" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Bawah" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Tengah" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Kiri" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Kanan" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Lebarkan" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Atas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Gunakan bawaan" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Lebar" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Baris" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Kotak" @@ -4369,7 +4329,7 @@ msgstr "Isian kelompok" msgid "workspace.options.group-stroke" msgstr "Sapuan kelompok" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Tambahkan alur" @@ -4377,19 +4337,19 @@ msgstr "Tambahkan alur" msgid "workspace.options.guides.remove-guide" msgstr "Hapus alur" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Panduan" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Sakelar pemandu" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Tinggi" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspeksi" @@ -4605,103 +4565,103 @@ msgstr "Interaksi" msgid "workspace.options.interactions.add-interaction" msgstr "Tambahkan interaksi" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Warna" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Bakaran warna" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Penghindaran warna" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Gelapkan" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Perbedaan" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Tidak termasukan" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Cahaya keras" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Rona" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Terangkan" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Kilau" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Kalikan" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Hamparan" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturasi" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Layar" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Cahaya lembut" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Sakelar keterlihatan lapisan" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Max.Height" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Max.Width" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Min.Height" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Min.Width" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Lebih banyak warna" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Lebih banyak warna pustaka" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opasitas" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototipe" @@ -4733,19 +4693,19 @@ msgstr "Tutup radius tersendiri" msgid "workspace.options.radius.show-single-corners" msgstr "Buka radius tersendiri" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Terkini" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Coba lagi" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotasi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Cari fon" @@ -4753,7 +4713,7 @@ msgstr "Cari fon" msgid "workspace.options.select-a-shape" msgstr "Pilih bentuk, papan, atau kelompok untuk menyeret koneksi ke papan lain." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Warna terpilih" @@ -4821,47 +4781,47 @@ msgstr "Sakelar bayangan" msgid "workspace.options.show-fill-on-export" msgstr "Tampilkan dalam ekspor" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Tampilkan dalam mode penampil" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Ukuran" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Prasetel ukuran" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Lingkaran" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Berlian" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Tanda panah" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Tidak ada" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Bulat" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Kotak" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Persegi panjang" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Segitiga" @@ -4869,7 +4829,7 @@ msgstr "Segitiga" msgid "workspace.options.stroke-color" msgstr "Warna sapuan" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Lebar sapuan" @@ -4877,35 +4837,35 @@ msgstr "Lebar sapuan" msgid "workspace.options.stroke.add-stroke" msgstr "Tambahkan warna garis" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Tengah" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Garis" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Titik" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Dalam" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Tercampur" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Luar" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Hapus garis" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Padat" @@ -4941,11 +4901,11 @@ msgstr "Lebar otomatis" msgid "workspace.options.text-options.grow-fixed" msgstr "Tetap" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Spasi Huruf" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Tinggi garis" @@ -4989,122 +4949,122 @@ msgstr "Garis bawah (%s)" msgid "workspace.options.use-play-button" msgstr "Gunakan tombol main di tajuk untuk menjalankan tampilan prototipe." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Lebar" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Sumbu X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Sumbu Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Gambar simpul (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Gabungkan simpul (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Ke sudut (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Ke kurva (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Gabungkan simpul (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Pindahkan simpul (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Simpul terpisah (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Tancap simpul (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Buka" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Jelajahi [lebih banyak plugin](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Belum ada plugin yang terpasang" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Manifes plugin tidak benar." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Anda perlu menjadi penyunting untuk menggunakan plugin ini" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Plugin tidak ada atau URL tidak benar." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Pasang" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Plugin terpasang" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Pengelola plugin" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Plugin" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "PERBARUI PLUGIN INI" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Plugin ini telah diubah sejak terakhir kali Anda membukanya. Ini sekarang " "memerlukan akses:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Memulai pengunduhan berkas." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Membaca komentar dan balasan Anda." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Membaca dan mengubah komentar Anda dan membalas dengan nama Anda." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Membaca konten berkas yang dapat diakses pengguna." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Membaca dan mengubah konten berkas yang dapat diakses pengguna." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Harap diingat bahwa plugin ini dibuat oleh pihak eksternal, jadi pastikan " @@ -5112,74 +5072,74 @@ msgstr "" "Anda penting bagi kami. Jika Anda memiliki masalah, silakan hubungi " "dukungan." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Membaca pustaka dan aset Anda." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Membaca dan mengubah pustaka dan aset Anda." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "PLUGIN '%s' MEMINTA AKSES:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Membaca informasi profil pengguna sasaat ini." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Daftar Plugin" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Hapus plugin" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Tuliskan URL plugin" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Plugin" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "NANTI" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Ingin melihat? Ini akan membuka dalam draf baru untuk tim Anda saat ini. " "(Jika tidak, Anda selalu dapat mencarinya dalam plugin terpasang dalam " "berkas apa pun.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "PLUGIN '%s' TERPASANG UNTUK PENGGUNA ANDA!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "COBA PLUGIN" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Tambahkan tata letak flex" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Tambahkan tata letak kisi" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Tambahkan tata letak" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Buat varian" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Buat properti baru" @@ -5191,7 +5151,7 @@ msgstr "Kirim ke paling belakang" msgid "workspace.shape.menu.backward" msgstr "Kirim ke belakang" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Salin" @@ -5223,27 +5183,27 @@ msgstr "Salin sebagai teks" msgid "workspace.shape.menu.create-annotation" msgstr "Buat anotasi" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Seleksi ke papan" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Buat komponen" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Buat beberapa komponen" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Potong" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Hapus" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Hapus awalan alur" @@ -5255,7 +5215,7 @@ msgstr "Lepaskan bagian" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Lepaskan bagian" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Perbedaan" @@ -5263,15 +5223,15 @@ msgstr "Perbedaan" msgid "workspace.shape.menu.duplicate" msgstr "Gandakan" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Sunting" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Tidak termasuk" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Ratakan" @@ -5283,7 +5243,7 @@ msgstr "Balikkan secara horizontal" msgid "workspace.shape.menu.flip-vertical" msgstr "Balikkan secara vertikal" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Awalan alur" @@ -5295,31 +5255,31 @@ msgstr "Bawa ke depan" msgid "workspace.shape.menu.front" msgstr "Bawa ke paling depan" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Kelompok" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Sembunyikan" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Tampilkan/Sembunyikan Antarmuka" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Persimpangan" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Kunci" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Topeng" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Tempelkan" @@ -5327,23 +5287,23 @@ msgstr "Tempelkan" msgid "workspace.shape.menu.paste-props" msgstr "Tempel properti" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Jalur" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Hapus tata letak flex" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Hapus tata letak kisi" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Hapus tata letak" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Hapus properti" @@ -5363,11 +5323,11 @@ msgstr "Pulihkan komponen utama" msgid "workspace.shape.menu.select-layer" msgstr "Pilih lapisan" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Tampilkan" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Tampilkan dalam panel aset" @@ -5383,19 +5343,19 @@ msgstr "Hapus gambar kecil" msgid "workspace.shape.menu.thumbnail-set" msgstr "Tetapkan sebagai gambar kecil" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Lepaskan kelompok" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Serikat" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Buka kunci" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Buka topeng" @@ -5411,47 +5371,47 @@ msgstr "Tutup bilah samping" msgid "workspace.sidebar.expand" msgstr "Buka bilah samping" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Riwayat" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Lapisan" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Komponen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Papan" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Kelompok" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Gambar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Topeng" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Cari lapisan" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Bentuk" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Teks" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Atribut SVG Diimpor" @@ -5463,7 +5423,7 @@ msgstr "Halaman" msgid "workspace.sidebar.sitemap.add-page" msgstr "Tambahkan halaman" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Peta Situs" @@ -5479,7 +5439,7 @@ msgstr "Tambahkan tema baru" msgid "workspace.tokens.add-set-to-group" msgstr "Tambahkan set ke kelompok ini" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Diterapkan pada" @@ -5491,7 +5451,7 @@ msgstr "Axis" msgid "workspace.tokens.back-to-themes" msgstr "Kembali ke daftar tema" -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Warna" @@ -5499,7 +5459,7 @@ msgstr "Warna" msgid "workspace.tokens.create-new-theme" msgstr "Buat tema pertama Anda sekarang." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Buat baru." @@ -5519,7 +5479,7 @@ msgstr "Hapus tema" msgid "workspace.tokens.duplicate" msgstr "Gandakan token" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "salin" @@ -5587,7 +5547,7 @@ msgstr "Tidak ada tema aktif" msgid "workspace.tokens.no-permisions-set" msgstr "Anda perlu menjadi penyunting untuk mengaktifkan / menonaktifkan set" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Anda perlu menjadi penyunting untuk menggunakan tema" @@ -5599,7 +5559,7 @@ msgstr "Belum ada set yang ditetapkan. Buatlah terlebih dahulu." msgid "workspace.tokens.no-sets-yet" msgstr "Belum ada set." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Belum ada tema." @@ -5611,7 +5571,7 @@ msgstr "Anda saat ini belum memiliki tema." msgid "workspace.tokens.num-active-sets" msgstr "%s set aktif" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Nilai asli: %s" @@ -5623,7 +5583,7 @@ msgstr "Padding" msgid "workspace.tokens.radius" msgstr "Radius" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Nilai terselesaikan: %s" @@ -5665,11 +5625,11 @@ msgstr "Daftar tema" msgid "workspace.tokens.token-description" msgstr "Deskripsi" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nama" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " bukanlah nama token yang valid.\n" @@ -5680,11 +5640,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Nilai" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Masukkan nilai atau alias dengan {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKEN - %s" @@ -5692,7 +5652,7 @@ msgstr "TOKEN - %s" msgid "workspace.tokens.tools" msgstr "Peralatan" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Nilai tidak valid" @@ -5700,55 +5660,55 @@ msgstr "Nilai tidak valid" msgid "workspace.toolbar.assets" msgstr "Aset" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Palet Warna (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Komentar (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Kurva (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elips (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Papan (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Gambar (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Pindahkan (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Jalur (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Plugin (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Persegi Panjang (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Teks (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Tipografi (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Saklar bilah alat" @@ -5761,7 +5721,7 @@ msgstr "Selesai" msgid "workspace.top-bar.view-only" msgstr "**Menginspeksi kode** (Hanya Lihat)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Tidak ada riwayat perubahan sejauh ini" @@ -5901,19 +5861,19 @@ msgstr "aset tipografi" msgid "workspace.undo.entry.unknown" msgstr "Operasi pada %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Abaikan" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Info lebih lanjut" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Ada pembaruan dalam pustaka terbagi" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Perbarui" diff --git a/frontend/translations/ig.po b/frontend/translations/ig.po index 0c6048ae17..3c9f6655e9 100644 --- a/frontend/translations/ig.po +++ b/frontend/translations/ig.po @@ -25,7 +25,7 @@ msgstr "" "Ihe bụ ọrụ ozigosi, e jile ya rụọ ezigbo ọrụ, ọrụ ndị a ga-ekpochapụ site " "na oge ruo na oge." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Chefuru akara mpibanye ?" @@ -33,27 +33,27 @@ msgstr "Chefuru akara mpibanye ?" msgid "auth.fullname" msgstr "Aha n'uju" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Banye ebe a" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "Obi Git" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "Ụlọ nchọcha Git" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Gọgụlụ" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "Mepe ID" @@ -73,7 +73,7 @@ msgstr "E nyochaghị nchịkọta , Biko nyocha nchịkọta tupu ị gaa n'ihu msgid "auth.notifications.recovery-token-sent" msgstr "E zigara akara njikọ nnweghachi akara mpibanye n'igbe mbata ozi gị ." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Sonyere n'otu nke ọma" @@ -101,11 +101,11 @@ msgstr "Chefuru akara mpịbanye ?" msgid "auth.recovery-submit" msgstr "Gbanye akara mpịbanye." -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "E nweghị ebe Ntinye ihe ma kịta?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Mepee ebe Ntinye ihe" @@ -113,93 +113,93 @@ msgstr "Mepee ebe Ntinye ihe" msgid "auth.register-title" msgstr "Mepee ebe ntinye ihe" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Ọnọdụ ọrụ" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "bipụta" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Ndị niile ji ite mkpịsị arụ ọrụ" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(kee ugbu a )" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Mebie njiko" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Weta njiko" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "E setere njiko nke ọma" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Jikwa ikike" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "nwere ike ikwu okwu" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Nwere Ike inyocha akara" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Onye ọ bụla ga-enwe ohere mbanye" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Ihu akwụkwọ ekekoritara" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Njikọ e nwere ike ikekorita ga-apụta" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Sọọsọ ndị otu" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Kee atụ gasị" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Ebiputaghị" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Ite mkpịsị bụ maka otu . Kpọọ ndị otu ka arụkọta ọnụ on arụmarụ na ederede " "ọnụ" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Jikota ọnụ !" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Tinye dị ka ọ a nkwekọrịta" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Gbanwee ozi - n" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(sere)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Mebe otu ọhụrụ" @@ -207,11 +207,11 @@ msgstr "Mebe otu ọhụrụ" msgid "dashboard.default-team-name" msgstr "Ite mkpịsị gị" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "kacha otu" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Butuo ederede ite mkpịsị (.penpot)" @@ -227,116 +227,88 @@ msgstr "Mee o yiri %s ederede" msgid "dashboard.export-binary-multi" msgstr "Butuo %s ederede ite mkpịsị (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Bupu bọọdụ dị ka PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Bupu dị ka PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "A họrọ %s nke %s ndị na" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Ị nwere ike ị tinye ntọala mbupu na ndịna site na ngwongwo nse (n'ala akụkụ " "aka nri ihe ndepụta)" -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Ozi etu esi ahazi mbupu n'ite mkpịsị" -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "E nweghị ndịna ọ bụla na ntọala mbupu" -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Nhọrọ mbupu" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Mkpụrụ edemede na-efu efu" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Gbasaa ha niile" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Mkpụrụ edemede ndị ị bugoro ga-apụta ebe." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Bugo ha niile" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" "Anyị chọpụtara nsogbu nwere ike ịpụta na mkpụrụ edide gị gbasara ntụ kwụ " "ọtọ " -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Ewuu! Anyị enweghị ike bubata ederede a" -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Ụfọdụ ederede nwere ihe ndị adabaghị na ya nke ewepugoro. ." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Nhazi agwụgwara gasị" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Nhazi ngwa gasị" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Nhazi mgbasa ozi" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Nhazi ihu akwụkwọ : %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Nhazi akara nkụpụta" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Ibugo njatụle n'ebe nkesa (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Ibugo ederede : %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Kpọta mmadụ" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Hapụ otu" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Ọba ederede gasị & ndebiri" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Nyocha ọtụtụ n'ime ha ma mara etu ị ga-esi tinye ọnụ" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "E nwere nsogbu ibu aba ndebiri . E bubataghị ndebiri." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Ọba ederede gasị" @@ -344,7 +316,7 @@ msgstr "Ọba ederede gasị" msgid "dashboard.loading-files" msgstr "Mbụbata ederede gị …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "mbubata mkpụrụ edemede gị …" @@ -360,35 +332,35 @@ msgstr "Buga %s ederede gasị na" msgid "dashboard.move-to-other-team" msgstr "Gaa n'otu ọzọ" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Ederede ọhụrụ" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Ederede ọhụrụ" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nchọcha ọhụrụ" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Arụmarụ ọhụrụ" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Ọ nweghị ihe ndabara e nwetara maka “%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Arụmarụ a kụdoro ga-apụta ebe a" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Adreesị ozi - n gị ahazigharịala gara nke ọma" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "E nyochala adreesị ozi - n gị nke ọma" @@ -396,7 +368,7 @@ msgstr "E nyochala adreesị ozi - n gị nke ọma" msgid "dashboard.notifications.password-saved" msgstr "E dọkwara akara mpịbanye nke ọma !" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s Ndị otu" @@ -404,7 +376,7 @@ msgstr "%s Ndị otu" msgid "dashboard.open-in-new-tab" msgstr "Mepe ederede na tabụ ọhụrụ" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Nhọrọ gasị" @@ -412,31 +384,31 @@ msgstr "Nhọrọ gasị" msgid "dashboard.password-change" msgstr "Gbanwee akara mpịbanye" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Nọmba /Wepụ nọmba" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Arụmarụ" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Chọọ iwepu ebe Ntinye ihe gị ?" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Dokwa ntọala" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Chọọ…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Na-achọ “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Họrọ asụsụ UI" @@ -444,7 +416,7 @@ msgstr "Họrọ asụsụ UI" msgid "dashboard.success-duplicate-project" msgstr "E megharịala arụmarụ gị nke ọma\"" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "E wepula ederede gị nke ọma\"" @@ -452,78 +424,78 @@ msgstr "E wepula ederede gị nke ọma\"" msgid "dashboard.success-move-project" msgstr "E bupula arụmarụ gị nke ọma" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Ozi otu" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Ndị otu" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Arụmarụ otu" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Isiokwu UI" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Chọọ risọltụ gasị" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Pinye ịchọ risọltụ" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Ọba ederede ebiputaghị" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Hazie ntọala" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Dị gara gara" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Mgbe a kpọlitere nkọ a, ọ ga-Ezipụta nkọwa emume n'uju" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Ụdị ndịna" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Mepe nko - ududọ" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "E mepere nko-ududọ nke ọma ." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Nko-ududọ bụ ụzọ dị mfe ga-ekwe ka ebe ududọ na apps ndị ọzọ nweta ozi Mgbe " "ụfọdụ emume na eme n'ite mkpịsị . Anyị ga e ziga OZI arịrịọ nwe URLs ọ bụla " "ị nwetara" -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Pịa mpị \"Tinye nko-ududọ \" ịtinye otu ." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "E nweghị Nko-ududọ e ebere ruo ugbu a ." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Ebe Ntinye ihe gị" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Ozi-n" @@ -535,7 +507,7 @@ msgstr "Okay" msgid "ds.alert-title" msgstr "Gee ntị" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Kagbuo" @@ -547,15 +519,15 @@ msgstr "Okay" msgid "ds.confirm-title" msgstr "Ọ doro gị anya ?" -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Ọ dị ka enyochaghị gị ma ọ bụ na oge agwụla ." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "E nweghị ike bugo %s mkpụrụ edide" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "E nweghị ike bugo %s mkpụrụ edide" @@ -563,15 +535,15 @@ msgstr "E nweghị ike bugo %s mkpụrụ edide" msgid "errors.cannot-upload" msgstr "E nweghị ike ị ugo ederede ." -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Your browser cannot do this operation" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E jibuola ozi - n rụọ ọrụ" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Aha ozi-n adabala." @@ -579,25 +551,25 @@ msgstr "Aha ozi-n adabala." msgid "errors.email-as-password" msgstr "Ị nweghị ike iji aha ozi-n gị dị ka akara mpịbanye" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Ozi-n «%s» nwere ọtụtụ ozi nkọwa mbịaghachigide." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "E nwela ozi nkọwa na ozi-n«%s» dị ka ozi abaghị uru ma ọ bụ mbịaghachigide." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Ọ dị ka ị na-emepe ederede nwere a gwara naọ gara aga '%s' mana ihu " "itemkpịsị gị akwadoghị ya ma ọ bụ gosiri ọ gaghị aga." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Akwadoghị agwara '%s' ." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Ihe ezighị ezi emela ." @@ -609,7 +581,7 @@ msgstr "Agwụgwara adabaghị" msgid "errors.invalid-email" msgstr "Debanye aha ozi-n dabara adaba" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Ozi-n nnabata ga-adabrịrị" @@ -629,7 +601,7 @@ msgstr "This invite might be canceled or may be expired." msgid "errors.ldap-disabled" msgstr "LDAP mbinye aka agaghị ." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "I ruola '%s' ole a chọrọ . Chọọ enyemaka nkwado ." @@ -641,7 +613,7 @@ msgstr "Onyinyo a ebuka ibu maka itinye ." msgid "errors.media-type-mismatch" msgstr "Dị ka ihe dị na onyinyo adabaghị na mgbatị ederede." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Nchịkọta gị nwere ozi-n mechiri emechi (akụkọ ozi-n adịghị mma ma ọ bụ " @@ -651,7 +623,7 @@ msgstr "" msgid "errors.profile-blocked" msgstr "A gbachiri nchịkọta agbachi" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Nchịkọta gị lnwere ozi-n mechiri emechi(akụkọ ozi-n adịghị mma ma ọ bụ " @@ -661,15 +633,15 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Ndebanye merụrụ emerụ ugbu a." -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "Ndị otu ezughị okè ịhapụ otu, ọ nyere ike na ị chọrọ ihichapụ ya ." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Onye otu ị na-achọ inye adịghị ." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "O wee enweghị ike ịhapụ otu, ị ga-enyegharịrị ọrụ onwee." @@ -677,23 +649,23 @@ msgstr "O wee enweghị ike ịhapụ otu, ị ga-enyegharịrị ọrụ onwee. msgid "errors.unexpected-error" msgstr "Ndejo atughị anya ya pụtara ." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Ọdịmara amaghị ama" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Mmejọ, e nwetaghị URL" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL enweghị nkwado ." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Mmejọ na nkwado SSL ." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Oge ezuola" @@ -709,13 +681,13 @@ msgstr "Akara mpịbanye ochie adịghị mma" msgid "feedback.description" msgstr "Nkọwa" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Obi dị anyị añụrị ịhụ gị ebea. Ọ bụrụ na enyemaka dị mkpa , biko chọọ tupu " "ị ịga ozi ." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Ogbe Ite mkpịsị" @@ -729,7 +701,7 @@ msgstr "" "Biko, kọwaa ihe isi ozi-n gị , kwuo ma ọ bụ nsogbu , ncheputara ma ọ bụ " "ekaenyeghị. Onye otu anyị ga-azaghachi ozugbo ọ nwere ike ." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Ebe inyere gị aka n'ajụjụ nkà gị ." @@ -761,7 +733,7 @@ msgstr "Butuo onyinyo nsina" msgid "inspect.attributes.shadow" msgstr "Oninyo" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Nhazi na ọnọdụ" @@ -769,35 +741,23 @@ msgstr "Nhazi na ọnọdụ" msgid "inspect.attributes.stroke" msgstr "Akara" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Etiti" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Ime" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Ezi" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Akara nkụpụta" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Ezinụlọ mkpụrụ edide" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Nhazi mkpụrụ edide" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Ndesa mkpụrụ edemede" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Ogo akara" @@ -809,7 +769,7 @@ msgstr "Ncha ncha" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "N'okpuru" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Mkpụrụ edemede isiokwu" @@ -817,7 +777,7 @@ msgstr "Mkpụrụ edemede isiokwu" msgid "inspect.attributes.typography.text-transform.none" msgstr "None" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Mkpụrụ edemede nnukwu" @@ -875,31 +835,31 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Ederede" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Ụzọ mkpirisi" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Nabata" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Gara gara" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Tinye mkpụrụ edide a haziri ahazi" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Ọchịkwa" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Ngwa nrụkọrịta ọrụ Adobe" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Ha niile" @@ -907,23 +867,23 @@ msgstr "Ha niile" msgid "labels.and" msgstr "na" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Azụ" -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Ajọ ụzọ mbanye" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Kagbuo" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Kanva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Megbuo" @@ -931,11 +891,11 @@ msgstr "Megbuo" msgid "labels.code" msgstr "Akara" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Okwu gasị" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Ogbe" @@ -943,27 +903,27 @@ msgstr "Ogbe" msgid "labels.confirm-password" msgstr "Nabata akara mpịbanye" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Ga n'ihu" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "I Nwere Ike ịganihu" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Dọrọ njikọ" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Meputa" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Mepee otu ọhụrụ" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Hichaa" @@ -975,7 +935,7 @@ msgstr "Hichaa okwu" msgid "labels.delete-comment-thread" msgstr "Hichapụ eri" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Hichapụ oku" @@ -983,23 +943,23 @@ msgstr "Hichapụ oku" msgid "labels.delete-multi-files" msgstr "Hichapụ ederede %s" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Onye nrụpụta" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Ndesita" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Dezie" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Odezi" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Mebiri" @@ -1011,43 +971,43 @@ msgstr "Nzaghachi merụrụ emeru" msgid "labels.feedback-sent" msgstr "Zigara nzaghachi" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Ihe onyonyo" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Ezinụlọ mkpụrụ edide" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Ụdịdị gasị" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Mkpụrụ edide gasị" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Ọ a Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Nye nzaghachi" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Gaa azụ" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Ebe enyemaka" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Zoo nkwupụta e kpebiri" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Arụghị ọrụ" @@ -1055,47 +1015,47 @@ msgstr "Arụghị ọrụ" msgid "labels.info" msgstr "Ozi" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Mkpụrụ edide gasị ewubere" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Nrụjọ ndịnime" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "Mmakpo" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Akwụkwọ ịkpọ ọkụ gasị" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Asụsụ" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Ọba ederede na Ndebiri gasị" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Banye ma ọ bụ debanye" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Pụọ" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Ịzụ ahịa" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Onye otu" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Ndị otu" @@ -1103,19 +1063,19 @@ msgstr "Ndị otu" msgid "labels.new-password" msgstr "Akara mpịbanye ọhụrụ" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Ọzọ" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "E jidere unu niile! Ngosi nkwupụta ọhụrụ ga-apụta ebe a ." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "E nweghị ọkụ na-echere" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "E wuu !" @@ -1123,119 +1083,119 @@ msgstr "E wuu !" msgid "labels.old-password" msgstr "Akara mpịbanye ochie" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Ọzọ (zipụta)" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Onye nwe" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Akara mpịbanye" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Na-echere" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Onye njikwa ngwaahịa ma ọ bụ arụmarụ" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Nchịkọta" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Ọrụ gasị" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Wepụta ndetu" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Wepu" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Wepụ onye otu" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Nyegharịa aha" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Nyegharịa otu" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Nwagharịa" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Ọrụ" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Dokwa" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Chọọ mkpụrụ edide" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Họrọ nhọ" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Ziga" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Na-eziga…" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Ntọala" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Ọba ederede" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Gosi nkwupụta niile" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Gosi ndepụta nkwupụta gasị" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Gosi nanị nkwupụta gị gasị" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Eserese" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Bido" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Ọnọdụ" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Nwata akwụkwọ ma ọ bụ onye nkụzi" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Nkuzi" @@ -1243,23 +1203,23 @@ msgstr "Nkuzi" msgid "labels.unpublish-multi-files" msgstr "%s ederede ebipụtaghị" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Hazigharịa" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Hazigharịa otu" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Bugo" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Bugo ederede gasị ahaziri" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Na-ebugo…" @@ -1267,11 +1227,11 @@ msgstr "Na-ebugo…" msgid "labels.view-only" msgstr "Sọọsọ nkiri" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Ndị nkiri" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Nko-ududọ" @@ -1279,11 +1239,11 @@ msgstr "Nko-ududọ" msgid "labels.write-new-comment" msgstr "Dee nkwupụta ọhụrụ" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(gị)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Akara mbata gị" @@ -1312,45 +1272,45 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Tinye “%s” dị ka ọba ederede ekekoritara" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "nnukwu nkpatụ" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Gbanwee ozi nl" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Mebe nko - ududo" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Mebe nko-ududo" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Isi ozi URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://ọmụmaaụ.com/ozi anabara" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Kagbuo ma dobe ebe ntinye ihe m" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Eee, hichapụ ebe ntinye ihe m" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "Site na iwepu ebe ntinye ihe gị, arụmarụ gị gasị ugbu a naebe nchekwa " "ga-efu ." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Ọ kara gị obi na ị chọrọ ị hichapụ ebe ntinye ihe gị?" @@ -1390,15 +1350,15 @@ msgstr "Na-ehichapụ ederede" msgid "modals.delete-file-multi-confirm.title" msgstr "Na-ehichapụ ederede %s" -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Na-ehichapụ mkpụrụ edide" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Ọ kara gị obi na ị chọrọ ị hichapụ ihu akwụkwọ a?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Hichapụ ihu akwụkwọ" @@ -1428,133 +1388,133 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Na-ehichapụ otu" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Hichapụ onye otu" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Ọ kara gị obi na ị chọrọ ị hichapụ onye otu n'otu a?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Hichapụ onye otu" -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Hichapụ nko-ududọ" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Ọ kara gị obi na ị chọrọ ị hichapụ nko-ududọ ?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Na-ehichapụ nko-ududo" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Dezie nko-ududo" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Dezie nko-ududo" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Ziga akwụkwọ ozi mkpọta" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Ozi-n gasị, Rịkọm e kewara" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "Ụfọdụ ozi n sitere na ndị ugbu a A gaghị e ịga akwụkwọ ozi mkpọta ha" -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "kpọta ndị otu n'otu a" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Etu ị bụ sọọ otu onye otu , a ga e hichapụ otu a ya n'ọrụ na ederede ya " "gasịits projects and files." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Ọ kara gị obi na ị chọrọ ịhapụ otu %s ?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "I nweghị ike ịhapụ otu ma ọ bụrụ na e nweghị onye otu ọzọ akwalitere ga na " "onye nwe ya . Ị nwere ike chọọ ị hichapụ otu ." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Gị bụ onye nwee otu a, . Biko, họrọ onye otu ọzọ maka ịkwalite gana onye " "nweeya tupu ị hapụ ." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Kwalite ma hapụ" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Họrọ onye otu maka ịkwalite" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Tupu ị hapụ" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Hapụ otu" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Ọ kara gị obi na ịchọrọ ịhapụ otu ?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Nhapụ otu" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "ọnụ ọgụgụ nkpatụ" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Ọ bụrụ na Inyefe onwunwe , ị ga-agbanwe ọrụ gị ka ọchikwa, ụfọdụ ikikere " "n'isi ndị otu na-efu " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "G bụ onye nwe otu a ugbu a, Ọ doro gị anya na ị chọrọ ime%s Onye ọhụrụ nwe " "otu a?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Onye nwe otu ọhụrụ" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Biputa" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Your library is empty. Are you sure you want to publish it?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Biputa ọba ederede efu" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "kpatụ obere" @@ -1570,15 +1530,15 @@ msgstr "Kagbuo" msgid "notifications.by-code.upgrade-version" msgstr "A new version is available." -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "I nweghị ike ị hichapụ nchịkọta gị . Nyegharịa ya ndị otu gị tupu ị ganihu" -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "E dọkwara nchịkọta nke ọma" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Ozi nnyocha n zigara na%s. Mepe ozi n gị !" @@ -1596,15 +1556,15 @@ msgstr "Nabata ite mkpịsị !" msgid "onboarding.choice.team-up.create-team-desc" msgstr "I nye ha otu gị aha , I nwere ike ị kpọta ndị mmadụ ka ha sonye." -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Kpọta ndị otu" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "kpọta tinye ọrụ :" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Ka anyị bido!" @@ -1650,38 +1610,38 @@ msgstr "Bido osise" msgid "profile.recovery.go-to-login" msgstr "Gaa na mbanye" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Wega ebe azụ" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Mkpị" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Nyado" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Kachapụ" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Umezi nlekwasa anya" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Gbanye nlekwasa anya" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Mee ọtụtụ akọrọngwa ga" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Agba[ Palette (Na narị)" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "E nwegasịrị nhazizigharị nime ọba ederede ga e kere eke" diff --git a/frontend/translations/it.po b/frontend/translations/it.po index 3fa5b6d7b9..2c5a54362f 100644 --- a/frontend/translations/it.po +++ b/frontend/translations/it.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:14+0000\n" -"Last-Translator: Anonymous \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Italian " "\n" "Language: it\n" @@ -9,12 +9,16 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Hai già un account?" +#: src/app/main/ui/auth/recovery_request.cljs:111, src/app/main/ui/auth/register.cljs:250 +msgid "auth.check-email" +msgstr "Controlla la tua email!" + #: src/app/main/ui/auth/recovery.cljs:83 msgid "auth.confirm-password" msgstr "Conferma la password" @@ -29,7 +33,7 @@ msgstr "" "Questo è un servizio di prova, non utilizzare per il lavoro reale, i " "progetti verranno eliminati periodicamente." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Hai dimenticato la password?" @@ -37,37 +41,37 @@ msgstr "Hai dimenticato la password?" msgid "auth.fullname" msgstr "Nome completo" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Accedi al mio account" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Accedi qui." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot è lo strumento di design open-source e gratuito per la " "collaborazione tra Design e Sviluppo" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -75,7 +79,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Inserisci una nuova password" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "Ora fai parte di %s" @@ -91,7 +95,7 @@ msgstr "Il profilo non è verificato. Verifica il profilo prima di continuare." msgid "auth.notifications.recovery-token-sent" msgstr "Il link per il recupero password è stato inviato alla tua e-mail." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Unito al team con successo" @@ -123,7 +127,7 @@ msgstr "Hai dimenticato la password?" msgid "auth.recovery-submit" msgstr "Cambia la tua password" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Non hai ancora un account?" @@ -137,7 +141,7 @@ msgstr "" msgid "auth.register-account-title" msgstr "Il tuo nome" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Crea un account" @@ -150,10 +154,14 @@ msgstr "Crea un account" msgid "auth.terms-and-privacy-agreement" msgstr "Accetto i [termini di servizio](%s) e la [politica sulla privacy](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Condizioni di servizio" +#: src/app/main/ui/auth/register.cljs:251 +msgid "auth.verification-sent-email" +msgstr "Abbiamo inviato un'email di verifica a:" + #: src/app/main/ui/auth/login.cljs:185, src/app/main/ui/auth/recovery_request.cljs:77, src/app/main/ui/auth/register.cljs:182 msgid "auth.work-email" msgstr "Email di lavoro" @@ -168,93 +176,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Nessuna persona trovata per @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Pubblica" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Tutti gli utenti Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Sei sicuro di voler eliminare questo link? Se sì, non sarà più disponibile " "per nessuno" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(attuale)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Elimina link" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Ottieni link" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Link copiato con successo" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Gestisci permessi" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 pagina condivisa" msgstr[1] "%s pagine condivise" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Può commentare" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Può ispezionare il codice" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Chiunque possieda questo link può accedere" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Pagine condivise" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Il codice per condividere apparirà qui" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Solo i membri del team" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Condividi i prototipi" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Seleziona tutto" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Rimuovi pubblicazione" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Gestisci team" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot è studiato per i team. Invita membri per lavorare insieme a file e " "progetti" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Fai squadra!" @@ -270,47 +278,103 @@ msgstr "Ripristina file" msgid "dashboard.add-file" msgstr "Aggiungi file" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Aggiungi come libreria condivisa" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Cambia indirizzo e-mail" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "SPOSTA TEAM" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Sposta in:" -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:611 +msgid "dashboard.change-organization-modal.description" +msgstr "" +"I progetti e i file rimarranno disponibili ai membri del team. Il team " +"otterrà la configurazione dalla nuova organizzazione." + +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Seleziona un'organizzazione" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "Cambia organizzazione del team" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/check_updates.cljs:334 +msgid "dashboard.check-updates.available-message" +msgstr "" +"Le nuove versioni includono miglioramenti alle prestazioni, nuove " +"funzionalità e correzioni che mantengono la tua installazione attuale." + +#: src/app/main/ui/dashboard/check_updates.cljs:309 +msgid "dashboard.check-updates.available-title" +msgstr "È DISPONIBILE UNA NUOVA VERSIONE DI PENPOT" + +#: src/app/main/ui/dashboard/check_updates.cljs:341 +msgid "dashboard.check-updates.highlights-title" +msgstr "Novità - In evidenza" + +#: src/app/main/ui/dashboard/check_updates.cljs:318 +msgid "dashboard.check-updates.installed-version" +msgstr "Versione installata" + +#: src/app/main/ui/dashboard/check_updates.cljs:326 +msgid "dashboard.check-updates.latest-version" +msgstr "Ultima versione" + +#: src/app/main/ui/dashboard/check_updates.cljs:235 +msgid "dashboard.check-updates.try-again" +msgstr "Riprova" + +#: src/app/main/ui/dashboard/check_updates.cljs:230 +msgid "dashboard.check-updates.unable-hint" +msgstr "Controlla la tua connessione o la configurazione dell'istanza e riprova." + +#: src/app/main/ui/dashboard/check_updates.cljs:215 +msgid "dashboard.check-updates.unable-title" +msgstr "IMPOSSIBILE CONTROLLARE GLI AGGIORNAMENTI" + +#: src/app/main/ui/dashboard/check_updates.cljs:265 +msgid "dashboard.check-updates.uptodate-message" +msgstr "Stai eseguendo l'ultima versione stabile:" + +#: src/app/main/ui/dashboard/check_updates.cljs:254 +msgid "dashboard.check-updates.uptodate-title" +msgstr "PENPOT È AGGIORNATO" + +#: src/app/main/ui/dashboard/check_updates.cljs:371 +msgid "dashboard.check-updates.view-changelog" +msgstr "Visualizza changelog completo" + +#: src/app/main/ui/dashboard/check_updates.cljs:374 +msgid "dashboard.check-updates.view-release-notes" +msgstr "Visualizza note di rilascio" + +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Svuota il cestino" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copia)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Crea org" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Crea un nuovo team" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Appunta questa versione" @@ -318,7 +382,7 @@ msgstr "Appunta questa versione" msgid "dashboard.default-team-name" msgstr "Il tuo Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Sei sicuro di voler eliminare definitivamente tutti i progetti e file " @@ -330,42 +394,42 @@ msgstr "" "Sei sicuro di voler eliminare definitivamente %s? Questa azione è " "irreversibile." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s file sono stati eliminati con successo." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Elimina definitivamente" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Elimina progetto" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Sei sicuro di voler eliminare definitivamente il progetto %s? Eliminerai " "definitivamente anche tutti i file al suo interno. Questa azione è " "irreversibile." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s è stato eliminato con successo." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Elimina team" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "Il tuo cestino è vuoto. I file e i progetti eliminati appariranno qui." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Sarà eliminato %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Scarica il file Penpot (.penpot)" @@ -377,19 +441,19 @@ msgstr "Duplica" msgid "dashboard.duplicate-multi" msgstr "Duplica %s file" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Quando un membro del progetto crea una bozza, verrà mostrata qui." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Nessuna bozza presente." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Quando un membro del progetto crea un file, verrà mostrato qui." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Nessun file presente." @@ -437,23 +501,23 @@ msgstr "Importa un file .penpot" msgid "dashboard.empty-project.start" msgstr "Inizia a creare cose straordinarie" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "Si è verificato un errore durante l'eliminazione dei file." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "Si è verificato un errore durante l'eliminazione del progetto %s." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "Si è verificato un errore durante il ripristino del file %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "Si è verificato un errore durante il ripristino dei file." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "" "Si è verificato un errore durante il ripristino del progetto %s e dei suoi " @@ -463,37 +527,37 @@ msgstr "" msgid "dashboard.export-binary-multi" msgstr "Scarica %s file Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Esporta le tavole da disegno in PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Esporta in PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s di %s elementi selezionati" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Esporta" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Puoi aggiungere dei parametri di esportazione agli elementi accedendo alle " "proprietà del design (in fondo alla barra laterale destra)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informazioni utili su come configurare l'esportazione in Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Non ci sono elementi con parametri di esportazione." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Esporta selezionati" @@ -509,35 +573,43 @@ msgid_plural "dashboard.file-menu.restore-files-option" msgstr[0] "Ripristina file" msgstr[1] "Ripristina file" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/dashboard/layout_toggle.cljs:32 +msgid "dashboard.files-layout.grid" +msgstr "Visualizzazione a griglia" + +#: src/app/main/ui/dashboard/layout_toggle.cljs:28 +msgid "dashboard.files-layout.list" +msgstr "Visualizzazione a elenco" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Carattere mancante" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Chiudi tutto" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "I caratteri personalizzati che carichi appariranno qui." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Nessun carattere personalizzato presente." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Quando un membro del progetto carica un carattere personalizzato, verrà " "mostrato qui." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 carattere aggiunto" msgstr[1] "%s caratteri aggiunti" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -548,7 +620,7 @@ msgstr "" "È possibile caricare caratteri con i seguenti formati: **TTF, OTF e WOFF** " "(uno solo di questi è necessario)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -558,11 +630,11 @@ msgstr "" "di Penpot](https://penpot.app/terms.html). Potresti anche voler " "approfondire le [licenze dei caratteri](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Carica tutto" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -573,111 +645,109 @@ msgstr "" "utilizzare [Transfonter](https://transfonter.org/) per generare webfont e " "correggere errori. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Vai alla console amministratore" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/grid.cljs:253 +msgid "dashboard.grid.last-modified-at" +msgstr "Ultima modifica %s" + +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importa file Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Ops! Non possiamo importare questo file" +#: src/app/main/ui/dashboard/import.cljs:698 +msgid "dashboard.import.auto-linked-libraries" +msgid_plural "dashboard.import.auto-linked-libraries" +msgstr[0] "1 libreria è stata collegata automaticamente tramite nome." +msgstr[1] "%s librerie sono state collegate automaticamente tramite nome." + #: src/app/main/ui/dashboard.cljs:264 msgid "dashboard.import.bad-url" msgstr "Importazione non riuscita. L'URL del modello non è corretto" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:838 +msgid "dashboard.import.confirm-library-links" +msgstr "Conferma collegamenti libreria" + +#: src/app/main/ui/dashboard/import.cljs:814 +msgid "dashboard.import.connect-selected-libraries" +msgstr "Connetti le librerie selezionate" + +#: src/app/main/ui/dashboard/import.cljs:288 +msgid "dashboard.import.file-name-label" +msgstr "Nome file" + +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "Impossibile verificare questo file." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Questo file sembra essere danneggiato." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Non tutti i file sono stati importati" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "I seguenti file presentano errori:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "I file con errori non verranno caricati." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "Qualcosa è andato storto durante l'elaborazione di questo file." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 file è stato importato con successo." msgstr[1] "%s file sono stati importati con successo." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Alcuni file contenenti oggetti non validi sono stati rimossi." +#: src/app/main/ui/dashboard/import.cljs:815 +msgid "dashboard.import.next-file" +msgstr "Prossimo file" + #: src/app/main/ui/dashboard.cljs:265 msgid "dashboard.import.no-perms" msgstr "Non hai i permessi per importare in questo team" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Elaborazione dei colori" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Elaborazione dei componenti" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Elaborazione dei media" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Elaborazione pagina: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Elaborazione degli elementi tipografici" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Caricamento dei dati sul server (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Caricamento file: %s" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Stai per eliminare gli inviti a:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Stai per reinviare gli inviti a:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Elimina inviti" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Reinvia inviti" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "Non hai i permessi per invitare persone ad unirsi a questo team oppure " "modificare o eliminare gli inviti." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Invita nel team" @@ -689,29 +759,29 @@ msgstr "Lascia org" msgid "dashboard.leave-organization.toast" msgstr "Non fai più parte dell'organizzazione %s." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Abbandona il team" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Librerie e modelli" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "Ecco alcune librerie e modelli che puoi aggiungere al tuo progetto" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Esplorane di più e scopri come contribuire" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "" "Si è verificato un problema nell'importazione del modello. Il modello non è " "stato importato." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Librerie condivise" @@ -719,7 +789,7 @@ msgstr "Librerie condivise" msgid "dashboard.loading-files" msgstr "caricamento dei file …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "caricamento dei caratteri…" @@ -739,61 +809,61 @@ msgstr "Sposta %s file in" msgid "dashboard.move-to-other-team" msgstr "Sposta in un altro team" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nuovo File" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Nuovo File" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nuovo progetto" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nuovo progetto" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Nessuna corrispondenza trovata per \"%s\"" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "Non hai i permessi per aggiungere team a nessuna delle tue organizzazioni." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "Non sei autorizzato a creare team nell'organizzazione %s. Per maggiori " "informazioni, contatta il proprietario dell'organizzazione." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "Non sei autorizzato ad eliminare team nell'organizzazione %s. Per maggiori " "informazioni, contatta il proprietario dell'organizzazione." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "Non sei autorizzato a spostare team che fanno parte dell'organizzazione %s. " "Per maggiori informazioni, contatta il proprietario dell'organizzazione." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "I progetti appuntati appariranno qui" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notifiche" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Il tuo indirizzo e-mail è stato aggiornato con successo" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Il tuo indirizzo e-mail è stato verificato con successo" @@ -805,11 +875,11 @@ msgstr "Impostazioni di notifica aggiornate" msgid "dashboard.notifications.password-saved" msgstr "La password è stata salvata con successo!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Visualizza notifiche" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s membri" @@ -817,19 +887,19 @@ msgstr "%s membri" msgid "dashboard.open-in-new-tab" msgstr "Apri file in una nuova scheda" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opzioni" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Ordina per ruolo" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Ordina per stato" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "L'organizzazione %s è stata eliminata." @@ -853,7 +923,7 @@ msgstr "Ora sei il proprietario di questo team." msgid "dashboard.permissions-change.viewer" msgstr "Ora sei un visualizzatore di questo team." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Appunta/Rimuovi" @@ -869,27 +939,27 @@ msgstr "Impossibile analizzare il manifesto del plugin" msgid "dashboard.plugins.try-plugin" msgstr "Prova il plugin: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "Eliminazione file…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "Ripristino file…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "Eliminazione inaspettatamente lenta" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "Ripristino inaspettatamente lento" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Progetti" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Desideri eliminare il tuo account?" @@ -897,106 +967,106 @@ msgstr "Desideri eliminare il tuo account?" msgid "dashboard.removed-from-team" msgstr "Non fai più parte del team \"%s\"." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "" "Stai per ripristinare tutti i tuoi progetti e file. Potrebbe richiedere un " "po' di tempo." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Ripristina tutti i progetti e file" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Ripristina tutto" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s file sono stati ripristinati con successo." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Ripristina progetto" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Stai per ripristinare il progetto %s e tutti i file al suo interno." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Ripristina progetto" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s è stato ripristinato con successo." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Salva impostazioni" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Cerca…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Ricerca di \"%s\"…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Organizzazioni" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "AGGIUNGI ALL'ORGANIZZAZIONE" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "Scegli un'organizzazione:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" autorizza solo i membri dell'organizzazione ad unirsi al suo team. " "Qualsiasi invito in sospeso a persone al di fuori dell'organizzazione verrà " "revocato." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Gli inviti in sospeso ad utenti esterni verranno annullati." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Qui trovi tutte le organizzazioni dove sei autorizzato a creare o " "aggiungere team." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "Seleziona un'organizzazione" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "Aggiungi team ad una organizzazione" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Seleziona la lingua per l'interfaccia utente" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Seleziona un tema" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Scuro (predefinito)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Chiaro" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Tema di sistema" @@ -1060,11 +1130,11 @@ msgstr "Aggiorna impostazioni" msgid "dashboard.settings.notifications.title" msgstr "Notifiche" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Mostra tutti i file" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Cronologia versione" @@ -1088,7 +1158,7 @@ msgstr[1] "I tuoi file sono stati duplicati con successo" msgid "dashboard.success-duplicate-project" msgstr "Il tuo progetto è stato duplicato con successo" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Il tuo file è stato spostato con successo" @@ -1100,112 +1170,112 @@ msgstr "I tuoi file sono stati spostati con successo" msgid "dashboard.success-move-project" msgstr "Il tuo progetto è stato spostato con successo" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s ora appartiene a %s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Informazioni sul team" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Membri del team" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s non appartiene più a %s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Organizzazione del team" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Aggiungi ad una organizzazione" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "" "Non hai i permessi per aggiungere questo team a nessuna delle tue " "organizzazioni." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Cambia organizzazione del team" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "" "Solo le persone all'interno della tua organizzazione possono essere " "invitate." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "Questo team non appartiene a nessuna organizzazione" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Rimuovi team dall'organizzazione" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Progetti del team" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Aggiungi al tuo progetto" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema dell'interfaccia utente" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Risultati della ricerca" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "I file eliminati rimarranno nel cestino per" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s giorni. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "In seguito, verranno eliminati definitivamente." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Se cambi idea, puoi ripristinarli o eliminarlo definitivamente dal menu di " "ciascun file." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Scrivi per cercare" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Annulla pubblicazione libreria" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Aggiorna le impostazioni" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "Non sei più un membro dell'organizzazione %s" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Beta" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "Il rendering WebGL può migliorare le prestazioni, ma è in versione beta e " @@ -1213,74 +1283,74 @@ msgstr "" "tra l'area di lavoro, le esportazioni (SVG/PDF) e la modalità di " "visualizzazione." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Disabilitato" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Abilitato" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Invia feedback" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Stato" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "Rendering WebGL" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "È attivo" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Quando questo webhook viene attivato verranno inviati i dati dell'evento" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Puoi solo eliminare o modificare i webhook creati da te." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Tipo di contenuto" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Crea webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook creato con successo." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "I webhook sono un modo semplice per consentire ad altri siti web e app di " "essere notificati quando si verificano determinati eventi su Penpot. " "Invieremo una richiesta POST a ciascuno degli URL che fornisci." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Premi il bottone \"Aggiungi webhook\" per aggiungerne uno." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Nessun webhook presente." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Il tuo account" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-mail" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Il tuo nome" @@ -1296,11 +1366,11 @@ msgstr "Attenzione" msgid "ds.component-subtitle" msgstr "Componenti da aggiornare:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Acconsenti" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Annulla" @@ -1320,7 +1390,7 @@ msgstr "Nessun token applicabile nei set o temi attivi." msgid "ds.inputs.numeric-input.no-matches" msgstr "Nessuna corrispondenza trovata." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Apri elenco token" @@ -1332,15 +1402,15 @@ msgstr "Provider di autenticazione non consentito per questo profilo" msgid "errors.auth-provider-not-configured" msgstr "Provider di autenticazione non configurato." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Sembra che tu non ti sia autenticato o che la sessione sia scaduta." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Il carattere %s non può essere caricato" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "I caratteri %s non posso essere caricati" @@ -1352,55 +1422,55 @@ msgstr "Impossible caricare il file multimediale." msgid "errors.character-limit-exceeded" msgstr "Limite di caratteri superato" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "L'API Clipboard non è disponibile. Distribuisci Penpot tramite HTTPS per " "abilitare l'accesso agli appunti" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Il tuo browser non può effettuare questa operazione" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "Accesso agli appunti negato. Consenti l'autorizzazione agli appunti nel tuo " "browser per incollare i contenuti" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "Si è verificato un errore con il commento" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Spiacenti! Questo è un file obsoleto che utilizza un tipo di risorsa Penpot " "non più supportato e non può essere aperto." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "e cercheremo di aiutarti." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Anche se Penpot non supporta più questo tipo di risorse, puoi" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "contattarci" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "" "Impossibile rilasciare un set genitore in un percorso appartenente a un " "proprio elemento figlio." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Indirizzo e-mail già utilizzato" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Indirizzo e-mail già convalidato." @@ -1412,29 +1482,29 @@ msgstr "Non è possibile utilizzare il tuo indirizzo e-mail come password" msgid "errors.email-does-not-match-invitation" msgstr "L’email non corrisponde all’invito." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Dominio non consentito" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "L'email «%s» ha molti report di rimbalzi permanenti." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "L'e-mail \"%s\" è stata riportata come spam o respinta in modo permanente." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Sembra che tu stia aprendo un file con la funzione '%s' abilitata, ma la " "versione attuale di Penpot non la supporta o l'ha disabilitata." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "La funzione '%s' non è supportata." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Deve contenere al massimo %s caratteres." @@ -1442,7 +1512,7 @@ msgstr "Deve contenere al massimo %s caratteres." msgid "errors.field-min-length" msgstr "Deve contenere almeno 1 carattere." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Campo vuoto" @@ -1450,24 +1520,24 @@ msgstr "Campo vuoto" msgid "errors.field-not-all-whitespace" msgstr "Il nome deve contenere alcuni caratteri oltre allo spazio." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Sembra che ci sia una discrepanza tra le funzioni abilitate e quelle del " "file che stai cercando di aprire. È necessario applicare le migrazioni per " "'%s' prima di poter aprire il file." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "Il nome della famiglia di caratteri può contenere solo lettere, numeri, " "spazi, trattini, trattini bassi e punti." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Si è verificato un problema." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Qualcosa è andato storto con il web worker." @@ -1483,7 +1553,7 @@ msgstr "Dati non validi" msgid "errors.invalid-email" msgstr "Inserisci una email valida" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "L'indirizzo e-mail di conferma deve corrispondere" @@ -1519,11 +1589,11 @@ msgstr "Questo è invito può essere stato revocato o può essere scaduto." msgid "errors.ldap-disabled" msgstr "Autenticazione LDAP disabilitata." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Hai raggiunto la quota '%s'. Contatta il supporto." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "È stato raggiunto il massimo numero di email (%s) che possono essere " @@ -1543,21 +1613,21 @@ msgstr "" msgid "errors.media-type-not-allowed" msgstr "L'immagine non sembra valida." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Il profilo che stai invitando ha le email disattivate (rapporti di spam o " "alti tassi di rimbalzo)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migrazione in corso" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Solo il creatore della versione può bloccarla" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Solo il creatore della versione può sbloccarla" @@ -1571,7 +1641,7 @@ msgstr "" msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "Il proprietario dell'organizzazione non può lasciare l'organizzazione." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "L'organizzazione non esiste" @@ -1583,7 +1653,7 @@ msgstr "La password di conferma deve corrispondere" msgid "errors.password-too-short" msgstr "La password deve contenere almeno 8 caratteri" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Dati non validi negli appunti" @@ -1591,7 +1661,7 @@ msgstr "Dati non validi negli appunti" msgid "errors.profile-blocked" msgstr "Questo profilo è bloccato" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Il tuo profilo ha le email disattivate (rapporti di spam o alti tassi di " @@ -1601,21 +1671,21 @@ msgstr "" msgid "errors.registration-disabled" msgstr "La registrazione è al momento disabilitata." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "L'SVG non è valido o è danneggiato" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Rilevata funzione incompatibile '%s'" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Il numero di membri non è sufficiente per abbandonare il team. " "Probabilmente lo vuoi eliminare." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Il membro che stai provando ad assegnare non esiste." @@ -1623,7 +1693,7 @@ msgstr "Il membro che stai provando ad assegnare non esiste." msgid "errors.team-leave.only-owner-can-delete" msgstr "Solo il proprietario di un team può eliminarlo." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "Il proprietario non può abbandonare il team. Devi riassegnare il ruolo di " @@ -1639,13 +1709,13 @@ msgstr "" msgid "errors.token-set-already-exists" msgstr "Esiste già un set con lo stesso nome" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "Impossibile completare il rilascio: esiste già un set con lo stesso nome in " "questo percorso." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Esiste già un'opzione tema con lo stesso nome" @@ -1653,13 +1723,13 @@ msgstr "Esiste già un'opzione tema con lo stesso nome" msgid "errors.token-theme-not-existing-sets" msgstr "Il tema fa riferimento ad alcuni set non esistenti: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "L'interlinea dipende dalla dimensione del carattere. Aggiungi una " "dimensione carattere per ottenere il valore risolto." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "Questo campo non può essere vuoto" @@ -1679,13 +1749,13 @@ msgstr "Errore di importazione:" msgid "errors.tokens.invalid-color" msgstr "Valore colore invalido: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "" "Valore del token non valido: puoi fare riferimento solo a un token " "font-family" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Valore del peso del font non valido: usa valori numerici (100-950) o nomi " @@ -1707,37 +1777,37 @@ msgstr "" "I nomi dei token devono contenere solo lettere e cifre, separate dal " "carattere . e non devono iniziare con il simbolo $." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "" "Tipologia ombra non valida: sono consentite solo 'innerShadow' o " "'dropShadow'" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Valore del token non valido: sono accettati solo none, Uppercase, Lowercase " "o Capitalize" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "" "Valore del token non valido: sono accettati solo none, underline e " "strike-through" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "Valore non valido: deve fare riferimento a un token ombra composito." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "Valore non valido: deve fare riferimento a un token tipografico composito." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Valore token non valido: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Riferimenti al token mancanti: " @@ -1749,33 +1819,33 @@ msgstr "Nessun token, set o tema trovato in questo file." msgid "errors.tokens.number-too-large" msgstr "Valore token non valido. Il valore risolto è troppo grande: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "" "L'opacità deve essere compresa tra 0 e 100% o tra 0 e 1 (ad esempio 50% o " "0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Il token ha un riferimento a se stesso" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "La sfocatura dell'ombra deve essere maggiore o uguale a 0." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "La diffusione dell'ombra deve essere maggiore o uguale a 0." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "La larghezza della traccia deve essere maggiore o uguale a 0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Valore non valido: % non è consentito." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Valore non valido: le unità non sono consentite." @@ -1783,55 +1853,55 @@ msgstr "Valore non valido: le unità non sono consentite." msgid "errors.unexpected-error" msgstr "Si è verificato un errore inaspettato." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Token sconosciuto" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Questa versione è già bloccata" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Questa versione è bloccata e non può essere eliminata da altri" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Il file ha un numero di versione incompatibile" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL ha smesso di funzionare. Ricarica la pagina per ripristinarlo" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Oops! Il contesto della canvas è stato perso" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Errore di connessione, l'URL è irraggiungibile" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "L'URL non ha superato la validazione." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Errore nell'ultimo invio." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Errore nella validazione SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Timeout" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Errore inaspettato durante la validazione" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Stato inaspettato %s" @@ -1851,17 +1921,17 @@ msgstr "Descrizione" msgid "feedback.description-placeholder" msgstr "Descrivi il motivo del tuo feedback" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Siamo felici di averti qui. Se hai bisogno di aiuto, fai una ricerca prima " "di postare una richiesta." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Community di Penpot" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Altri modi per contattarci" @@ -1884,11 +1954,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Contattaci" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Siamo qui per aiutarti con le tue domande tecniche." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Account di supporto X" @@ -1908,10 +1978,6 @@ msgstr "Idea" msgid "feedback.type.issue" msgstr "Problema" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Scarica file" - #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "Si è verificato un errore" @@ -1948,7 +2014,7 @@ msgstr "Anteprima dell’immagine di riempimento della forma" msgid "inspect.attributes.shadow" msgstr "Ombra" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Dimensione e posizione" @@ -1956,43 +2022,31 @@ msgstr "Dimensione e posizione" msgid "inspect.attributes.stroke" msgstr "Contorno" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centro" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Interno" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Esterno" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografia" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Famiglia di caratteri" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Dimensione del carattere" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Stile del carattere" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Spaziatura delle lettere" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Interlinea" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "Famiglie di caratteri miste" @@ -2004,11 +2058,11 @@ msgstr "Nessuno" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Sottolineato" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Prime lettere maiuscole" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Minuscolo" @@ -2020,7 +2074,7 @@ msgstr "Nessuno" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Annulla impostazione" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Maiuscolo" @@ -2144,23 +2198,23 @@ msgstr "Valore risolto:" msgid "inspect.tabs.styles.variants-panel" msgstr "Proprietà delle varianti" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "Crea nuovo token di accesso" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "Premi il pulsante \"Crea nuovo token di accesso\" per generarne uno." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "Non hai ancora nessun token." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "Token di accesso personale" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "I token di accesso personale funzionano come alternativa al nostro sistema " @@ -2237,25 +2291,25 @@ msgstr "" "Il server MCP di Penpot consente ai client MCP di interagire direttamente " "con i file di design di Penpot." -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "Il server MCP di Penpot consente ai client MCP di interagire direttamente " "con i file di design di Penpot." -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Stato" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Disabilitato" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Abilitato" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "La chiave MCP utilizzata per connettersi al server MCP è scaduta. Di " @@ -2265,10 +2319,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "Server MCP" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "Beta" - #: src/app/main/ui/settings/integrations.cljs:392 msgid "integrations.no-expiration" msgstr "Nessuna data di scadenza" @@ -2279,47 +2329,47 @@ msgstr "" "Questo token unico non è recuperabile. In caso di smarrimento, sarà " "necessario crearne uno nuovo." -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Contassegna tutto come letto" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Scorciatoie" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Informazioni su Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Accetto" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Attivo" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Aggiungi" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Aggiungi un carattere personalizzato" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Aggiungendo…" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Amministratore" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Tutti" @@ -2327,17 +2377,17 @@ msgstr "Tutti" msgid "labels.and" msgstr "e" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Indietro" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Sembra che tu debba aspettare un po' e riprovare; stiamo eseguendo dei " "piccoli lavori di manutenzione sui nostri server." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Gateway non corretto" @@ -2345,15 +2395,15 @@ msgstr "Gateway non corretto" msgid "labels.blur" msgstr "Sfocatura" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Annulla" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Chiudi" @@ -2361,7 +2411,7 @@ msgstr "Chiudi" msgid "labels.code" msgstr "Codice" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Comprimi" @@ -2373,15 +2423,15 @@ msgstr "Colore" msgid "labels.comment" msgstr "Commenta" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Commenti" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Community" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Comunità e contributi" @@ -2393,19 +2443,19 @@ msgstr "Calcolato" msgid "labels.confirm-password" msgstr "Conferma la password" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Contatta il supporto" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Contattaci" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continua" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Puoi continuare con un account Penpot" @@ -2417,31 +2467,31 @@ msgstr "Copia" msgid "labels.copy-color" msgstr "Copia colore" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Copia link" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-presente" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Crea" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Crea un nuovo team" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Inserisci il nome del nuovo team" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Pannello di controllo" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Elimina" @@ -2453,7 +2503,7 @@ msgstr "Elimina il commento" msgid "labels.delete-comment-thread" msgstr "Elimina il thread" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Elimina l'invito" @@ -2461,23 +2511,23 @@ msgstr "Elimina l'invito" msgid "labels.delete-multi-files" msgstr "Elimina %s file" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Eliminati" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Sviluppo" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Scarta" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "Scarica %s" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Bozze" @@ -2485,27 +2535,27 @@ msgstr "Bozze" msgid "labels.duplicate" msgstr "Duplica" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Modifica" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Redattore" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Vuoto" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Errore" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Scaduto" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Esporta" @@ -2517,7 +2567,7 @@ msgstr "Feedback disabilitato" msgid "labels.feedback-sent" msgstr "Feedback inviato" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2525,55 +2575,55 @@ msgstr "Figma" msgid "labels.fill" msgstr "Riempimento" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Famiglia di caratteri" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Stili" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Font" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Repository Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Dai la tua opinione" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Torna indietro" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Graphic design" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Supporto" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Aiuto e apprendimento" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Nascondi" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Non mostrare i commenti risolti" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importa" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inattivo" @@ -2581,49 +2631,49 @@ msgstr "Inattivo" msgid "labels.info" msgstr "Informazione" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Caratteri installati" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Qualcosa è andato storto." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "" "Puoi riprovare l’operazione oppure contattare il supporto per segnalare " "l’errore." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Errore interno" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Inviti" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Lingua" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Layout" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Centro di apprendimento" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Librerie e modelli" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Caricamento…" @@ -2631,7 +2681,7 @@ msgstr "Caricamento…" msgid "labels.lock" msgstr "Blocca" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Connettiti o iscriviti" @@ -2639,19 +2689,19 @@ msgstr "Connettiti o iscriviti" msgid "labels.login" msgstr "Accesso" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Disconnetti" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Membro" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Membri" @@ -2663,43 +2713,43 @@ msgstr "Menziona" msgid "labels.new-password" msgstr "Nuova password" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Successivo" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Non ci sono notifiche di commenti in attesa." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Non ci sono inviti." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Raduna le persone e costruite grandi cose insieme." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Ops!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notifiche" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 file" msgstr[1] "%s file" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 tavola da disegno" msgstr[1] "%s tavole da disegno" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 progetto" @@ -2709,39 +2759,39 @@ msgstr[1] "%s progetti" msgid "labels.old-password" msgstr "Vecchia password" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Opzioni" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Altro (specifica)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Altro" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Proprietario" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Password" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "In attesa" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Registro delle modifiche di Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Hub di Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Progetti in evidenza" @@ -2749,31 +2799,31 @@ msgstr "Progetti in evidenza" msgid "labels.post" msgstr "Pubblica" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Precedente" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Design del prodotto o UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Gestione del prodotto" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profilo" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Progetti" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Recenti" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Riferimento" @@ -2781,27 +2831,27 @@ msgstr "Riferimento" msgid "labels.refresh" msgstr "Ricarica" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Note di versione" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Ricarica pagina" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Rimuovi" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Rimuovi membro" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Rinomina" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Rinomina il team" @@ -2825,11 +2875,11 @@ msgstr "nuova risposta" msgid "labels.reply.thread" msgstr "Rispondi" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Reinvia" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Invia di nuovo l'invito" @@ -2837,43 +2887,43 @@ msgstr "Invia di nuovo l'invito" msgid "labels.restore" msgstr "Ricarica file" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Riprova" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Ruolo" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Salva" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Cerca" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Cerca un carattere" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Seleziona opzione" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Invia" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Invio…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Stiamo effettuando la manutenzione programmata dei nostri sistemi." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Servizio non disponibile" @@ -2881,55 +2931,55 @@ msgstr "Servizio non disponibile" msgid "labels.sets" msgstr "Set" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Configurazione" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Ombra" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Condividi" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Librerie condivise" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Mostra" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Mostra tutti i commenti" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Mostra la lista dei commenti" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Mostra solo le tue menzioni" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Mostra solo i tuoi commenti" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Fonti" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Inizia" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Stato" @@ -2937,7 +2987,7 @@ msgstr "Stato" msgid "labels.stroke" msgstr "Traccia" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Studente o docente" @@ -2949,7 +2999,7 @@ msgstr "Stili" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Cambia" @@ -2957,19 +3007,19 @@ msgstr "Cambia" msgid "labels.text" msgstr "Testo" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Temi" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutorial" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Tipografia" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Errore sconosciuto" @@ -2981,31 +3031,31 @@ msgstr "Sblocca" msgid "labels.unpublish-multi-files" msgstr "Rimuovi pubblicazione a %s file" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Aggiorna" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Aggiorna team" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Carica" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Carica caratteri personalizzati" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Caricamento…" -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variante" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Note della versione %s" @@ -3013,7 +3063,7 @@ msgstr "Note della versione %s" msgid "labels.view-only" msgstr "Solo visualizzazione" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Visualizzatore" @@ -3021,7 +3071,7 @@ msgstr "Visualizzatore" msgid "labels.visibility" msgstr "Visibilità" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhook" @@ -3029,11 +3079,11 @@ msgstr "Webhook" msgid "labels.write-new-comment" msgstr "Scrivi un nuovo commento" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(tu)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Il tuo account" @@ -3170,63 +3220,63 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Aggiungi \"%s\" come libreria condivisa" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Grande scatto" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Conferma il nuovo indirizzo e-mail" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Ti invieremo un'e-mail al tuo attuale indirizzo e-mail \"%s\" per " "verificare la tua identità." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Nuovo indirizzo e-mail" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Cambiare indirizzo e-mail" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Cambia il tuo indirizzo e-mail" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Crea webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Crea webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL del payload" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Annulla e mantieni il mio account" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Sì, cancella il mio account" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "Cancellando il tuo account, perderai tutti i tuoi archivi e progetti " "attuali." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Eliminare il proprio account?" @@ -3276,29 +3326,29 @@ msgstr "Eliminare %s files?" msgid "modals.delete-file-multi-confirm.title" msgstr "Eliminazione %s files" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Eliminare questo stile del carattere? Se è usato in un file, non verrà " "caricato." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Eliminazione stile del carattere" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "Eliminare questo carattere? Se è usato in un file, non verrà caricato." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Eliminazione carattere" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Eliminare questa pagina?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Elimina pagina" @@ -3314,31 +3364,31 @@ msgstr "Eliminare questo progetto?" msgid "modals.delete-project-confirm.title" msgstr "Elimina progetto" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Elimina file" msgstr[1] "Elimina i file" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Non è attivato in alcun file." msgstr[1] "Non sono attivati in alcun file." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Questa libreria è attivata qui: " msgstr[1] "Queste librerie sono attivate qui: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Eliminare questo file?" msgstr[1] "Eliminare questi file?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Eliminazione del file" @@ -3358,19 +3408,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Eliminazione del team in corso" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Elimina membro" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Eliminare questo membro dal team?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Elimina membro del team" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3380,23 +3430,23 @@ msgstr[1] "" "Le risorse già utilizzare in questi file rimarranno lì (nessun design verrà " "compromesso)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Elimina webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Sei sicuro di voler eliminare questo webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Eliminazione del webhook in corso" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Modifica webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Modifica webhook" @@ -3414,145 +3464,145 @@ msgstr "Importa token" msgid "modals.import-library-tokens.title" msgstr "Importare i token dalla libreria?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Invia invito" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Email, separate da virgole" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "Alcuni membri sono già nel team. Inviteremo i restanti." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Puoi invitare membri nel team affinché possano accedere a questo file e a " "tutti i file del team." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Invita membri al team" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Poiché sei il solo membro di questo team, il team verrà eliminato insieme " "ai sui file e progetti." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Lasciare il team di %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Non puoi lasciare il team se non c'è un altro membro da promuovere a " "proprietario. Potresti voler eliminare il team." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Sei il proprietario di questo team. Per favore seleziona un altro membro da " "promuovere a proprietario prima di uscire." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promuovi e esci" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Seleziona un membro da promuovere" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Prima di uscire" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Lascia il team" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Vuoi lasciare questo team?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Lasciando il team" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Sposta" msgstr[1] "Sposta" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Sei sicuro di voler spostare questa libreria?" msgstr[1] "Sei sicuro di voler spostare queste librerie?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Sposta libreria" msgstr[1] "Sposta librerie" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Ampiezza scatto" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Trasferisci proprietà" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Trasferendo la proprietà, il tuo ruolo diverrà quello di Admin, perdendo " "alcuni privilegi su questo team. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Sei l'attuale proprietario di questo team. Trasferire la proprietà del team " "a %s?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Nuovo proprietario del team" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Pubblica" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "La tua libreria è vuota. Sei sicuro di volerla pubblicare?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Pubblica libreria vuota" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Piccolo scatto" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Rimuovi pubblicazione" msgstr[1] "Rimuovi pubblicazioni" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Annullare la pubblicazione di questa libreria?" msgstr[1] "Annullare la pubblicazione di queste librerie?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Annulla pubblicazione libreria" @@ -3576,11 +3626,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Aggiorna un componente in una libreria condivisa" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Questa pagina non esiste" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Errore 404" @@ -3598,11 +3648,11 @@ msgstr "Abbiamo inviato un email di recupero a" msgid "not-found.login.sent-recovery-check" msgstr "Controlla la tua email e clicca nel link per creare una nuova password." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Iscriviti gratuitamente" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "E inizia ad utilizzare Penpot in pochi secondi!" @@ -3610,67 +3660,67 @@ msgstr "E inizia ad utilizzare Penpot in pochi secondi!" msgid "not-found.made-with-love" msgstr "Fatto con AMORE e Open Source" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Hai già richiesto l'accesso a questo file." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Hai già richiesto l'accesso a questo file o ad altri file o progetti di " "questo team." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Hai già richiesto l'accesso a questo progetto o ad altri progetti o file di " "questo team." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Hai già richiesto l'accesso a questo progetto." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "RICHIEDI ACCESSO" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Ricorda che, se il proprietario lo consente, verrai invitato nel team." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "La tua richiesta è stata inviata correttamente!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Non hai accesso a questo file." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Vai al tuo Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Se il proprietario lo consente, sarai invitato al team." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "File Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Non hai accesso a questo progetto." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROGETTO" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Per accedere a questo file, puoi chiedere al proprietario del team." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Per accedere a questo progetto, puoi chiedere al proprietario del team." @@ -3684,27 +3734,27 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Una nuova versione è disponibile." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Invito eliminato con successo" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Invito inviato con successo" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Link d'invito copiato" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "Non puoi eliminare il tuo profilo. Riassegna i tuoi team prima di procedere." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profilo salvato con successo!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Email di verifica inviata a %s. Controlla la tua email!" @@ -3734,78 +3784,78 @@ msgstr "Dopo aver nominato il tuo team, potrai invitare persone ad unirsi ad ess msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Crea team" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Invita membri" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Invita con il ruolo:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Iniziamo!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Generare codice partendo dal design" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Creare Design System" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototipazione" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Progettare la UI/UX di un'applicazione" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Per cosa utilizzerai Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Facci sapere qualcosa su di te per aiutarci ad ottimizzare Penpot rispetto " "alle tue esigenze. Le tue risposte ci aiuteranno a dare priorità a nuove " "funzionalità e a guidarti nella giusta direzione per iniziare." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Aiutaci a conoscerti meglio" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Quale di questi strumenti utilizzi maggiormente?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Che genere di lavori fai?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Parlaci del tuo lavoro" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Da dove ti piacerebbe iniziare?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Educazione" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Personale" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Lavoro" @@ -3855,11 +3905,11 @@ msgstr "Inizia a progettare" msgid "profile.recovery.go-to-login" msgstr "Vai all'accesso" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Scollega" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Misto" @@ -3871,35 +3921,35 @@ msgstr "Rimuovi colore" msgid "settings.select-this-color" msgstr "Seleziona gli elementi che utilizzano questo stile" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Commenti" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Allinea in basso" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Allinea al centro orizzontalmente" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Allinea a sinistra" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Allinea a destra" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Allinea in alto" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Allinea al centro verticalmente" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Crea una tavola da disegno a partire dalla selezione" @@ -3907,55 +3957,55 @@ msgstr "Crea una tavola da disegno a partire dalla selezione" msgid "shortcuts.bold" msgstr "Attiva/Disattiva grassetto" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Differenza" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Escludi" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Interseca" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Unisci" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Porta sotto" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Porta dietro" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Porta avanti" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Porta in primo piano" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Pulisci cronologia" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copia" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Copia link negli appunti" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Copia proprietà" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Crea componente / variante" @@ -3963,35 +4013,35 @@ msgstr "Crea componente / variante" msgid "shortcuts.create-new-project" msgstr "Crea nuovo" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Taglia" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Zoom indietro" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Elimina" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Elimina nodo" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Scollega componente" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Curva" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Ellisse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Tavola da disegno" @@ -3999,39 +4049,39 @@ msgstr "Tavola da disegno" msgid "shortcuts.draw-nodes" msgstr "Disegna tracciato" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Tracciato" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rettangolo" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Testo" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplica" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Annulla" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Esporta forme" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Adatta zoom a tutto" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Capovolgi orizzontalmente" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Capovolgi verticalmente" @@ -4055,23 +4105,23 @@ msgstr "Vai alle librerie condivise" msgid "shortcuts.go-to-search" msgstr "Cerca" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Raggruppa" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribuisci orizzontalmente" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Mostra / Nascondi UI" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Aumenta zoom" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Inserisci immagine" @@ -4079,7 +4129,7 @@ msgstr "Inserisci immagine" msgid "shortcuts.italic" msgstr "Attiva/Disattiva corsivo" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Unisci nodi" @@ -4087,39 +4137,39 @@ msgstr "Unisci nodi" msgid "shortcuts.line-through" msgstr "Attiva/disattiva testo barrato" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Crea angolo" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Crea curva" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Maschera" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Fondi nodi" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Sposta" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Sposta rapidamente in basso" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Sposta rapidamente a sinistra" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Sposta rapidamente a destra" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Sposta rapidamente in alto" @@ -4127,333 +4177,333 @@ msgstr "Sposta rapidamente in alto" msgid "shortcuts.move-nodes" msgstr "Sposta nodo" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Sposta in basso" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Sposta a sinistra" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Sposta a destra" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Sposta in alto" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Tavola da disegno successiva" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Nessuna scorciatoia trovata" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Imposta opacità al 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Imposta opacità al 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Imposta opacità al 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Imposta opacità al 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Imposta opacità al 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Imposta opacità al 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Imposta opacità al 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Imposta opacità al 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Imposta opacità all'80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Imposta opacità al 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Selettore colore" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Vai alla sezione commenti" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Vai al pannello di controllo" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Vai alla sezione di ispezione" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Vai alla sezione interazioni" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Vai alla sezione interazioni del visualizzatore" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Vai all'area di lavoro" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " o " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Incolla" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Incolla proprietà" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Gestore plugin" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Tavola da disegno precedente" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Ripeti" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Rinomina" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Reimposta zoom" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Ridimensiona" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Nozioni di base" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Pannello di controllo" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Visualizzatore" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Area di lavoro" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Seleziona tutto" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Seleziona livello successivo" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Seleziona livello genitore" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Seleziona livello precedente" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Separa nodi" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Mostra/Nascondi griglia pixel" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Mostra/Nascondi scorciatoie" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Aggancia ai nodi" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Aggancia alla griglia pixel" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Inizia a modificare" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Allineamento" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Modifica" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Generico" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu principale" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modifica livelli" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigazione" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigazione" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigazione" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Pannelli" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Tracciati" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Forme" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Testi" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Strumenti" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Imposta miniature" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Scorciatoie da tastiera" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Attiva/Disattiva allineamento dinamico" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Attiva/Disattiva risorse" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Attiva/Disattiva palette colori" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Attiva/Disattiva modalità focus" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Attiva/Disattiva schermo intero" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Mostra/Nascondi guide" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Attiva/Disattiva cronologia" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Attiva/Disattiva livelli" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Aggiungi / Rimuovi layout flex" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Aggiungi / Rimuovi layout grid" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Blocca/Sblocca" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Blocca proporzioni" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Mostra/Nascondi righelli" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Aggancia alle guide" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Aggancia alle guide del righello" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Attiva/Disattiva palette testi" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Cambia tema" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Mostra/Nascondi" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Attiva/Disattiva stile zoom" @@ -4461,31 +4511,31 @@ msgstr "Attiva/Disattiva stile zoom" msgid "shortcuts.underline" msgstr "Attiva/Disattiva sottolineatura" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Annulla" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Separa" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Rimuovi maschera" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribuisci verticalmente" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Diminuire lo zoom" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Aumentare lo zoom" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zoom su selezione" @@ -4577,204 +4627,204 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Invita persone con il piano Unlimited" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Abbonamento" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Aggiungi un metodo di pagamento per continuare dopo il periodo di prova" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Tutti i vantaggi del piano Professional e:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Tutti i vantaggi del piano Unlimited e:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s editor)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise (versione di prova)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "Versioni di salvataggio automatico a 90 giorni e recupero file" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Fattura mensile fissa" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Spazio di archiviazione illimitato" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Gestisci il tuo abbonamento" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Aggiungi dettagli di pagamento" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Aggiungi subito i tuoi dati di pagamento per mantenere attiva la " "sottoscrizione dopo il periodo di prova e continuare a supportare il nostro " "progetto open-source. Non ti verrà ancora addebitato nulla." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Salta per ora e inizia la prova" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Aiutaci a crescere e rendi la tua prova ancora più semplice" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Attualmente hai %s persona nei tuoi team che può modificare." msgstr[1] "Attualmente hai %s persone nei tuoi team che possono modificare." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Attenzione: passare a un piano inferiore significa meno spazio di " "archiviazione e backup e cronologia delle versioni più brevi." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Editor" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "" "(Proprietari, Amministratore ed Editor. I Visualizzatori non vengono " "considerati Editor)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "Non puoi impostare un numero di editor inferiore a quello attuale. Modifica " "il ruolo (editor/admin in visualizzatore) per le persone che non modificano " "effettivamente i file nelle impostazioni del team." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "(Non verrà effettuato nessun pagamento in questo momento)" -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "$7 per editor/mese x %s" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Applica %s ai tuoi team" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Suggerimento: puoi aumentare ora il numero di posti per anticipare gli " "inviti. Con oltre 25 editor nei team, pagherai un costo fisso di 175 $/mese." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Membro Penpot dal %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Ulteriori informazioni" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Altri piani Penpot" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "organizzazione per mese" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professional" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "Versioni di salvataggio automatico a 7 giorni e recupero file" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10GB di spazio di archiviazione" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Team illimitati. Fino a 8 editor nei team di tua proprietà." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Consigliato" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Il tuo abbonamento" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Inizia la prova gratuita" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Abbonati" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Puoi modificare il tuo abbonamento in qualsiasi momento tramite la pagina " "'Abbonamento' nei dettagli del tuo account." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Goditi il tuo piano!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Grazie per aver scelto il piano Penpot %s!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Sei %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Provalo gratuitamente per 14 giorni" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Illimitato" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unlimited (prova)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "Versioni di salvataggio automatico a 30 giorni e recupero file" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Fattura mensile con tetto massimo" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GB di spazio di archiviazione" @@ -4801,41 +4851,41 @@ msgstr "" "Se desideri aumentare questo limite, [esegui l'upgrade del tuo " "piano|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 invito selezionato" msgstr[1] "%s inviti selezionati" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Fornitori dei caratteri - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Caratteri - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Progetti - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Cerca - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Librerie condivise - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Libertà di design per i team" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Fornisci feedback - Penpot" @@ -4843,7 +4893,7 @@ msgstr "Fornisci feedback - Penpot" msgid "title.settings.notifications" msgstr "Notifiche - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Impostazioni - Penpot" @@ -4851,27 +4901,27 @@ msgstr "Impostazioni - Penpot" msgid "title.settings.password" msgstr "Password - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profilo - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Inviti - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Membri - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Impostazioni - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhook - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Modalità di visualizzazione - Penpot" @@ -4883,99 +4933,99 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Scollega token" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Nessuna tavola da disegno trovata nella pagina." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Tavola da disegno non trovata." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Commenti (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Non mostrare le interazioni" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Schermo intero" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Ispeziona (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interazioni" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interazioni (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Copia link" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Mostra interazioni" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Mostra interazioni al click" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Sitemap" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "L'ultimo invio è avvenuto con successo." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Allinea orizzontalmente al centro (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribuisci spaziatura orizzontale (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Allinea a sinistra (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Allinea a destra (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Allinea in basso (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Allinea verticalmente al centro (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribuisci spaziatura verticale (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Allinea in alto (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Aggiungi libreria" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Tutte le risorse" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Colori" @@ -4987,7 +5037,7 @@ msgstr "Aggiungi colore" msgid "workspace.assets.component-group-options" msgstr "Opzioni gruppo componenti" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Componenti" @@ -4995,7 +5045,7 @@ msgstr "Componenti" msgid "workspace.assets.components.add-component" msgstr "Aggiungi componente" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s Varianti" @@ -5003,17 +5053,17 @@ msgstr "%s Varianti" msgid "workspace.assets.create-group" msgstr "Crea un gruppo" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "I tuoi elementi verrano rinominati automaticamente come \"Nome gruppo / " "nome elemento\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Elimina" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplica" @@ -5021,39 +5071,39 @@ msgstr "Duplica" msgid "workspace.assets.duplicate-main" msgstr "Duplica principale" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Modifica" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtra" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Vista a griglia" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Gruppo" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nome gruppo" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Vista a elenco" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "libreria locale" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Gestisci libreria" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Nessuna risorsa trovata" @@ -5061,7 +5111,7 @@ msgstr "Nessuna risorsa trovata" msgid "workspace.assets.open-library" msgstr "Apri file libreria" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Rinomina" @@ -5069,11 +5119,11 @@ msgstr "Rinomina" msgid "workspace.assets.rename-group" msgstr "Rinomina gruppo" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Cerca risorse" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Librerie condivise" @@ -5083,43 +5133,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 componente" msgstr[1] "%s componenti" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Ordina" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Elementi tipografici" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Aggiungi elemento tipografico" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Dimensione" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Stile del carattere" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Vai alla libreria dello stile del file per modificare" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Spaziatura tra lettere" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Interlinea" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Trasforma testo" @@ -5131,65 +5181,65 @@ msgstr "Separa" msgid "workspace.colorpicker.color-tokens" msgstr "Token colore" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "I componenti non possono essere annidati dentro sé stessi." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "Alcune copie non possono essere sostituite. I componenti non possono essere " "annidati dentro sé stessi." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Crea area" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Crea tavola da disegno" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Unisci celle" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Aggiungi 1 colonna a destra" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Aggiungi 1 colonna a sinistra" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Elimina colonna" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Elimina colonna e forme" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Duplica colonna" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Aggiungi 1 riga sotto" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Aggiungi 1 riga sopra" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Elimina riga" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Elimina riga e forme" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Duplica riga" @@ -5197,15 +5247,15 @@ msgstr "Duplica riga" msgid "workspace.debug.title" msgstr "Strumenti di debug" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Modalità focus" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Disattiva modalità focus" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Attiva modalità focus" @@ -5217,191 +5267,191 @@ msgstr "Gradiente lineare" msgid "workspace.gradients.radial" msgstr "Gradiente radiale" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Disattiva allineamento dinamico" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Disattiva ridimensionamento proporzionale" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Disattiva allineamento a guide" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Disattiva allineamento al pixel" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Disattiva allineamento alle guide righello" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Attiva allineamento dinamico" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Attiva ridimensionamento proporzionale" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Allinea alle guide" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Attiva allineamento al poxel" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Allinea alle guide righello" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Nascondi nomi delle tavole da disegno" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Nascondi guide" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Nascondi palette colori" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Nascondi griglia pixel" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Nascondi righelli" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Nascondi palette caratteri" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Modifica" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "File" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Aiuto e informazioni" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferenze" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Visualizza" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Ripeti" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Seleziona tutto" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Mostra nomi delle tavole da disegno" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Mostra guide" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Mostra palette colori" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Mostra griglia pixel" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Mostra righelli" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Mostra palette caratteri" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Passa al tema scuro" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Passa al tema chiaro" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Passa al tema di sistema" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Annulla" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Reimposta" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Errore durante il salvataggio" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Salvato" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "In salvataggio" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Condividi" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Modalità di visualizzazione (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoom" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Riempi - Ridimensiona per riempire" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Adatta - Riduci per adattare" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Adatta zoom a tutto" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Schermo intero" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zoom su selezione" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Modifica griglia" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Esci" @@ -5409,7 +5459,7 @@ msgstr "Esci" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Padding inferiore" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Mostra opzioni di padding su 4 lati" @@ -5445,29 +5495,29 @@ msgstr "Fatto" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Individua" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Individua layout grid" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 colore" msgstr[1] "%s colori" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Non ci sono ancora stili di colore nella tua libreria" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Non ci sono ancora stili di carattere nella tua libreria" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Libreria del file" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Colori recenti" @@ -5479,93 +5529,93 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Salva stile di colore" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 componente" msgstr[1] "%s componenti" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Connesso a" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "O aggiungi alcuni di questi per provare:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "Non ci sono librerie condivise nel tuo team, puoi cercare" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "alcuni modelli qui" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Libreria del file" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 grafica" msgstr[1] "%s grafiche" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "LIBRERIE IN QUESTO FILE" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "LIBRERIE" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "AGGIORNAMENTI DELLA LIBRERIA" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Caricamento…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Non ci sono librerie condivise che necessitano un aggiornamento" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Nessun risultato trovato per “%s”" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Cerca librerie condivise" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "LIBRERIE CONDIVISE" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Connetti libreria" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 tipografia" msgstr[1] "%s tipografie" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Disconnetti libreria" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Aggiorna" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "vedi tutti i cambiamenti" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "AGGIORNAMENTI" @@ -5601,117 +5651,117 @@ msgstr "Attiva/Disattiva sfocatura" msgid "workspace.options.canvas-background" msgstr "Colore di sfondo" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Ritaglia contenuto" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Componente" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Annotazione" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Copia" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Crea un'annotazione" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Modifica un'annotazione" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Principale" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Sostituisci componente" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Non ci sono ancora risorse in questa libreria" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Scollegato" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Individua varianti in conflitto" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Questo componente ha varianti in conflitto. Verifica che ciascuna variante " "abbia un set di valori di proprietà univoci." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Individua varianti duplicate" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Alcune varianti hanno proprietà e valori identici" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Queste varianti hanno proprietà e valori identici. Modifica i valori " "affinché possano essere distinti." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Questa variante ha proprietà e valori identici ad un'altra variante. " "Modifica i valori affinché possano essere distinti." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Alcune di queste varianti hanno proprietà e valori identici. Modifica i " "valori affinché possano essere distinti." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Questo componente ha varianti con nomi non validi. Verifica che ogni " "variante rispetti la struttura corretta." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Individua varianti non valide" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Alcune varianti hanno nomi non validi" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Individua varianti non valide" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Queste varianti hanno nomi non validi." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Questa variante ha un nome non valido." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Alcune di queste varianti hanno nomi non validi." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[proprietà]=[valore], [proprietà]=[valore]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Prova a usare la seguente struttura:" @@ -5750,7 +5800,7 @@ msgstr "Si trovano allo stesso livello gerarchico." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Gruppi, tavole da disegno e layout sono considerati equivalenti." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Come rimangono connesse le varianti" @@ -5794,11 +5844,11 @@ msgstr "Superiore" msgid "workspace.options.constraints.topbottom" msgstr "Superiore e Inferiore" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Esporta" @@ -5806,37 +5856,37 @@ msgstr "Esporta" msgid "workspace.options.export-multiple" msgstr "Esporta selezione" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Esporta 1 elemento" msgstr[1] "Esporta %s elementi" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Aggiungi esportazione" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Rimuovi esportazione" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Suffisso" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Esportazione completata" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "In esportazione…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Esportazione fallita" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Esportazione inaspettatamente lenta" @@ -5852,7 +5902,7 @@ msgstr "Aggiungi riempimento" msgid "workspace.options.fill.remove-fill" msgstr "Elimina riempimento" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Adatta tavola da disegno al contenuto" @@ -5876,67 +5926,67 @@ msgstr "Inizi flusso" msgid "workspace.options.grid.auto" msgstr "Auto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Colonne" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Colore" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Interspazio" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Altezza" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margine" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Imposta come predefinito" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Basso" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Sinistra" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Destra" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Estendi" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Alto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Usa predefinito" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Larghezza" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Righe" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Quadrato" @@ -5948,7 +5998,7 @@ msgstr "Riempimento di gruppo" msgid "workspace.options.group-stroke" msgstr "Traccia di gruppo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Aggiungi guida" @@ -5956,19 +6006,19 @@ msgstr "Aggiungi guida" msgid "workspace.options.guides.remove-guide" msgstr "Rimuovi guida" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Guide" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Attiva/Disattiva guida" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Altezza" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Ispeziona" @@ -6184,107 +6234,107 @@ msgstr "Interazioni" msgid "workspace.options.interactions.add-interaction" msgstr "Aggiungi interazione" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Colore" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Colore brucia" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Colore scherma" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Scurisci" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Differenza" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Esclusione" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Luce intensa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Tonalità" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Schiarisci" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminosità" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Moltiplica" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normale" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Sovrapponi" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturazione" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Scolora" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Luce soffusa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Attiva/Disattiva visibilità livello" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Altezza.Max" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Larghezza.Max" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Altezza.Min" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Larghezza.Min" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Più colori" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Più librerie colori" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "Altri token colore" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacità" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototipo" @@ -6316,19 +6366,19 @@ msgstr "Comprimi raggio indipendente" msgid "workspace.options.radius.show-single-corners" msgstr "Mostra raggio indipendente" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Recenti" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Riprova" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotazione" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Cerca carattere" @@ -6338,7 +6388,7 @@ msgstr "" "Seleziona una forma, una tavola da disegno o un gruppo per trascinare una " "connessione verso un'altra tavola." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Colori selezionati" @@ -6406,55 +6456,55 @@ msgstr "Attiva/Disattiva ombra" msgid "workspace.options.show-fill-on-export" msgstr "Mostra in esportazione" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Mostra in modalità visualizzazione" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Dimensione" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Dimensioni predefinite" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Blocca proporzioni" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Sblocca proporzioni" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Cerchio" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamante" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Freccia" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Nessuno" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Rotondo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Quadrato" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rettangolo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triangolo" @@ -6462,7 +6512,7 @@ msgstr "Triangolo" msgid "workspace.options.stroke-color" msgstr "Colore traccia" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Spessore traccia" @@ -6470,35 +6520,35 @@ msgstr "Spessore traccia" msgid "workspace.options.stroke.add-stroke" msgstr "Aggiungi colore traccia" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Tratteggiato" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Puntinato" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Interno" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Misto" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Esterno" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Rimuovi traccia" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Solido" @@ -6534,11 +6584,11 @@ msgstr "Larghezza automatica" msgid "workspace.options.text-options.grow-fixed" msgstr "Fisso" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Spaziatura tra lettere" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Interlinea" @@ -6584,126 +6634,126 @@ msgstr "" "Utilizza il pulsante di riproduzione nell'header per avviare la " "visualizzazione del prototipo." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Larghezza" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Asse X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Asse Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Disegna nodi (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Unisci nodi (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Concerti in angolo (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Converti in curva (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Fondi nodi (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Sposta nodo (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Separa nodi (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Aggancia nodi (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Apri" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Scopri [altri plugin](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Nessun plugin ancora installata" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Il manifesto del plugin è errato." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Devi essere un editor per utilizzare questo plugin" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Il plugin non esiste o l'URL non è corretto." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Installa" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Plugin installati" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Manager dei plugin" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Plugin" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "AGGIORNA QUESTO PLUGIN" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Il plugin è stato modificato da quando l'hai aperto l'ultima volta. Ora " "richiede l'accesso a:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Inizia il download dei file." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Memorizza i dati nel browser." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Leggi i tuoi commenti e risposte." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Leggi e modifica i tuoi commenti e rispondi a tuo nome." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Leggi il contenuto dei file a cui gli utenti hanno accesso." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Leggi e modifica il contenuto dei file a cui gli utenti hanno accesso." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Tieni presente che questo plugin è creato da una terza parte, quindi " @@ -6711,74 +6761,74 @@ msgstr "" "sicurezza dei dati sono importanti per noi. Se hai dubbi, contatta il " "supporto." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Leggere le tue librerie e risorse." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Leggere e modificare le tue librerie e risorse." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "IL PLUGIN '%s' VUOLE ACCEDERE A:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Leggere le informazioni del profilo dell'utente attuale." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Lista plugin" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Rimuovi plugin" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Scrivi URL di un plugin" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Plugin" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "NON ORA" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Vuoi dare un'occhiata? Si aprirà in una nuova bozza per il tuo team " "attuale. (In caso contrario, puoi sempre trovarlo nei plugin installati di " "qualsiasi file.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "IL PLUGIN '%s' È INSTALLATO PER IL TUO UTENTE!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "PROVA PLUGIN" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Aggiungi layout flex" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Aggiungi layout grid" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Aggiungi layout" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Crea variante" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Aggiungi nuova proprietà" @@ -6790,7 +6840,7 @@ msgstr "Porta in fondo" msgid "workspace.shape.menu.backward" msgstr "Porta indietro" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Combina come varianti" @@ -6798,7 +6848,7 @@ msgstr "Combina come varianti" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "I componenti devono essere nella stessa pagina" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Copia" @@ -6834,27 +6884,27 @@ msgstr "Copia come testo" msgid "workspace.shape.menu.create-annotation" msgstr "Crea annotazione" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Tavola da disegno da selezione" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Crea componente" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Crea componenti multipli" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Taglia" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Elimina" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Elimina inizio del flusso" @@ -6866,7 +6916,7 @@ msgstr "Scollega istanza" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Scollega istanze" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Differenza" @@ -6874,15 +6924,15 @@ msgstr "Differenza" msgid "workspace.shape.menu.duplicate" msgstr "Duplica" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Modifica" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Escludi" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Appiattisci" @@ -6894,7 +6944,7 @@ msgstr "Capovolgi orizzontalmente" msgid "workspace.shape.menu.flip-vertical" msgstr "Capovolgi verticalmente" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Inizio del flusso" @@ -6906,31 +6956,31 @@ msgstr "Porta avanti" msgid "workspace.shape.menu.front" msgstr "Porta in primo piano" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Raggruppa" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Nascondi" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Mostra/Nascondi UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Interesezione" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Blocca" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Maschera" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Incolla" @@ -6938,27 +6988,27 @@ msgstr "Incolla" msgid "workspace.shape.menu.paste-props" msgstr "Incolla proprietà" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Tracciato" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Rimuovi layout flex" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Rimuovi layout grid" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Rimuovi layout" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Rimuovi proprietà" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "Le varianti devono avere almeno una proprietà" @@ -6982,11 +7032,11 @@ msgstr "Ripristina variante" msgid "workspace.shape.menu.select-layer" msgstr "Seleziona livello" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Mostra" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Mostra nel pannello delle risorse" @@ -7002,19 +7052,19 @@ msgstr "Rimuovi miniatura" msgid "workspace.shape.menu.thumbnail-set" msgstr "Imposta come miniatura" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Separa" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Unione" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Sblocca" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Rimuovi maschera" @@ -7030,51 +7080,51 @@ msgstr "Comprimi barra laterale" msgid "workspace.sidebar.expand" msgstr "Espandi barra laterale" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Cronologia" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Livelli" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Componenti" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Filtro" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Tavole da disegno" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Gruppi" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Immagini" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Maschere" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Cerca livelli" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Forme" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Testi" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Attributi SVG importati" @@ -7086,7 +7136,7 @@ msgstr "Pagine" msgid "workspace.sidebar.sitemap.add-page" msgstr "Aggiungi pagina" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Sitemap" @@ -7106,7 +7156,7 @@ msgstr "Aggiungi il set a questo gruppo" msgid "workspace.tokens.add-token" msgstr "Aggiungi token: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Applicato a" @@ -7128,7 +7178,7 @@ msgstr "" "La dimensione base del carattere deve essere un valore in pixel o senza " "unità." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Colore" @@ -7136,7 +7186,7 @@ msgstr "Colore" msgid "workspace.tokens.create-new-theme" msgstr "Crea ora il tuo prima tema." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Creane uno." @@ -7156,7 +7206,7 @@ msgstr "Elimina tema" msgid "workspace.tokens.duplicate" msgstr "Duplica token" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "copia" @@ -7204,7 +7254,7 @@ msgstr "Anteprima:" msgid "workspace.tokens.export.single-file" msgstr "File singolo" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Dimensione carattere o {alias}" @@ -7214,7 +7264,7 @@ msgstr "" "Errore nell’impostazione del peso/stile del font. Questo stile non esiste " "nel font corrente" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Font weight (300, Bold, Regular Italic...) o {alias}" @@ -7252,7 +7302,7 @@ msgstr "" "In un singolo file JSON, le chiavi di primo livello devono corrispondere ai " "nomi dei set di token." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Importa token" @@ -7262,17 +7312,17 @@ msgstr "" "L'importazione dei token sovrascriverà tutti i tuoi attuali token, set e " "temi." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Inattivo" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Questo set non è attivo. Cambia tema o attiva questo set per vedere " "modifiche nell'anteprima" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Utilizza token individuali" @@ -7292,11 +7342,11 @@ msgstr "Tema" msgid "workspace.tokens.label.theme-placeholder" msgstr "Aggiungi un tema (es. Chiaro)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Letter spacing o {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Line height (moltiplicatore, px, %) o {alias}" @@ -7312,11 +7362,11 @@ msgstr "Dimensione max" msgid "workspace.tokens.min-size" msgstr "Dimensione min" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Riferimento mancante" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Fai clic con il tasto destro per vedere le opzioni" @@ -7332,7 +7382,7 @@ msgstr "Nessun tema attivo" msgid "workspace.tokens.no-permisions-set" msgstr "Devi essere un editor per attivare / disattivare i set" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Devi essere un editor per usare i temi" @@ -7344,7 +7394,7 @@ msgstr "Non sono ancora stati definiti dei set. Creane prima uno." msgid "workspace.tokens.no-sets-yet" msgstr "Non ci sono ancora dei set." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Non ci sono temi." @@ -7360,7 +7410,7 @@ msgstr "Non riassegnare" msgid "workspace.tokens.num-active-sets" msgstr "% set attivi" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Valore originale: %s" @@ -7372,11 +7422,11 @@ msgstr "Padding" msgid "workspace.tokens.radius" msgstr "Raggio" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Inserisci un alias tipografico del token" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Inserisci un alias per il token ombra" @@ -7392,7 +7442,7 @@ msgstr "Riassegnare tutti i token che usano `%s` a `%s`?" msgid "workspace.tokens.remap-warning-time" msgstr "Questa azione può richiedere un po' di tempo." -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Valore risolto: %" @@ -7426,27 +7476,27 @@ msgstr "" msgid "workspace.tokens.settings" msgstr "Impostazioni token" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Aggiungi ombra" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Sfocatura" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Interna" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Rimuovi ombra" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Diffusione" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "Il valore della sfocatura non può essere negativo" @@ -7454,11 +7504,11 @@ msgstr "Il valore della sfocatura non può essere negativo" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "Il valore della diffusione non può essere negativo" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -7466,11 +7516,11 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "Dimensione" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize o {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through o {alias}" @@ -7488,27 +7538,27 @@ msgstr "Elenco temi" msgid "workspace.tokens.token-description" msgstr "Descrizione" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Seleziona famiglia di caratteri" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Famiglia di caratteri" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Famiglia di caratteri o elenco di caratteri separati da virgola (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nome" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "Il nome deve essere di almeno 1 carattere" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " non è un nome di token valido.\n" @@ -7519,11 +7569,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Valore" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Inserisci un valore o un alias con {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKEN - %s" @@ -7542,11 +7592,11 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "Il tipo '%s' non è supportato (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Utilizza un riferimento" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Il valore non è valido" @@ -7554,55 +7604,55 @@ msgstr "Il valore non è valido" msgid "workspace.toolbar.assets" msgstr "Risorse" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Palette colori (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Commenti (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Curva (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Ellisse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Tavola da disegno (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Immagine (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Sposta (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Tracciato (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Plugin (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rettangolo (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Testo (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Tipografie (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Attiva/Disattiva barra degli strumenti" @@ -7615,7 +7665,7 @@ msgstr "Fatto" msgid "workspace.top-bar.view-only" msgstr "**Ispezione codice** (Solo visualizzazione)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Non ci sono cambiamenti nella cronologia al momento" @@ -7755,19 +7805,19 @@ msgstr "risorsa tipografica" msgid "workspace.undo.entry.unknown" msgstr "Operazione su %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Ignora" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Maggiori informazioni" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Sono presenti degli aggiornamenti nelle librerie condivise" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Aggiorna" diff --git a/frontend/translations/jpn_JP.po b/frontend/translations/jpn_JP.po index 65ee25f35b..08fdb0977f 100644 --- a/frontend/translations/jpn_JP.po +++ b/frontend/translations/jpn_JP.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: Tatsuto Yamamoto \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Japanese " "\n" "Language: jpn_JP\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "アカウントをお持ちですか?" @@ -31,7 +31,7 @@ msgstr "デモアカウントを作成" msgid "auth.demo-warning" msgstr "これはデモ用のサービスです。実際の業務には使用しないでください。プロジェクトは定期的に削除されます。" -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "パスワードをお忘れですか?" @@ -39,35 +39,35 @@ msgstr "パスワードをお忘れですか?" msgid "auth.fullname" msgstr "氏名" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "アカウントにログイン" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "こちらからログイン" -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpotは、デザインとコードの共同作業のための無料のオープンソースデザインツールです" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -75,7 +75,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "新しいパスワードを入力" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "%sに参加しました" @@ -91,7 +91,7 @@ msgstr "プロフィールが確認されていません。続行する前にプ msgid "auth.notifications.recovery-token-sent" msgstr "パスワード再設定用のリンクをメールで送信しました。" -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "チームに参加しました" @@ -123,7 +123,7 @@ msgstr "パスワードをお忘れですか?" msgid "auth.recovery-submit" msgstr "パスワードを変更" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "アカウントをお持ちでないですか?" @@ -135,7 +135,7 @@ msgstr "ダッシュボードやメールで表示するお名前を教えてく msgid "auth.register-account-title" msgstr "お名前" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "アカウントを作成" @@ -148,7 +148,7 @@ msgstr "アカウントを作成" msgid "auth.terms-and-privacy-agreement" msgstr "[利用規約](%s)と[プライバシーポリシー](%s)に同意します。" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "利用規約" @@ -168,88 +168,88 @@ msgstr "利用できるカラートークンがありません。有効なセッ msgid "comments.mentions.not-found" msgstr "@%sに一致するユーザーが見つかりません" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "公開" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "すべてのPenpotユーザー" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "このリンクを削除しますか?削除すると、誰もこのリンクを使用できなくなります。" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(現在)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "リンクを削除" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "リンクを取得" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "リンクをコピーしました" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "権限を管理" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "%sページを共有" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "コメント可能" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "コードをインスペクト可能" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "リンクを知っている全員がアクセスできます" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "共有するページ" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "共有リンクがここに表示されます" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "チームメンバーのみ" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "プロトタイプを共有" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "すべて選択" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "公開を停止" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "チームを管理" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "Penpotはチームで使うためのツールです。メンバーを招待して、プロジェクトやファイルで一緒に作業しましょう" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "チームで使おう!" @@ -265,31 +265,31 @@ msgstr "ファイルを復元" msgid "dashboard.add-file" msgstr "ファイルを追加" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "共有ライブラリとして追加" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "メールアドレスを変更" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "チームを移動" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "移動先:" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "プロジェクトとファイルは引き続きチームメンバーが利用できます。チームには新しい組織の設定が適用されます。" -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "組織を選択" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "チームの組織を変更" @@ -345,23 +345,23 @@ msgstr "すべての変更履歴を表示" msgid "dashboard.check-updates.view-release-notes" msgstr "リリースノートを表示" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "ゴミ箱を空にする" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(コピー)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "組織を新規作成" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "チームを新規作成" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "このバージョンを固定" @@ -369,7 +369,7 @@ msgstr "このバージョンを固定" msgid "dashboard.default-team-name" msgstr "個人プロジェクト" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "削除済みのプロジェクトとファイルをすべて完全に削除しますか?この操作は元に戻せません。" @@ -377,39 +377,39 @@ msgstr "削除済みのプロジェクトとファイルをすべて完全に削 msgid "dashboard.delete-file-forever-confirmation.description" msgstr "%sを完全に削除しますか?この操作は元に戻せません。" -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s個のファイルを削除しました。" -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "完全に削除" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "プロジェクトを削除" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "プロジェクト「%s」を完全に削除しますか?プロジェクトとその中のすべてのファイルが完全に削除されます。この操作は元に戻せません。" -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%sを削除しました。" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "チームを削除" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "ゴミ箱は空です。削除したファイルやプロジェクトはここに表示されます。" -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "%sに削除されます" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Penpotファイル(.penpot)をダウンロード" @@ -421,19 +421,19 @@ msgstr "複製" msgid "dashboard.duplicate-multi" msgstr "%s個のファイルを複製" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "プロジェクトメンバーが下書きを作成すると、ここに表示されます。" -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "下書きはまだありません。" -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "プロジェクトメンバーがファイルを作成すると、ここに表示されます。" -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "ファイルはまだありません。" @@ -480,23 +480,23 @@ msgstr ".penpotファイルをインポート" msgid "dashboard.empty-project.start" msgstr "すばらしいものを作り始めましょう" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "ファイルの削除中にエラーが発生しました。" -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "プロジェクト「%s」の削除中にエラーが発生しました。" -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "ファイル「%s」の復元中にエラーが発生しました。" -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "ファイルの復元中にエラーが発生しました。" -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "プロジェクト「%s」とそのファイルの復元中にエラーが発生しました。" @@ -504,35 +504,35 @@ msgstr "プロジェクト「%s」とそのファイルの復元中にエラー msgid "dashboard.export-binary-multi" msgstr "%s個のPenpotファイル(.penpot)をダウンロード" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "ボードをPDFとしてエクスポート" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "PDFとしてエクスポート" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s / %s個の要素を選択中" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "エクスポート" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "デザインプロパティ(右サイドバーの下部)から、要素にエクスポート設定を追加できます。" -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Penpotでのエクスポート設定方法" -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "エクスポート設定のある要素がありません。" -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "選択範囲をエクスポート" @@ -554,32 +554,32 @@ msgstr "グリッド表示" msgid "dashboard.files-layout.list" msgstr "リスト表示" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "見つからないフォント" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "すべて破棄" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "アップロードしたカスタムフォントはここに表示されます。" -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "カスタムフォントはまだありません。" -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "プロジェクトメンバーがカスタムフォントをアップロードすると、ここに表示されます。" -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "%s個のフォントを追加しました" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -587,7 +587,7 @@ msgstr "" "のフォントは**1つのフォントファミリー**としてまとめられます。アップロードできる形式は**TTF、OTF、WOFF**です(いずれか1" "つで十分です)。" -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -595,11 +595,11 @@ msgstr "" "s)の「コンテンツの権利」セクションをご覧ください。[フォントのライセンス](https://www.typography.com/faq)" "についての情報も参考にしてください。" -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "すべてアップロード" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -607,23 +607,23 @@ msgstr "" "metrics.netlify.app/)のようなフォントの垂直メトリクス確認サービスを利用できます。また、Webフォントの生成とエラーの修正には[" "Transfonter](https://transfonter.org/)の使用をおすすめします。" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "管理コンソールへ移動" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "最終更新:%s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Penpotファイルをインポート" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "このファイルをインポートできませんでした" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "%s個のライブラリを名前で自動的にリンクしました。" @@ -632,52 +632,52 @@ msgstr[0] "%s個のライブラリを名前で自動的にリンクしました msgid "dashboard.import.bad-url" msgstr "インポートに失敗しました。テンプレートのURLが正しくありません" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "ライブラリのリンクを確認" -#: src/app/main/ui/dashboard/import.cljs:709 +#: src/app/main/ui/dashboard/import.cljs:814 msgid "dashboard.import.connect-selected-libraries" msgstr "選択したライブラリを接続" -#: src/app/main/ui/dashboard/import.cljs:296 +#: src/app/main/ui/dashboard/import.cljs:288 msgid "dashboard.import.file-name-label" msgstr "ファイル名" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "このファイルを検証できませんでした。" -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "このファイルは破損しているようです。" -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "一部のファイルがインポートされませんでした" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "次のファイルにエラーがあります:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "エラーのあるファイルはアップロードされません。" -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "このファイルの処理中に問題が発生しました。" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "%s個のファイルをインポートしました。" -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "一部のファイルに無効なオブジェクトが含まれていたため、削除しました。" -#: src/app/main/ui/dashboard/import.cljs:710 +#: src/app/main/ui/dashboard/import.cljs:815 msgid "dashboard.import.next-file" msgstr "次のファイル" @@ -685,95 +685,67 @@ msgstr "次のファイル" msgid "dashboard.import.no-perms" msgstr "このチームにインポートする権限がありません" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "色を処理しています" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "コンポーネントを処理しています" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "メディアを処理しています" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "ページを処理しています:%s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "タイポグラフィを処理しています" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "サーバーにデータをアップロードしています (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "ファイルをアップロードしています:%s" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "一部のライブラリを自動的にリンクできませんでした。それぞれに正しいライブラリを選択してください:" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "確定する前にライブラリのリンクを確認してください:" -#: src/app/main/ui/dashboard/import.cljs:459 +#: src/app/main/ui/dashboard/import.cljs:447 msgid "dashboard.import.resolve-libraries.connect-to" msgstr "接続先" -#: src/app/main/ui/dashboard/import.cljs:453 +#: src/app/main/ui/dashboard/import.cljs:441 msgid "dashboard.import.resolve-libraries.original-library" msgstr "元のライブラリ" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "自動リンク済み" -#: src/app/main/ui/dashboard/import.cljs:529 +#: src/app/main/ui/dashboard/import.cljs:517 msgid "dashboard.import.summary.manually-linked" msgstr "手動でリンク" -#: src/app/main/ui/dashboard/import.cljs:537 +#: src/app/main/ui/dashboard/import.cljs:525 msgid "dashboard.import.summary.new" msgstr "新規" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "ライブラリが選択されていません" -#: src/app/main/ui/dashboard/import.cljs:534 +#: src/app/main/ui/dashboard/import.cljs:522 msgid "dashboard.import.summary.original" msgstr "元" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "次の宛先への招待を削除します:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "次の宛先に招待を再送信します:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "招待を削除" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "招待を再送信" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "このチームにユーザーを招待したり、招待を編集・削除したりする権限がありません。" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "メンバーを招待" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "このチームにユーザーを招待する権限がありません" @@ -785,27 +757,27 @@ msgstr "組織から退出" msgid "dashboard.leave-organization.toast" msgstr "組織「%s」から退出しました。" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "チームから退出" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "ライブラリとテンプレート" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "プロジェクトに追加できるライブラリとテンプレートです" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "さらに探して、貢献する方法を知る" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "テンプレートのインポート中に問題が発生しました。テンプレートはインポートされませんでした。" -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "ライブラリ" @@ -813,7 +785,7 @@ msgstr "ライブラリ" msgid "dashboard.loading-files" msgstr "ファイルを読み込んでいます…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "フォントを読み込んでいます…" @@ -833,59 +805,59 @@ msgstr "%s個のファイルを移動" msgid "dashboard.move-to-other-team" msgstr "他のチームへ移動" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ 新規ファイル" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "新規ファイル" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ 新規プロジェクト" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "新規プロジェクト" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "「%s」に一致する結果が見つかりませんでした" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "どの組織にもチームを追加する権限がありません。" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:185 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 msgid "dashboard.no-organizations-yet" msgstr "組織はまだありません。" -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "組織「%s」内でチームを作成する権限がありません。詳しくは組織のオーナーにお問い合わせください。" -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "組織「%s」に属するチームを削除する権限がありません。詳しくは組織のオーナーにお問い合わせください。" -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "組織「%s」に属するチームを移動する権限がありません。詳しくは組織のオーナーにお問い合わせください。" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "ピン留めしたプロジェクトはここに表示されます" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "通知" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "メールアドレスを更新しました" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "メールアドレスを確認しました" @@ -897,11 +869,11 @@ msgstr "通知設定を更新しました" msgid "dashboard.notifications.password-saved" msgstr "パスワードを保存しました!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "通知を表示" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s人のメンバー" @@ -909,23 +881,23 @@ msgstr "%s人のメンバー" msgid "dashboard.open-in-new-tab" msgstr "新しいタブでファイルを開く" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "オプション" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "役割で並べ替え" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "ステータスで並べ替え" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "組織「%s」は削除されました。" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "その他のチーム" @@ -949,11 +921,11 @@ msgstr "このチームのオーナーになりました。" msgid "dashboard.permissions-change.viewer" msgstr "このチームでのあなたのロールが閲覧者になりました。" -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "個人プロジェクト" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "ピン留め/ピン留めを解除" @@ -969,27 +941,27 @@ msgstr "プラグインのマニフェストを解析できません" msgid "dashboard.plugins.try-plugin" msgstr "プラグインを試す: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "ファイルを削除しています…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "ファイルを復元しています…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "削除に予想以上に時間がかかっています" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "復元に予想以上に時間がかかっています" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "プロジェクト" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "アカウントを削除しますか?" @@ -997,107 +969,107 @@ msgstr "アカウントを削除しますか?" msgid "dashboard.removed-from-team" msgstr "あなたはチーム「%s」のメンバーではなくなりました。" -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "すべてのプロジェクトとファイルを復元します。しばらく時間がかかる場合があります。" -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "すべてのプロジェクトとファイルを復元" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "すべて復元" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s個のファイルを復元しました。" -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "プロジェクトを復元" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "プロジェクト「%s」と、その中のすべてのファイルを復元します。" -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "プロジェクトを復元" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "「%s」を復元しました。" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "設定を保存" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "検索…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "「%s」を検索しています…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "組織" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "チーム" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "組織に追加" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "組織を選択してください:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "「%s」では、組織のメンバーのみがチームに参加できます。組織外のユーザーへの保留中の招待はすべて取り消されます。" -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "外部ユーザーへの保留中の招待はキャンセルされます。" -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "ここには、あなたが所属していて、チームを移動できる組織が表示されます。" -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "ここには、あなたが所属していて、チームを作成または追加できる組織が表示されます。" -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "組織を選択" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "チームを組織に追加" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "表示言語を選択" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "テーマを選択" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot ダーク(デフォルト)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot ライト" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "システムのテーマ" @@ -1161,11 +1133,11 @@ msgstr "設定を更新" msgid "dashboard.settings.notifications.title" msgstr "通知" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "すべてのファイルを表示" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "バージョン履歴" @@ -1187,7 +1159,7 @@ msgstr[0] "ファイルを複製しました" msgid "dashboard.success-duplicate-project" msgstr "プロジェクトを複製しました" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "ファイルを移動しました" @@ -1199,179 +1171,179 @@ msgstr "ファイルを移動しました" msgid "dashboard.success-move-project" msgstr "プロジェクトを移動しました" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s は組織「%s」に所属しました" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "チーム情報" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "チームのメンバー" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s は組織「%s」に所属しなくなりました" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "チームの組織" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "組織に追加" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "このチームを、あなたが所属するどの組織にも追加する権限がありません。" -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "チームの組織を変更" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "組織内のユーザーのみ招待できます。" -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "このチームはどの組織にも所属していません" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "チームを組織から削除" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "チームのプロジェクト" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "プロジェクトに追加" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "UIテーマ" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "検索結果" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "削除したファイルはゴミ箱に" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr "%s日間" -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "保持され、その後完全に削除されます。" -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "気が変わった場合は、各ファイルのメニューから復元または完全に削除できます。" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "入力して検索" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "ライブラリの公開を停止" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "設定を更新" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "あなたは組織「%s」のメンバーではなくなりました" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "ベータ" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "WebGL " "レンダリングはパフォーマンスを向上させる可能性がありますが、ベータ版のため動作が不安定な場合があります。キャンバス、エクスポート(SVG/" "PDF)、表示モードの間で見た目に違いが出ることがあります。" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "無効" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "有効" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "フィードバックを送る" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "ステータス" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "WebGL レンダリング" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "有効" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "このWebhookがトリガーされると、イベントの詳細が送信されます" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "削除または変更できるのは、自分が作成したWebhookのみです。" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "コンテンツタイプ" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Webhookを作成" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhookを作成しました。" -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhookを使うと、Penpotで特定のイベントが発生したときに、他のWebサイトやアプリに簡単に通知できます。指定した各URLにPOSTリクエスト" "を送信します。" -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "「Webhookを追加」ボタンを押して追加してください。" -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "まだWebhookは作成されていません。" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "あなたのアカウント" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "メールアドレス" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "名前" @@ -1387,11 +1359,11 @@ msgstr "注意" msgid "ds.component-subtitle" msgstr "更新するコンポーネント:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "許可" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "キャンセル" @@ -1411,7 +1383,7 @@ msgstr "アクティブなセットまたはテーマに適用可能なトーク msgid "ds.inputs.numeric-input.no-matches" msgstr "一致するものが見つかりません。" -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "トークン一覧を開く" @@ -1431,15 +1403,15 @@ msgstr "このプロフィールではこの認証プロバイダーは使用で msgid "errors.auth-provider-not-configured" msgstr "認証プロバイダーが設定されていません。" -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "認証されていないか、セッションの有効期限が切れているようです。" -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "フォント%sを読み込めませんでした" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "フォント%sを読み込めませんでした" @@ -1451,55 +1423,55 @@ msgstr "メディアファイルをアップロードできません。" msgid "errors.character-limit-exceeded" msgstr "文字数の上限を超えています" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "ライブラリを追加できません: 循環依存が発生します" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "クリップボードAPIを利用できません。クリップボードにアクセスするには、PenpotをHTTPSで提供してください" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "お使いのブラウザではこの操作を実行できません" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "クリップボードへのアクセスが拒否されました。内容を貼り付けるには、ブラウザでクリップボードの権限を許可してください" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "コメントでエラーが発生しました" -#: src/app/main/errors.cljs:499 +#: src/app/main/errors.cljs:586 msgid "errors.connection-error" msgstr "サーバーに接続できません。接続を確認して、もう一度お試しください。" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "申し訳ありません。このファイルは廃止された形式のPenpotアセットを使用している古いファイルのため、開くことができません。" -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "サポートいたします。" -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Penpotはこの形式のアセットをサポートしていませんが、" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "お問い合わせいただければ" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "親セットを自身の子パスにドロップすることはできません。" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "このメールアドレスはすでに使用されています" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "メールアドレスはすでに確認済みです。" @@ -1511,27 +1483,27 @@ msgstr "メールアドレスをパスワードとして使用することはで msgid "errors.email-does-not-match-invitation" msgstr "メールアドレスが招待と一致しません。" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "このドメインは許可されていません" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "メールアドレス「%s」には多数の恒久的な配信エラーが報告されています。" -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "メールアドレス「%s」はスパムとして報告されたか、恒久的な配信エラーが発生しています。" -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "機能「%s」が有効になっているファイルを開こうとしていますが、現在のPenpotのバージョンではこの機能がサポートされていないか、無効になっています。" -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "機能「%s」はサポートされていません。" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "%s文字以内で入力してください。" @@ -1539,7 +1511,7 @@ msgstr "%s文字以内で入力してください。" msgid "errors.field-min-length" msgstr "1文字以上入力してください。" -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "空のフィールド" @@ -1547,19 +1519,19 @@ msgstr "空のフィールド" msgid "errors.field-not-all-whitespace" msgstr "名前にはスペース以外の文字を含める必要があります。" -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "有効な機能と、開こうとしているファイルの機能が一致していないようです。ファイルを開く前に「%s」の移行を適用する必要があります。" -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "フォントファミリー名に使用できるのは、英数字、スペース、ハイフン、アンダースコア、ドットのみです。" -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "問題が発生しました。" -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Webワーカーで問題が発生しました。" @@ -1575,7 +1547,7 @@ msgstr "無効なデータ" msgid "errors.invalid-email" msgstr "有効なメールアドレスを入力してください" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "確認用メールアドレスが一致しません" @@ -1607,11 +1579,11 @@ msgstr "この招待はキャンセルされたか、有効期限が切れてい msgid "errors.ldap-disabled" msgstr "LDAP認証は無効になっています。" -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "「%s」の上限に達しました。サポートにお問い合わせください。" -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "1回のリクエストで招待できるメールアドレスの上限(%s件)に達しました" @@ -1627,19 +1599,19 @@ msgstr "画像の内容がファイルの拡張子と一致していないよう msgid "errors.media-type-not-allowed" msgstr "有効な画像ではないようです。" -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "招待しようとしているユーザーは、メールがミュートされています(スパム報告または配信エラーの多発)。" -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "移行中です" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "このバージョンをロックできるのは作成者のみです" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "このバージョンのロックを解除できるのは作成者のみです" @@ -1651,7 +1623,7 @@ msgstr "組織からの退出中に問題が発生しました。もう一度お msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "組織のオーナーは組織から退出できません。" -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "その組織は存在しません" @@ -1663,7 +1635,7 @@ msgstr "確認用パスワードが一致しません" msgid "errors.password-too-short" msgstr "パスワードは8文字以上で入力してください" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "クリップボードのデータが無効です" @@ -1671,7 +1643,7 @@ msgstr "クリップボードのデータが無効です" msgid "errors.profile-blocked" msgstr "このプロフィールはブロックされています" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "あなたのプロフィールはメールがミュートされています(スパム報告または配信エラーの多発)。" @@ -1679,27 +1651,27 @@ msgstr "あなたのプロフィールはメールがミュートされていま msgid "errors.registration-disabled" msgstr "新規登録は現在無効になっています。" -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:700 msgid "errors.save-failed" msgstr "エラーのため自動保存が機能していません。サポートに連絡してエラーを報告し、再読み込みして最後に保存されたバージョンから続けてください。" -#: src/app/main/data/persistence.cljs:307 +#: src/app/main/data/persistence.cljs:447 msgid "errors.save-retrying" msgstr "接続が切断されました。変更の保存を再試行しています。" -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVGが無効か、形式が正しくありません" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "互換性のない機能「%s」が検出されました" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "メンバーが不足しているためチームから退出できません。チームを削除することを検討してください。" -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "割り当てようとしたメンバーは存在しません。" @@ -1707,7 +1679,7 @@ msgstr "割り当てようとしたメンバーは存在しません。" msgid "errors.team-leave.only-owner-can-delete" msgstr "チームを削除できるのはオーナーのみです。" -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "オーナーはチームから退出できません。オーナーのロールを他のメンバーに譲渡してください。" @@ -1719,11 +1691,11 @@ msgstr "チーム名に次の文字は使用できません: '.'、':'、'/'" msgid "errors.token-set-already-exists" msgstr "同じ名前のセットがすでに存在します" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "ドロップできません。同じパスに同じ名前のセットがすでに存在します。" -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "同じ名前のテーマオプションが存在します" @@ -1735,11 +1707,11 @@ msgstr "テーマが存在しないセットを参照しています: %s" msgid "errors.tokens.circular-reference" msgstr "トークンに循環参照があります" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "行の高さはフォントサイズに依存します。解決された値を得るには、フォントサイズを追加してください。" -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "このフィールドは空にできません" @@ -1759,11 +1731,11 @@ msgstr "インポートエラー:" msgid "errors.tokens.invalid-color" msgstr "無効な色の値: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "無効なトークンの値: 参照できるのはfont-familyトークンのみです" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "無効なフォントの太さの値: " @@ -1783,35 +1755,35 @@ msgstr "" "「%s」は有効なトークン名ではありません。\n" "トークン名には、. で区切られた英字と数字のみを使用でき、$記号で始めることはできません。" -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "無効なシャドウの種類: 使用できるのは「innerShadow」または「dropShadow」のみです" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "無効なトークンの値: 使用できるのはnone、Uppercase、Lowercase、Capitalizeのみです" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "無効なトークンの値: 使用できるのはnone、underline、strike-throughのみです" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "無効な値: 複合シャドウトークンを参照する必要があります。" -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "無効な値: 複合タイポグラフィトークンを参照する必要があります。" -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "無効なトークンの値: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "見つからないトークン参照: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "グループ名%sが、別のアクティブなセットにある同じ名前のトークンと競合しています。" @@ -1823,31 +1795,31 @@ msgstr "このファイルにはトークン、セット、テーマが見つか msgid "errors.tokens.number-too-large" msgstr "無効なトークンの値。解決された値が大きすぎます: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "不透明度は0〜100%または0〜1の範囲で指定してください(例: 50%または0.5)。" -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "トークンが自身を参照しています" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "シャドウのぼかしは0以上である必要があります。" -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "シャドウの広がりは0以上である必要があります。" -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "線の幅は0以上である必要があります。" -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "無効な値: %は使用できません。" -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "無効な値: 単位は使用できません。" @@ -1855,19 +1827,19 @@ msgstr "無効な値: 単位は使用できません。" msgid "errors.unexpected-error" msgstr "予期しないエラーが発生しました。" -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "不明なトークン" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "このバージョンはすでにロックされています" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "このバージョンはロックされているため、他のユーザーは削除できません" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "ファイルのバージョン番号に互換性がありません" @@ -1900,39 +1872,39 @@ msgstr "1文字以上の大文字" msgid "errors.weak-password.too-short" msgstr "8文字以上" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGLが動作を停止しました。ページを再読み込みしてリセットしてください" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "キャンバスのコンテキストが失われました" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "接続エラー:URLに到達できません" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URLの検証に失敗しました。" -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "前回の配信は失敗しました。" -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "SSLの検証でエラーが発生しました。" -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "タイムアウト" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "検証中に予期しないエラーが発生しました" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "予期しないステータス%s" @@ -1952,15 +1924,15 @@ msgstr "説明" msgid "feedback.description-placeholder" msgstr "フィードバックの理由を記入してください" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "ご参加ありがとうございます。お困りの際は、投稿する前に検索してください。" -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpotコミュニティ" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "その他のお問い合わせ方法" @@ -1980,11 +1952,11 @@ msgstr "メールの目的を、問題・アイデア・質問のどれに当た msgid "feedback.title-contact-us" msgstr "お問い合わせ" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "技術的なご質問にお答えします。" -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Xサポートアカウント" @@ -2004,39 +1976,35 @@ msgstr "アイデア" msgid "feedback.type.issue" msgstr "問題" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "ファイルをダウンロード" - -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "リンクされたライブラリのアセットはファイルに含まれません。" -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "アセットを基本オブジェクトとして扱う" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "リンクされたライブラリを持つファイルは、リンクを保ったままエクスポートに含まれます。" -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "ファイルとライブラリをエクスポート" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "インポート時に、名前が一致する既存のライブラリにリンクできます。" -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "インポート時に一致するライブラリをリンク" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "ライブラリのアセットをローカルライブラリに統合してファイルをエクスポートします。" -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "ライブラリのアセットをファイルに埋め込む" @@ -2044,7 +2012,7 @@ msgstr "ライブラリのアセットをファイルに埋め込む" msgid "generic.error" msgstr "エラーが発生しました" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "適用" @@ -2092,7 +2060,7 @@ msgstr "シェイプの塗り画像のプレビュー" msgid "inspect.attributes.shadow" msgstr "シャドウ" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "サイズと位置" @@ -2100,43 +2068,31 @@ msgstr "サイズと位置" msgid "inspect.attributes.stroke" msgstr "線" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "中央" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "内側" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "外側" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "タイポグラフィ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "フォントファミリー" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "フォントサイズ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "フォントスタイル" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "文字間隔" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "行の高さ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "複数のフォントファミリー" @@ -2152,11 +2108,11 @@ msgstr "取り消し線" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "下線" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "先頭を大文字" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "小文字" @@ -2168,7 +2124,7 @@ msgstr "なし" msgid "inspect.attributes.typography.text-transform.unset" msgstr "未設定" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "大文字" @@ -2292,23 +2248,23 @@ msgstr "解決された値:" msgid "inspect.tabs.styles.variants-panel" msgstr "バリアントのプロパティ" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "アクセストークンを作成" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "「アクセストークンを作成」ボタンを押して生成してください。" -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "まだトークンがありません。" -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "個人用アクセストークン" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "個人用アクセストークンは、ログイン/" @@ -2390,47 +2346,47 @@ msgstr "このMCPキーは%sに期限切れになります" msgid "integrations.mcp-key.will-not-expire" msgstr "このMCPキーには有効期限がありません" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "Penpot MCPサーバーを使うと、MCPクライアントがPenpotのデザインファイルを直接操作できます。" -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "MCPクライアントの設定方法" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "これは、MCPクライアントをPenpot MCPサーバーに接続するための設定に必要なサーバーURLです。" -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "MCPキーを再生成" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "MCPキー" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "MCPクライアントの設定にはMCPキーが必要です" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "ステータス" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "無効" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "有効" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "MCPサーバーへの接続に使用するMCPキーの有効期限が切れています。そのため、接続を確立できません。" -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "MCPキーを再生成し、新しいキーでクライアントの設定を更新してください。" @@ -2438,10 +2394,6 @@ msgstr "MCPキーを再生成し、新しいキーでクライアントの設定 msgid "integrations.mcp-server.title" msgstr "MCPサーバー" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "ベータ" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "名前" @@ -2490,7 +2442,7 @@ msgstr "MCPキーを再生成" msgid "integrations.regenerate-mcp-key.title.created" msgstr "MCPキーを再生成しました" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "連携" @@ -2506,47 +2458,47 @@ msgstr "このトークンは%sに期限切れになります" msgid "integrations.token.will-not-expire" msgstr "このトークンには有効期限がありません" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "すべて既読にする" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "ショートカット" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Penpotについて" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "承諾" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "有効" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "追加" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "カスタムフォントを追加" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "追加中…" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "管理者" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "すべて" @@ -2554,15 +2506,15 @@ msgstr "すべて" msgid "labels.and" msgstr "および" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "戻る" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "サーバーの小規模なメンテナンスを実施しています。しばらく待ってから再度お試しください。" -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Bad Gateway" @@ -2574,23 +2526,23 @@ msgstr "ぼかし" msgid "labels.blur-effects" msgstr "ぼかし効果" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "キャンセル" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "アップデートを確認" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "アップデートを確認中…" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "閉じる" @@ -2598,7 +2550,7 @@ msgstr "閉じる" msgid "labels.code" msgstr "コード" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "折りたたむ" @@ -2610,15 +2562,15 @@ msgstr "カラー" msgid "labels.comment" msgstr "コメント" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "コメント" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "コミュニティ" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "コミュニティと貢献" @@ -2630,19 +2582,19 @@ msgstr "計算値" msgid "labels.confirm-password" msgstr "パスワードを確認" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "サポートに問い合わせる" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "お問い合わせ" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "続行" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Penpotアカウントで続行できます" @@ -2654,23 +2606,23 @@ msgstr "コピー" msgid "labels.copy-color" msgstr "カラーをコピー" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "リンクをコピー" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-present" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "作成" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "チームを作成" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "新しいチーム名を入力" @@ -2678,7 +2630,7 @@ msgstr "新しいチーム名を入力" msgid "labels.current" msgstr "現在" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "ダッシュボード" @@ -2686,7 +2638,7 @@ msgstr "ダッシュボード" msgid "labels.default" msgstr "デフォルト" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "削除" @@ -2698,7 +2650,7 @@ msgstr "コメントを削除" msgid "labels.delete-comment-thread" msgstr "スレッドを削除" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "招待を削除" @@ -2706,39 +2658,35 @@ msgstr "招待を削除" msgid "labels.delete-multi-files" msgstr "%s個のファイルを削除" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "プロフィール写真を削除してもよろしいですか?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "プロフィール写真を削除" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "削除済み" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "開発" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "破棄" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "%sをダウンロード" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "ダウンロード" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "ファイルをダウンロード中…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "下書き" @@ -2746,19 +2694,19 @@ msgstr "下書き" msgid "labels.duplicate" msgstr "複製" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "編集" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "編集者" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "空" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "エラー" @@ -2766,11 +2714,11 @@ msgstr "エラー" msgid "labels.exit" msgstr "終了" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "期限切れ" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "エクスポート" @@ -2782,7 +2730,7 @@ msgstr "フィードバックは無効です" msgid "labels.feedback-sent" msgstr "フィードバックを送信しました" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2790,63 +2738,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "塗り" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "フレックスレイアウト" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "フォントファミリー" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "スタイル" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "フォント" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "GitHubリポジトリ" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "フィードバックを送信" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "戻る" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "グラフィックデザイン" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "グリッドレイアウト" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "ヘルプセンター" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "ヘルプと学習" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "非表示" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "解決済みのコメントを非表示" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "インポート" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "無効" @@ -2854,51 +2802,51 @@ msgstr "無効" msgid "labels.info" msgstr "情報" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "インストール済みのフォント" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "連携" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "問題が発生しました。" -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "操作をやり直すか、サポートに連絡してエラーを報告してください。" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "内部エラー" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "招待" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "言語" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "レイアウト" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "ラーニングセンター" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "ライブラリとテンプレート" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "読み込み中…" @@ -2906,7 +2854,7 @@ msgstr "読み込み中…" msgid "labels.lock" msgstr "ロック" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "ログインまたは新規登録" @@ -2914,19 +2862,19 @@ msgstr "ログインまたは新規登録" msgid "labels.login" msgstr "ログイン" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "ログアウト" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "マーケティング" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "メンバー" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "メンバー" @@ -2938,39 +2886,39 @@ msgstr "メンション" msgid "labels.new-password" msgstr "新しいパスワード" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "次へ" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "このインスタンスには、最新バージョンで必要になった設定手順が必要です。" -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "詳しくは" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "Penpotをまだ起動できません。" -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "テクニカルガイドをご覧ください" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "システムの問題により、Penpotは一時的に利用できません。しばらくしてからもう一度お試しください。" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "すべて確認済みです。新しいコメントの通知はここに表示されます。" -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "保留中の招待はありません。" -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "仲間を集めて、一緒に素晴らしいものを作りましょう。" @@ -2978,25 +2926,25 @@ msgstr "仲間を集めて、一緒に素晴らしいものを作りましょう msgid "labels.no-matches" msgstr "一致する項目がありません" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "おっと!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "通知" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "%s個のファイル" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "%s個のボード" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "%s個のプロジェクト" @@ -3005,43 +2953,43 @@ msgstr[0] "%s個のプロジェクト" msgid "labels.old-password" msgstr "現在のパスワード" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "開く" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "オプション" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "その他(具体的に入力)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "その他" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "オーナー" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "パスワード" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "保留中" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Penpot変更履歴" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot Hub" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "ピン留めしたプロジェクト" @@ -3049,31 +2997,31 @@ msgstr "ピン留めしたプロジェクト" msgid "labels.post" msgstr "投稿" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "前へ" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "プロダクトデザインまたはUXデザイン" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "プロダクトマネジメント" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "プロフィール" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "プロジェクト" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "最近" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "参照" @@ -3081,27 +3029,27 @@ msgstr "参照" msgid "labels.refresh" msgstr "再読み込み" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "リリースノート" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "ページを再読み込み" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "削除" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "メンバーを削除" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "名前を変更" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "チーム名を変更" @@ -3125,11 +3073,11 @@ msgstr "件の新しい返信" msgid "labels.reply.thread" msgstr "返信" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "再送信" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "招待を再送信" @@ -3137,43 +3085,43 @@ msgstr "招待を再送信" msgid "labels.restore" msgstr "復元" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "再試行" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "ロール" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "保存" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "検索" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "フォントを検索" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "選択してください" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "送信" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "送信中…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "現在、システムの定期メンテナンス中です。" -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "サービスを利用できません" @@ -3181,71 +3129,71 @@ msgstr "サービスを利用できません" msgid "labels.sets" msgstr "セット" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "設定" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "シャドウ" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "共有" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "ライブラリ" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "表示" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "すべてのコメントを表示" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "コメント一覧を表示" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "自分へのメンションのみ表示" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "自分のコメントのみ表示" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/import.cljs:703 +#: src/app/main/ui/dashboard/import.cljs:808 msgid "labels.skip" msgstr "スキップ" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "リソース" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "組織のIDプロバイダーでのログインが完了しませんでした。プロバイダーが利用できないか、アカウントがまだディレクトリに登録されていない可能性があります。" -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "もう一度試す" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "%sにログインできませんでした" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "開始" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "ステータス" @@ -3253,7 +3201,7 @@ msgstr "ステータス" msgid "labels.stroke" msgstr "線" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "学生または教員" @@ -3265,11 +3213,11 @@ msgstr "スタイル" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "入れ替え" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:657 msgid "labels.team-management" msgstr "チーム管理" @@ -3277,19 +3225,19 @@ msgstr "チーム管理" msgid "labels.text" msgstr "テキスト" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "テーマ" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "チュートリアル" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "タイポグラフィ" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "不明なエラー" @@ -3301,35 +3249,31 @@ msgstr "ロックを解除" msgid "labels.unpublish-multi-files" msgstr "%s個のファイルの公開を停止" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "更新" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "チームを更新" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "アップロード" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "カスタムフォントをアップロード" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "アップロード中…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "ファイルをアップロード中…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "バリアント" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "バージョン%sのリリースノート" @@ -3337,7 +3281,7 @@ msgstr "バージョン%sのリリースノート" msgid "labels.view-only" msgstr "閲覧のみ" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "閲覧者" @@ -3350,7 +3294,7 @@ msgid "labels.warning-count" msgid_plural "labels.warning-count" msgstr[0] "%s件の警告" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhook" @@ -3358,11 +3302,11 @@ msgstr "Webhook" msgid "labels.write-new-comment" msgstr "新しいコメントを入力" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(あなた)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "アカウント" @@ -3494,7 +3438,7 @@ msgstr "「%s」を共有ライブラリとして追加" msgid "modals.before-leave-organization.message" msgstr "あなたが唯一のメンバーであるチームがあります。これらのチームは、プロジェクトやファイルとともに削除されます。" -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "組織を離れる前に" @@ -3502,75 +3446,75 @@ msgstr "組織を離れる前に" msgid "modals.before-leave-organization.warning" msgstr "あなたが唯一のメンバーであるチームはすべて削除されます。" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "大きい移動量" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "新しいメールアドレスを確認" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "本人確認のため、現在のメールアドレス「%s」にメールを送信します。" -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "新しいメールアドレス" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "メールアドレスを変更" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "メールアドレスの変更" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Webhookを作成" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Webhookを作成" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "ペイロードURL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "キャンセルしてアカウントを残す" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "はい、アカウントを削除します" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "アカウントを削除すると、現在のプロジェクトとアーカイブがすべて失われます。" -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "この操作は元に戻せません。アカウント、サブスクリプション、組織はすぐに削除されます。現在のプロジェクトやファイルにもアクセスできなくなります。" -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "削除される組織" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s人のメンバー" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s個のチーム" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "アカウントを削除してもよろしいですか?" @@ -3627,19 +3571,19 @@ msgstr "%s個のファイルを削除してもよろしいですか?" msgid "modals.delete-file-multi-confirm.title" msgstr "%s個のファイルの削除" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "このフォントスタイルを削除してもよろしいですか?ファイルで使用されている場合、読み込まれなくなります。" -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "フォントスタイルの削除" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "このフォントを削除してもよろしいですか?ファイルで使用されている場合、読み込まれなくなります。" -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "フォントの削除" @@ -3647,19 +3591,19 @@ msgstr "フォントの削除" msgid "modals.delete-organization-team-confirm.message" msgstr "組織「%s」に属するこのチームを削除してもよろしいですか?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "このページを削除してもよろしいですか?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "ページを削除" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "選択したページを削除してもよろしいですか?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "ページを削除" @@ -3675,27 +3619,27 @@ msgstr "このプロジェクトを削除してもよろしいですか?" msgid "modals.delete-project-confirm.title" msgstr "プロジェクトを削除" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "ファイルを削除" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "どのファイルでも有効になっていません。" -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "このライブラリは次のファイルで有効になっています: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "選択したファイルを削除してもよろしいですか?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "ファイルの削除" @@ -3712,40 +3656,40 @@ msgstr "このチームを削除してもよろしいですか?チームに関 msgid "modals.delete-team-confirm.title" msgstr "チームの削除" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "メンバーを削除" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "このメンバーをチームから削除してもよろしいですか?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "チームメンバーを削除" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "ファイル内ですでに使用されているアセットはそのまま残ります(デザインが崩れることはありません)。" -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Webhookを削除" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "このWebhookを削除してもよろしいですか?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Webhookの削除" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Webhookを編集" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Webhookを編集" @@ -3761,15 +3705,15 @@ msgstr "トークンをインポート" msgid "modals.import-library-tokens.title" msgstr "ライブラリからトークンをインポートしますか?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "招待を送信" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "メールアドレス(カンマ区切り)" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "一部のメンバーはすでにチームに参加しています。残りのメンバーを招待します。" @@ -3785,87 +3729,87 @@ msgstr "招待は送信されませんでした" msgid "modals.invite-restricted-members.all-organization-members-in-team" msgstr "このチームに招待できるのは組織「%s」のメンバーのみですが、全員がすでにチームのメンバーです。詳しくは組織のオーナーにお問い合わせください。" -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "招待が届かないアドレス" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "キャンセル" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "招待できるのは組織の既存メンバーのみです。" -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "招待を送信" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "一部の招待を送信できません" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "リストの一部の人には招待が届きません。" -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "メンバーをチームに招待すると、このファイルとチームのすべてのファイルにアクセスできるようになります。" -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "チームにメンバーを招待" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "あなたはこのチームの唯一のメンバーのため、チームはプロジェクトやファイルとともに削除されます。" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "チーム「%s」から退出してもよろしいですか?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "オーナーに昇格できる他のメンバーがいないため、チームから退出できません。チームの削除を検討してください。" -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "あなたはこのチームのオーナーです。退出する前に、オーナーに昇格させる別のメンバーを選択してください。" -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "昇格して退出" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "昇格させるメンバーを選択" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "退出する前に" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "チームから退出" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "このチームから退出してもよろしいですか?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "チームからの退出" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "あなたは組織内の一部のチームのオーナーです。別のメンバーをオーナーに昇格させてください。" -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "あなたが唯一のメンバーであるチームがあります。それらのチームはプロジェクトやファイルとともに削除されます。" -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "また、あなたは組織内の一部のチームのオーナーです。別のメンバーをオーナーに昇格させてください。" @@ -3881,50 +3825,50 @@ msgstr "組織内のすべてのチーム、プロジェクト、ファイルへ msgid "modals.leave-organization-confirm.title" msgstr "組織「%s」から退出しますか?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "移動" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "ライブラリを移動してもよろしいですか?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "ライブラリを移動" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "矢印キーでの移動量" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "オーナー権限を譲渡" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "オーナー権限を譲渡すると、あなたのロールは管理者に変更され、このチームに対する一部の権限を失います。 " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "あなたは現在このチームのオーナーです。%sをチームの新しいオーナーにしてもよろしいですか?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "チームの新しいオーナー" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "公開" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "ライブラリが空です。公開してもよろしいですか?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "空のライブラリを公開" @@ -3944,21 +3888,21 @@ msgstr "チーム「%s」を組織「%s」から削除してもよろしいで msgid "modals.remove-team-organization.title" msgstr "組織からチームを削除" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "小さい移動量" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "公開を停止" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "ライブラリの公開を停止してもよろしいですか?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "ライブラリの公開を停止" @@ -4043,31 +3987,31 @@ msgstr "有効化" msgid "nitrate.code-activation.title" msgstr "Enterpriseを有効化" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "営業担当に問い合わせる" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "このプランに切り替えるには、営業チームにお問い合わせください。サブスクリプションの変更と、すべてが正しく設定されるようサポートいたします。" -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "組織は削除されます。" -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "ストレージの総容量、自動バージョン履歴、ファイルの復元期間が制限されます。" -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "チーム、プロジェクト、ファイルはどの組織にも属さなくなりますが、引き続き利用できます。" -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "ダウングレードすると:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "%sプランに切り替えますか?" @@ -4083,15 +4027,15 @@ msgstr "Enterpriseを14日間お試しいただくには、お問い合わせく msgid "nitrate.form.contact-upgrade" msgstr "Enterpriseへのアップグレードについては、お問い合わせください:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "Enterpriseプランを14日間お試しいただくには、お問い合わせください:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "Enterpriseプランへのアップグレードについては、お問い合わせください:" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "今すぐ請求されることはありません。お支払いはトライアル終了時に処理されます。いつでもキャンセルできます。" @@ -4107,7 +4051,7 @@ msgstr "専用の管理コンソールで、誰がどこで何をするかを一 msgid "nitrate.form.enterprise-feature-3" msgstr "オープンソースの自由はそのままに、組織に必要なガバナンスを" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "チームが大規模に安全に作業するために必要なものがすべて揃っています%s" @@ -4115,7 +4059,7 @@ msgstr "チームが大規模に安全に作業するために必要なものが msgid "nitrate.form.enterprise.price" msgstr "メンバー1人あたり月額" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "14日間無料で試す" @@ -4131,7 +4075,7 @@ msgstr "Enterpriseを始める" msgid "nitrate.form.subscribe-with-code" msgstr "アクティベーションコードで登録" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "Enterpriseの機能を利用する" @@ -4143,7 +4087,7 @@ msgstr "14日間無料で試す" msgid "nitrate.modal-success.title" msgstr "Enterpriseへようこそ!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "%sまで有効" @@ -4151,23 +4095,23 @@ msgstr "%sまで有効" msgid "nitrate.subscription.renew-success" msgstr "Enterpriseサブスクリプションを更新しました。" -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "サブスクリプションをキャンセル" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "Enterpriseサブスクリプションをキャンセルするには、次の連絡先までお問い合わせください:" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "アクティベーションコードで更新" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "このページは存在しません" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404エラー" @@ -4183,11 +4127,11 @@ msgstr "パスワード再設定用のメールを次のアドレスに送信し msgid "not-found.login.sent-recovery-check" msgstr "メールを確認し、リンクをクリックして新しいパスワードを作成してください。" -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "無料で新規登録" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "数秒でPenpotを使い始められます!" @@ -4195,63 +4139,63 @@ msgstr "数秒でPenpotを使い始められます!" msgid "not-found.made-with-love" msgstr "LOVEとオープンソースで作られています" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "このファイルへのアクセスはすでにリクエスト済みです。" -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "このファイル、またはこのチームの他のファイルやプロジェクトへのアクセスはすでにリクエスト済みです。" -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "このプロジェクト、またはこのチームの他のプロジェクトやファイルへのアクセスはすでにリクエスト済みです。" -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "このプロジェクトへのアクセスはすでにリクエスト済みです。" -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "アクセスをリクエスト" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "オーナーが許可すると、チームに招待されます。" -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "リクエストを送信しました!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "このファイルへのアクセス権がありません。" -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "個人プロジェクトへ移動" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "オーナーが許可すると、チームに招待されます。" -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpotファイル" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "このプロジェクトへのアクセス権がありません。" -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "プロジェクト" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "このファイルにアクセスするには、チームのオーナーに依頼してください。" -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "このプロジェクトにアクセスするには、チームのオーナーに依頼してください。" @@ -4263,31 +4207,31 @@ msgstr "メンテナンスのお知らせ:5分以内に短時間のメンテ msgid "notifications.by-code.upgrade-version" msgstr "新しいバージョンが利用可能です。" -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "この招待は無効になりました。" -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "招待を削除しました" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "招待を送信しました" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "招待リンクをコピーしました" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "プロフィールを削除できません。先にチームのオーナーを他のメンバーに割り当ててください。" -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "プロフィールを保存しました!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "%sに確認メールを送信しました。メールを確認してください!" @@ -4323,7 +4267,7 @@ msgstr "チーム名" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "チームを作成" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "メンバーを招待" @@ -4331,97 +4275,97 @@ msgstr "メンバーを招待" msgid "onboarding.choice.team-up.invite-members-desc" msgstr "開発者、デザイナー、マネージャーなど、誰でも歓迎です。後から追加で招待することもできます。" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "付与するロール:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "さあ、始めましょう!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "デザインから実際のコードを生成" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "デザインシステムの作成" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "プロトタイピング" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "アプリのUI/UXデザイン" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "ワイヤーフレーム作成" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Penpotを何に使用しますか?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Penpotをより使いやすくするために、あなたについて少し教えてください。いただいた回答は、新機能の優先順位付けや、スムーズに使い始めるためのご案内に役" "立てます。" -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "あなたについて教えてください" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "最もよく使用しているツールはどれですか?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "どのようなお仕事をされていますか?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "会社の規模はどのくらいですか?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "どこから始めますか?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1,001〜5,000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101〜500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2〜100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501〜1,000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "自分だけ" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5,001以上" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "教育" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "個人" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "仕事" @@ -4501,11 +4445,11 @@ msgstr "デフォルト設定に戻す" msgid "restore-shortcuts.restore" msgstr "復元" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "切り離す" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "混在" @@ -4517,35 +4461,35 @@ msgstr "カラーを削除" msgid "settings.select-this-color" msgstr "このスタイルを使用している要素を選択" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "コメント" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "下揃え" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "水平方向の中央揃え" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "左揃え" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "右揃え" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "上揃え" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "垂直方向の中央揃え" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "選択範囲からボードを作成" @@ -4553,55 +4497,55 @@ msgstr "選択範囲からボードを作成" msgid "shortcuts.bold" msgstr "太字の切り替え" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "ブーリアン:型抜き" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "ブーリアン:除外" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "ブーリアン:交差" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "ブーリアン:合体" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "最背面へ移動" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "背面へ移動" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "前面へ移動" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "最前面へ移動" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "元に戻す履歴を消去" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "コピー" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "リンクをコピー" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "プロパティをコピー" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "コンポーネント/バリアントを作成" @@ -4609,63 +4553,63 @@ msgstr "コンポーネント/バリアントを作成" msgid "shortcuts.create-new-project" msgstr "新規作成" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "切り取り" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "ズームアウト" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "削除" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "ノードを削除" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "ノードとセグメントを削除" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "すべての選択を解除" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "コンポーネントを切り離す" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "ショートカットを無効化" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "無効" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "無効なショートカット" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "矢印" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "曲線" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "楕円" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "ボード" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "直線" @@ -4673,63 +4617,63 @@ msgstr "直線" msgid "shortcuts.draw-nodes" msgstr "パスを描画" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "パス" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "長方形" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "テキスト" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "複製" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "このキーの組み合わせは「%s」に割り当てられています。保存すると、そちらの割り当ては削除されます。" -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "保存すると、このショートカットは元の値に戻ります:" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "このキーの組み合わせは使用可能で、競合なく割り当てられます。" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "設定でカスタマイズ" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "キャンセル" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "シェイプをエクスポート" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "検索" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "検索と置換" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "全体を表示" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "左右反転" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "上下反転" @@ -4753,27 +4697,27 @@ msgstr "共有ライブラリへ移動" msgid "shortcuts.go-to-search" msgstr "検索" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "グループ化" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "水平方向に等間隔に分布" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "UIの表示/非表示" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "インポート/エクスポート" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "ズームイン" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "画像を挿入" @@ -4781,11 +4725,11 @@ msgstr "画像を挿入" msgid "shortcuts.italic" msgstr "斜体の切り替え" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "ノードを連結" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "キーの組み合わせを押してください" @@ -4793,39 +4737,39 @@ msgstr "キーの組み合わせを押してください" msgid "shortcuts.line-through" msgstr "取り消し線の切り替え" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "コーナーにする" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "曲線にする" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "マスク" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "ノードを結合" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "下へ大きく移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "左へ大きく移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "右へ大きく移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "上へ大きく移動" @@ -4833,377 +4777,377 @@ msgstr "上へ大きく移動" msgid "shortcuts.move-nodes" msgstr "ノードを移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "下へ移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "左へ移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "右へ移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "上へ移動" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "次のボード" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "無効なショートカットはありません" -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "「すべて」からショートカットのカスタマイズを始めましょう。" -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "検索に一致するショートカットはありません" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "ショートカットが見つかりません" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "不透明度を100%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "不透明度を10%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "不透明度を20%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "不透明度を30%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "不透明度を40%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "不透明度を50%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "不透明度を60%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "不透明度を70%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "不透明度を80%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "不透明度を90%に設定" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "カラーピッカー" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "ビューアーのコメントへ移動" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "ダッシュボードへ移動" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "ビューアーのインスペクトへ移動" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "ビューアーのインタラクションへ移動" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "ビューアーのインタラクションへ移動" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "ワークスペースへ移動" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " または " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "このショートカットは%sに再割り当てされました" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "ショートカットページ" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "貼り付け" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "プロパティを貼り付け" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "貼り付けて置換" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "カスタマイズ済み" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "プラグインマネージャー" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "前のボード" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "やり直し" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "ショートカットを編集したら、開いている他のPenpotタブを再読み込みして変更を反映してください。" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "名前を変更" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "デフォルトに戻す" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "ズームをリセット" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "拡大縮小" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "基本" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "ダッシュボード" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "ビューアー" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "ワークスペース" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "すべて選択" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "次のレイヤーを選択" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "親レイヤーを選択" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "前のレイヤーを選択" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "ノードを分離" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "寸法を表示" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "ピクセルグリッドの表示/非表示" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "ショートカットの表示/非表示" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "ノードにスナップ" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "ピクセルグリッドにスナップ" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "編集を開始" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "整列" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "編集" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "一般" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "メインメニュー" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "レイヤーの変更" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "ナビゲーション" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "ナビゲーション" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "ナビゲーション" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "パネル" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "パス" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "シェイプ" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "テキスト" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "ツール" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "ズーム" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "ズーム" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "サムネイルに設定" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "キーボードショートカット" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "動的な整列の切り替え" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "アセットの切り替え" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "カラーパレットの切り替え" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "コメントの表示切り替え" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "フォーカスモードの切り替え" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "フルスクリーンの切り替え" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "ガイドの表示/非表示" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "履歴の切り替え" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "レイヤーの切り替え" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "フレックスレイアウトを追加/削除" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "グリッドレイアウトを追加/削除" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "ロック/ロック解除" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "縦横比を固定" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "定規の表示/非表示" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "ガイドにスナップ" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "定規ガイドにスナップ" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "テキストパレットの切り替え" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "テーマを変更" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "表示/非表示" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "ズーム方式の切り替え" @@ -5211,31 +5155,31 @@ msgstr "ズーム方式の切り替え" msgid "shortcuts.underline" msgstr "下線の切り替え" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "元に戻す" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "グループ解除" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "マスクを解除" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "垂直方向に等間隔に分布" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "ズームレンズで縮小" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "ズームレンズで拡大" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "選択範囲にズーム" @@ -5253,7 +5197,7 @@ msgstr "Enterpriseを体験" msgid "subscription.current-plan.enterprise" msgstr "Enterprise" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Enterprise" @@ -5265,7 +5209,7 @@ msgstr "Enterprise(トライアル)" msgid "subscription.current-plan.professional" msgstr "Professional" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "ご利用中のサブスクリプション" @@ -5379,261 +5323,261 @@ msgstr "今後の請求対象になるのは、所有するチーム全体で新 msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Unlimitedプランでのメンバー招待" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "支払いが完了しませんでした。準備ができたら、もう一度お試しください。" -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "支払いが完了しませんでした。もう一度お試しください。問題が解決しない場合は、support@penpot.app までお問い合わせください。" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "サブスクリプション" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "アクティベーションコードを入力" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "トライアル終了後も続けるには、支払い方法を追加してください" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "管理コンソール" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Professionalプランのすべての特典に加えて:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Unlimitedプランのすべての特典に加えて:" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "Unlimitedプランのすべての特典に加えて:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(編集者%s人分)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise(トライアル)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90日間の自動保存されたバージョンとファイルの復元" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "月額定額料金" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "高度な管理:" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "エンタープライズ向けセキュリティ:" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "複数組織の管理:" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "ネイティブのSAML/SSOと、コンプライアンス報告のための完全な監査ログ。" -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "きめ細かなプラグインの許可リスト。AI連携のための安全で隔離されたMCPサーバーも含みます。" -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "複雑なチーム階層と独立したサブ組織に対応。" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "ストレージ無制限" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "サブスクリプションを管理" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "支払い情報を追加" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "今のうちに支払い情報を追加しておけば、トライアル終了後もサブスクリプションをスムーズに継続でき、オープンソースプロジェクトの支援も続けられます。まだ料金" "は発生しません。" -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "今はスキップしてトライアルを開始" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Penpotの成長を支え、トライアルをもっと手軽に" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "現在、チーム全体で編集できるメンバーは%s人です。" -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "ご注意:下位のプランに切り替えると、ストレージが減り、バックアップとバージョン履歴の保存期間も短くなります。" -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "編集者" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(オーナー、管理者、編集者が対象です。閲覧者は編集者に含まれません)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "現在の人数より少ない編集者数には設定できません。実際にファイルを編集しないメンバーは、チーム設定でロールを変更してください(編集者/" "管理者から閲覧者へ)。" -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "料金はトライアル終了後に請求されます。現時点ではクレジットカードは不要です。" -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/月" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "%sをチームに適用" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "ヒント:招待に備えて、今のうちにシート数を増やしておけます。チーム全体で編集者が25人以上になると、月額$175の定額料金になります。" -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Penpotメンバー登録日:%s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "詳細情報" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "組織メンバー/月" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Penpotのその他のプラン" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "組織あたり/月" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "ユーザー/月" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professional" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7日間の自動保存されたバージョンとファイルの復元" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "すべてのデザイン・プロトタイプ機能。コミュニティサポート。" -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "データとインフラを完全に管理できます。" -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "ユーザー、チーム、ファイルが無制限。" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10GBのストレージ" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "チーム数無制限。所有するチーム全体で最大8人の編集者。" -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "おすすめ" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "ご利用中のサブスクリプション" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "無料トライアルを開始" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "登録する" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "%sプランを%sからご利用中です。" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "サブスクリプションは、アカウント情報の「サブスクリプション」ページからいつでも変更できます。" -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "プランをお楽しみください!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Penpot %sプランをお選びいただき、ありがとうございます!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "%sプランになりました!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "14日間無料でお試し" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Unlimited" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unlimited(トライアル)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30日間の自動保存バージョンとファイル復旧" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "月額料金の上限は$175" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GBのストレージ" @@ -5656,44 +5600,44 @@ msgstr "この上限を引き上げたい場合は、チームのオーナーに msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "この上限を引き上げたい場合は、[プランをアップグレード|target:self](%s)してください" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "%s件の招待を選択中" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "フォントプロバイダー - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "フォント - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "プロジェクト - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "検索 - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "共有ライブラリ - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot | フルスタックデザイン" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "フィードバックを送る - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "連携 - Penpot" @@ -5701,7 +5645,7 @@ msgstr "連携 - Penpot" msgid "title.settings.notifications" msgstr "通知 - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "設定 - Penpot" @@ -5709,27 +5653,27 @@ msgstr "設定 - Penpot" msgid "title.settings.password" msgstr "パスワード - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "プロフィール - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "招待 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "メンバー - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "設定 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhook - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - 表示モード - Penpot" @@ -5741,51 +5685,51 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "トークンを切り離す" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "このページにボードがありません。" -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "ボードが見つかりません。" -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "コメント(%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "インタラクションを表示しない" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "フルスクリーン" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "インスペクト(%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "インタラクション" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "インタラクション(%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "リンクをコピー" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "インタラクションを表示" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "クリック時にインタラクションを表示" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "サイトマップ" @@ -5819,67 +5763,67 @@ msgstr "詳しくは、" msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "WebGLトラブルシューティングガイド" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "WebGL レンダリングを無効にしました" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "WebGL レンダリングを有効にしました" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "WebGLコンテキストが失われました" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "WebGLコンテキストが復旧しました" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "前回の配信は成功しました。" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "水平方向中央揃え(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "水平方向に等間隔に分布(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "左揃え(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "右揃え(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "下揃え(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "垂直方向中央揃え(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "垂直方向に等間隔に分布(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "上揃え(%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "ライブラリを追加" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "すべてのアセット" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "カラー" @@ -5891,7 +5835,7 @@ msgstr "カラーを追加" msgid "workspace.assets.component-group-options" msgstr "コンポーネントグループのオプション" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "コンポーネント" @@ -5899,7 +5843,7 @@ msgstr "コンポーネント" msgid "workspace.assets.components.add-component" msgstr "コンポーネントを追加" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s個のバリアント" @@ -5907,11 +5851,11 @@ msgstr "%s個のバリアント" msgid "workspace.assets.create-group" msgstr "グループを作成" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "アイテムには「グループ名 / アイテム名」の形式で自動的に名前が付けられます" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "削除" @@ -5919,11 +5863,11 @@ msgstr "削除" msgid "workspace.assets.delete-group" msgstr "グループを削除" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "ページを削除" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "複製" @@ -5931,39 +5875,39 @@ msgstr "複製" msgid "workspace.assets.duplicate-main" msgstr "メインコンポーネントを複製" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "編集" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "フィルター" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "グリッド表示" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "グループ化" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "グループ名" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "リスト表示" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "ローカルライブラリ" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "ライブラリを管理" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "アセットが見つかりません" @@ -5971,7 +5915,7 @@ msgstr "アセットが見つかりません" msgid "workspace.assets.open-library" msgstr "ライブラリファイルを開く" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "名前を変更" @@ -5979,11 +5923,11 @@ msgstr "名前を変更" msgid "workspace.assets.rename-group" msgstr "グループ名を変更" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "アセットを検索" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "共有ライブラリ" @@ -5992,43 +5936,43 @@ msgid "workspace.assets.sidebar.components" msgid_plural "workspace.assets.sidebar.components" msgstr[0] "%s個のコンポーネント" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "並べ替え" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "タイポグラフィ" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "タイポグラフィを追加" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "サイズ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "フォントスタイル" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "編集するにはスタイルライブラリのファイルを開いてください" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "文字間隔" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "行の高さ" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "大文字小文字" @@ -6036,15 +5980,15 @@ msgstr "大文字小文字" msgid "workspace.assets.ungroup" msgstr "グループ解除" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "画像をコピー中…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "画像をクリップボードにコピーしました" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "画像のコピー中にエラーが発生しました" @@ -6056,83 +6000,83 @@ msgstr "画面から色を抽出" msgid "workspace.colorpicker.color-tokens" msgstr "カラートークン" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "コンポーネントを自分自身の中に入れ子にすることはできません。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "一部のコピーを切り替えられませんでした。コンポーネントを自分自身の中に入れ子にすることはできません。" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "エリアを作成" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "列をコピー" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "行をコピー" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "ボードを作成" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "セルを結合" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "貼り付け" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "右に1列追加" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "左に1列追加" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "列を削除" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "列とシェイプを削除" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "列を複製" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "下に1行追加" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "上に1行追加" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "行を削除" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "行とシェイプを削除" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "行を複製" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "ガイドの色" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "ガイドを削除" @@ -6140,15 +6084,15 @@ msgstr "ガイドを削除" msgid "workspace.debug.title" msgstr "デバッグツール" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "フォーカスモード" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "フォーカスモードをオフ" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "フォーカスモードをオン" @@ -6160,247 +6104,247 @@ msgstr "線形グラデーション" msgid "workspace.gradients.radial" msgstr "放射状グラデーション" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "動的整列を無効化" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "比率を保った拡大縮小を無効化" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "ガイドへのスナップを無効化" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "ピクセルへのスナップを無効化" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "定規ガイドへのスナップを無効化" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "WebGL レンダリングを無効化(ベータ)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "動的整列を有効化" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "比率を保った拡大縮小を有効化" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "ガイドにスナップ" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "ピクセルへのスナップを有効化" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "定規ガイドにスナップ" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "WebGL レンダリングを有効化(ベータ)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "検索" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "検索と置換" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "ボード名を非表示" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "コメントを非表示" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "ガイドを非表示" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "カラーパレットを非表示" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "ピクセルグリッドを非表示" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "定規を非表示" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "フォントパレットを非表示" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "ガイドをロック" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "接続" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "切断" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "管理(ステータス:無効)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "管理(ステータス:有効)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "編集" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "ファイル" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "ヘルプと情報" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "MCPサーバー" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "環境設定" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "表示" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "やり直し" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "すべて選択" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "ボード名を表示" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "コメントを表示" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "ガイドを表示" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "カラーパレットを表示" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "ピクセルグリッドを表示" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "定規を表示" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "フォントパレットを表示" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "ダークテーマに切り替え" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "ライトテーマに切り替え" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "システムテーマに切り替え" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "元に戻す" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "ガイドのロックを解除" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "リセット" -#: src/app/main/ui/workspace/left_header.cljs:125 +#: src/app/main/ui/workspace/left_header.cljs:133 msgid "workspace.header.retrying" msgstr "保存を再試行中" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "保存エラー" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "保存済み" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "保存中" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "共有" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "表示モード(%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "ズーム" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "フィル - 画面いっぱいに拡大" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "フィット - 画面に収まるよう縮小" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "全体を表示" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "フルスクリーン" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "選択範囲にズーム" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "グリッドを編集" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "終了" @@ -6408,7 +6352,7 @@ msgstr "終了" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "下パディング" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "4辺のパディングオプションを表示" @@ -6444,36 +6388,36 @@ msgstr "完了" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "位置を表示" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "グリッドレイアウトの位置を表示" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "%s色" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "カラーをライブラリに追加" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "ライブラリにはまだカラースタイルがありません" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "最近使用したカラーはまだありません" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "ライブラリにはまだタイポグラフィスタイルがありません" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "ファイルライブラリ" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "最近使用したカラー" @@ -6485,97 +6429,97 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "カラースタイルを保存" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "カラーパレットの表示切り替え" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "%s個のコンポーネント" -#: src/app/main/ui/workspace/libraries.cljs:842 +#: src/app/main/ui/workspace/libraries.cljs:864 msgid "workspace.libraries.connected-libraries" msgstr "接続済みライブラリ" -#: src/app/main/ui/workspace/libraries.cljs:183 +#: src/app/main/ui/workspace/libraries.cljs:235 msgid "workspace.libraries.connected-through" msgstr "経由:" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "接続先:" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "または、以下のライブラリを追加して試してみましょう:" -#: src/app/main/ui/workspace/libraries.cljs:812 +#: src/app/main/ui/workspace/libraries.cljs:834 msgid "workspace.libraries.empty.no-assets" msgstr "アセットはまだありません" -#: src/app/main/ui/workspace/libraries.cljs:895 +#: src/app/main/ui/workspace/libraries.cljs:918 msgid "workspace.libraries.empty.no-connected-libraries" msgstr "接続済みのライブラリはまだありません。" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "チームに共有ライブラリがありません。" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "こちらでテンプレートを探せます" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "ファイルライブラリ" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "%s個のグラフィック" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "このファイルのライブラリ" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "ライブラリ" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "ライブラリの更新" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "読み込み中…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "更新が必要な共有ライブラリはありません" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "「%s」に一致する結果は見つかりませんでした" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "共有ライブラリを検索" -#: src/app/main/ui/workspace/libraries.cljs:827, src/app/main/ui/workspace/libraries.cljs:874 +#: src/app/main/ui/workspace/libraries.cljs:849, src/app/main/ui/workspace/libraries.cljs:897 msgid "workspace.libraries.set-as-tokens-source" msgstr "トークンのソースに設定" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "共有ライブラリ" -#: src/app/main/ui/workspace/libraries.cljs:621 +#: src/app/main/ui/workspace/libraries.cljs:674 msgid "workspace.libraries.shared-library-added" msgstr "追加済み" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "ライブラリを接続" @@ -6599,43 +6543,43 @@ msgstr "すべてのアセットのリンクを解除" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "すべてのトークンを切り離す" -#: src/app/main/ui/workspace/libraries.cljs:167, src/app/main/ui/workspace/libraries.cljs:211 +#: src/app/main/ui/workspace/libraries.cljs:219, src/app/main/ui/workspace/libraries.cljs:263 msgid "workspace.libraries.token-sets" msgid_plural "workspace.libraries.token-sets" msgstr[0] "%s個のセット" -#: src/app/main/ui/workspace/libraries.cljs:170, src/app/main/ui/workspace/libraries.cljs:215 +#: src/app/main/ui/workspace/libraries.cljs:222, src/app/main/ui/workspace/libraries.cljs:267 msgid "workspace.libraries.token-themes" msgid_plural "workspace.libraries.token-themes" msgstr[0] "%s個のテーマ" -#: src/app/main/ui/workspace/libraries.cljs:164, src/app/main/ui/workspace/libraries.cljs:207 +#: src/app/main/ui/workspace/libraries.cljs:216, src/app/main/ui/workspace/libraries.cljs:259 msgid "workspace.libraries.tokens" msgid_plural "workspace.libraries.tokens" msgstr[0] "%s個のトークン" -#: src/app/main/ui/workspace/libraries.cljs:820, src/app/main/ui/workspace/libraries.cljs:866 +#: src/app/main/ui/workspace/libraries.cljs:842, src/app/main/ui/workspace/libraries.cljs:889 msgid "workspace.libraries.tokens-source" msgstr "トークンのソース" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "%s個のタイポグラフィ" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "ライブラリの接続を解除" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "更新" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "すべての変更を表示" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "更新" @@ -6695,7 +6639,7 @@ msgstr "ぼかしの切り替え" msgid "workspace.options.blur-options.toggle-more-options" msgstr "その他のオプションを表示/非表示" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "キャンセル" @@ -6703,107 +6647,107 @@ msgstr "キャンセル" msgid "workspace.options.canvas-background" msgstr "キャンバスの背景" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "コンテンツをクリップ" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "コンポーネント" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "注釈" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "コピー" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "注釈を作成" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "注釈を編集" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "メイン" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "コンポーネントを入れ替え" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "このライブラリにはまだアセットがありません" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "リンクなし" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "競合しているバリアントを表示" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "このコンポーネントには競合しているバリアントがあります。各バリアントのプロパティ値の組み合わせが一意になるようにしてください。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "重複しているバリアントを表示" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "プロパティと値が同じバリアントがあります" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "これらのバリアントはプロパティと値が同じです。取得できるように値を調整してください。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "このバリアントは、別のバリアントとプロパティと値が同じです。取得できるように値を調整してください。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "これらのバリアントの一部はプロパティと値が同じです。取得できるように値を調整してください。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "このコンポーネントには無効な名前のバリアントがあります。すべてのバリアントが正しい構造に従っているか確認してください。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "無効なバリアントを表示" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "無効な名前のバリアントがあります" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "無効なバリアントを表示" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "これらのバリアントの名前は無効です。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "このバリアントの名前は無効です。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "これらのバリアントの一部は名前が無効です。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[プロパティ]=[値], [プロパティ]=[値]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "次の構造を使ってみてください:" @@ -6835,7 +6779,7 @@ msgstr "階層レベルが同じである。" msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "グループ、ボード、レイアウトは同等とみなされます。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "バリアントの接続を保つしくみ" @@ -6883,11 +6827,11 @@ msgstr "上下" msgid "workspace.options.convert-to-typography" msgstr "タイポグラフィスタイルを作成" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "デザイン" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "エクスポート" @@ -6895,52 +6839,52 @@ msgstr "エクスポート" msgid "workspace.options.export-multiple" msgstr "選択範囲をエクスポート" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "%s個の要素をエクスポート" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "エクスポートを追加" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "エクスポートを削除" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "接尾辞" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "エクスポートサービスが混み合っています。しばらくしてからもう一度お試しください" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "エクスポートをキャンセルしました" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "キャンセル中…" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "エクスポートが完了しました" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "エクスポート中…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "エクスポートに失敗しました" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "エクスポートに予想以上に時間がかかっています" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "待機中…" @@ -6960,7 +6904,7 @@ msgstr "塗りを削除" msgid "workspace.options.fill.section" msgstr "塗りセクション" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "ボードをコンテンツに合わせてサイズ変更" @@ -6980,7 +6924,7 @@ msgstr "フローの開始点" msgid "workspace.options.flows.flow-starts" msgstr "フローの開始点" -#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:300, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 msgid "workspace.options.font-not-available" msgstr "このテキストで使用されているフォント(%s)は利用できなくなりました。タイポグラフィスタイルを作成するには、フォントを再アップロードしてください" @@ -6988,67 +6932,67 @@ msgstr "このテキストで使用されているフォント(%s)は利用 msgid "workspace.options.grid.auto" msgstr "自動" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "列" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "カラー" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "ガター" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "高さ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "マージン" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "デフォルトに設定" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "下" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "中央" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "左" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "右" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "引き伸ばし" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "上" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "デフォルトを使用" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "幅" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "行" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "正方形" @@ -7060,7 +7004,7 @@ msgstr "グループの塗り" msgid "workspace.options.group-stroke" msgstr "グループの線" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "ガイドを追加" @@ -7068,19 +7012,19 @@ msgstr "ガイドを追加" msgid "workspace.options.guides.remove-guide" msgstr "ガイドを削除" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "ガイド" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "ガイドの表示切り替え" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "高さ" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "インスペクト" @@ -7296,111 +7240,111 @@ msgstr "インタラクション" msgid "workspace.options.interactions.add-interaction" msgstr "インタラクションを追加" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "カラー" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "焼き込みカラー" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "覆い焼きカラー" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "比較(暗)" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "差の絶対値" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "除外" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "ハードライト" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "色相" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "比較(明)" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "輝度" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "乗算" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "通常" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "オーバーレイ" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "彩度" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "スクリーン" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "ソフトライト" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "レイヤーメニューセクション" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "レイヤーの表示切り替え" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "最大の高さ" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "最大幅" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "最小の高さ" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "最小幅" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "その他のカラー" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "その他のライブラリカラー" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "その他のカラートークン" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "一致するサイズプリセットがありません" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "不透明度" @@ -7408,7 +7352,7 @@ msgstr "不透明度" msgid "workspace.options.pixel-grid-color" msgstr "ピクセルグリッドの色" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "プロトタイプ" @@ -7444,23 +7388,23 @@ msgstr "角の半径セクション" msgid "workspace.options.radius.show-single-corners" msgstr "角ごとの半径を表示" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "最近使用したフォント" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "再試行" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "回転" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "フォントを検索" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "サイズプリセットを検索" @@ -7468,11 +7412,11 @@ msgstr "サイズプリセットを検索" msgid "workspace.options.select-a-shape" msgstr "シェイプ、ボード、またはグループを選択し、ドラッグして他のボードへの接続を作成してください。" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "選択中の色" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "カラー選択セクション" @@ -7540,55 +7484,55 @@ msgstr "シャドウの表示を切り替え" msgid "workspace.options.show-fill-on-export" msgstr "エクスポートに表示" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "表示モードで表示" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "サイズ" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "サイズプリセット" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "比率を固定" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "比率の固定を解除" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "円" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "ひし形" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "矢印" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "なし" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "丸型" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "角型" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "四角形" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "三角形" @@ -7596,31 +7540,31 @@ msgstr "三角形" msgid "workspace.options.stroke-color" msgstr "線の色" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "ダッシュ" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "間隔" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "線の幅" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "下(ブロック終端)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "左(インライン始端)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "右(インライン終端)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "上(ブロック始端)" @@ -7628,47 +7572,47 @@ msgstr "上(ブロック始端)" msgid "workspace.options.stroke.add-stroke" msgstr "線の色を追加" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "中央" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "破線" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "点線" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "内側" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "混在" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "外側" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "辺ごとの線" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "辺ごとの線は新しいレンダリングでのみ利用できます。使用するにはレンダリングを切り替えてください。" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "線を削除" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "実線" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "線の表示を切り替え" @@ -7704,11 +7648,11 @@ msgstr "幅の自動調整" msgid "workspace.options.text-options.grow-fixed" msgstr "固定" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "文字間隔" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "行の高さ" @@ -7756,222 +7700,222 @@ msgstr "下線(%s)" msgid "workspace.options.use-play-button" msgstr "ヘッダーの再生ボタンを使ってプロトタイプを表示してください。" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "幅" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X軸" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y軸" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "ノードを描画(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "角度を揃える" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "独立" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "角度と長さを揃える" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "ハンドルの動作" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "ノードを接続(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "コーナーに変換(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "カーブに変換(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "ノードを結合(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "ノードを移動(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "ノードを分離(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "ノードにスナップ(%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "開く" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "[他のプラグイン](%s)を探す" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "インストール済みのプラグインはまだありません" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "プラグインのマニフェストが正しくありません。" -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "このプラグインを使用するには編集者である必要があります" -#: src/app/main/data/plugins.cljs:158 +#: src/app/main/data/plugins.cljs:160 msgid "workspace.plugins.error.unreachable" msgstr "プラグインのURLにアクセスできませんでした。" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "プラグインが存在しないか、URLが正しくありません。" -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "インストール" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "インストール済みのプラグイン" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "プラグインマネージャー" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "プラグイン" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "このプラグインを更新" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "前回開いたときからプラグインが変更されています。新たに次へのアクセスを求めています:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "ファイルのダウンロードを開始する。" -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "ブラウザにデータを保存する。" -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "クリップボードの内容を読み取る。" -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "クリップボードの読み取りと書き込みを行う。" -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "あなたのコメントと返信を読み取る。" -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "あなたのコメントの読み取りと変更、およびあなたの名前での返信を行う。" -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "ユーザーがアクセスできるファイルの内容を読み取る。" -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "ユーザーがアクセスできるファイルの内容を読み取り、変更する。" -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "このプラグインは外部の開発者によって作成されています。アクセスを許可する前に、信頼できるプラグインであることを確認してください。私たちはお客様のデータの" "プライバシーとセキュリティを重視しています。ご不明な点があれば、サポートまでお問い合わせください。" -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "あなたのライブラリとアセットを読み取る。" -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "あなたのライブラリとアセットを読み取り、変更する。" -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "プラグイン「%s」が次へのアクセスを求めています:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "現在のユーザーのプロフィール情報を読み取る。" -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "プラグイン一覧" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "プラグインを削除" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "プラグインのURLを入力" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "プラグイン" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "後で" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "試してみますか?現在のチームの新しい下書きで開きます。(試さない場合も、どのファイルでもインストール済みのプラグインからいつでも開けます。)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "プラグイン「%s」があなたのユーザーにインストールされました!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "プラグインを試す" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "フレックスレイアウトを追加" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "グリッドレイアウトを追加" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "レイアウトを追加" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "バリアントを作成" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "プロパティを追加" @@ -7983,11 +7927,11 @@ msgstr "最背面へ移動" msgid "workspace.shape.menu.backward" msgstr "背面へ移動" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "ボードのガイドをクリア" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "バリアントとして結合" @@ -7995,7 +7939,7 @@ msgstr "バリアントとして結合" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "コンポーネントは同じページにある必要があります" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "コピー" @@ -8039,27 +7983,27 @@ msgstr "テキストとしてコピー" msgid "workspace.shape.menu.create-annotation" msgstr "注釈を作成" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "選択範囲をボードに変換" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "コンポーネントを作成" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "複数のコンポーネントを作成" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "切り取り" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "削除" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "フローの開始点を削除" @@ -8071,7 +8015,7 @@ msgstr "コピーを切り離す" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "コピーを切り離す" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "型抜き" @@ -8079,15 +8023,15 @@ msgstr "型抜き" msgid "workspace.shape.menu.duplicate" msgstr "複製" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "編集" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "除外" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "平坦化" @@ -8099,7 +8043,7 @@ msgstr "左右反転" msgid "workspace.shape.menu.flip-vertical" msgstr "上下反転" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "フローの開始点を追加" @@ -8111,31 +8055,31 @@ msgstr "前面へ移動" msgid "workspace.shape.menu.front" msgstr "最前面へ移動" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "グループ化" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "非表示" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "UIの表示/非表示" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "交差" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "ロック" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "マスク" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "貼り付け" @@ -8143,27 +8087,27 @@ msgstr "貼り付け" msgid "workspace.shape.menu.paste-props" msgstr "プロパティを貼り付け" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "パス" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "フレックスレイアウトを削除" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "グリッドレイアウトを削除" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "レイアウトを削除" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "プロパティを削除" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "バリアントには少なくとも1つのプロパティが必要です" @@ -8187,11 +8131,11 @@ msgstr "バリアントを復元" msgid "workspace.shape.menu.select-layer" msgstr "レイヤーを選択" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "表示" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "アセットパネルで表示" @@ -8199,7 +8143,7 @@ msgstr "アセットパネルで表示" msgid "workspace.shape.menu.show-main" msgstr "メインコンポーネントを表示" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "線をパスに変換" @@ -8211,19 +8155,19 @@ msgstr "サムネイルを削除" msgid "workspace.shape.menu.thumbnail-set" msgstr "サムネイルに設定" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "グループ解除" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "合体" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "ロック解除" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "マスクを解除" @@ -8239,79 +8183,79 @@ msgstr "サイドバーを折りたたむ" msgid "workspace.sidebar.expand" msgstr "サイドバーを展開" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "履歴" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "レイヤー" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "コンポーネント" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "フィルター" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "ボード" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "グループ" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "画像" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "マスク" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "一致なし" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "置換" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "すべて置換" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "置換後の文字列…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "検索…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "検索と置換" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "テキスト内容" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "レイヤー名" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "シェイプ" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "テキスト" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "インポートしたSVG属性" @@ -8323,7 +8267,7 @@ msgstr "ページ" msgid "workspace.sidebar.sitemap.add-page" msgstr "ページを追加" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "サイトマップ" @@ -8355,7 +8299,7 @@ msgstr "このグループにセットを追加" msgid "workspace.tokens.add-token" msgstr "トークンを追加:%s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "適用先" @@ -8379,7 +8323,7 @@ msgstr "基準フォントサイズはピクセル値または単位なしの値 msgid "workspace.tokens.change-token-source" msgstr "トークンのソースを変更" -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "カラー" @@ -8391,7 +8335,7 @@ msgstr "トークンのパスをコピー" msgid "workspace.tokens.create-new-theme" msgstr "最初のテーマを作成しましょう。" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "新しく作成" @@ -8419,7 +8363,7 @@ msgstr "トークンを複製" msgid "workspace.tokens.duplicate-group" msgstr "トークングループを複製" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "コピー" @@ -8443,7 +8387,7 @@ msgstr "%sトークンを編集" msgid "workspace.tokens.enter-token-name" msgstr "%sトークンの名前を入力" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "テキストの編集中はトークンを適用できません。代わりにテキストレイヤーを選択してください。" @@ -8471,7 +8415,7 @@ msgstr "プレビュー:" msgid "workspace.tokens.export.single-file" msgstr "単一ファイル" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "フォントサイズまたは{alias}" @@ -8479,7 +8423,7 @@ msgstr "フォントサイズまたは{alias}" msgid "workspace.tokens.font-variant-not-found" msgstr "フォントの太さ/スタイルを設定できませんでした。このフォントスタイルは現在のフォントに存在しません" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "フォントの太さ(300、Bold Italic…)または{alias}" @@ -8515,7 +8459,7 @@ msgstr "複数ファイルの場合、ファイル名/パスがセット名に msgid "workspace.tokens.import-single-file" msgstr "単一のJSONファイルの場合、第1階層のキーをトークンセット名にしてください。" -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "トークンをインポート" @@ -8523,15 +8467,15 @@ msgstr "トークンをインポート" msgid "workspace.tokens.import-warning" msgstr "トークンをインポートすると、現在のトークン、セット、テーマがすべて上書きされます。" -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "非アクティブ" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "このセットはアクティブではありません。ビューポートで変更を確認するには、テーマを変更するかこのセットを有効にしてください" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "個別のトークンを使用" @@ -8551,11 +8495,11 @@ msgstr "テーマ" msgid "workspace.tokens.label.theme-placeholder" msgstr "テーマを追加(例:Light)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "文字間隔または{alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "行の高さ(倍率、px、%)または{alias}" @@ -8571,11 +8515,11 @@ msgstr "最大サイズ" msgid "workspace.tokens.min-size" msgstr "最小サイズ" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "参照先が見つかりません" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "右クリックでオプションを表示" @@ -8591,7 +8535,7 @@ msgstr "アクティブなテーマはありません" msgid "workspace.tokens.no-permisions-set" msgstr "セットを有効/無効にするには編集者である必要があります" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "テーマを使用するには編集者である必要があります" @@ -8603,7 +8547,7 @@ msgstr "セットがまだ定義されていません。先にセットを作成 msgid "workspace.tokens.no-sets-yet" msgstr "セットはまだありません。" -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "テーマはありません。" @@ -8615,7 +8559,7 @@ msgstr "現在、テーマはありません。" msgid "workspace.tokens.not-remap" msgstr "再マッピングしない" -#: src/app/main/data/workspace/libraries.cljs:847 +#: src/app/main/data/workspace/libraries.cljs:848 msgid "workspace.tokens.notifications.source-sets-or-themes-updated" msgstr "トークンのソースのセットまたはテーマが変更されました" @@ -8627,7 +8571,7 @@ msgstr "%s個のアクティブなセット" msgid "workspace.tokens.open-source-new-tab" msgstr "トークンのソースを新しいタブで開く" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "元の値:%s" @@ -8639,15 +8583,15 @@ msgstr "パディング" msgid "workspace.tokens.radius" msgstr "角の半径" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "{%s}の参照が無効か、どのアクティブなセットにも含まれていません。" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "タイポグラフィトークンのエイリアスを入力" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "シャドウトークンのエイリアスを入力" @@ -8683,7 +8627,7 @@ msgstr "トークングループの名前を変更" msgid "workspace.tokens.rename-group-name-hint" msgstr "トークンの名前は自動的に%s.(suffix).(tokenName)に変更されます" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "解決された値:%s" @@ -8719,27 +8663,27 @@ msgstr "ここでは1remの値となる基準フォントサイズを設定で msgid "workspace.tokens.settings" msgstr "トークンの設定" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "シャドウを追加" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "ぼかし" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "シャドウの種類" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "シャドウを削除" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "広がり" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "ぼかしの値は負の数にできません" @@ -8747,11 +8691,11 @@ msgstr "ぼかしの値は負の数にできません" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "広がりの値は負の数にできません" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -8763,11 +8707,11 @@ msgstr "サイズ" msgid "workspace.tokens.source" msgstr "ソース:" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize または{alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through または{alias}" @@ -8787,31 +8731,31 @@ msgstr "このファイル" msgid "workspace.tokens.token-description" msgstr "説明" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "フォントファミリーを選択" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "フォントファミリー" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "フォントファミリー、またはカンマ(,)区切りのフォントのリスト" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "名前" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "パス%s、またはその上位のパスにトークンがすでに存在します。" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "名前は1文字以上にしてください" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " は有効なトークン名ではありません。\n" @@ -8821,11 +8765,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "値" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "値、または{alias}形式のエイリアスを入力" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "トークン - %s" @@ -8843,15 +8787,15 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "タイプ「%s」はサポートされていません(%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "参照を使用" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "値が無効です" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "矢印(%s)" @@ -8859,79 +8803,75 @@ msgstr "矢印(%s)" msgid "workspace.toolbar.assets" msgstr "アセット" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "カラーパレット(%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "コメント(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "曲線(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "楕円(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "ボード(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "画像(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:413 +#: src/app/main/ui/workspace/top_toolbar.cljs:362 msgid "workspace.toolbar.label" msgstr "描画ツール" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "直線(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "ここで接続" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP接続済み" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "移動(%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "パレットバー" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "パス(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "プラグイン(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "長方形(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "テキスト(%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "タイポグラフィ(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "ツールバーの表示を切り替え" @@ -8952,11 +8892,11 @@ msgstr "レンダリングを利用できません。編集を再開するには msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "再読み込み" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "変更履歴はまだありません" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "%sによる" @@ -9096,19 +9036,19 @@ msgstr "タイポグラフィアセット" msgid "workspace.undo.entry.unknown" msgstr "%sに対する操作" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "閉じる" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "詳細" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "共有ライブラリに更新があります" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "更新" diff --git a/frontend/translations/ko.po b/frontend/translations/ko.po index e3beca6dcc..9e8ca9ddc4 100644 --- a/frontend/translations/ko.po +++ b/frontend/translations/ko.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:14+0000\n" -"Last-Translator: Anonymous \n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Korean " "\n" "Language: ko\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "이미 계정이 있으신가요?" @@ -27,7 +27,7 @@ msgstr "데모 계정 생성" msgid "auth.demo-warning" msgstr "이것은 데모 서비스이며, 절대 실제 작업에 사용하지 마십시오. 프로젝트는 주기적으로 삭제됩니다." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "비밀번호를 잊으셨나요?" @@ -35,35 +35,35 @@ msgstr "비밀번호를 잊으셨나요?" msgid "auth.fullname" msgstr "이름 (성명)" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "내 계정에 로그인" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "여기서 로그인하세요." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot은 디자인과 코드 협업을 위한 무료 오픈소스 디자인 도구입니다" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "깃허브 (GitHub)" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "깃랩 (GitLab)" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "구글 (Google)" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "오픈아이디 (OpenID)" @@ -83,7 +83,7 @@ msgstr "프로필이 인증되지 않았습니다. 계속하기 전에 프로필 msgid "auth.notifications.recovery-token-sent" msgstr "비밀번호 복구 링크가 메일함으로 전송되었습니다." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "팀에 성공적으로 참가했습니다" @@ -115,7 +115,7 @@ msgstr "비밀번호를 잊으셨나요?" msgid "auth.recovery-submit" msgstr "비밀번호 변경" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "계정이 없으신가요?" @@ -127,7 +127,7 @@ msgstr "대시보드와 이메일에 표시될 이름을 입력해주세요." msgid "auth.register-account-title" msgstr "당신의 이름" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "계정 생성" @@ -140,7 +140,7 @@ msgstr "계정 생성" msgid "auth.terms-and-privacy-agreement" msgstr "[서비스 이용약관](%s) 및 [개인정보 처리방침](%s)에 동의합니다." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "서비스 이용약관" @@ -156,88 +156,88 @@ msgstr "사용 가능한 색상 token이 없습니다. 활성 세트/테마를 msgid "comments.mentions.not-found" msgstr "@%s 사용자를 찾을 수 없습니다" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "게시하기" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "모든 Penpot 사용자" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "이 링크를 삭제하시겠습니까? 삭제하면 더 이상 누구도 접근할 수 없습니다" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(현재)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "링크 삭제" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "링크 가져오기" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "링크가 성공적으로 복사되었습니다" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "권한 관리" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "%s개의 페이지가 공유되었습니다" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "댓글 작성 가능" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "코드 검사 가능" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "링크가 있는 누구나 접근 가능합니다" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "공유된 페이지" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "공유 가능한 링크가 여기에 표시됩니다" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "팀 구성원만" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "프로토타입 공유" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "모두 선택" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "게시 취소" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "팀 관리" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "Penpot은 팀을 위한 도구입니다. 프로젝트와 파일에서 함께 작업할 수 있도록 구성원을 초대하세요" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "팀을 구성하세요!" @@ -253,31 +253,31 @@ msgstr "파일 복원" msgid "dashboard.add-file" msgstr "파일 추가" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "공유 라이브러리로 추가" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "이메일 변경" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "휴지통 비우기" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(복사)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "새 조직 생성" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "새 팀 생성" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "이 버전 고정" @@ -285,7 +285,7 @@ msgstr "이 버전 고정" msgid "dashboard.default-team-name" msgstr "내 Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "삭제된 모든 프로젝트와 파일을 영구적으로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다." @@ -293,41 +293,41 @@ msgstr "삭제된 모든 프로젝트와 파일을 영구적으로 삭제하시 msgid "dashboard.delete-file-forever-confirmation.description" msgstr "%s 파일을 영구적으로 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s개 파일이 성공적으로 삭제되었습니다." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "영구 삭제" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "프로젝트 삭제" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "%s 프로젝트를 영구적으로 삭제하시겠습니까? 해당 프로젝트와 그 안에 포함된 모든 파일이 함께 영구 삭제됩니다. 이 작업은 되돌릴 수 " "없습니다." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s가 성공적으로 삭제되었습니다." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "팀 삭제" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "휴지통이 비어있습니다. 삭제된 파일 및 프로젝트가 여기에 표시됩니다." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "%s에 삭제될 예정" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Penpot 파일(.penpot) 다운로드" @@ -339,19 +339,19 @@ msgstr "복제" msgid "dashboard.duplicate-multi" msgstr "%s개 파일 복제" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "프로젝트 구성원이 초안을 생성하면 이곳에 표시됩니다." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "아직 초안이 없습니다." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "프로젝트 구성원이 파일을 생성하면 이곳에 표시됩니다." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "아직 파일이 없습니다." @@ -398,23 +398,23 @@ msgstr ".penpot 파일 가져오기" msgid "dashboard.empty-project.start" msgstr "놀라운 것들을 만들기 시작하세요" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "파일을 삭제하는 중 오류가 발생했습니다." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "%s 프로젝트를 삭제하는 중 오류가 발생했습니다." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "%s 파일을 복원하는 중 오류가 발생했습니다." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "파일을 복원하는 중 오류가 발생했습니다." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "%s 프로젝트와 해당 파일을 복원하는 중 오류가 발생했습니다." @@ -422,35 +422,35 @@ msgstr "%s 프로젝트와 해당 파일을 복원하는 중 오류가 발생했 msgid "dashboard.export-binary-multi" msgstr "%s Penpot 파일 (.penpot) 다운로드" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "보드를 PDF로 내보내기" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "PDF로 내보내기" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "전체 %s개 중 %s개 요소가 선택됨" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "내보내기" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "디자인 속성(오른쪽 사이드바 하단)에서 요소에 내보내기 설정을 추가할 수 있습니다." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Penpot에서 내보내기 설정 방법 안내." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "내보내기 설정이 지정된 요소가 없습니다." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "선택 영역 내보내기" @@ -464,39 +464,39 @@ msgid "dashboard.file-menu.restore-files-option" msgid_plural "dashboard.file-menu.restore-files-option" msgstr[0] "파일 복원" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "누락된 글꼴" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "모두 무시" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "업로드한 사용자 지정 글꼴이 여기에 표시됩니다." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "아직 사용자 지정 폰트가 없습니다." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "프로젝트 구성원이 사용자 지정 폰트를 업로드하면 여기에 표시됩니다." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "글꼴 %s개 추가됨" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" "여기에 업로드하는 모든 웹 글꼴은 이 팀의 파일 텍스트 속성에서 사용할 수 있는 글꼴 모음 목록에 추가됩니다. 동일한 글꼴 모음 이름을 " "가진 글꼴들은**하나의 글꼴 모음**으로 그룹화됩니다. 지원 형식: **TTF, OTF, WOFF** (하나만 필요)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -504,11 +504,11 @@ msgstr "" "[Penpot의 서비스 이용약관](%s)의 콘텐츠 권리 섹션에서 확인할 수 있습니다. 추가로 [글꼴 라이선스 " "안내](https://www.typography.com/faq)를 참고하시기 바랍니다." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "모두 업로드" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -516,11 +516,11 @@ msgstr "" "서비스](https://vertical-metrics.netlify.app/)와 같은 수직 메트릭 서비스를 사용할 수 있습니다. 또한, " "웹 폰트 생성 및 오류 수정을 위해 [Transfonter](https://transfonter.org/) 사용을 권장합니다. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Penpot 파일 가져오기" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "이런! 이 파일을 가져올 수 없습니다" @@ -528,24 +528,24 @@ msgstr "이런! 이 파일을 가져올 수 없습니다" msgid "dashboard.import.bad-url" msgstr "가져오기 실패. 템플릿 URL이 올바르지 않습니다" -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "일부 파일이 누락되었습니다" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "다음 파일에 오류가 있습니다:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "오류가 있는 파일은 업로드되지 않습니다." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "파일 %s개를 성공적으로 가져왔습니다." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "일부 파일에 포함된 유효하지 않은 객체가 제거되었습니다." @@ -553,75 +553,47 @@ msgstr "일부 파일에 포함된 유효하지 않은 객체가 제거되었습 msgid "dashboard.import.no-perms" msgstr "이 팀으로 가져올 권한이 없습니다" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "컬러 처리 중" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "컴포넌트 처리 중" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "미디어 처리 중" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "페이지 처리 중: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "타이포그래피 처리 중" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "서버로 데이터 업로드 중 (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "파일 업로드 중: %s" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "다음에 대한 초대를 삭제합니다:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "다음에 대한 초대를 재전송합니다:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "초대 삭제" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "초대 재전송" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "사람 초대" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "팀 나가기" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "라이브러리 및 템플릿" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "프로젝트에 추가할 수 있는 라이브러리와 템플릿입니다" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "더 많은 항목을 둘러보고 기여하는 방법을 알아보세요" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "템플릿을 가져오는 중 문제가 발생했습니다. 템플릿이 누락되었습니다." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "라이브러리" @@ -629,7 +601,7 @@ msgstr "라이브러리" msgid "dashboard.loading-files" msgstr "파일 로드 중…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "폰트 로드 중…" @@ -649,39 +621,39 @@ msgstr "%s개 파일을 다음으로 이동" msgid "dashboard.move-to-other-team" msgstr "다른 팀으로 이동" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ 새 파일" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "새 파일" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ 새 프로젝트" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "새 프로젝트" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "\"%s\"에 대한 검색 결과가 없습니다" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "고정된 프로젝트가 여기에 표시됩니다" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "알림" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "이메일 주소가 성공적으로 업데이트되었습니다" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "이메일 주소가 성공적으로 인증되었습니다" @@ -693,11 +665,11 @@ msgstr "알림 설정이 업데이트되었습니다" msgid "dashboard.notifications.password-saved" msgstr "비밀번호가 성공적으로 저장되었습니다!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "알림 보기" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "멤버 %s명" @@ -705,15 +677,15 @@ msgstr "멤버 %s명" msgid "dashboard.open-in-new-tab" msgstr "새 탭에서 파일 열기" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "옵션" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "역할순 정렬" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "상태순 정렬" @@ -737,7 +709,7 @@ msgstr "이제 이 팀의 소유자(Owner)입니다." msgid "dashboard.permissions-change.viewer" msgstr "이제 이 팀의 뷰어(Viewer)입니다." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "고정/고정 해제" @@ -753,27 +725,27 @@ msgstr "플러그인 매니페스트를 구문 분석할 수 없습니다" msgid "dashboard.plugins.try-plugin" msgstr "플러그인 체험하기: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "파일 삭제 중…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "파일 복원 중…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "삭제가 예상보다 느립니다" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "복원이 예상보다 느립니다" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "프로젝트" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "계정을 삭제하시겠습니까?" @@ -781,67 +753,67 @@ msgstr "계정을 삭제하시겠습니까?" msgid "dashboard.removed-from-team" msgstr "더 이상 \"%s\" 팀의 멤버가 아닙니다." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "모든 프로젝트와 파일을 복원하려 합니다. 시간이 다소 소요될 수 있습니다." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "모든 프로젝트 및 파일 복원" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "모두 복원" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s개 파일이 성공적으로 복원되었습니다." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "프로젝트 복원" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "%s 프로젝트와 그 안에 포함된 모든 파일을 복원하려고 합니다." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "프로젝트 복원" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s이(가) 성공적으로 복원되었습니다." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "설정 저장" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "검색…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "\"%s\" 검색 중…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "UI 언어 선택" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "테마 선택" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot 다크 (기본)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot 라이트" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "시스템 테마" @@ -905,11 +877,11 @@ msgstr "설정 업데이트" msgid "dashboard.settings.notifications.title" msgstr "알림" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "모든 파일 보기" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "버전 히스토리" @@ -931,7 +903,7 @@ msgstr[0] "파일이 성공적으로 복사되었습니다" msgid "dashboard.success-duplicate-project" msgstr "프로젝트가 성공적으로 복제되었습니다" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "파일이 성공적으로 이동되었습니다" @@ -943,105 +915,105 @@ msgstr "파일들이 성공적으로 이동되었습니다" msgid "dashboard.success-move-project" msgstr "프로젝트가 성공적으로 이동되었습니다" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "팀 정보" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "팀 구성원" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "팀 프로젝트" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "프로젝트에 추가" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "UI 테마" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "검색 결과" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "삭제된 파일은 휴지통에" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s일 동안 보관됩니다. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "그 이후에는 영구적으로 삭제됩니다." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "필요한 경우, 각 파일 메뉴에서 복원하거나 영구 삭제할 수 있습니다." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "검색어를 입력하세요" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "라이브러리 게시 취소" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "설정 업데이트" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "활성화 상태" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "이 훅이 실행되면 이벤트 상세 정보가 전달됩니다" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "본인이 생성한 웹훅만 삭제하거나 수정할 수 있습니다." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "콘텐츠 유형" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "웹훅 생성" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "웹훅이 성공적으로 생성되었습니다." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "웹훅은 Penpot에서 특정 이벤트가 발생했을 때 다른 웹사이트나 앱이 알림을 받을 수 있는 간단한 방법입니다. 제공하신 각 URL로 " "POST 요청을 보냅니다." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "새 웹훅을 추가하려면 \"웹훅 추가\" 버튼을 누르세요." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "아직 생성된 웹훅이 없습니다." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "내 계정" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "이메일" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "이름" @@ -1057,11 +1029,11 @@ msgstr "주의" msgid "ds.component-subtitle" msgstr "업데이트할 컴포넌트:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "허용" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "취소" @@ -1081,7 +1053,7 @@ msgstr "활성 세트나 테마에 적용 가능한 token이 없습니다." msgid "ds.inputs.numeric-input.no-matches" msgstr "일치하는 항목이 없습니다." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "token 목록 열기" @@ -1093,15 +1065,15 @@ msgstr "이 프로필에 허용되지 않는 인증 제공자입니다" msgid "errors.auth-provider-not-configured" msgstr "인증 제공자가 구성되지 않았습니다." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "인증되지 않았거나 세션이 만료된 것 같습니다." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "%s 글꼴을 로드할 수 없습니다" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "%s 글꼴들을 로드할 수 없습니다" @@ -1113,39 +1085,39 @@ msgstr "미디어 파일을 업로드할 수 없습니다." msgid "errors.character-limit-exceeded" msgstr "글자 수 제한 초과" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "브라우저에서 이 작업을 지원하지 않습니다" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "댓글 처리 중 오류가 발생했습니다" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "죄송합니다! 이 파일은 더 이상 지원되지 않는 이전 버전의 Penpot 에셋을 사용하고 있어 열 수 없습니다." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "문의해 주시면 도와드리겠습니다." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Penpot은 더 이상 이 유형의 에셋을 지원하지 않지만," -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "고객 지원팀에 문의" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "상위 세트를 하위 세트의 경로로 옮길 수 없습니다." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "이미 사용 중인 이메일입니다" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "이미 인증된 이메일입니다." @@ -1157,27 +1129,27 @@ msgstr "이메일을 비밀번호로 사용할 수 없습니다" msgid "errors.email-does-not-match-invitation" msgstr "초대받은 이메일과 일치하지 않습니다." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "허용되지 않는 도메인입니다" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "«%s» 이메일은 지속적인 반송(Bounce) 리포트가 발생하고 있습니다." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "«%s» 이메일은 스팸으로 신고되었거나 지속적으로 반송되고 있습니다." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "'%s' 기능이 활성화된 파일을 열려고 하지만, 현재 Penpot 버전에서 지원하지 않거나 비활성화되어 있습니다." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "'%s' 기능은 지원되지 않습니다." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "최대 %s자까지 입력 가능합니다." @@ -1185,7 +1157,7 @@ msgstr "최대 %s자까지 입력 가능합니다." msgid "errors.field-min-length" msgstr "최소 1자 이상 입력해야 합니다." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "필수 입력 항목입니다" @@ -1193,15 +1165,15 @@ msgstr "필수 입력 항목입니다" msgid "errors.field-not-all-whitespace" msgstr "이름은 공백 외에 다른 문자를 포함해야 합니다." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "활성화된 기능과 열려는 파일의 기능이 일치하지 않습니다. 파일을 열기 전에 '%s'에 대한 마이그레이션이 필요합니다." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "문제가 발생했습니다." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "웹 워커에서 문제가 발생했습니다." @@ -1217,7 +1189,7 @@ msgstr "유효하지 않은 데이터" msgid "errors.invalid-email" msgstr "올바른 이메일 주소를 입력해주세요" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "확인용 이메일이 일치하지 않습니다" @@ -1241,11 +1213,11 @@ msgstr "이 초대는 취소되었거나 만료되었을 수 있습니다." msgid "errors.ldap-disabled" msgstr "LDAP 인증이 비활성화되었습니다." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "'%s' 할당량에 도달했습니다. 고객 지원팀에 문의하세요." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "한 번의 요청으로 초대할 수 있는 최대 이메일 수(%s개)에 도달했습니다" @@ -1261,19 +1233,19 @@ msgstr "이미지 내용이 파일 확장자와 일치하지 않는 것 같습 msgid "errors.media-type-not-allowed" msgstr "유효한 이미지가 아닌 것 같습니다." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "초대하려는 프로필의 이메일 수신이 거부되었습니다(스팸 신고 또는 높은 반송률)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "마이그레이션 진행 중" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "버전 생성자만 잠글 수 있습니다" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "버전 생성자만 잠금 해제할 수 있습니다" @@ -1285,7 +1257,7 @@ msgstr "확인용 비밀번호가 일치하지 않습니다" msgid "errors.password-too-short" msgstr "비밀번호는 최소 8자 이상이어야 합니다" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "클립보드에 유효하지 않은 데이터가 있습니다" @@ -1293,7 +1265,7 @@ msgstr "클립보드에 유효하지 않은 데이터가 있습니다" msgid "errors.profile-blocked" msgstr "차단된 프로필입니다" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "사용자 프로필의 이메일 수신이 거부되었습니다(스팸 신고 또는 높은 반송률)." @@ -1301,23 +1273,23 @@ msgstr "사용자 프로필의 이메일 수신이 거부되었습니다(스팸 msgid "errors.registration-disabled" msgstr "현재 회원가입이 비활성화되어 있습니다." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG가 유효하지 않거나 형식이 잘못되었습니다" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "호환되지 않는 기능 '%s'이(가) 감지되었습니다" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "팀에 본인만 남아 있어 나갈 수 없습니다. 팀을 삭제해야 합니다." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "할당하려는 멤버가 존재하지 않습니다." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "소유자(Owner)는 팀을 나갈 수 없습니다. 소유자 역할을 먼저 다른 분께 위임해야 합니다." @@ -1325,11 +1297,11 @@ msgstr "소유자(Owner)는 팀을 나갈 수 없습니다. 소유자 역할을 msgid "errors.token-set-already-exists" msgstr "동일한 이름의 token 세트가 이미 존재합니다" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "이동을 완료할 수 없습니다. 해당 경로에 동일한 이름의 세트가 이미 존재합니다." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "동일한 이름의 테마 옵션이 이미 존재합니다" @@ -1337,55 +1309,55 @@ msgstr "동일한 이름의 테마 옵션이 이미 존재합니다" msgid "errors.unexpected-error" msgstr "예기치 않은 오류가 발생했습니다." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "알 수 없는 token" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "이 버전은 이미 잠겨 있습니다" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "이 버전은 잠겨 있어 다른 사람이 삭제할 수 없습니다" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "파일의 버전 번호가 호환되지 않습니다" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL이 작동을 멈췄습니다. 문제를 해결하려면 페이지를 새로고침하세요" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "캔버스 연결이 끊어졌습니다" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "연결 오류, URL에 연결할 수 없습니다" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL 유효성 검사를 통과하지 못했습니다." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "마지막 전송이 성공하지 못했습니다." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "SSL 인증 오류." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "시간 초과" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "유효성 검사 중 예기치 않은 오류 발생" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "예기치 않은 상태 %s" @@ -1405,15 +1377,15 @@ msgstr "설명" msgid "feedback.description-placeholder" msgstr "의견을 보내시는 이유를 설명해주세요" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "함께해주셔서 기쁩니다. 도움이 필요하시면 게시하기 전에 먼저 검색을 이용해보세요." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot 커뮤니티" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "기타 문의 방법" @@ -1433,11 +1405,11 @@ msgstr "문의하시는 이유를 이슈, 제안, 궁금한 점 등으로 구분 msgid "feedback.title-contact-us" msgstr "문의하기" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "기술적인 문의 사항을 도와드립니다." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X(Twitter) 지원 계정" @@ -1457,10 +1429,6 @@ msgstr "아이디어/제안" msgid "feedback.type.issue" msgstr "이슈/문제" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "파일 다운로드" - #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "오류가 발생했습니다" @@ -1497,7 +1465,7 @@ msgstr "도형 채우기 이미지 미리보기" msgid "inspect.attributes.shadow" msgstr "그림자" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "크기 및 위치" @@ -1505,39 +1473,27 @@ msgstr "크기 및 위치" msgid "inspect.attributes.stroke" msgstr "선" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "중앙" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "안쪽" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "바깥쪽" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "타이포그래피" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "글꼴 모음" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "글꼴 크기" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "글꼴 스타일" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "자간" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "행간" @@ -1549,11 +1505,11 @@ msgstr "없음" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "밑줄" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "첫 글자 대문자" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "소문자" @@ -1565,7 +1521,7 @@ msgstr "없음" msgid "inspect.attributes.typography.text-transform.unset" msgstr "설정 해제" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "대문자" @@ -1685,47 +1641,47 @@ msgstr "계산된 값:" msgid "inspect.tabs.styles.variants-panel" msgstr "베리언트 속성" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "모두 읽음으로 표시" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "단축키" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Penpot 정보" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "수락" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "활성" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "추가" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "커스텀 폰트 추가" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "추가 중..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "관리자(Admin)" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "전체" @@ -1733,15 +1689,15 @@ msgstr "전체" msgid "labels.and" msgstr "및" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "뒤로" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "잠시 기다린 후 다시 시도해주세요. 현재 서버 유지보수 작업을 진행 중입니다." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "게이트웨이 오류가 발생했습니다" @@ -1749,15 +1705,15 @@ msgstr "게이트웨이 오류가 발생했습니다" msgid "labels.blur" msgstr "블러" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "취소" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "닫기" @@ -1765,7 +1721,7 @@ msgstr "닫기" msgid "labels.code" msgstr "코드" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "접기" @@ -1777,15 +1733,15 @@ msgstr "색상" msgid "labels.comment" msgstr "댓글" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "댓글" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "커뮤니티" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "커뮤니티 및 기여" @@ -1797,19 +1753,19 @@ msgstr "계산됨" msgid "labels.confirm-password" msgstr "비밀번호 확인" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "고객 지원 문의" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "문의하기" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "계속하기" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Penpot 계정으로 계속할 수 있습니다" @@ -1821,31 +1777,31 @@ msgstr "복사" msgid "labels.copy-color" msgstr "색상 복사" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "링크 복사" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-present" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "생성" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "새 팀 생성" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "새 팀 이름을 입력하세요" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "대시보드" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "삭제" @@ -1857,7 +1813,7 @@ msgstr "댓글 삭제" msgid "labels.delete-comment-thread" msgstr "스레드 삭제" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "초대 삭제" @@ -1865,23 +1821,23 @@ msgstr "초대 삭제" msgid "labels.delete-multi-files" msgstr "%s개 파일 삭제" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "삭제된 항목" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "개발" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "취소" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "%s 다운로드" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "초안" @@ -1889,27 +1845,27 @@ msgstr "초안" msgid "labels.duplicate" msgstr "복제" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "편집" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "작성자" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "비어 있음" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "오류" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "만료됨" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "내보내기" @@ -1921,7 +1877,7 @@ msgstr "의견 보내기 비활성화됨" msgid "labels.feedback-sent" msgstr "의견이 전송되었습니다" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -1929,55 +1885,55 @@ msgstr "Figma" msgid "labels.fill" msgstr "채우기" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "글꼴 모음" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "스타일" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "글꼴" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Github 저장소" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "의견 보내기" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "뒤로 가기" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "그래픽 디자인" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "도움말 센터" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "도움말 및 학습" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "숨기기" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "해결된 댓글 숨기기" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "가져오기" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "비활성화" @@ -1985,47 +1941,47 @@ msgstr "비활성화" msgid "labels.info" msgstr "정보" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "설치된 글꼴" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "문제가 발생했습니다." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "작업을 다시 시도하거나 지원팀에 문의하세요." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "내부 오류" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "초대" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "언어" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "레이아웃" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "학습 센터" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "라이브러리 및 템플릿" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "로드 중…" @@ -2033,7 +1989,7 @@ msgstr "로드 중…" msgid "labels.lock" msgstr "잠금" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "로그인 또는 회원가입" @@ -2041,19 +1997,19 @@ msgstr "로그인 또는 회원가입" msgid "labels.login" msgstr "로그인" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "로그아웃" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "마케팅" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "구성원" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "구성원" @@ -2065,41 +2021,41 @@ msgstr "멘션" msgid "labels.new-password" msgstr "새 비밀번호" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "다음" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "모든 알림을 확인했습니다! 새 댓글 알림이 여기에 표시됩니다." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "대기 중인 초대가 없습니다." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "사람들을 모아 멋진 결과물을 함께 만들어보세요." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "죄송합니다!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "알림" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "파일 %s개" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "보드 %s개" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "프로젝트 %개" @@ -2108,39 +2064,39 @@ msgstr[0] "프로젝트 %개" msgid "labels.old-password" msgstr "이전 비밀번호" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "옵션" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "기타 (직접 입력)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "기타" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "소유자(Owner)" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "비밀번호" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "대기 중" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Penpot 변경 사항" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot 허브" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "고정된 프로젝트" @@ -2148,31 +2104,31 @@ msgstr "고정된 프로젝트" msgid "labels.post" msgstr "게시" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "이전" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "제품 또는 UX 디자인" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "제품 관리(PM)" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "프로필" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "프로젝트" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "최근 항목" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "참조" @@ -2180,27 +2136,27 @@ msgstr "참조" msgid "labels.refresh" msgstr "새로고침" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "릴리즈 노트" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "페이지 새로고침" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "제거" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "구성원 제거" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "이름 바꾸기" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "팀 이름 바꾸기" @@ -2224,11 +2180,11 @@ msgstr "새 답글" msgid "labels.reply.thread" msgstr "답글 달기" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "재전송" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "초대 재전송" @@ -2236,43 +2192,43 @@ msgstr "초대 재전송" msgid "labels.restore" msgstr "복원" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "다시 시도" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "역할" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "저장" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "검색" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "글꼴 검색" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "옵션 선택" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "전송" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "전송 중…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "시스템 정기 점검 중입니다." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "서비스를 사용할 수 없음" @@ -2280,55 +2236,55 @@ msgstr "서비스를 사용할 수 없음" msgid "labels.sets" msgstr "세트" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "설정" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "그림자" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "공유" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "라이브러리" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "표시" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "모든 댓글 보기" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "댓글 목록 보기" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "내 멘션만 보기" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "내 댓글만 보기" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "원본" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "시작" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "상태" @@ -2336,7 +2292,7 @@ msgstr "상태" msgid "labels.stroke" msgstr "선" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "학생 또는 교사" @@ -2348,7 +2304,7 @@ msgstr "스타일" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "전환" @@ -2356,19 +2312,19 @@ msgstr "전환" msgid "labels.text" msgstr "텍스트" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "테마" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "튜토리얼" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "타이포그래피" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "알 수 없는 오류" @@ -2380,31 +2336,31 @@ msgstr "잠금 해제" msgid "labels.unpublish-multi-files" msgstr "%s개 파일 게시 취소" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "업데이트" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "팀 업데이트" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "업로드" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "사용자 지정 글꼴 업로드" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "업로드 중…" -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "베리언트" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "버전 %s 노트" @@ -2412,7 +2368,7 @@ msgstr "버전 %s 노트" msgid "labels.view-only" msgstr "보기 전용" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "뷰어" @@ -2420,7 +2376,7 @@ msgstr "뷰어" msgid "labels.visibility" msgstr "가시성" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "웹훅" @@ -2428,11 +2384,11 @@ msgstr "웹훅" msgid "labels.write-new-comment" msgstr "새 댓글 작성" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(나)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "내 계정" @@ -2562,59 +2518,59 @@ msgstr "공유 라이브러리로 추가하면, 이 파일 라이브러리의 msgid "modals.add-shared-confirm.message" msgstr "\"%s\"를 공유 라이브러리로 추가" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "크게 이동" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "새 이메일 확인" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "본인 확인을 위해 현재 이메일인 \"%s\"로 이메일을 보내드립니다." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "새 이메일" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "이메일 변경" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "이메일 주소 변경" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "웹훅 생성" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "웹훅 생성" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "페이로드(Payload) URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "취소하고 계정 유지" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "네, 계정을 삭제합니다" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "계정을 삭제하면 현재의 모든 프로젝트와 아카이브를 잃게 됩니다." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "정말 계정을 삭제하시겠습니까?" @@ -2662,27 +2618,27 @@ msgstr "%s개의 파일을 삭제하시겠습니까?" msgid "modals.delete-file-multi-confirm.title" msgstr "%s개 파일 삭제 중" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "정말 이 글꼴 스타일을 삭제하시겠습니까? 파일에서 사용 중인 경우 로드되지 않습니다." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "글꼴 스타일 지우는 중" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "이 글꼴을 삭제하시겠습니까? 파일에서 사용 중인 경우 로드되지 않습니다." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "글꼴 삭제 중" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "이 페이지를 삭제하시겠습니까?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "페이지 삭제" @@ -2698,27 +2654,27 @@ msgstr "이 프로젝트를 삭제하시겠습니까?" msgid "modals.delete-project-confirm.title" msgstr "프로젝트 삭제" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "파일 삭제" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "어떤 파일에서도 활성화되어 있지 않습니다." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "다음 위치에서 이 라이브러리가 활성화되어 있습니다: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "이 파일들을 삭제하시겠습니까?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "파일 삭제 중" @@ -2735,165 +2691,165 @@ msgstr "이 팀을 삭제하시겠습니까? 팀과 관련된 모든 프로젝 msgid "modals.delete-team-confirm.title" msgstr "팀 삭제 중" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "구성원 삭제" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "이 구성원을 팀에서 삭제하시겠습니까?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "팀 구성원 삭제" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "해당 파일에서 이미 사용된 에셋은 그대로 유지되며, 디자인에는 문제가 발생하지 않습니다." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "웹훅 삭제" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "이 웹훅을 삭제하시겠습니까?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "웹훅 삭제 중" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "웹훅 수정" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "웹훅 수정" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "초대 전송" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "이메일 주소 (쉼표로 구분)" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "일부 구성원은 이미 팀에 소속되어 있습니다. 나머지 구성원들만 초대합니다." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "팀에 구성원을 초대하여 이 파일과 모든 팀 파일에 접근할 수 있도록 할 수 있습니다." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "팀에 구성원 초대" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "귀하가 이 팀의 유일한 구성원이므로, 팀을 나가면 프로젝트 및 파일과 함께 팀이 삭제됩니다." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "정말 %s 팀을 나가시겠습니까?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "소유자(Owner) 역할을 위임할 다른 구성원이 없으면 팀을 나갈 수 없습니다. 팀 삭제를 고려해보세요." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "귀하는 이 팀의 소유자입니다. 나가기 전에 소유자 역할을 위임할 다른 구성원을 선택해주세요." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "위임하고 나가기" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "위임할 구성원 선택" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "팀을 나가기 전에" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "팀 나가기" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "이 팀을 나가시겠습니까?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "팀 나가는 중" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "이동" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "이 라이브러리를 이동하시겠습니까?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "라이브러리 이동" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "이동 간격" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "소유권 이전" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "소유권을 이전하면 귀하의 역할이 관리자(Admin)로 변경되며, 이 팀에 대한 일부 권한을 잃게 됩니다. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "귀하는 현재 이 팀의 소유자입니다. 정말 %s님을 팀의 새 소유자로 지정하시겠습니까?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "새 팀 소유자" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "게시" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "라이브러리가 비어 있습니다. 정말 게시하시겠습니까?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "빈 라이브러리 게시" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "미세 이동" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "게시 취소" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "이 라이브러리의 게시를 취소하시겠습니까?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "라이브러리 게시 취소" @@ -2914,11 +2870,11 @@ msgstr "공유 라이브러리의 컴포넌트를 업데이트하려고 합니 msgid "modals.update-remote-component.message" msgstr "공유 라이브러리의 컴포넌트 업데이트" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "페이지가 존재하지 않습니다" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 오류" @@ -2934,11 +2890,11 @@ msgstr "다음을 통해 복구 이메일을 보냈습니다:" msgid "not-found.login.sent-recovery-check" msgstr "이메일을 확인하고 링크를 클릭하여 새 비밀번호를 생성하세요." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "무료 회원가입" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "몇 초 안에 Penpot 사용을 시작해보세요!" @@ -2946,63 +2902,63 @@ msgstr "몇 초 안에 Penpot 사용을 시작해보세요!" msgid "not-found.made-with-love" msgstr "사랑과 오픈 소스의 힘으로 만들어졌습니다" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "이미 이 파일에 대한 접근 권한을 요청하셨습니다." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "이미 이 파일 또는 이 팀의 다른 파일/프로젝트에 대한 접근 권한을 요청하셨습니다." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "이미 이 프로젝트 또는 이 팀의 다른 프로젝트/파일에 대한 접근 권한을 요청하셨습니다." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "이미 이 프로젝트에 대한 접근 권한을 요청하셨습니다." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "접근 권한 요청" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "소유자가 승인하면 팀에 초대될 것임을 잊지 마세요." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "요청이 성공적으로 전송되었습니다!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "이 파일에 대한 접근 권한이 없습니다." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "내 Penpot으로 이동" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "소유자가 승인하면 팀에 초대됩니다." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot 파일" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "이 프로젝트에 대한 접근 권한이 없습니다." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "프로젝트" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "이 파일에 접근하려면 팀 소유자에게 문의하세요." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "이 프로젝트에 접근하려면 팀 소유자에게 문의하세요." @@ -3014,27 +2970,27 @@ msgstr "시스템 점검 안내: 5분 이내에 짧은 점검을 위해 서비 msgid "notifications.by-code.upgrade-version" msgstr "새 버전이 출시되었습니다. 페이지를 새로고침해주세요" -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "초대가 성공적으로 삭제되었습니다" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "초대가 성공적으로 전송되었습니다" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "초대 링크가 복사되었습니다" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "프로필을 삭제할 수 없습니다. 진행하기 전에 팀 소유권을 먼저 위임해주세요." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "프로필이 성공적으로 저장되었습니다!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "인증 이메일을 %s로 보냈습니다. 이메일을 확인해주세요!" @@ -3062,77 +3018,77 @@ msgstr "팀 이름을 정한 후, 구성원들을 초대할 수 있습니다." msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "팀 생성" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "구성원 초대" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "다음 역할로 초대:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "시작해볼까요!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "디자인에서 실제 코드 추출" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "디자인 시스템 구축" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "프로토타이핑" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "앱의 UI/UX 디자인" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "와이어프레임 제작" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "어떤 용도로 Penpot을 사용하실 예정인가요?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Penpot이 사용자분께 더 잘 맞도록 도와드리기 위해 정보를 조금만 알려주세요. 답변해주신 내용은 새 기능의 우선순위를 정하고 시작 " "가이드를 제공하는 데 도움이 됩니다." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "사용자분에 대해 알려주세요" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "가장 많이 사용하는 디자인 도구는 무엇인가요?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "주로 어떤 작업을 하시나요?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "사용자분의 업무에 대해 알려주세요" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "어디서부터 시작하고 싶으신가요?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "교육용" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "개인용" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "업무용" @@ -3180,11 +3136,11 @@ msgstr "디자인 시작하기" msgid "profile.recovery.go-to-login" msgstr "로그인으로 이동" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "해제" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "혼합됨" @@ -3196,35 +3152,35 @@ msgstr "색상 제거" msgid "settings.select-this-color" msgstr "이 스타일을 사용하는 항목 선택" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "댓글" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "아래 정렬" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "가로 가운데 정렬" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "왼쪽 정렬" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "오른쪽 정렬" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "위쪽 정렬" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "세로 가운데 정렬" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "선택 영역을 보드로 만들기" @@ -3232,55 +3188,55 @@ msgstr "선택 영역을 보드로 만들기" msgid "shortcuts.bold" msgstr "굵게 표시/해제" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "빼기" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "제외" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "교차" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "합치기" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "맨 뒤로 보내기" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "뒤로 보내기" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "앞으로 가져오기" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "맨 앞으로 가져오기" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "실행 취소 기록 삭제" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "복사" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "링크 복사" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "속성 복사" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "컴포넌트 / 베리언트 생성" @@ -3288,35 +3244,35 @@ msgstr "컴포넌트 / 베리언트 생성" msgid "shortcuts.create-new-project" msgstr "새로 생성" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "잘라내기" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "축소" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "삭제" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "노드 삭제" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "컴포넌트 해제" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "곡선" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "타원" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "보드" @@ -3324,39 +3280,39 @@ msgstr "보드" msgid "shortcuts.draw-nodes" msgstr "경로 그리기" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "경로" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "사각형" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "텍스트" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "복제" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "취소" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "도형 내보내기" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "전체 맞춤" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "좌우 반전" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "상하 반전" @@ -3380,23 +3336,23 @@ msgstr "공유 라이브러리로 이동" msgid "shortcuts.go-to-search" msgstr "검색" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "그룹화" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "수평 간격 동일하게" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "UI 표시/숨기기" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "확대" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "이미지 삽입" @@ -3404,7 +3360,7 @@ msgstr "이미지 삽입" msgid "shortcuts.italic" msgstr "기울임꼴 표시/해제" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "노드 연결" @@ -3412,39 +3368,39 @@ msgstr "노드 연결" msgid "shortcuts.line-through" msgstr "취소선 표시/해제" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "직각 노드로 변경" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "곡선 노드로 변경" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "마스크" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "노드 병합" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "이동" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "아래로 크게 이동" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "왼쪽으로 크게 이동" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "오른쪽으로 크게 이동" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "위로 크게 이동" @@ -3452,333 +3408,333 @@ msgstr "위로 크게 이동" msgid "shortcuts.move-nodes" msgstr "노드 이동" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "아래로 이동" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "왼쪽으로 이동" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "오른쪽으로 이동" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "위로 이동" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "다음 보드" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "단축키를 찾을 수 없습니다" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "불투명도 100% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "불투명도 10% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "불투명도 20% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "불투명도 30% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "불투명도 40% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "불투명도 50% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "불투명도 60% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "불투명도 70% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "불투명도 80% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "불투명도 90% 설정" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "색상 선택기" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "뷰어 댓글 섹션으로 이동" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "대시보드로 이동" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "뷰어 검사 섹션으로 이동" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "뷰어 인터랙션 섹션으로 이동" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "뷰어 인터랙션 섹션으로 이동" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "워크스페이스로 이동" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " 또는 " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "붙여넣기" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "속성 붙여넣기" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "플러그인 관리자" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "이전 보드" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "다시 실행" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "이름 바꾸기" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "확대/축소 초기화" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "스케일" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "기본" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "대시보드" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "뷰어" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "워크스페이스" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "전체 선택" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "다음 레이어 선택" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "상위 레이어 선택" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "이전 레이어 선택" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "노드 분리" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "픽셀 그리드 표시/숨기기" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "단축키 표시/숨기기" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "노드에 스냅" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "픽셀 그리드에 스냅" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "편집 시작" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "정렬" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "편집" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "일반" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "메인 메뉴" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "레이어 수정" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "내비게이션" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "내비게이션" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "내비게이션" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "패널" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "경로" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "도형" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "텍스트" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "도구" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "확대/축소" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "확대/축소" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "썸네일 설정" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "키보드 단축키" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "동적 정렬 전환" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "에셋 전환" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "색상 선택자 켜기/끄기" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "포커스 모드 켜기/끄기" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "전체 화면 켜기/끄기" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "가이드 표시 / 숨기기" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "히스토리 패널 전환" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "레이어 패널 전환" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "플렉스 레이아웃 추가 / 제거" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "그리드 레이아웃 추가 / 제거" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "잠금 / 잠금 해제" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "비율 잠금" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "눈금자 표시 / 숨기기" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "가이드에 스냅" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "눈금자 가이드에 스냅" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "텍스트 팔레트 전환" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "테마 변경" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "표시 / 숨기기" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "확대/축소 스타일 전환" @@ -3786,31 +3742,31 @@ msgstr "확대/축소 스타일 전환" msgid "shortcuts.underline" msgstr "밑줄 전환" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "실행 취소" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "그룹 해제" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "마스크 해제" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "세로로 균등 배분" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "줌 렌즈 축소" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "줌 렌즈 확대" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "선택 항목에 맞추기" @@ -3890,187 +3846,187 @@ msgstr "소유한 팀 전체에 추가된 새로운 편집자만 향후 요금 msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "무제한 플랜 이용 중 사람 초대하기" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "구독" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "체험판 종료 후에도 계속 이용하시려면 결제 수단을 추가해 주세요" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "프로페셔널 플랜의 모든 혜택 및:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "무제한 플랜의 모든 혜택 및:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s명 편집자)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "엔터프라이즈" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "엔터프라이즈 (체험판)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90일 자동 저장 버전 및 파일 복구" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "월 정액제" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "무제한 저장 공간" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "나의 구독 관리" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "결제 수단 추가" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "결제 정보를 지금 추가하면 체험판 종료 후에도 구독이 끊김 없이 유지되며 당사의 오픈소스 프로젝트를 후원하실 수 있습니다. 지금은 " "요금이 청구되지 않습니다." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "지금은 건너뛰고 체험판 시작" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "체험판을 더 원활하게 시작할 수 있도록 도와주세요" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "현재 팀 전체에서 편집할 수 있는 사람이 %s명 있습니다." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "알림: 하위 플랜으로 변경하면 저장 공간이 줄어들고 백업 및 버전 히스토리 보존 기간이 단축됩니다." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "편집자" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(소유자, 관리자, 편집자 포함. 뷰어는 편집자에 포함되지 않음)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "현재 인원보다 적은 편집자 수를 설정할 수 없습니다. 실제 편집을 하지 않는 사용자는 팀 설정에서 역할(편집자/관리자에서 뷰어로)을 " "변경해 주세요." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "체험판 종료 후 청구됩니다. 지금은 신용카드가 필요하지 않습니다." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/월" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "내 팀에 %s 적용" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "팁: 향후 초대를 고려하여 지금 시트 수를 늘려두실 수 있습니다. 팀 전체 편집자가 25명 이상이면 월 $175의 고정 요금이 " "적용됩니다." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Penpot 가입일: %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "자세한 정보" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "다른 Penpot 플랜" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "조직/월" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "프로페셔널" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7일 자동 저장 버전 및 파일 복구" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10GB 저장 공간" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "무제한 팀 생성. 소유한 팀 전체 합산 최대 8명의 편집자." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "추천" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "내 구독 정보" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "무료 체험판 시작" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "구독하기" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "계정 상세 정보의 '구독' 페이지에서 언제든지 구독 내용을 수정하실 수 있습니다." -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Penpot %s 플랜을 선택해주셔서 감사합니다!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "14일 무료 체험" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "무제한" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "무제한 (체험판)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30일 자동 저장 버전 및 파일 복구" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "월 최대 $175 정액 청구" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GB 저장 공간" @@ -4093,40 +4049,40 @@ msgstr "이 한도를 늘리려면 팀 소유자에게 문의하세요: [mailto: msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "이 한도를 늘리려면 [플랜을 업그레이드하세요|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "초대 %s개 선택됨" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "글꼴 제공자 - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "글꼴 - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "프로젝트 - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "검색 - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "공유 라이브러리 - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - 팀을 위한 자유로운 디자인" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "의견 보내기 - Penpot" @@ -4134,7 +4090,7 @@ msgstr "의견 보내기 - Penpot" msgid "title.settings.notifications" msgstr "알림 - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "설정 - Penpot" @@ -4142,27 +4098,27 @@ msgstr "설정 - Penpot" msgid "title.settings.password" msgstr "비밀번호 - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "프로필 - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "초대 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "구성원 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "설정 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "웹훅 - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - 보기 모드 - Penpot" @@ -4170,99 +4126,99 @@ msgstr "%s - 보기 모드 - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "페이지에 보드가 없습니다." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "보드를 찾을 수 없습니다." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "댓글 (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "인터랙션 숨기기" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "전체 화면" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "검사 (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "인터랙션" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "인터랙션 (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "링크 복사" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "인터랙션 표시" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "클릭 시 인터랙션 표시" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "사이트맵" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "마지막 전송이 성공했습니다." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "가로 가운데 정렬 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "가로 간격 균등 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "왼쪽 정렬 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "오른쪽 정렬 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "아래쪽 정렬 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "세로 가운데 정렬 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "세로 간격 균등 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "위쪽 정렬 (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "라이브러리 추가" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "모든 에셋" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "색상" @@ -4274,7 +4230,7 @@ msgstr "색상 추가" msgid "workspace.assets.component-group-options" msgstr "컴포넌트 그룹 옵션" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "컴포넌트" @@ -4282,7 +4238,7 @@ msgstr "컴포넌트" msgid "workspace.assets.components.add-component" msgstr "컴포넌트 추가" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s개 베리언트" @@ -4290,15 +4246,15 @@ msgstr "%s개 베리언트" msgid "workspace.assets.create-group" msgstr "그룹 생성" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "항목 이름이 \"그룹 이름 / 항목 이름\" 형식으로 자동으로 지정됩니다" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "삭제" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "복제" @@ -4306,39 +4262,39 @@ msgstr "복제" msgid "workspace.assets.duplicate-main" msgstr "메인 복제" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "편집" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "필터" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "그리드 보기" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "그룹" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "그룹 이름" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "목록 보기" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "로컬 라이브러리" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "라이브러리 관리" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "에셋을 찾을 수 없습니다" @@ -4346,7 +4302,7 @@ msgstr "에셋을 찾을 수 없습니다" msgid "workspace.assets.open-library" msgstr "라이브러리 파일 열기" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "이름 바꾸기" @@ -4354,11 +4310,11 @@ msgstr "이름 바꾸기" msgid "workspace.assets.rename-group" msgstr "그룹 이름 바꾸기" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "에셋 검색" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "공유 라이브러리" @@ -4367,43 +4323,43 @@ msgid "workspace.assets.sidebar.components" msgid_plural "workspace.assets.sidebar.components" msgstr[0] "컴포넌트 %s개" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "정렬" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "타이포그래피" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "타이포그래피 추가" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "크기" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "글꼴 스타일" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "스타일 라이브러리 파일로 이동하여 편집" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "자간" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "행간" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "가나다" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "텍스트 변환" @@ -4415,63 +4371,63 @@ msgstr "그룹 해제" msgid "workspace.colorpicker.color-tokens" msgstr "색상 token" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "컴포넌트는 자기 자신 안에 중첩될 수 없습니다." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "일부 복사본을 전환할 수 없습니다. 컴포넌트는 자기 자신 안에 중첩될 수 없습니다." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "영역 생성" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "보드 생성" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "셀 병합" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "오른쪽에 열 1개 추가" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "왼쪽에 열 1개 추가" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "열 삭제" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "열 및 도형 삭제" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "열 복제" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "아래에 행 1개 추가" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "위에 행 1개 추가" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "행 삭제" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "행 및 도형 삭제" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "행 복제" @@ -4479,15 +4435,15 @@ msgstr "행 복제" msgid "workspace.debug.title" msgstr "디버깅 도구" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "포커스 모드" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "포커스 해제" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "포커스 설정" @@ -4499,191 +4455,191 @@ msgstr "선형 그래디언트" msgid "workspace.gradients.radial" msgstr "방사형 그래디언트" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "동적 정렬 비활성화" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "비율 유지 크기 조정 비활성화" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "가이드 스냅 비활성화" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "픽셀 스냅 비활성화" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "눈금자 가이드 스냅 비활성화" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "동적 정렬 활성화" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "비율 크기 조절 활성화" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "가이드에 스냅" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "픽셀 스냅 활성화" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "눈금자 가이드에 스냅" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "보드 이름 숨기기" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "가이드 숨기기" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "색상 팔레트 숨기기" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "픽셀 그리드 숨기기" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "눈금자 숨기기" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "글꼴 팔레트 숨기기" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "편집" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "파일" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "도움말 및 정보" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "환경 설정" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "보기" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "다시 실행" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "모두 선택" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "보드 이름 표시" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "가이드 표시" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "색상 팔레트 표시" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "픽셀 그리드 표시" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "눈금자 표시" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "글꼴 팔레트 표시" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "다크 테마로 전환" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "라이트 테마로 전환" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "시스템 테마로 전환" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "실행 취소" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "초기화" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "저장 오류" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "저장됨" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "저장 중" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "공유" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "보기 모드 (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "확대/축소" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "채우기 - 채우기 맞춤" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "맞추기 - 화면에 맞게 축소" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "전체 맞추기" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "전체 화면" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "선택 항목에 맞추기" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "그리드 편집" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "종료" @@ -4691,7 +4647,7 @@ msgstr "종료" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "아래쪽 패딩" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "4면 패딩 옵션 보기" @@ -4727,28 +4683,28 @@ msgstr "완료" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "위치 찾기" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "그리드 레이아웃 위치 찾기" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "색상 %s개" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "라이브러리에 아직 색상 스타일이 없습니다" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "라이브러리에 아직 타이포그래피 스타일이 없습니다" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "파일 라이브러리" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "최근 색상" @@ -4760,90 +4716,90 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "색상 스타일 저장" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "컴포넌트 %s개" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "연결됨:" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "또는 다음 중 하나를 추가해 보세요:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "팀에 공유 라이브러리가 없습니다. 여기서 찾아보세요" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "일부 템플릿 보기" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "파일 라이브러리" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "그래픽 %s개" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "이 파일의 라이브러리" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "라이브러리" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "라이브러리 업데이트" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "로딩 중…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "업데이트가 필요한 공유 라이브러리가 없습니다" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "\"%s\"에 대한 검색 결과가 없습니다" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "공유 라이브러리 검색" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "공유 라이브러리" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "라이브러리 연결" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "타이포그래피 %s개" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "라이브러리 연결 해제" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "업데이트" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "모든 변경 사항 보기" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "업데이트" @@ -4879,107 +4835,107 @@ msgstr "블러 전환" msgid "workspace.options.canvas-background" msgstr "캔버스 배경" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "콘텐츠 클리핑" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "컴포넌트" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "주석" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "복사" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "주석 만들기" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "주석 편집" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "메인" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "컴포넌트 교체" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "이 라이브러리에 에셋이 아직 없습니다" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "연결 해제됨" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "충돌하는 베리언트 위치 찾기" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "이 컴포넌트에 충돌하는 베리언트가 있습니다. 각 베리언트가 고유한 속성 조합을 갖도록 하세요." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "중복된 베리언트 위치 찾기" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "일부 베리언트의 속성과 값이 동일합니다" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "이 베리언트들은 속성과 값이 동일합니다. 각각 고유하게 식별될 수 있도록 값을 조정하세요." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "이 베리언트는 다른 베리언트와 속성 및 값이 동일합니다. 고유하게 식별될 수 있도록 값을 조정하세요." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "일부 베리언트의 속성과 값이 동일합니다. 각각 고유하게 식별될 수 있도록 값을 조정하세요." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "이 컴포넌트에 이름이 유효하지 않은 베리언트가 있습니다. 올바른 형식을 따르세요." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "유효하지 않은 베리언트 위치 찾기" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "일부 베리언트의 이름이 유효하지 않습니다" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "유효하지 않은 베리언트 위치 찾기" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "이 베리언트들의 이름이 유효하지 않습니다." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "이 베리언트의 이름이 유효하지 않습니다." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "일부 베리언트의 이름이 유효하지 않습니다." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[속성]=[값], [속성]=[값]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "다음 구조를 사용해 보세요:" @@ -5011,7 +4967,7 @@ msgstr "계층 수준이 동일합니다." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "그룹, 보드, 레이아웃은 동일한 것으로 간주됩니다." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "베리언트 연결 방식" @@ -5055,11 +5011,11 @@ msgstr "위쪽" msgid "workspace.options.constraints.topbottom" msgstr "위아래" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "디자인" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "내보내기" @@ -5067,36 +5023,36 @@ msgstr "내보내기" msgid "workspace.options.export-multiple" msgstr "선택 영역 내보내기" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "요소 %s개 내보내기" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "내보내기 추가" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "내보내기 제거" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "접미사" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "내보내기 완료" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "내보내는 중…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "내보내기 실패" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "내보내기가 예상보다 느립니다" @@ -5112,7 +5068,7 @@ msgstr "채우기 추가" msgid "workspace.options.fill.remove-fill" msgstr "채우기 제거" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "콘텐츠에 맞게 보드 크기 조절" @@ -5136,67 +5092,67 @@ msgstr "플로우 시작" msgid "workspace.options.grid.auto" msgstr "자동" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "열" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "색상" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "거터" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "높이" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "마진" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "기본값으로 설정" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "아래쪽" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "중앙" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "왼쪽" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "오른쪽" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "늘이기" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "위쪽" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "기본값 사용" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "너비" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "행" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "사각형" @@ -5208,7 +5164,7 @@ msgstr "그룹 채우기" msgid "workspace.options.group-stroke" msgstr "그룹 선" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "가이드 추가" @@ -5216,19 +5172,19 @@ msgstr "가이드 추가" msgid "workspace.options.guides.remove-guide" msgstr "가이드 제거" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "가이드" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "가이드 전환" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "높이" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "검사" @@ -5444,107 +5400,107 @@ msgstr "인터랙션" msgid "workspace.options.interactions.add-interaction" msgstr "인터랙션 추가" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "색상" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "색상 번" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "색상 닷지" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "어둡게" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "차이" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "제외" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "하드 라이트" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "색조" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "밝게" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "광도" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "곱하기" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "보통" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "오버레이" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "채도" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "스크린" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "소프트 라이트" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "레이어 표시 전환" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "최대 높이" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "최대 너비" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "최소 높이" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "최소 너비" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "색상 더보기" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "라이브러리 색상 더보기" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "색상 token 더보기" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "불투명도" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "프로토타입" @@ -5576,19 +5532,19 @@ msgstr "개별 반경 통합" msgid "workspace.options.radius.show-single-corners" msgstr "개별 반경 표시" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "최근" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "다시 시도" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "회전" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "글꼴 검색" @@ -5596,7 +5552,7 @@ msgstr "글꼴 검색" msgid "workspace.options.select-a-shape" msgstr "도형, 보드 또는 그룹을 선택하여 다른 보드로 연결하세요." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "선택 색상" @@ -5664,55 +5620,55 @@ msgstr "그림자 전환" msgid "workspace.options.show-fill-on-export" msgstr "내보내기에 표시" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "보기 모드에서 표시" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "크기" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "크기 프리셋" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "비율 잠금" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "비율 잠금 해제" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "원" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "다이아몬드" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "화살표" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "없음" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "둥근" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "사각형" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "직사각형" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "삼각형" @@ -5720,7 +5676,7 @@ msgstr "삼각형" msgid "workspace.options.stroke-color" msgstr "선 색상" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "선 두께" @@ -5728,35 +5684,35 @@ msgstr "선 두께" msgid "workspace.options.stroke.add-stroke" msgstr "선 색상 추가" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "중앙" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "점선" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "점선" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "안쪽" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "혼합" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "바깥쪽" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "선 제거" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "단색" @@ -5792,11 +5748,11 @@ msgstr "자동 너비" msgid "workspace.options.text-options.grow-fixed" msgstr "고정됨" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "자간" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "줄 높이" @@ -5840,194 +5796,194 @@ msgstr "밑줄 (%s)" msgid "workspace.options.use-play-button" msgstr "헤더의 재생 버튼을 클릭해 프로토타입 보기를 실행하세요." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "너비" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X축" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y축" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "노드 그리기 (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "노드 연결 (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "직선으로 변환 (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "곡선으로 변환 (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "노드 병합 (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "노드 이동 (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "노드 분리 (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "노드 스냅 (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "열기" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "[더 많은 플러그인](%s) 찾아보기" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "아직 설치된 플러그인이 없습니다" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "플러그인 매니페스트가 올바르지 않습니다." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "이 플러그인을 사용하려면 편집자여야 합니다" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "플러그인이 존재하지 않거나 URL이 올바르지 않습니다." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "설치" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "설치된 플러그인" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "플러그인 관리자" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "플러그인" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "이 플러그인 업데이트" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "마지막으로 열었던 이후 플러그인이 수정되었습니다. 이제 다음에도 접근하려 합니다:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "파일 다운로드 시작." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "브라우저에 데이터를 저장합니다." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "댓글과 답글을 읽습니다." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "댓글을 읽고 수정하며 귀하의 이름으로 답글을 답니다." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "사용자가 접근할 수 있는 파일의 내용을 읽습니다." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "사용자가 접근할 수 있는 파일의 내용을 읽고 수정합니다." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "이 플러그인은 외부 개발자가 만들었습니다. 접근 권한을 부여하기 전에 신뢰할 수 있는지 확인하세요. 개인정보 보호 및 보안이 " "중요합니다. 문의 사항은 지원팀에 연락하세요." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "라이브러리와 에셋을 읽습니다." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "라이브러리와 에셋을 읽고 수정합니다." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "'%s' 플러그인이 다음에 접근하려 합니다:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "현재 사용자의 프로필 정보를 읽습니다." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "플러그인 목록" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "플러그인 제거" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "플러그인 URL로 검색" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "플러그인" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "나중에" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "살펴보시겠습니까? 현재 팀의 새 초안에서 열립니다. (그렇지 않으면 언제든지 파일의 설치된 플러그인에서 찾을 수 있습니다.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "'%s' 플러그인이 설치되었습니다!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "플러그인 사용해보기" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "플렉스 레이아웃 추가" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "그리드 레이아웃 추가" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "레이아웃 추가" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "베리언트 생성" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "새 속성 추가" @@ -6039,7 +5995,7 @@ msgstr "맨 뒤로" msgid "workspace.shape.menu.backward" msgstr "뒤로" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "베리언트로 결합" @@ -6047,7 +6003,7 @@ msgstr "베리언트로 결합" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "컴포넌트가 같은 페이지에 있어야 합니다" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "복사" @@ -6083,27 +6039,27 @@ msgstr "텍스트로 복사" msgid "workspace.shape.menu.create-annotation" msgstr "주석 추가" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "선택 항목을 보드로" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "컴포넌트 생성" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "여러 컴포넌트 생성" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "잘라내기" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "삭제" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "플로우 시작 삭제" @@ -6115,7 +6071,7 @@ msgstr "인스턴스 분리" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "인스턴스 모두 분리" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "차이" @@ -6123,15 +6079,15 @@ msgstr "차이" msgid "workspace.shape.menu.duplicate" msgstr "복제복제" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "편집" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "제외" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "병합" @@ -6143,7 +6099,7 @@ msgstr "좌우 반전" msgid "workspace.shape.menu.flip-vertical" msgstr "상하 반전" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "플로우 시작" @@ -6155,31 +6111,31 @@ msgstr "앞으로" msgid "workspace.shape.menu.front" msgstr "맨 앞으로" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "그룹" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "숨기기" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "UI 표시/숨기기" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "교차" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "잠금" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "마스크" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "붙여넣기" @@ -6187,27 +6143,27 @@ msgstr "붙여넣기" msgid "workspace.shape.menu.paste-props" msgstr "속성 붙여넣기" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "경로" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "플렉스 레이아웃 제거" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "그리드 레이아웃 제거" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "레이아웃 제거" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "속성 제거" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "베리언트는 최소한 하나의 속성을 가져야 합니다" @@ -6231,11 +6187,11 @@ msgstr "베리언트 복원" msgid "workspace.shape.menu.select-layer" msgstr "레이어 선택" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "표시" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "에셋 패널에 표시" @@ -6251,19 +6207,19 @@ msgstr "썸네일 제거" msgid "workspace.shape.menu.thumbnail-set" msgstr "썸네일로 설정" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "그룹 해제" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "합집합" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "잠금 해제" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "마스크 해제" @@ -6279,51 +6235,51 @@ msgstr "사이드바 접기" msgid "workspace.sidebar.expand" msgstr "사이드바 펼치기" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "히스토리" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "레이어" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "컴포넌트" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "필터" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "보드" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "그룹" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "이미지" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "마스크" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "레이어 검색" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "도형" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "텍스트" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "가져온 SVG 속성" @@ -6335,7 +6291,7 @@ msgstr "페이지" msgid "workspace.sidebar.sitemap.add-page" msgstr "페이지 추가" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "사이트맵" @@ -6355,7 +6311,7 @@ msgstr "이 그룹에 세트 추가" msgid "workspace.tokens.add-token" msgstr "token 추가: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "적용됨" @@ -6375,7 +6331,7 @@ msgstr "기본 글꼴 크기" msgid "workspace.tokens.base-font-size.error" msgstr "기본 글꼴 크기는 픽셀 또는 단위가 없는 값이어야 합니다." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "색상" @@ -6383,7 +6339,7 @@ msgstr "색상" msgid "workspace.tokens.create-new-theme" msgstr "첫 번째 테마를 만들어 보세요." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "하나 생성하기." @@ -6403,7 +6359,7 @@ msgstr "테마 삭제" msgid "workspace.tokens.duplicate" msgstr "token 복제" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "복사" @@ -6451,7 +6407,7 @@ msgstr "미리보기:" msgid "workspace.tokens.export.single-file" msgstr "단일 파일" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "글꼴 크기 또는 {alias} 입력" @@ -6459,7 +6415,7 @@ msgstr "글꼴 크기 또는 {alias} 입력" msgid "workspace.tokens.font-variant-not-found" msgstr "글꼴 굵기/스타일 설정 오류. 현재 글꼴에 이 스타일이 없습니다" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "글꼴 굵기(300, Bold Italic 등) 또는 {alias} 입력" @@ -6495,7 +6451,7 @@ msgstr "여러 파일의 경우 파일 이름/경로가 세트 이름입니다." msgid "workspace.tokens.import-single-file" msgstr "단일 JSON 파일에서 최상위 키는 token 세트 이름이어야 합니다." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "token 가져오기" @@ -6503,15 +6459,15 @@ msgstr "token 가져오기" msgid "workspace.tokens.import-warning" msgstr "token을 가져오면 현재 token, 세트 및 테마가 모두 덮어써집니다." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "비활성" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "이 세트가 활성화되지 않았습니다. 테마를 변경하거나 이 세트를 활성화하여 변경 사항을 확인하세요" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "개별 token 사용" @@ -6531,11 +6487,11 @@ msgstr "테마" msgid "workspace.tokens.label.theme-placeholder" msgstr "테마 추가 (예: 라이트)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "자간 또는 {alias} 입력" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "행간(배수, px, %) 또는 {alias} 입력" @@ -6551,11 +6507,11 @@ msgstr "최대 크기" msgid "workspace.tokens.min-size" msgstr "최소 크기" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "누락된 참조" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "우클릭으로 옵션 보기" @@ -6571,7 +6527,7 @@ msgstr "활성 테마 없음" msgid "workspace.tokens.no-permisions-set" msgstr "세트를 활성화 / 비활성화하려면 편집자여야 합니다" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "테마를 사용하려면 편집자여야 합니다" @@ -6583,7 +6539,7 @@ msgstr "아직 정의된 세트가 없습니다. 먼저 하나를 만드세요." msgid "workspace.tokens.no-sets-yet" msgstr "아직 세트가 없습니다." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "테마가 없습니다." @@ -6599,7 +6555,7 @@ msgstr "재매핑 안 함" msgid "workspace.tokens.num-active-sets" msgstr "활성 세트 %s개" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "원래 값: %s" @@ -6611,11 +6567,11 @@ msgstr "패딩" msgid "workspace.tokens.radius" msgstr "반지름" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "타이포그래피 token 별칭 입력" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "그림자 token 별칭 입력" @@ -6631,7 +6587,7 @@ msgstr "`%s`을 사용하는 모든 토큰을 `%s`로 재매핑하시겠습니 msgid "workspace.tokens.remap-warning-time" msgstr "이 작업은 시간이 조금 걸릴 수 있습니다." -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "확인된 값: %s" @@ -6663,27 +6619,27 @@ msgstr "여기서 1rem의 값을 정의하는 기본 글꼴 크기를 설정할 msgid "workspace.tokens.settings" msgstr "token 설정" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "그림자 추가" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "블러" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "내부" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "그림자 제거" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "확산" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "블러 값은 음수일 수 없습니다" @@ -6691,11 +6647,11 @@ msgstr "블러 값은 음수일 수 없습니다" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "확산 값은 음수일 수 없습니다" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -6703,11 +6659,11 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "크기" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "없음 | 대문자 | 소문자 | 각 단어 첫 글자 대문자 또는 {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "없음 | 밑줄 | 취소선 또는 {alias}" @@ -6723,31 +6679,31 @@ msgstr "테마 목록" msgid "workspace.tokens.token-description" msgstr "설명" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "글꼴 모음 선택" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "글꼴 모음" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "글꼴 모음 또는 쉼표(,)로 구분된 글꼴 목록" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "이름" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "해당 경로에 이미 token이 있습니다: %s" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "이름은 최소 1자 이상이어야 합니다" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " 은(는) 유효한 token 이름이 아닙니다.\n" @@ -6757,11 +6713,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "값" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "값 또는 {alias}를 사용한 별칭을 입력하세요" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "token - %s" @@ -6777,11 +6733,11 @@ msgstr "가져오기가 성공적으로 완료되었습니다. 일부 token이 msgid "workspace.tokens.unknown-token-type-section" msgstr "유형 '%s'가 지원되지 않습니다 (%s)" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "참조 사용" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "값이 유효하지 않습니다" @@ -6789,55 +6745,55 @@ msgstr "값이 유효하지 않습니다" msgid "workspace.toolbar.assets" msgstr "에셋" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "색상 팔레트 (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "댓글 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "곡선 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "타원 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "보드 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "이미지 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "이동 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "경로 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "플러그인 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "직사각형 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "텍스트 (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "타이포그래피 (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "도구 모음 켜기/끄기" @@ -6850,7 +6806,7 @@ msgstr "완료" msgid "workspace.top-bar.view-only" msgstr "**코드 검사 중** (보기 전용)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "아직 기록 변경사항이 없습니다" @@ -6990,19 +6946,19 @@ msgstr "타이포그래피 에셋" msgid "workspace.undo.entry.unknown" msgstr "%s에 대한 작업" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "해제" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "자세히" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "공유 라이브러리에 업데이트가 있습니다" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "업데이트" diff --git a/frontend/translations/lt.po b/frontend/translations/lt.po index 5f3142a58e..a8a9265246 100644 --- a/frontend/translations/lt.po +++ b/frontend/translations/lt.po @@ -12,7 +12,7 @@ msgstr "" "(n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 2026.10.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Jau turite paskyrą?" @@ -30,7 +30,7 @@ msgstr "" "Tai yra DEMONSTRACINĖ versija, NEKURKITE tikrų darbų, nes projektai " "periodiškai - šalinami." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Pamiršote slaptažodį?" @@ -38,27 +38,27 @@ msgstr "Pamiršote slaptažodį?" msgid "auth.fullname" msgstr "Vardas ir Pavardė" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Prisijungimas čia" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -78,7 +78,7 @@ msgstr "Paskyra yra nepatvirtinta, prieš tęsdami patikrinkite paskyrą." msgid "auth.notifications.recovery-token-sent" msgstr "Slaptažodžio atkūrimo nuoroda išsiųsta į jūsų pašto dėžutę." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Sėkmingai prisijungė prie komandos" @@ -110,11 +110,11 @@ msgstr "Pamiršote slaptažodį?" msgid "auth.recovery-submit" msgstr "Slaptažodžio keitimas" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Dar neturite paskyros?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Sukurti paskyrą" @@ -122,114 +122,114 @@ msgstr "Sukurti paskyrą" msgid "auth.register-title" msgstr "Sukurti paskyrą" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Paslaugų teikimo sąlygos" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Paskelbti" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Visi Penpot vartotojai" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Ar tikrai norite pašalinti šią nuorodą? Jei tai padarysite, ji niekam " "nebebus pasiekiama" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(dabartinis)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Naikinti nuorodą" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Gauti nuorodą" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Nuoroda sėkmingai nukopijuota" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Valdyti leidimus" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "Bendrinamas 1 puslapis" msgstr[1] "Bendrinami % puslapiai" msgstr[2] "Bendrinama % puslapių" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Gali komentuoti" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Gali apžiūrėti kodą" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Kiekvienas, turintis nuorodą, turės prieigą" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Bendrinti puslapiai" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Bendrinama nuoroda bus rodoma čia" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Tik komandos nariams" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Dalinkitės prototipais" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Rinktis viską" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Atšaukti paskelbimą" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Komandos valdymas" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot yra skirtas komandoms. Pakvieskite narius bendram darbui su " "projektais ir failais" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Suburkite komandą!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Pridėti kaip bendrinamą biblioteką" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Keisti el. paštą" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopija)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Sukurti naują komandą" @@ -237,7 +237,7 @@ msgstr "Sukurti naują komandą" msgid "dashboard.default-team-name" msgstr "Jūsų Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Naikinti komandą" @@ -249,60 +249,60 @@ msgstr "Dublikatas" msgid "dashboard.duplicate-multi" msgstr "Dubliuoti %s failus" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Eksportuokite darbalaukius į PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Eksportuoti į PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "Pasirinkta %s iš %s elementų" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Eksportuoti" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Galite pridėti eksportavimo nustatymus prie elementų iš dizaino ypatybių " "(dešinės šoninės juostos apačioje)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informacija, kaip nustatyti eksportą \"Penpot\"." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Nėra elementų su eksporto nustatymais." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Eksportuoti pažymėtą sritį" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Šriftas ištrintas" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Atmesti visus" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Vis dar neįdiegėte tinkintų šriftų." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "Pridėtas 1 šriftas" msgstr[1] "Pridėti %s šriftai" msgstr[2] "Šriftas(-ai) pridėti" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -312,7 +312,7 @@ msgstr "" "šriftų šeima**. Galite įkelti šių formatų šriftus: **TTF, OTF ir WOFF** " "(reikės tik vieno)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -322,75 +322,47 @@ msgstr "" "galite paskaityti apie [šriftų " "licencijavimą](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Įkelti viską" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importuokite Penpot failus" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Oi! Nepavyko importuoti šio failo" -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Kai kuriuose failuose buvo netinkamų objektų, kurie buvo pašalinti." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Apdorojimo spalvos" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Komponentų apdorojimas" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Apdorojamos laikmenos" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Apdorojamas puslapis: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Tipografijų apdorojimas" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Įkeliami duomenys į serverį (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Įkeliamas failas: %s" - -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Naujas failas" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Naujas projektas" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Naujas projektas" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Nerasta jokių atitikmenų pagal \"%s\"" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Prisegti projektai bus rodomi čia" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Jūsų el. pašto adresas sėkmingai atnaujintas" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Jūsų el. pašto adresas buvo sėkmingai patvirtintas" @@ -398,7 +370,7 @@ msgstr "Jūsų el. pašto adresas buvo sėkmingai patvirtintas" msgid "dashboard.notifications.password-saved" msgstr "Slaptažodis sėkmingai išsaugotas!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s nariai" @@ -406,7 +378,7 @@ msgstr "%s nariai" msgid "dashboard.open-in-new-tab" msgstr "Atidarykite failą naujame skirtuke" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Nustatymai" @@ -414,19 +386,19 @@ msgstr "Nustatymai" msgid "dashboard.password-change" msgstr "Keisti slaptažodį" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Prisegti/Atsegti" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projektai" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Norite pašalinti paskyrą?" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Vartotojo sąsajos tema" diff --git a/frontend/translations/lv.po b/frontend/translations/lv.po index 33ab93be32..68098e7759 100644 --- a/frontend/translations/lv.po +++ b/frontend/translations/lv.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: Edgars Andersons \n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Latvian " "\n" "Language: lv\n" @@ -10,9 +10,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n % 10 == 0 || n % 100 >= 11 && n % 100 " "<= 19) ? 0 : ((n % 10 == 1 && n % 100 != 11) ? 1 : 2);\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Jau ir konts?" @@ -30,7 +30,7 @@ msgstr "" "Šis ir IZRĀDĪŠANAS pakalpojums, kas NAV IZMANTOJAMS īstam darbam, jo " "projekti ik pēc noteikta laika posma tiks izdzēsti." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Aizmirsta parole?" @@ -38,35 +38,35 @@ msgstr "Aizmirsta parole?" msgid "auth.fullname" msgstr "Pilns vārds" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Pieteikties manā kontā" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Pieteikties šeit." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot ir bezmaksas atvērtā pirmkoda dizaina rīks dizaina un koda sadarbībai" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "AtvērtoID (OpenID)" @@ -88,7 +88,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "Paroles atkopes saite ir nosūtīta e-pastā." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Pievienošanās komandai bija veiksmīga" @@ -120,7 +120,7 @@ msgstr "Aizmirsta parole?" msgid "auth.recovery-submit" msgstr "Nomainīt paroli" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Vēl nav konta?" @@ -134,7 +134,7 @@ msgstr "" msgid "auth.register-account-title" msgstr "Tavs vārds" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Izveidot kontu" @@ -147,7 +147,7 @@ msgstr "Izveidot kontu" msgid "auth.terms-and-privacy-agreement" msgstr "Piekrītu [pakalpojuma noteiktumiem](%s) un [privātuma nosacījumiem](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Pakalpojumu sniegšanas noteikumi" @@ -165,92 +165,92 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Netika atrasts neviens cilvēks @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publicēt" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Visi Penpot lietotāji" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "Vai tiešām noņemt šo saiti? Noņemot to, saite vairs nebūs pieejama nevienam" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(pašreizējais)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Iznīcināt saiti" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Iegūt saiti" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Saite ir veiksmīgi nokopēta" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Pārvaldīt atļaujas" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "%s kopīgotu lapu" msgstr[1] "%s kopīgota lapa" msgstr[2] "%s kopīgotas lapas" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Var komentēt" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Var apskatīt kodu" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Ikvienam, kam ir saite, būs piekļuve" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Kopīgotas lapas" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Šeit tiks parādīta kopīgojama saite" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Tikai komandas dalībnieki" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Kopīgot prototipus" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Atlasīt visu" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Atcelt publikāciju" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Komandas pārvaldība" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot ir paredzēta komandām. Jāuzaicina dalībnieki, lai kopā strādātu ar " "projektiem un datnēm" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Apvienojieties!" @@ -258,23 +258,23 @@ msgstr "Apvienojieties!" msgid "dashboard.add-file" msgstr "Pievienot datni" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Pievienot kā koplietojamu bibliotēku" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Mainīt e-pasta adresi" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopija)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Izveidot jaunu komandu" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Piespraust šo versiju" @@ -282,19 +282,19 @@ msgstr "Piespraust šo versiju" msgid "dashboard.default-team-name" msgstr "Mans Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Dzēst komandu" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "Atkritne ir tukša. Izdzēstās datnes un projekti parādīsies šeit." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Tiks izdzēsts %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Lejupielādēt Penpot datni (.penpot)" @@ -306,19 +306,19 @@ msgstr "Divkāršot" msgid "dashboard.duplicate-multi" msgstr "Divkāršot %s datnes" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Tiklīdz projekta dalībnieks izveidos melnrakstu, tas šeit tiks parādīts." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Vēl nav neviena melnraksta." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Tiklīdz projekta dalībnieks izveidos datni, tā šeit tiks parādīta." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Vēl nav nevienas datnes." @@ -370,37 +370,37 @@ msgstr "Sākt veidot apbrīnojamas lietas" msgid "dashboard.export-binary-multi" msgstr "Lejupielādēt %s Penpot datnes (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Izgūt plātnes kā PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Izgūt kā PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "Atlasīti elementi - %s no %s" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Izgūt" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Izgūšanas iestatījumus elementiem var pievienot no noformējuma īpašībām " "(labās sānjoslas apakšā)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informācija par to, kā iestatīt izgūšanu Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Nav elementu ar izgūšanas iestatījumiem." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Izgūt atlasi" @@ -418,36 +418,36 @@ msgstr[0] "Atjaunot datnes" msgstr[1] "Atjaunot datni" msgstr[2] "Atjaunot datnes" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Trūkstošs fonts" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Noraidīt visu" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Šeit tiks parādīti augšupielādētie pielāgotie fonti." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Vēl nav neviena pielāgota fonta." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Tiklīdz projekta dalībnieks augšupielādēs pielāgotu fontu, tas šeit tiks " "parādīts." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "Nav pievienoti fonti" msgstr[1] "Fonts pievienots" msgstr[2] "%s fonti pievienoti" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -456,7 +456,7 @@ msgstr "" "saimes nosaukumu tiks apkopoti kā **viena fontu saime**. Var augšupielādēt " "fontus šādos veidolos: **TTF, OTF un WOFF** (būs nepieciešams tikai viens)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -465,11 +465,11 @@ msgstr "" "sniegšanas noteikumos](%s). Varētu būt noderīgi izlasīt arī par [fontu " "licencēšanu](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Augšupielādēt visu" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -480,11 +480,11 @@ msgstr "" "[Transfonter](https://transfonter.org/), lai izveidotu tīmekļa fontus un " "novērstu kļūdas. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Ievietot Penpot datnes" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Ak vai! Šo datni nevarēja ievietot" @@ -492,26 +492,26 @@ msgstr "Ak vai! Šo datni nevarēja ievietot" msgid "dashboard.import.bad-url" msgstr "Ievietošana neizdevās. Sagataves URL ir nepareizs" -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Ne visas datnes tika ievietotas" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Šajās datnēs ir kļūdas:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Datnes ar kļūdām netiks augšupielādētas." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "%s datņu tika veiksmīgi ievietotas." msgstr[1] "%s datne tika veiksmīgi ievietota." msgstr[2] "%s datnes tika veiksmīgi ievietotas." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Dažās datnēs bija nederīgi objekti, kuri tika noņemti." @@ -519,75 +519,47 @@ msgstr "Dažās datnēs bija nederīgi objekti, kuri tika noņemti." msgid "dashboard.import.no-perms" msgstr "Nav atļaujas ievietot šajā komandā" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Krāsu apstrāde" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Apstrādā sastāvdaļas" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Multivides apstrāde" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "%s lapas apstrāde" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Apstrādā burtu stilus un veidus" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Augšupielādē datus serverī (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Augšupielādē datni: %s" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Tu grasies izdzēst uzaicinājumus:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Tu grasies atkārtoti nosūtīt uzaicinājumus:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Izdzēst uzaicinājumus" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Atkārtoti nosūtīt uzaicinājumus" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Uzaicināt personas" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Atstāt komandu" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliotēkas un veidnes" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "Šeit ir dažas bibliotēkas un sagataves, kuras var pievienot savam projektam" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Izpētīt vairāk un uzzinātt, kā sniegt ieguldījumu" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Veidnes ievietošanas laikā radās sarežģījumi. Veidne netika ievietota." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliotēkas" @@ -595,7 +567,7 @@ msgstr "Bibliotēkas" msgid "dashboard.loading-files" msgstr "ielādē datnes …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "notiek fontu ielāde …" @@ -615,39 +587,39 @@ msgstr "Pārvietot %s datnes uz" msgid "dashboard.move-to-other-team" msgstr "Pārvietot uz citu komandu" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Jauna datne" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Jauna datne" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Jauns projekts" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Jauns projekts" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "“%s” nav atrasta neviena atbilstība" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Šeit būs redzami piespraustie projekti" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Paziņojumi" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "E-pasta adrese tika veiksmīgi atjaunināta" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "E-pasta adrese tika veiksmīgi apliecināta" @@ -659,11 +631,11 @@ msgstr "Paziņojumu iestatījumi atjaunināti" msgid "dashboard.notifications.password-saved" msgstr "Parole ir veiksmīgi saglabāta." -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Apskatīt paziņojumus" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "Dalībieki: %s" @@ -671,15 +643,15 @@ msgstr "Dalībieki: %s" msgid "dashboard.open-in-new-tab" msgstr "Atvērt datni jaunā cilnē" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opcijas" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Kārtot pēc lomas" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Kārtot pēc stāvokļa" @@ -703,7 +675,7 @@ msgstr "Tu tagad esi šīs komandas īpašnieks." msgid "dashboard.permissions-change.viewer" msgstr "Tev tagad šajā komandā ir skatīšanās tiesības." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Piespraust/Atspraust" @@ -719,11 +691,11 @@ msgstr "Nevar apstrādāt spraudņa manifestu" msgid "dashboard.plugins.try-plugin" msgstr "Izmēģināt spraudni: " -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projekti" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Noņemt savu kontu?" @@ -731,35 +703,35 @@ msgstr "Noņemt savu kontu?" msgid "dashboard.removed-from-team" msgstr "Tu vairs neesi daļa no komandas \"%s\"." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Saglabāt iestatījumus" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Meklēt…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Meklē pēc “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Atlasīt saskarnes valodu" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Atlasīt dizainu" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot tumšs (noklusējuma)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot gaišs" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Sistēmas izskats" @@ -823,11 +795,11 @@ msgstr "Atjaunināt iestatījumus" msgid "dashboard.settings.notifications.title" msgstr "Paziņojumi" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Rādīt visas datnes" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Versiju vēsture" @@ -853,7 +825,7 @@ msgstr[2] "Datnes tika sekmīgi divkāršotas" msgid "dashboard.success-duplicate-project" msgstr "Projekts tika veiksmīgi divkāršots" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Datne tika sekmīgi pārvietota" @@ -865,90 +837,90 @@ msgstr "Datnes tika veiksmīgi pārvietotas" msgid "dashboard.success-move-project" msgstr "Projekts tika veiksmīgi pārvietots" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Par komandu" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Komandas dalībnieki" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Komandas projekti" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Pievienot savam projektam" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Saskarnes dizains" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Meklēšanas rezultāti" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Jāievada, lai meklētu" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Atcelt bibliotēkas publicēšanu" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Atjaunināt iestatījumus" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Ir aktīvs" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Kad tiks aktivizēts āķis, notikuma detaļas tiks piegādātās" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Var izdzēst vai mainīt tikai paša izveidotās tīmekļa aizķeres." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Satura tips" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Izveidot tīmekļa aizķeri" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Tīmekļa aizķere ir veiksmīgi izveidota." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Tīmekļa aizķeres ir vienkāršs veids, kā ļaut citām tīmekļa vietnēm un " "lietotnēm saņemt paziņojumus, kad Penpot notiek noteikti notikumi. Mēs " "nosūtīsim POST pieprasījumu katram norādītajam URL." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Jānospiež poga \"Pievienot tīmekļa aizķeri\", lai pievienotu kādu." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Līdz šim nav izveidota neviena tīmekļa aizķere." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Mans konts" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-pasta adrese" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Vārds" @@ -964,11 +936,11 @@ msgstr "Uzmanību" msgid "ds.component-subtitle" msgstr "Atjaunināmās sastāvdaļas:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Ļaut" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Atcelt" @@ -988,7 +960,7 @@ msgstr "Aktīvajās kopās vai izskatos nav pielietojamu tekstvienību." msgid "ds.inputs.numeric-input.no-matches" msgstr "Netika atrasts nekas atbilstošs." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Atvērt tekstvienību sarakstu" @@ -1000,15 +972,15 @@ msgstr "Autentificēšanās nodrošinātājs nav atļauts šim profilam" msgid "errors.auth-provider-not-configured" msgstr "Autentificēšanāš nodrošinātājs nav konfigurēts." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Šķiet, ka neesi autentificējies vai ir beidzies sesijas derīgums." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Fontu %s nevarēja ielādēt" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Fontus %s nevarēja ielādēt" @@ -1020,41 +992,41 @@ msgstr "Nevar augšupielādēt multivides datni." msgid "errors.character-limit-exceeded" msgstr "Pārsniegts rakstzīmju skaita ierobežojums" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Izmantotais pārlūks nevar veikt šo darbību" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "Bija kļūda saistībā ar piebildi" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Atvainojamies! Šī ir veca datne, kas izmanto novecojušu Penpot līdzekļu " "veidu, un to nevar atvērt." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "lai mēs varētu palīdzēt." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Lai arī Penpot vairs neatbalsta šo līdzekļu veidu, Tu vari" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "sazināties ar mums," -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Nevar nomest vecāka kopu paša bērna ceļā." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E-pasta adrese jau tiek izmantota" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-pasta adrese jau ir apliecināta." @@ -1066,29 +1038,29 @@ msgstr "E-pastu nevar izmantot kā paroli" msgid "errors.email-does-not-match-invitation" msgstr "E-pasta adrese neatbilst uzaicinājumā esošajai." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domēns nav atļauts" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "E-pastam “%s” ir daudz pastāvīgu atlēcienu atskaišu." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "E-pasta adrese “%s” ir atzīmēta surogātpasts vai pastāvīgi saņem atlēcienu." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Šķiet, ka tiek atvērta datne, kurā ir iespējota iespēja '%s', bet " "pašreizējā Penpot versija to neatbalsta vai tā ir atspējota." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Līdzeklis '%s' netiek atbalstīts." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Jāsatur ne vairāk par %s rakstzīmēm." @@ -1096,7 +1068,7 @@ msgstr "Jāsatur ne vairāk par %s rakstzīmēm." msgid "errors.field-min-length" msgstr "Jāsatur vismaz vienu rakstzīmi." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Tukšs lauks" @@ -1104,18 +1076,18 @@ msgstr "Tukšs lauks" msgid "errors.field-not-all-whitespace" msgstr "Nosaukumam jāsatur kādu citu rakstzīmi bez atstarpes." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Izskatās, ka ir nesaderība starp iespējotajām iespējām un iespējām datnē, " "kuru tiek mēģināts atvērt. Jāpiemēro '%s' migrācijas, pirms datne var tikt " "atvērta." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Noticis kaut kas nelāgs." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Kaut kas nogāja greizi ar tīmekļa strādni." @@ -1131,7 +1103,7 @@ msgstr "Nederīgi dati" msgid "errors.invalid-email" msgstr "Lūgums ievadīt derīgu e-pasta adresi" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Apstiprinājuma e-pastam jāatbilst" @@ -1155,11 +1127,11 @@ msgstr "Iespējams, ka šis uzaicinājums ir atcelts vai ir beidzies tā derīgu msgid "errors.ldap-disabled" msgstr "LDAP autentifikācija ir atspējota." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Ir sasniegts '%s' ierobežojums. Jāsazinās ar atbalstu." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Sasniegts lielākais pieļaujamais e-pasta adrešu skaits (%s), ko var " @@ -1177,21 +1149,21 @@ msgstr "Šķiet, ka attēla saturs neatbilst datnes paplašinājumam." msgid "errors.media-type-not-allowed" msgstr "Šķiet, ka šis nav derīgs attēls." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Uzaicinātajam profilam ir apklusināta e-pasta saņemšana (ziņojumi par " "surogātpastu vai daudz atlēcienu)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Notiek pārcelšana" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Tikai versijas izveidotājs var to aizslēgt" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Tikai versijas izveidotājs var to atslēgt" @@ -1203,7 +1175,7 @@ msgstr "Apstiprinājuma parolei ir jāsakrīt" msgid "errors.password-too-short" msgstr "Parolē ir jābūt vismaz 8 rakstzīmēm" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Starpliktuvē ir nederīgi dati" @@ -1211,7 +1183,7 @@ msgstr "Starpliktuvē ir nederīgi dati" msgid "errors.profile-blocked" msgstr "Profils ir bloķēts" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Profila epasta saņemšana ir apklusināta (ziņojumi par surogātpastu vai " @@ -1221,25 +1193,25 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Reģistrācija pašlaik ir atspējota." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG ir nederīgs vai bojāts" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Noteikta nesaderīga iespēja '%s'" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Komandā ir nepietiekams dalībnieku skaits, lai to pamestu. Iespējams, ka to " "ir vēlams izdzēst." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Dalībnieks, kuru mēģināt piešķirt, nepastāv." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Īpašnieks nevar pamest komandu, ir jāpiešķir īpašnieka loma citam." @@ -1247,11 +1219,11 @@ msgstr "Īpašnieks nevar pamest komandu, ir jāpiešķir īpašnieka loma citam msgid "errors.token-set-already-exists" msgstr "Jau pastāv kopa ar tādu pašu nosaukumu" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "Nevar pabeigt nomešanu, ceļā jau pastāv kopa ar tādu pašu nosaukumu." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Pastāv izskata iespēja ar tādu pašu nosaukumu" @@ -1286,11 +1258,11 @@ msgstr "" "Tekstvienību nosaukumos var būt tikai burti un cipari, kas atdalīti ar " "rakstzīmi \".\", un tie nedrīkst sākties ar zīmi \"$\"." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Nederīga tekstvienības vērtība: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Trūkst tekstvienību atsauces: " @@ -1298,19 +1270,19 @@ msgstr "Trūkst tekstvienību atsauces: " msgid "errors.tokens.number-too-large" msgstr "Nederīga tekstvienības vērtība. Aprēķinātā vērtība ir pārāk liela: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "Necaurspīdīgumam ir jābūt starp 0 un 100 % vai 0 un 1 (piem., 50% jeb 0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Tekstvienībai ir atsauce uz sevi" -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "Vilkuma platumam ir jābūt vienādam vai lielākam par 0." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Nederīga vērtība: mērvienības nav atļautas." @@ -1318,47 +1290,47 @@ msgstr "Nederīga vērtība: mērvienības nav atļautas." msgid "errors.unexpected-error" msgstr "Atgadījās neparedzēta kļūda." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Nezināma tekstvienība" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Šī versija jau ir aizslēgta" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Šī versija ir aizslēgta, un citi to nevar izdzēst" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Datnei ir nesaderīgs versijas numurs" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Savienojuma kļūda, URL nav sasniedzams" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL neiztur pārbaudi." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Pēdējā piegāde nebija veiksmīga." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Kļūda SSL pārbaudē." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Noilgums" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Pārbaudes laikā atgadījās neparedzēta kļūda" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Neparedzēts statuss %s" @@ -1378,17 +1350,17 @@ msgstr "Apraksts" msgid "feedback.description-placeholder" msgstr "Lūgums aprakstīt atgriezeniskās saites iemeslu" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Mēs esam priecīgi Tevi šeit redzēt. Ja ir nepieciešama palīdzība, lūgums " "meklēt pirms ieraksta veikšanas." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot kopiena" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Citi veidi, kā ar mums sazināties" @@ -1413,11 +1385,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Sazināties ar mums" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Šeit, lai palīdzētu ar tehniskajiem jautājumiem." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X atbalsta konts" @@ -1469,7 +1441,7 @@ msgstr "Lejupielādēt avota attēlu" msgid "inspect.attributes.shadow" msgstr "Ēna" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Izmērs un novietojums" @@ -1477,39 +1449,27 @@ msgstr "Izmērs un novietojums" msgid "inspect.attributes.stroke" msgstr "Vilkums" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centrs" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Iekšpuse" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Ārpuse" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Burtu stils un veids" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Fontu saime" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Fonta izmērs" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Fonta stils" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Burtu atstarpes" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Līnijas augstums" @@ -1521,11 +1481,11 @@ msgstr "Nav" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Pasvītrojums" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Virsraksta stils" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Mazie burti" @@ -1537,7 +1497,7 @@ msgstr "Nav" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Atiestatīt" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Lielie burti" @@ -1631,43 +1591,43 @@ msgstr "Ievietot starpliktuvē" msgid "inspect.tabs.styles.toggle-style" msgstr "Pārslēgt paneli %s" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Atzīmēt visu kā izlasītu" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Saīsnes" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Pieņemt" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Aktīvs" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Pievienot" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Pievienot pielāgotu fontu" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Pievieno..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Pārvaldnieks" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Visi" @@ -1675,29 +1635,29 @@ msgstr "Visi" msgid "labels.and" msgstr "un" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Atpakaļ" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Izskatās, ka mazliet jāuzgaida un jāmēģina vēlreiz; mēs veicam nelielus " "mūsu serveru uzturēšanas darbus." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Nepareiza vārteja" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Atcelt" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Aizvērt" @@ -1705,7 +1665,7 @@ msgstr "Aizvērt" msgid "labels.code" msgstr "Kods" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Sakļaut" @@ -1713,11 +1673,11 @@ msgstr "Sakļaut" msgid "labels.comment" msgstr "Piebilde" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Komentāri" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Kopiena" @@ -1729,11 +1689,11 @@ msgstr "Aprēķinātās" msgid "labels.confirm-password" msgstr "Apstiprināt paroli" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Turpināt" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Varat turpināt ar Penpot kontu" @@ -1745,27 +1705,27 @@ msgstr "Ievietot starpliktuvē" msgid "labels.copy-color" msgstr "Ievietot krāsu starpliktuvē" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Kopēt saiti" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Izveidot" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Izveidot jaunu komandu" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Jāievada jaunās komandas nosaukums" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Pārskata panelis" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Dzēst" @@ -1777,7 +1737,7 @@ msgstr "Dzēst komentāru" msgid "labels.delete-comment-thread" msgstr "Dzēst pavedienu" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Dzēst uzaicinājumu" @@ -1785,15 +1745,15 @@ msgstr "Dzēst uzaicinājumu" msgid "labels.delete-multi-files" msgstr "Izdzēst %s datnes" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Izstrādātājs" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Atmest" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Melnraksti" @@ -1801,27 +1761,27 @@ msgstr "Melnraksti" msgid "labels.duplicate" msgstr "Pavairot" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Labot" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Redaktors" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Tukšs" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Kļūda" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Beidzies derīgums" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Izgūt" @@ -1833,55 +1793,55 @@ msgstr "Atsauksme atspējota" msgid "labels.feedback-sent" msgstr "Atsauksme nosūtīta" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Fontu saime" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Stili" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fonti" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "GitHub repozitorijs" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Sniegt atsauksmi" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Atpakaļ" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Grafiskais dizains" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Palīdzības centrs" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Paslēpt" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Paslēpt atrisinātos komentārus" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Ievietot" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Neaktīvs" @@ -1889,31 +1849,31 @@ msgstr "Neaktīvs" msgid "labels.info" msgstr "Infomācija" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Instalētie fonti" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Iekšēja kļūda" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Uzaicinājumi" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Valoda" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Bibliotēkas un veidnes" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Lādē…" @@ -1921,7 +1881,7 @@ msgstr "Lādē…" msgid "labels.lock" msgstr "Aizslēgt" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Pieteikties vai reģistrēties" @@ -1929,19 +1889,19 @@ msgstr "Pieteikties vai reģistrēties" msgid "labels.login" msgstr "Pieteikties" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Atteikties" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Tirgvedība" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Dalībnieks" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Dalībnieki" @@ -1953,45 +1913,45 @@ msgstr "Pieminēt" msgid "labels.new-password" msgstr "Jaunā parole" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Nākamais" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Jūs esat pieķerti! Šeit tiks parādīti jaunu komentāru paziņojumi." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Nav gaidošu uzaicinājumu." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Sapulcē savus cilvēkus, un kopā radiet lieliskas lietas!" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Ups!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Paziņojumi" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "0 datņu" msgstr[1] "%s datne" msgstr[2] "%s datnes" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "%s plātņu" msgstr[1] "%s plātne" msgstr[2] "%s plātnes" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "0 projektu" @@ -2002,31 +1962,31 @@ msgstr[2] "%s projekti" msgid "labels.old-password" msgstr "Vecā parole" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Iespējas" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Cits (jānorāda)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Cits" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Īpašnieks" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Parole" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Gaidīšana" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Piepsraustie projekti" @@ -2034,23 +1994,23 @@ msgstr "Piepsraustie projekti" msgid "labels.post" msgstr "Ieraksts" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Iepriekšējais" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Produktu vai lietotāju pieredzes dizains" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Produktu vadība" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profils" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projekti" @@ -2058,23 +2018,23 @@ msgstr "Projekti" msgid "labels.refresh" msgstr "Atsvaidzināt" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Laidiena apraksts" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Noņemt" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Noņemt dalībnieku" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Pārdēvēt" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Pārdēvēt grupu" @@ -2098,7 +2058,7 @@ msgstr "jauna atbilde" msgid "labels.reply.thread" msgstr "Atbildēt" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Nosūtīt uzaicinājumu vēlreiz" @@ -2106,43 +2066,43 @@ msgstr "Nosūtīt uzaicinājumu vēlreiz" msgid "labels.restore" msgstr "Atjaunot" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Pamēģināt vēlreiz" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Loma" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Saglabāt" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Meklēt" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Meklēt fontu" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Atlasīt iespēju" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Nosūtīt" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Nosūtīšana…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Mēs esam ieplānotos sistēmu uzturēšanas darbos." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Pakalpojums nav pieejams" @@ -2150,67 +2110,67 @@ msgstr "Pakalpojums nav pieejams" msgid "labels.sets" msgstr "Kopas" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Iestatījumi" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Kopīgot" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibliotēkas" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Parādīt" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Rādīt visus komentārus" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Rādīt komentāru sarakstu" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Rādīt tikai manas pieminēšanas" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Rādīt tikai manas piebildes" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Avoti" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Sākt" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Statuss" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Students vai pasniedzējs" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Izskati" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Pamācības" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Nezināma kļūda" @@ -2222,23 +2182,23 @@ msgstr "Atslēgt" msgid "labels.unpublish-multi-files" msgstr "Atcelt %s datņu publicēšanu" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Atjaunināt" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Atjaunināt komandu" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Augšupielādēt" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Augšupielādēt pielāgotos fontus" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Augšupielādē…" @@ -2246,11 +2206,11 @@ msgstr "Augšupielādē…" msgid "labels.view-only" msgstr "TIKAI SKATĪT" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Pārlūks" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Tīmekļa aizķeres" @@ -2258,11 +2218,11 @@ msgstr "Tīmekļa aizķeres" msgid "labels.write-new-comment" msgstr "Rakstīt jaunu komentāru" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(Jūs)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Mans konts" @@ -2399,61 +2359,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Pievienot “%s” kā koplietojamu bibliotēku" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Liela nobīde" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Apstiprināt jauno e-pasta adresi" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Mēs nosūtīsim e-pasta ziņojumu uz pašreizējo e-pasta adresi “%s”, lai " "pārbaudītu identitāti." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Jauna e-pasta adrese" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Mainīt e-pasta adresi" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "E-pasta maiņa" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Izveidot tīmekļa aizķeri" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Izveidot tīmekļa aizķeri" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Vērtuma URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Atcelt un paturēt manu kontu" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Jā, dzēst manu kontu" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Pēc konta noņemšanas tiks zaudēti visi pašreizējie projekti un arhīvi." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Vai tiešām izdzēst savu kontu?" @@ -2501,29 +2461,29 @@ msgstr "Vai tiešām izdzēst %s datnes?" msgid "modals.delete-file-multi-confirm.title" msgstr "Izdzēš %s datnes" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Vai tiešām izdzēst šo fontu stilu? Tas netiks ielādēts, ja tiek izmantots " "datnē." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Fonta stila dzēšana" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "Vai tiešām izdzēst šo fontu? Tas netiks ielādēts, ja tiek izmantots datnē." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Fonta dzēšana" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Vai tiešām izdzēst šo lapu?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Dzēst lapu" @@ -2539,35 +2499,35 @@ msgstr "Vai tiešām izdzēst šo projektu?" msgid "modals.delete-project-confirm.title" msgstr "Dzēst projektu" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Nav izdzēšamu datņu" msgstr[1] "Izdzēst datni" msgstr[2] "Izdzēst datnes" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Tie nav aktivēti nevienā datnē." msgstr[1] "Tas nav aktivēts nevienā datnē." msgstr[2] "Tie nav aktivēti nevienā datnē." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Šīs bibliotēkas ir aktivētas šeit: " msgstr[1] "Šī bibliotēka ir aktivēta šeit: " msgstr[2] "Šīs bibliotēkas ir aktivētas šeit: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Nav izdzēšamu datņu" msgstr[1] "Vai tiešām izdzēst šo datni?" msgstr[2] "Vai tiešām izdzēst šīs datnes?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Nav izdzēšamu datņu" @@ -2588,19 +2548,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Notiek komandas dzēšana" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Dzēst dalībnieku" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Vai tiešām izdzēst šo dalībnieku no komandas?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Dzēst komandas dalībnieku" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -2613,170 +2573,170 @@ msgstr[2] "" "Līdzekļi, kas jau tiek izmantoti šajā datnē, paliks tajā (dizains netiks " "salauzts)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Izdzēst tīmekļa aizķeri" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Vai tiešām izdzēst šo tīmekļa aizķeri?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Notiek tīmekļa aizķeres izdzēšana" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Labot tīmekļa aizķeri" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Labot tīmekļa aizķeri" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Nosūtīt uzaicinājumu" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-pasta ziņojumi, atdalīti ar komatiem" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "Daži dalībnieki jau ir komandā. Mēs uzaicināsim pārējos." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Komandā var uzaicināt dalībniekus, lai viņi varētu piekļūt šai un citām " "komandas datnēm." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Uzaicināt dalībniekus uz komandu" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Tā kā esi šīs komandas vienīgais dalībnieks, tā tiks izdzēsta līdz ar tās " "projektiem un datnēm." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Vai tiešām pamest komandu %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Nevar pamest komandu, ja nav cita dalībnieka, ko norādīt kā īpašnieku. " "Iespējams, ka komandu ir vēlams izdzēst." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Tu esi šīs komandas īpašnieks. Lūgums pirms pamešanas atlasīt citu " "dalībnieku, lai to norādītu kā īpašnieku." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Nozīmēt un pamest" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Atlasīt dalībnieku, ko nozīmēt" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Pirms pamešanas" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Pamest komandu" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Vai tiešām pamest šo komandu?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Komandas pamešana" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Pārvietot" msgstr[1] "Pārvietot" msgstr[2] "Pārvietot" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Vai tiešām pārvietot šīs bibliotēkas?" msgstr[1] "Vai tiešām pārvietot šīs bibliotēkas?" msgstr[2] "Vai tiešām pārvietot šīs bibliotēkas?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Pārvietot bibliotēkas" msgstr[1] "Pārvietot bibliotēkas" msgstr[2] "Pārvietot bibliotēkas" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Nobīdes apjoms" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Nodot īpašumtiesības" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Ja tiks nodotas īpašumtiesības, ieņemamā loma tiks nomainīta uz " "\"Pārvaldnieks\", zaudējot dažas atļaujas šajā komandā. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Tu esi šīs komandas pašreizējais īpašnieks. Vai tiešām iecelt %s par jauno " "komandas īpašnieku?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Jauns komandas īpašnieks" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publicēt" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Bibliotēka ir tukša. Vai tiešām publicēt to?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publicēt tukšu bibliotēku" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Maza nobīde" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Nav atlases" msgstr[1] "Atcelt publicēšanu" msgstr[2] "Atcelt publicēšanu" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Nav izvēlēta neviena bibliotēka" msgstr[1] "Vai tiešām atcelt šīs bibliotēkas publicēšanu?" msgstr[2] "Vai tiešām atcelt šo bibliotēku publicēšanu?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Atcelt bibliotēkas publicēšanu" @@ -2801,11 +2761,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Atjaunināt sastāvdaļu koplietojamā bibliotēkā" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Šī lapa nepastāv" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 kļūda" @@ -2825,11 +2785,11 @@ msgstr "" "Jāpārbauda savs e-pasts un jāklikšķina uz saites, lai izveidotu jaunu " "paroli." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Reģistrēties bez maksas" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "un sāc izmantot Penpot dažu sekunžu laikā!" @@ -2837,67 +2797,67 @@ msgstr "un sāc izmantot Penpot dažu sekunžu laikā!" msgid "not-found.made-with-love" msgstr "Izstrādāts ar MĪLESTĪBU un atvērta pirmkoda" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Jau ir pieprasīta piekļuve šai datnei." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Jau ir pieprasīta piekļuve šai datnei vai šīs komandas citām datnēm vai " "projektiem." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Tu jau pieprasīji piekļuvi šim projektam vai šīs komandas citiem projektiem " "vai datnēm." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Tu jau pieprasīji piekļuvi šim projektam." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "PIEPRASĪT PIEKĻUVI" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Jāatceras, ka, ja īpašnieks to ļauj, tiksi uzaicināts pievienoties komandai." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Pieprasījums tika veiksmīgi nosūtīts." -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Nav piekļuves šai datnei." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Doties uz savu Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Ja īpašnieks to ļauj, tiksi uzaicināts pievienoties komandai." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot datne" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Nav piekļuves šim projektam." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJEKTS" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Lai piekļūtu šai datnei, jāvaicā komandas īpašniekam." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Lai piekļūtu šim projektam, jāvaicā komandas īpašniekam." @@ -2911,23 +2871,23 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Ir pieejama jauna versija." -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Uzaicinājums veiksmīgi nosūtīts" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Uzaicinājuma saite nokopēta" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "Profilu nevar izdzēst. Pirms turpināšanas jāpiešķir savas komandas citiem." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profils ir veiksmīgi saglabāts." -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "" "Patiesuma pārbaudes e-pasta ziņojums tika nosūtīts uz %s. Jāpārbauda savs " @@ -2961,78 +2921,78 @@ msgstr "" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Izveidot komandu" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Uzaicināt dalībniekus" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Uzaicināt ar lomu:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Ķeramies pie darba!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Īsta koda izveidošana no dizainiem" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Dizaina sistēmu izveidošana" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototipēšana" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Izstrādāt lietotnes lietotāju saskarni/pieredzi" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Karkasattēlošana" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Kādam nolūkam Penpot tiks izmantota?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Ļauj mums mazliet uzzināt par Tevi, lai palīdzētu mums panākt, lai Penpot " "Tev darbojas. Tavas atbildes palīdzēs mums noteikt svarīgas iespējas un " "norādīt Tev īsto uzsākšanas virzienu." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Palīdzi mums Tevi iepazīt" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Kuru no šiem rīkiem Tu izmanto visvairāk?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Kāda veida darbu veic?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Pastāsti mums par savu darbu" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Kur Tu vēlētos sākt?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Izglītība" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Savām vajadzībām" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Darbs" @@ -3086,11 +3046,11 @@ msgstr "{%s} tekstvienība nepastāv vai ir izdzēsta." msgid "profile.recovery.go-to-login" msgstr "Doties uz pieteikšanos" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Atdalīt" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Jaukts" @@ -3102,35 +3062,35 @@ msgstr "Noņemt krāsu" msgid "settings.select-this-color" msgstr "Atlasīt vienumus, kas izmanto šo stilu" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Komentāri" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Līdzināt pie apakšas" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Līdzināt vidū līmeniski" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Līdzināt pie kreisās malas" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Līdzināt pa labi" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Līdzināt pie augšas" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Līdzināt vidū stateniski" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Izveidot plātni no atlases" @@ -3138,51 +3098,51 @@ msgstr "Izveidot plātni no atlases" msgid "shortcuts.bold" msgstr "Pārslēgt treknrakstu" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Būla starpība" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Būla izslēgšana" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Būla krustošanās" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Būla apvienošana" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Sūtīt uz aizmuguri" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Sūtīt atpakaļ" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Virzīt uz priekšu" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Izvirzīt priekšā" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Iztīrīt atsaukšanu" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Kopēt" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Ievietot saiti starpliktuvē" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Ievietot īpašības starpliktuvē" @@ -3190,35 +3150,35 @@ msgstr "Ievietot īpašības starpliktuvē" msgid "shortcuts.create-new-project" msgstr "Izveidot jaunu" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Izgriezt" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Tālināt" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Dzēst" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Dzēst mezglu" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Atdalīt sastāvdaļu" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Līkne" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Plātne" @@ -3226,39 +3186,39 @@ msgstr "Plātne" msgid "shortcuts.draw-nodes" msgstr "Zīmēt ceļu" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Ceļš" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Taisnstūris" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Teksts" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Divkāršot" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Atcelt" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Izgūt apveidus" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Tālummainīt, lai ietilpinātu visu" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Apvērst līmeniski" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Apvērst stateniski" @@ -3282,23 +3242,23 @@ msgstr "Doties uz koplietojamām bibliotēkām" msgid "shortcuts.go-to-search" msgstr "Meklēt" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Apkopot" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Izkliedēt līmeniski" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Rādīt / paslēpt lietotāja saskarni" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Tuvināt" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Ievietot attēlu" @@ -3306,7 +3266,7 @@ msgstr "Ievietot attēlu" msgid "shortcuts.italic" msgstr "Pārslēgt slīprakstu" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Savienot mezglus" @@ -3314,39 +3274,39 @@ msgstr "Savienot mezglus" msgid "shortcuts.line-through" msgstr "Pārslēgt svītrojumu" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Izveidot stūri" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Izveidot līkni" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Maska" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Sapludināt mezglus" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Pārvietot" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Strauji pārvietot uz leju" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Strauji pārvietot pa kreisi" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Strauji pārvietot pa labi" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Strauji pārvietot uz augšu" @@ -3354,333 +3314,333 @@ msgstr "Strauji pārvietot uz augšu" msgid "shortcuts.move-nodes" msgstr "Pārvietot mezglu" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Pārvietot uz leju" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Pārvietot pa kreisi" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Pārvietot pa labi" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Pārvietot uz augšu" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Nākamā plātne" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Saīsnes nav atrastas" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Iestatīt necaurspīdīgumu uz 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Iestatīt necaurspīdīgumu uz 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Iestatīt necaurspīdīgumu uz 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Iestatīt necaurspīdīgumu uz 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Iestatīt necaurspīdīgumu uz 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Iestatīt necaurspīdīgumu uz 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Iestatīt necaurspīdīgumu uz 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Iestatīt necaurspīdīgumu uz 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Iestatīt necaurspīdīgumu uz 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Iestatīt necaurspīdīgumu uz 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Krāsu atlasītājs" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Atvērt pārlūka komentāru sadaļu" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Doties uz pārskata paneli" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Doties uz skatītāja apskatīšanas sadaļu" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Doties uz skatītāja mijiedarbības sadaļu" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Doties uz skatītāja mijiedarbības sadaļu" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Doties uz darbvietu" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " vai " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Ielīmēt" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Ielīmēt īpašības" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Spraudņu pārvaldnieks" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Iepriekšējā plātne" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Atatsaukt" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Pārdēvēt" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Atiestatīt tālummaiņu" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Mērogot" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Pamati" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Pārskata panelis" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Pārlūks" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Darbvieta" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Atlasīt visu" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Atlasīt nākamo slāni" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Atlasīt vecākslāni" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Atlasīt iepriekšējo slāni" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Atdalīt mezglus" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Rādīt/paslēpt pikseļu režģi" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Rādīt/paslēpt saīsnes" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Pieķerties mezgliem" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Pieķerties pikseļu režģim" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Sākt labošanu" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Līdzināšana" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Labot" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Vispārējs" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Galvenā izvēlne" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Mainīt slāņus" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigācija" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigācija" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigācija" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Paneļi" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Ceļi" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formas" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Teksti" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Rīki" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Tālummaiņa" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Tālummaiņa" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Iestatīt sīktēlus" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Īsinājumtaustiņi" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Pārslēgt dinamisko līdzināšanu" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Pārslēgt līdzekļus" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Pārslēgt krāsu paleti" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Pārslēgt fokusa režīmu" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Pārslēgt pilnekrāna režīmu" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Rādīt/paslēpt vadlīnijas" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Pārslēgt vēsturi" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Pārslēgt slāņus" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Pievienot / Noņemt elastīgo izkārtojumu" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Pievienot/noņemt režģa izkārtojumu" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Slēgt / Atslēgt" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Slēgt proporcijas" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Rādīt/paslēpt mērjoslas" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Pievilkt pie vadlīnijām" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Pievilkt pie mērjoslu vadlīnijām" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Pārslēgt teksta paleti" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Mainīt izskatu" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Pārslēgt redzamību" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Pārslēgt tālummaiņas stilu" @@ -3688,31 +3648,31 @@ msgstr "Pārslēgt tālummaiņas stilu" msgid "shortcuts.underline" msgstr "Pārslēgt pasvītrojumu" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Atsaukt" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Atapkopot" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Noņemt masku" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Izkliedēt stateniski" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Tālummaiņas samazinājums" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Tālummaiņas palielinājums" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Tālummainīt uz atlasi" @@ -3765,145 +3725,145 @@ msgstr "" msgid "subscription.dashboard.team-plan" msgstr "Komandas plāns" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Abonements" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Pievieno maksājumu veidu, lai turpinātu pēc sava izmēģinājuma laika" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Visas lietpratēju plāna priekšrocības un:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Visas neierobežotā plāna priekšrocības un:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s redaktori)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Uzņēmējdarbība" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Uzņēmējdarbībai (izmēģinājuma)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90 dienu automātiski saglabātas versijas un datņu atkope" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Noteikts ikmēneša rēķins" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Pārvaldīt savu abonementu" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Pievienot maksājumu informāciju" -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Pagaidām izlaist un uzsākt izmēģinājumu" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Palīdzi mums attīstīties un padarīt Tavu izmēģinājumu vienkāršāku" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Pašreiz ir %s cilvēku Tavās komandās, kuri var labot." msgstr[1] "Pašreiz ir %s cilvēks Tavās komandās, kas var labot." msgstr[2] "Pašreiz ir %s cilvēki Tavās komandās, kuri var labot." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Uzmanību: pārslēgšanās uz zemāku plānu nozīmē mazāk krātuves un īsāku " "rezerves kopiju un versiju vēsturi." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "" "Pēc izmēģinājuma tiks ieturēta maksa. Šobrīd nav nepieciešama maksājumu " "karte." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/mēnesī" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Pielietot %s savām komandām" -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Penpot dalībnieks kopš: %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Vairāk informācijas" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Citi Penpot plāni" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "apvienība mēnesī" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Profesionāls" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Tavs abonements" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Uzsākt bezmaksas izmēģinājumu" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Abonēt" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Tu vari labot savu abonementu jebkurā laikā sava konta informācijas lapā " "\"Abonements\"." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Izbaudi savu plānu!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Tu esi %s." -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Izmēģini 14 dienas bez maksas" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Neierobežots" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Neierobežots (izmēģinājuma)" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Ikmēneša rēķins ierobežots līdz $175" @@ -3911,42 +3871,42 @@ msgstr "Ikmēneša rēķins ierobežots līdz $175" msgid "subscription.workspace.header.menu.option.power-up" msgstr "Iedarbini savu plānu" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "Atlasīti %s uzaicinājumi" msgstr[1] "Atlasīts %s uzaicinājums" msgstr[2] "Atlasīti %s uzaicinājumi" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Fontu nodrošinātāji - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Fonti - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projekti - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Meklēšana - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Koplietojamās bibliotēkas - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Modelēšanas brīvība komandām" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Sniegt atsauksmes - Penpot" @@ -3954,7 +3914,7 @@ msgstr "Sniegt atsauksmes - Penpot" msgid "title.settings.notifications" msgstr "Paziņojumi - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Iestatījumi - Penpot" @@ -3962,27 +3922,27 @@ msgstr "Iestatījumi - Penpot" msgid "title.settings.password" msgstr "Parole - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profils - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Uzaicinājumi - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Dalībnieki - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Iestatījumi - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Tīmekļa aizķeres - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - skatīšana - Penpot" @@ -3994,99 +3954,99 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Atdalīt tekstvienību" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Lapā nav atrasta neviena plātne." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Plātne netika atrasta." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Komentāri (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Nerādīt mijiedarbības" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Pilnekrāns" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Apskatīt (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Mijiedarbības" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Mijiedarbības (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Kopēt saiti" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Rādīt mijiedarbības" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Rādīt mijiedarbības pēc klikšķa" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Vietnes karte" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Pēdējā piegāde bija veiksmīga." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Līdzināt līmeniskajā vidū (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Izlīdzināt līmeniskās atstarpes (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Līdzināt pa kreisi (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Līdzināt pa labi (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Līdzināt pie apakšas (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Līdzināt stateniskajā vidū (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Izlīdzināt stateniskās atstarpes (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Līdzināt pie augšas (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Pievienot bibliotēku" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Visi līdzekļi" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Krāsas" @@ -4094,7 +4054,7 @@ msgstr "Krāsas" msgid "workspace.assets.colors.add-color" msgstr "Pievienot krāsu" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Sastāvdaļas" @@ -4102,7 +4062,7 @@ msgstr "Sastāvdaļas" msgid "workspace.assets.components.add-component" msgstr "Pievienot sastāvdaļu" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s varianti" @@ -4110,15 +4070,15 @@ msgstr "%s varianti" msgid "workspace.assets.create-group" msgstr "Izveidot kopu" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Vienumi tiks automātiski nosaukti kā “kopas nosaukums/vienuma nosaukums”" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Dzēst" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Divkāršot" @@ -4126,39 +4086,39 @@ msgstr "Divkāršot" msgid "workspace.assets.duplicate-main" msgstr "Divkāršot galveno" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Labot" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Atlasīt" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Režģa skats" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Kopa" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Kopas nosaukums" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Saraksta skats" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "vietējā bibliotēka" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Pārvaldīt bibliotēku" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Līdzekļi nav atrasti" @@ -4166,7 +4126,7 @@ msgstr "Līdzekļi nav atrasti" msgid "workspace.assets.open-library" msgstr "Atvērt bibliotēkas datni" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Pārdēvēt" @@ -4174,11 +4134,11 @@ msgstr "Pārdēvēt" msgid "workspace.assets.rename-group" msgstr "Pārdēvēt kopu" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Meklēt līdzekļus" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Koplietojama bibliotēka" @@ -4189,43 +4149,43 @@ msgstr[0] "%s sastāvdaļu" msgstr[1] "%s sastāvdaļa" msgstr[2] "%s sastāvdaļas" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Kārtot" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Burtu stili un veidi" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Pievienot tipogrāfiju" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Izmērs" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Fonta stils" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Doties uz stilu bibliotēkas datni, lai labotu" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Burtstarpa" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Rindas augstums" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Teksta pārveide" @@ -4233,67 +4193,67 @@ msgstr "Teksta pārveide" msgid "workspace.assets.ungroup" msgstr "Atapkopot" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Izveidot laukumu" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Izveidot plātni" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Apvienot šūnas" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Pievienot 1 sleju pa labi" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Pievienot 1 sleju pa kreisi" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Izdzēst sleju" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Izdzēst slejas un apveidus" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Divkāršota sleja" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Pievienot 1 rindu zem" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Pievienot 1 rindu virs" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Izdzēst rindu" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Izdzēst rindu un apveidus" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Divkāršot rindu" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Fokusa režīms" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Izslēgt fokusu" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Ieslēgt fokusu" @@ -4305,191 +4265,191 @@ msgstr "Lineārais gradients" msgid "workspace.gradients.radial" msgstr "Radiālais gradients" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Atspējot dinamisko līdzināšanu" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Atspējot proporcionālo mērogu" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Atspējot pieķeršanos vadotnēm" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Atspējot pieķeršanos pikselim" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Atspējot pievilkšanu mērjoslu vadlīnijām" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Iespējot dinamisko līdzināšanu" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Iespējot proporcionālo mērogu" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Pieķerties vadotnēm" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Iespējot pieķeršanos pikselim" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Pievilkt pie mērjoslu vadlīnijām" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Paslēpt plātņu nosaukumus" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Paslēpt vadlīnijas" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Paslēpt krāsu paleti" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Paslēpt pikseļu režģi" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Paslēpt mērjoslas" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Paslēpt fontu paleti" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Labot" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Datne" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Palīdzība un informācija" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Pielāgojumi" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Skatīt" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Atkārtot" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Atlasīt visu" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Rādīt plātņu nosaukumus" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Parādīt vadlīnijas" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Rādīt krāsu paleti" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Rādīt pikseļu režģi" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Rādīt mērjoslas" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Rādīt fontu paleti" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Pārslēgties uz tumšo izskatu" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Pārslēgties uz gaišo izskatu" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Pārslēgties uz sistēmas izskatu" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Atsaukt" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Atiestatīt" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Saglabāšanas kļūda" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Saglabāts" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Saglabāšana" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Kopīgot" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Skatīšana (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Tālummaiņa" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Aizpildījums — mērogot, lai aizpildītu" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Ietilpināt — samazināt, lai ietilpinātu" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Tālummainīt, lai ietilpinātu visu" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Pilnekrāns" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Tālummainīt uz atlasi" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Labot režģi" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Iziet" @@ -4497,7 +4457,7 @@ msgstr "Iziet" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Apakšējā atbīde" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Rādīt četrpusējas atbīdes iespējas" @@ -4533,30 +4493,30 @@ msgstr "Darīts" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Noteikt atrašanās vietu" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Norādīt režģa izkārtojumu" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "%s krāsu" msgstr[1] "%s krāsa" msgstr[2] "%s krāsas" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Bibliotēkā vēl nav krāsu stilu" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Bibliotēkā vēl nav burtu stilu un veidu" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Datņu bibliotēka" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Nesen izmantotās krāsas" @@ -4568,96 +4528,96 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Saglabāt krāsu stilu" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "%s sastāvdaļu" msgstr[1] "%s sastāvdaļa" msgstr[2] "%s sastāvdaļas" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Savienota ar" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Vai pievieno kādas no šīm, lai izmēģinātu:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "Tavā komandā nav kopīgotu bibliotēku, kuras var meklēt" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "dažas sagataves šeit" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Datņu bibliotēka" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "%s attēli" msgstr[1] "%s attēls" msgstr[2] "%s attēli" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTĒKAS ŠAJĀ DATNĒ" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTĒKAS" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "BIBLIOTĒKAS JAUNINĀJUMI" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Ielādē…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Nav nevienas koplietojamās bibliotēkas, kas būtu jāatjaunina" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "“%s” nav atrasta neviena atbilstība" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Meklēt koplietojamās bibliotēkas" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "KOPLIETOJAMĀS BIBLIOTĒKAS" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Savienot bibliotēku" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "%s burtu stilu un veidu" msgstr[1] "%s burtu stils un veids" msgstr[2] "%s burtu stili un veidi" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Atvienot bibliotēku" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Atjaunināt" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "apskatīt visas izmaiņas" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "ATJAUNINĀJUMI" @@ -4693,107 +4653,107 @@ msgstr "Pārslēgt aizmiglojumu" msgid "workspace.options.canvas-background" msgstr "Kanvas fons" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Apcirpt saturu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Sastāvdaļa" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Piezīme" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Ievietot starpliktuvē" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Izveidot piezīmi" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Labot piezīmi" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Galvenais" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Mijmainīt sastāvdaļu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Šajā bibliotēkā vēl nav līdzekļu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Atsaistīta" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Noteikt nesaderīgus variantus" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Šai sastāvdaļai ir nesaderīgi varianti. Jāpārliecinās, ka katram no tiem ir " "neatkārtojama īpašību vērtību kopa." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Atrast atkārtojošos variantus" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Dažiem variantiem ir vienādas īpašības un vērtības" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Šiem variantiem ir vienādas īpašības un vērtības. Jāpielāgo vērtības, lai " "tās varētu iegūt." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Šim variantam ir tādas pašas īpašības un vērtības kā citam. Jāpielāgo " "vērtības, lai tās varētu iegūt." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Dažiem no šiem variantiem ir vienādas īpašības un vērtības. Jāpielāgo " "vērtības, lai tās varētu iegūt." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Atrast nederīgus variantus" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Dažiem variantiem ir nederīgi nosaukumi" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Šiem variantiem ir nederīgi nosaukumi." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Šim variantam ir nederīgs nosaukums." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Dažiem no šiem variantiem ir nederīgi nosaukumi." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[īpašība]=[vērtība], [īpašība]=[vērtība]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Jāmēģina izmantot šāda uzbūve:" @@ -4825,7 +4785,7 @@ msgstr "Ir tāds pats hierarhijas līmenis." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Kopas, dēļi un izkārtojumi tiek uztverti kā līdzvērtīgi." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Kā varianti paliek savienoti" @@ -4869,11 +4829,11 @@ msgstr "Augša" msgid "workspace.options.constraints.topbottom" msgstr "Augša un apakša" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Dizains" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Izguve" @@ -4881,38 +4841,38 @@ msgstr "Izguve" msgid "workspace.options.export-multiple" msgstr "Izgūt atlasi" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Izgūt %s elementus" msgstr[1] "Izgūt %s elementu" msgstr[2] "Izgūt %s elementus" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Pievienot izguvi" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Noņemt izguvi" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Piedēklis" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Izguve pabeigta" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Notiek izgūšana…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Izgūšana neizdevās" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Izgūšana ir neparedzēti lēna" @@ -4928,7 +4888,7 @@ msgstr "Pievienot aizpildījumu" msgid "workspace.options.fill.remove-fill" msgstr "Noņemt aizpildījumu" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Mainīt paneļa izmēru, lai ietilpinātu saturu" @@ -4952,67 +4912,67 @@ msgstr "Plūsma sākas" msgid "workspace.options.grid.auto" msgstr "Auto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Slejas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Krāsa" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Rene" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Augstums" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Mala" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Iestatīt kā noklusējumu" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Apakša" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centrs" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Pa kreiss" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Pa labi" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Izstiept" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Augša" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Izmantot noklusējumu" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Platums" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Rindas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Kvadrāts" @@ -5024,7 +4984,7 @@ msgstr "Kopas aizpildījums" msgid "workspace.options.group-stroke" msgstr "Kopas vilkums" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Pievienot vadlīniju" @@ -5032,19 +4992,19 @@ msgstr "Pievienot vadlīniju" msgid "workspace.options.guides.remove-guide" msgstr "Noņemt vadlīniju" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Vadotnes" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Pārslēgt vadlīniju" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Augstums" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Apskatīt" @@ -5260,103 +5220,103 @@ msgstr "Mijiedarbības" msgid "workspace.options.interactions.add-interaction" msgstr "Pievienot mijiedarbību" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Krāsa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Krāsas izbalēšana" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Krāsas izcēlums" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Aptumšot" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Starpība" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Izslēgšana" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Konturgaisma" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Nokrāsa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Izgaismot" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Spožums" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Vairot" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normāls" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Pārklājums" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Piesātinājums" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Ekrāns" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Izkliedgaisma" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Pārslēgt slāņa redzamību" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Lielākais augstums" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Lielākais platums" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Mazākais augstums" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Mazākais platums" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Vairāk krāsu" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Vairāk bibliotēkas krāsu" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Caurspīdīgums" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototips" @@ -5388,19 +5348,19 @@ msgstr "Sakļaut neatkarīgu rādiusu" msgid "workspace.options.radius.show-single-corners" msgstr "Rādīt neatkarīgu rādiusu" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Pēdējie" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Atkārtot" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Pagriešana" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Meklēt fontu" @@ -5408,7 +5368,7 @@ msgstr "Meklēt fontu" msgid "workspace.options.select-a-shape" msgstr "Jāatlasa apveids, plātne vai kopa, lai vilktu savienojumu uz citu plātni." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Atlasītās krāsas" @@ -5476,55 +5436,55 @@ msgstr "Pārslēgt ēnu" msgid "workspace.options.show-fill-on-export" msgstr "Rādīt izguvēs" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Rādīt skatītājā" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Izmērs" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Izmēra standarti" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Paturēt malu attiecību" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Nepaturēt malu attiecību" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Aplis" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Dimants" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Bulta" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Nav" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Apaļš" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Kvadrāts" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Taisnstūris" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Trijstūris" @@ -5532,7 +5492,7 @@ msgstr "Trijstūris" msgid "workspace.options.stroke-color" msgstr "Vilkuma krāsa" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Vilkuma biezums" @@ -5540,35 +5500,35 @@ msgstr "Vilkuma biezums" msgid "workspace.options.stroke.add-stroke" msgstr "Pievienot vilkuma krāsu" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centrs" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Pārtraukts" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Punktots" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Iekšā" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Jaukts" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Ārā" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Noņemt vilkumu" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Blīvs" @@ -5604,11 +5564,11 @@ msgstr "Automātiskais platums" msgid "workspace.options.text-options.grow-fixed" msgstr "Fiksēts" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Burtu atstarpes" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Rindas augstums" @@ -5652,122 +5612,122 @@ msgstr "Pasvītrojums (%s)" msgid "workspace.options.use-play-button" msgstr "Jāizmanto atskaņosanas poga galvenē, lai palaistu prototipa skatu." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Platums" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X ass" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y ass" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Zīmēt mezglus (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Savienot mezglus (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Uz stūri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Uz līkni (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Mezglu sapludināšana (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Pārvietot mezglus (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Atdalīt mezglus (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Pieķert mezglus (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Atvērt" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Atklāt [vairāk spraudņu](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Vēl nav uzstādīts neviens spraudnis" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Spraudņa deklarācija ir nepareiza." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Jābūt redaktoram, lai izmantotu šo spraudni" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Spraudnis nepastāv vai URL ir nepareizs." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Uzstādīt" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Uzstādītie spraudņi" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Spraudņu pārvaldnieks" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Spraudņi" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "ATJAUNINĀT ŠO SPRAUDNI" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Spraudnis kopš pēdējās atvēršanas reizes ir mainīts. Tam tagad ir vajadzīga " "piekļuve:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Uzsākt datņu lejupielādi." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Lasīt savas piebildes un atbildes." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Lasīt un mainīt savas piebildes un atbildēt savā vārdā." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Lasīt datņu, kurām lietotājiem ir piekļuve, saturu." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Lasīt un mainīt datņu, kurām lietotājiem ir piekļuve, saturu." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Lūgums ņemt vērā, ka šo spraudni izveidoja trešā puse, tāpēc pirms " @@ -5775,74 +5735,74 @@ msgstr "" "Tavu datu privātums un drošība. Ja ir kādas neskaidrības, lūgums sazināties " "ar atbalstu." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Lasīt bibliotēkas un līdzekļus." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Lasīt un mainīt bibliotēkas un līdzekļus." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "SPRAUDNIS \"%s\" PIEPRASA PIEKĻUVI:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Lasīt pašreizējā lietotāja profila informāciju." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Spraudņu saraksts" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Noņemt spraudni" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Ierakstīt spraudņa URL" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Spraudņi" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "NE TAGAD" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Vēlies izmēģināt? Tas tiks atvērts jaunā melnrakstā Tavai pašreizējai " "komandai. (Ja nē, vienmēr to var atrast jebkuras datnes uzstādītajos " "spraudņos.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "SPRAUDNIS \"%s\" IR UZSTĀDĪTS." -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "IZMĒĢINĀT SPRAUDNI" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Pievienot elastīgo izkārtojumu" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Pievienot režģa izkārtojumu" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Pievienot izkārtojumu" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Izveidot variantu" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Pievienot jaunu īpašību" @@ -5854,7 +5814,7 @@ msgstr "Sūtīt atpakaļ" msgid "workspace.shape.menu.backward" msgstr "Sūtīt uz aizmuguri" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Kopēt" @@ -5890,27 +5850,27 @@ msgstr "Ievietot starpliktuvē kā tekstu" msgid "workspace.shape.menu.create-annotation" msgstr "Izveidot piezīmi" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Atlase uz plātni" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Izveidot sastāvdaļu" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Izveidot vairākas sastāvdaļas" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Izgriezt" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Dzēst" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Izdzēst plūsmas sākumu" @@ -5922,7 +5882,7 @@ msgstr "Atvienot instanci" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Atvienot instances" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Starpība" @@ -5930,15 +5890,15 @@ msgstr "Starpība" msgid "workspace.shape.menu.duplicate" msgstr "Divkāršot" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Labot" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Izslēgt" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Izklāt" @@ -5950,7 +5910,7 @@ msgstr "Apvērst līmeniski" msgid "workspace.shape.menu.flip-vertical" msgstr "Apvērst stateniski" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Plūsmas sākums" @@ -5962,31 +5922,31 @@ msgstr "Virzīt uz priekšu" msgid "workspace.shape.menu.front" msgstr "Virzīt priekšā" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Kopa" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Slēpt" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Rādīt/paslēpt UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Šķēlums" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Slēgt" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Maska" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Ielīmēt" @@ -5994,23 +5954,23 @@ msgstr "Ielīmēt" msgid "workspace.shape.menu.paste-props" msgstr "Ielīmēt īpašības" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Ceļš" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Noņemt elastīgo izkārtojumu" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Noņemt režģa izkārtojumu" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Noņemt izkārtojumu" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Noņemt īpašību" @@ -6034,11 +5994,11 @@ msgstr "Atjaunot variantu" msgid "workspace.shape.menu.select-layer" msgstr "Atlasīt slāni" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Pāradīt" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Rādīt līdzekļu panelī" @@ -6054,19 +6014,19 @@ msgstr "Noņemt sīktēlu" msgid "workspace.shape.menu.thumbnail-set" msgstr "Iestatīt kā sīktēlu" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Atapkopot" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Apvienot" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Atslēgt" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Noņemt masku" @@ -6082,51 +6042,51 @@ msgstr "Sakļaut sānjoslu" msgid "workspace.sidebar.expand" msgstr "Izvērst sānjoslu" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Vēsture" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Slāņi" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Sastāvdaļas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Atsijāt" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Plātnes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Kopas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Attēli" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Maskas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Meklēt slāņus" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Formas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Teksti" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Ievietotie SVG atribūti" @@ -6138,7 +6098,7 @@ msgstr "Lapas" msgid "workspace.sidebar.sitemap.add-page" msgstr "Pievienot lapu" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Vietnes karte" @@ -6158,7 +6118,7 @@ msgstr "Pievienot kopu šai grupai" msgid "workspace.tokens.add-token" msgstr "Pievienot tekstvienību: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Pielietota" @@ -6178,7 +6138,7 @@ msgstr "Pamata fonta izmērs" msgid "workspace.tokens.base-font-size.error" msgstr "Pamata fonta izmēram jābūt vērtībai pikseļos vai bez mērvienības." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Krāsa" @@ -6186,7 +6146,7 @@ msgstr "Krāsa" msgid "workspace.tokens.create-new-theme" msgstr "Tagad izveido savu pirmo izskatu!" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Izveidot kādu." @@ -6206,7 +6166,7 @@ msgstr "Izdzēst izskatu" msgid "workspace.tokens.duplicate" msgstr "Pavairot tekstvienību" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "dublējums" @@ -6268,7 +6228,7 @@ msgstr "" "Vienā JSON datnē pirmā līmeņa atslēgām vajadzētu būt tekstvienību kopu " "nosaukumiem." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Ievietot tekstvienības" @@ -6278,11 +6238,11 @@ msgstr "" "Tekstvienību ievietošana aizvietos visas pašreizējāš tekstvienības, kopas " "un izskatus." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Neaktīva" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Šī kopa nav aktīva. Jānomaina izskats vai jāaktivē šī kopa, lai skatlaukā " @@ -6328,7 +6288,7 @@ msgstr "Nav izvēlēts izskats" msgid "workspace.tokens.no-permisions-set" msgstr "Nepieciešams būt redaktoram, lai aktivētu kopas vai atceltu to aktivēšanu" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Jābūt redaktoram, lai izmantotu izskatus" @@ -6340,7 +6300,7 @@ msgstr "Vēl nav nevienas kopas. Vispirms ir jāizveido kāda." msgid "workspace.tokens.no-sets-yet" msgstr "Šeit vēl nav nevienas kopas." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Šeit nav izskatu." @@ -6352,7 +6312,7 @@ msgstr "Pašlaik nav izskatu." msgid "workspace.tokens.num-active-sets" msgstr "%s spēkā esošas kopas" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Sākotnējā vērtība: %s" @@ -6364,7 +6324,7 @@ msgstr "Atbīdes" msgid "workspace.tokens.radius" msgstr "Rādiuss" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Atrisinātā vērtība: %s" @@ -6398,11 +6358,11 @@ msgstr "Šeit var konfigurēt pamata fonta izmēru, kas nosaka 1rem vērtību:" msgid "workspace.tokens.settings" msgstr "Tekstvienību iestatījumi" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -6424,11 +6384,11 @@ msgstr "Izskatu saraksts" msgid "workspace.tokens.token-description" msgstr "Apraksts" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nosaukums" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " nav derīgs tekstvienības nosaukums.\n" @@ -6439,11 +6399,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Vērtība" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Jāievada vērtība vai aizstājvārds ar {aizstājvārds}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TEKSTVIENĪBAS - %s" @@ -6455,7 +6415,7 @@ msgstr "Rīki" msgid "workspace.tokens.unknown-token-type-section" msgstr "Veids \"%s\" nav atbalstīts (%s):" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Vērtība nav derīga" @@ -6463,55 +6423,55 @@ msgstr "Vērtība nav derīga" msgid "workspace.toolbar.assets" msgstr "Līdzekļi" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Krāsu palete (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Komentāri (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Līkne (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elipse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Plātne (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Attēls (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Pārvietot (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Ceļš (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Spraudņi (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Taisnstūris (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Teksts (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Burtu stili un veidi (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Pārslēgt rīkjoslu" @@ -6524,7 +6484,7 @@ msgstr "Darīts" msgid "workspace.top-bar.view-only" msgstr "**Apskata kodu** (tikai skatīšana)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Līdz šim nav notikušas nekādas vēsturiskās izmaiņas" @@ -6664,19 +6624,19 @@ msgstr "burtu stila un veida līdzeklis" msgid "workspace.undo.entry.unknown" msgstr "Darbība ar %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Izlaist" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Vairāk informācijas" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Koplietojamajās bibliotēkās ir atjauninājumi" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Atjaunināt" diff --git a/frontend/translations/ml.po b/frontend/translations/ml.po index 6a48cb9eda..9abcce2539 100644 --- a/frontend/translations/ml.po +++ b/frontend/translations/ml.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2026.10.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "നിലവിൽ അക്കൗണ്ടുണ്ടോ?" @@ -29,7 +29,7 @@ msgstr "" "ഇതൊരു ഡെമോ സേവനമാണ്, ഒരു യഥാർത്ഥ ജോലിക്ക് ഉപയോഗിക്കരുത്, പ്രൊജക്റ്റുകൾ " "നിശ്ചിതസമയങ്ങളിൽ മായ്ക്കപ്പെടും." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "പാസ്‌വേഡ് മറന്നോ?" @@ -37,37 +37,37 @@ msgstr "പാസ്‌വേഡ് മറന്നോ?" msgid "auth.fullname" msgstr "മുഴുവൻ പേര്" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "എന്റെ അക്കൗണ്ടിലേക്ക് പ്രവേശിക്കുക" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "ഇവിടെ ലോഗിൻ ചെയ്യുക." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "ഡിസൈനിങിലും കോഡിങിലും സഹകരിച്ച് പ്രവർത്തിക്കാനുള്ള ഒരു ഓപ്പൺസോഴ്സ് ഡിസൈൻ " "ഉപകരണമാണ് പെൻപോട്ട്" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "ഗിറ്റ്ഹബ്ബ്" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "ഗിറ്റ്ലാബ്" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "ഗൂഗിൾ" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "ഓപ്പൺഐഡി" @@ -75,7 +75,7 @@ msgstr "ഓപ്പൺഐഡി" msgid "auth.new-password" msgstr "പുതിയൊരു പാസ്‌വേഡ് ചേർക്കുക" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "നിങ്ങളിപ്പോൾ %s-ന്റെ ഭാഗമാണ്" @@ -91,7 +91,7 @@ msgstr "പ്രൊഫൈൽ സാധൂകരിച്ചിട്ടില msgid "auth.notifications.recovery-token-sent" msgstr "പാസ്‌വേഡ് വീണ്ടെടുപ്പ് കണ്ണി നിങ്ങളുടെ ഇൻബോക്സിലേക്ക് അയച്ചിട്ടുണ്ട്." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "വിജയകരമായി സംഘത്തിൽ ചേർന്നു" @@ -123,7 +123,7 @@ msgstr "പാസ്‌വേഡ് മറന്നോ?" msgid "auth.recovery-submit" msgstr "പാസ്‌വേഡ് മാറ്റുക" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "ഇതുവരെ അക്കൗണ്ട് ഇല്ലേ?" @@ -135,7 +135,7 @@ msgstr "നിങ്ങളെ ഞങ്ങൾ എന്താണ് ഡാഷ് msgid "auth.register-account-title" msgstr "നിങ്ങളുടെ പേര്" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "അക്കൗണ്ട് സൃഷ്ടിക്കുക" @@ -143,7 +143,7 @@ msgstr "അക്കൗണ്ട് സൃഷ്ടിക്കുക" msgid "auth.register-title" msgstr "അക്കൗണ്ട് സൃഷ്ടിക്കുക" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "ഉപയോഗനിബന്ധനകൾ" @@ -151,75 +151,75 @@ msgstr "ഉപയോഗനിബന്ധനകൾ" msgid "auth.work-email" msgstr "ജോലിക്കുള്ള ഇമെയിൽ" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "എല്ലാ പെൻപോട്ട് ഉപയോക്താക്കളും" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "ഈ കണ്ണി നീക്കം ചെയ്യണമെന്നത് നിങ്ങൾക്ക് തീർച്ചയാണോ? നിങ്ങളത് ചെയ്താൽ, അത് " "ആർക്കും ലഭ്യമല്ലാതാകും" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "കണ്ണി നശിപ്പിക്കുക" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "കണ്ണി നേടുക" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "കണ്ണി വിജയകരമായി പകർത്തി" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "അനുമതികൾ കൈകാര്യം ചെയ്യുക" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 താൾ പങ്കുവെച്ചു" msgstr[1] "%s താളുകൾ പങ്കുവെച്ചു" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "കണ്ണിയുള്ള ആർക്കും പ്രാപ്യമാകും" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "പങ്കുവെച്ച താളുകൾ" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "പങ്കുവെക്കാവുന്ന കണ്ണി ഇവിടെ ലഭ്യമാകും" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "സംഘാംഗങ്ങൾ മാത്രം" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "പ്രോട്ടോടൈപ്പുകൾ പങ്കുവെയ്ക്കുക" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "എല്ലാം തിരഞ്ഞെടുക്കുക" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "പങ്കിട്ട ലൈബ്രറിയായി ചേർക്കുക" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "ഇമെയിൽ മാറ്റുക" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(പകർത്തുക)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "പുതിയ സംഘം രൂപീകരിക്കുക" @@ -227,7 +227,7 @@ msgstr "പുതിയ സംഘം രൂപീകരിക്കുക" msgid "dashboard.default-team-name" msgstr "നിങ്ങളുടെ പെൻപോട്ട്" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "സംഘത്തെ നീക്കുക" @@ -239,15 +239,15 @@ msgstr "പകർപ്പ്" msgid "dashboard.duplicate-multi" msgstr "%s ഫയലുകളുടെ പകർപ്പ്" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "ആർട്ട്ബോർഡുകൾ പിഡിഎഫായി എക്സ്പോർട്ട് ചെയ്യുക" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "പിഡിഎഫായി എക്സ്പോർട്ട് ചെയ്യുക" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "എക്സ്പോർട്ട്" diff --git a/frontend/translations/ms.po b/frontend/translations/ms.po index 42d2623a31..005a837d2c 100644 --- a/frontend/translations/ms.po +++ b/frontend/translations/ms.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:19+0000\n" -"Last-Translator: Revenant \n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Malay " "\n" "Language: ms\n" @@ -9,7 +9,7 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" #: src/app/main/ui/auth/register.cljs:239 msgid "auth.create-demo-account" @@ -21,7 +21,7 @@ msgstr "" "Ini adalah perkhidmatan DEMO, JANGAN GUNAKAN untuk kerja sebenar, projek " "akan dipadam secara berkala." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Lupa kata laluan?" @@ -29,27 +29,27 @@ msgstr "Lupa kata laluan?" msgid "auth.fullname" msgstr "Nama penuh" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Log masuk disini" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "Github" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID Connect" @@ -69,7 +69,7 @@ msgstr "Profil tidak disahkan, sila sahkan profil sebelum meneruskan." msgid "auth.notifications.recovery-token-sent" msgstr "Pautan pemulihan kata laluan dihantar ke peti masuk anda." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Berjaya menyertai pasukan" @@ -101,11 +101,11 @@ msgstr "Lupa kata laluan?" msgid "auth.recovery-submit" msgstr "Tukar kata laluan anda" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Tiada akaun lagi?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Cipta akaun" @@ -113,112 +113,112 @@ msgstr "Cipta akaun" msgid "auth.register-title" msgstr "Cipta akaun" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Syarat perkhidmatan" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Terbitkan" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Semua pengguna Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Adakah anda pasti untuk mengalih keluar pautan ini? Jika anda melakukannya, " "ia tidak lagi tersedia untuk sesiapa sahaja" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(semasa)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Musnahkan pautan" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Dapatkan pautan" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Pautan berjaya disalin" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Urus kebenaran" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "%s halaman dikongsi" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Boleh komen" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Boleh memeriksa kod" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Sesiapa yang mempunyai pautan akan mendapat akses" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Halaman dikongsi" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Pautan boleh kongsi akan dipaparkan di sini" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Hanya ahli pasukan" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Kongsi prototaip" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Pilih semua" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Nyahterbitkan" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Pengurusan pasukan" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot dibuat untuk pasukan. Jemput ahli untuk bekerjasama dalam projek dan " "fail" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Berganding bahu!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Tambahkan sebagai Perpustakaan kongsi" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "tukar e-mel" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(salin)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Buat pasukan baharu" @@ -226,11 +226,11 @@ msgstr "Buat pasukan baharu" msgid "dashboard.default-team-name" msgstr "Penpot anda" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Padam pasukan" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Muat turun fail Penpot (.penpot)" @@ -246,58 +246,58 @@ msgstr "Pendua %s fail" msgid "dashboard.export-binary-multi" msgstr "Muat turun %s fail Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Eksport papan sebagai PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Eksport sebagai PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s daripada %s elemen dipilih" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Eksport" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Anda boleh menambah tetapan eksport ke elemen daripada sifat reka bentuk " "(di bahagian bawah bar sisi kanan)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Maklumat cara untuk menetapkan eksport di Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Tiada unsur dengan tetapan eksport." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Eksport Pemilihan" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Fon hilang" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Tolak semua" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Fon tersuai yang anda muat naik akan dipaparkan di sini." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "%s fon ditambah" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -307,7 +307,7 @@ msgstr "" "tunggal**. Anda boleh memuat naik fon dengan format berikut: **TTF, OTF dan " "WOFF** (hanya satu sahaja diperlukan)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -316,11 +316,11 @@ msgstr "" "kandungan [Syarat Perkhidmatan Penpot](%s). Anda juga mungkin ingin membaca " "tentang [pelesenan fon](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Muat naik semua" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -331,72 +331,44 @@ msgstr "" "menggunakan [Transfonter](https://transfonter.org/) untuk menjana fon web " "dan membetulkan ralat. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Import fail Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Alamak! Kami tidak dapat mengimport fail ini" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "%s fail telah berjaya diimport." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Sesetengah fail mengandungi objek tidak sah telah dialih keluar." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "memproses warna" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Memproses komponen" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Memproses media" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Memproses halaman: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Memproses tipografi" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Memuat naik data ke pelayan (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Memuat naik fail: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Jemput orang ramai" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Tinggalkan pasukan" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Perpustakaan & Templat" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Terokai lebih banyak daripada mereka dan ketahui cara untuk menyumbang" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Terdapat masalah mengimport templat. Templat tidak diimport." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Perpustakaan" @@ -404,7 +376,7 @@ msgstr "Perpustakaan" msgid "dashboard.loading-files" msgstr "memuatkan fail anda…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "memuatkan fon anda…" @@ -420,35 +392,35 @@ msgstr "Pindahkan fail %s ke" msgid "dashboard.move-to-other-team" msgstr "Pindah ke pasukan lain" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Fail Baharu" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Fail Baharu" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Projek baharu" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Projek Baharu" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Tiada padanan ditemui untuk \"%s\"" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Projek yang disemat akan muncul di sini" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Alamat e-mel anda telah berjaya dikemas kini" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Alamat e-mel anda telah berjaya disahkan" @@ -456,7 +428,7 @@ msgstr "Alamat e-mel anda telah berjaya disahkan" msgid "dashboard.notifications.password-saved" msgstr "Kata laluan berjaya disimpan!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s ahli" @@ -464,7 +436,7 @@ msgstr "%s ahli" msgid "dashboard.open-in-new-tab" msgstr "Buka fail dalam tab baharu" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Pilihan" @@ -472,39 +444,39 @@ msgstr "Pilihan" msgid "dashboard.password-change" msgstr "Tukar kata laluan" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Semat/Nyahsemat" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projek" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Mahu mengalih keluar akaun anda?" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Simpan tetapan" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Cari…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Mencari \"%s\"…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Pilih Bahasa UI" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Pilih tema" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Tunjukkan semua fail" @@ -526,7 +498,7 @@ msgstr[0] "Fail anda telah berjaya dibuat pendua" msgid "dashboard.success-duplicate-project" msgstr "Projek anda telah berjaya dibuat pendua" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Fail anda berjaya dialihkan" @@ -538,82 +510,82 @@ msgstr "Fail anda telah berjaya dialihkan" msgid "dashboard.success-move-project" msgstr "Projek anda berjaya dialihkan" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Maklumat pasukan" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Anggota kumpulan" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Projek pasukan" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema UI" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Hasil carian" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Taip untuk hasil carian" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Nyahterbit Perpustakaan" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Kemas kini tetapan" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Adalah aktif" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Apabila cangkuk ini dicetuskan, butiran peristiwa akan dihantar" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Jenis kandungan" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Cipta webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook berjaya dihasilkan." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhooks adalah cara mudah untuk membolehkan tapak web dan apl lain " "dimaklumkan apabila acara tertentu berlaku di Penpot. Kami akan menghantar " "permintaan POST ke setiap URL yang anda berikan." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Tekan butang \"Tambah webhook\" untuk menambahnya." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Tiada webhook dibuat setakat ini." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Akaun anda" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-mel" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Nama anda" @@ -629,7 +601,7 @@ msgstr "Perhatian" msgid "ds.component-subtitle" msgstr "Komponen untuk dikemas kini:" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Batal" @@ -645,15 +617,15 @@ msgstr "Adakah anda pasti?" msgid "errors.auth-provider-not-configured" msgstr "Pembekal pengesahan tidak dikonfigurasikan." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Nampaknya anda belum disahkan atau sesi telah tamat tempoh." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Fon %s tidak dapat dimuatkan" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Fon %s tidak dapat dimuatkan" @@ -661,15 +633,15 @@ msgstr "Fon %s tidak dapat dimuatkan" msgid "errors.cannot-upload" msgstr "Tidak boleh memuat naik fail media." -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Pelayar anda tidak dapat melakukan operasi ini" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E-mel sudah digunakan" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-mel sudah disahkan." @@ -677,32 +649,32 @@ msgstr "E-mel sudah disahkan." msgid "errors.email-as-password" msgstr "Anda tidak boleh menggunakan e-mel sebagai kata laluan" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "E-mel «%s» mempunyai banyak laporan lantunan kekal." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "E-mel «%s» telah dilaporkan sebagai spam atau melantun secara kekal." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Nampaknya anda sedang membuka fail yang mendayakan ciri '%s' tetapi versi " "penpot semasa tidak menyokongnya atau menyahdayakannya." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Ciri '%s' tidak disokong." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Nampaknya terdapat ketidakpadanan antara ciri yang didayakan dan ciri fail " "yang anda cuba buka. Migrasi untuk '%s' perlu digunakan sebelum fail boleh " "dibuka." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Sesuatu yang tidak kena telah berlaku." @@ -714,7 +686,7 @@ msgstr "Warna tidak sah" msgid "errors.invalid-email" msgstr "Sila masukkan e-mel yang sah" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "E-mel pengesahan mesti sepadan" @@ -734,7 +706,7 @@ msgstr "Jemputan ini mungkin dibatalkan atau mungkin tamat tempoh." msgid "errors.ldap-disabled" msgstr "Pengesahan LDAP dilumpuhkan." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Anda telah mencapai petikan '%s'. Hubungi sokongan." @@ -750,7 +722,7 @@ msgstr "Nampaknya kandungan imej tidak sepadan dengan sambungan fail." msgid "errors.media-type-not-allowed" msgstr "Nampaknya ini bukan imej yang sah." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Profil yang anda jemput mempunyai e-mel yang diredamkan (laporan spam atau " @@ -764,7 +736,7 @@ msgstr "Kata laluan pengesahan mesti sepadan" msgid "errors.password-too-short" msgstr "Kata laluan hendaklah sekurang-kurangnya 8 aksara" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Data tidak sah dalam papan klip" @@ -772,7 +744,7 @@ msgstr "Data tidak sah dalam papan klip" msgid "errors.profile-blocked" msgstr "Profil disekat" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Profil anda mempunyai e-mel yang diredamkan (laporan spam atau lantunan " @@ -782,21 +754,21 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Pendaftaran dilumpuhkan pada masa ini." -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Ciri tidak serasi '%s' dikesan" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Ahli tidak mencukupi untuk meninggalkan pasukan, anda mungkin mahu " "memadamkannya." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Ahli yang anda cuba tetapkan tidak wujud." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "Pemilik tidak boleh meninggalkan pasukan, anda mesti menetapkan semula " @@ -806,39 +778,39 @@ msgstr "" msgid "errors.unexpected-error" msgstr "Ralat yang tidak dijangka berlaku." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Token tidak diketahui" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Fail mempunyai nombor versi yang tidak serasi" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Ralat sambungan, URL tidak dapat dicapai" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL tidak lulus pengesahan." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Penghantaran terakhir tidak berjaya." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Ralat pada pengesahan SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Masa tamat" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Ralat yang tidak dijangka semasa mengesahkan" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Status tidak dijangka %s" @@ -854,13 +826,13 @@ msgstr "Kata laluan lama tidak betul" msgid "feedback.description" msgstr "Penerangan" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Kami berbesar hati anda berada disini. Jika anda memerlukan bantuan, sila " "cari sebelum anda menyiarkan siaran baharu." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Komuniti Penpot" @@ -874,11 +846,11 @@ msgstr "" "Sila terangkan sebab e-mel anda, nyatakan sama ada isu, idea atau keraguan. " "Seorang ahli pasukan kami akan menjawab secepat mungkin." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Kami di sini untuk membantu dengan pertanyaan teknikal anda." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Akaun sokongan Twitter" @@ -910,7 +882,7 @@ msgstr "Muat turun sumber gambar" msgid "inspect.attributes.shadow" msgstr "Bayang" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Ukuran dan kedudukan" @@ -918,39 +890,27 @@ msgstr "Ukuran dan kedudukan" msgid "inspect.attributes.stroke" msgstr "Gurisan" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Tengah" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Dalam" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Luar" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Keluarga Fon" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Saiz huruf" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Gaya Huruf" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Jarak Huruf" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Tinggi Garis" @@ -962,11 +922,11 @@ msgstr "Tiada" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Garis bawah" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Huruf Tajuk" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Huruf Kecil" @@ -978,7 +938,7 @@ msgstr "Tiada" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Nyahset" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Huruf Besar" @@ -1040,31 +1000,31 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Teks" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Pintasan" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Terima" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Aktif" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Tambah fon tersuai" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Pentadbir" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Semua" @@ -1072,29 +1032,29 @@ msgstr "Semua" msgid "labels.and" msgstr "dan" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Kembali" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Nampaknya anda perlu menunggu sebentar dan mencuba semula; kami sedang " "melakukan penyelenggaraan kecil pelayan kami." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Ralat pelayan (Bad Gateway)" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Batal" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Tutup" @@ -1102,11 +1062,11 @@ msgstr "Tutup" msgid "labels.code" msgstr "Kod" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Komen" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Komuniti" @@ -1114,35 +1074,35 @@ msgstr "Komuniti" msgid "labels.confirm-password" msgstr "Sahkan kata laluan" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Teruskan" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Anda boleh meneruskan dengan akaun Penpot" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Salin pautan" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Cipta" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Cipta pasukan baharu" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Masukkan nama pasukan baharu" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Papan pemuka" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Padam" @@ -1154,7 +1114,7 @@ msgstr "Padam komen" msgid "labels.delete-comment-thread" msgstr "Padam bebenang" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Padam jemputan" @@ -1162,31 +1122,31 @@ msgstr "Padam jemputan" msgid "labels.delete-multi-files" msgstr "Padam fail %s" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Pembangun" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Buang" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Draf" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Sunting" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Penyunting" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Tamat tempoh" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Eksport" @@ -1198,43 +1158,43 @@ msgstr "Maklum balas dilumpuhkan" msgid "labels.feedback-sent" msgstr "Maklum balas dihantar" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Keluarga Fon" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Gaya" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fon" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Repositori Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Beri maklum balas" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Pergi balik" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Pusat Bantuan" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Sembunyikan komen yang diselesaikan" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "tidak aktif" @@ -1242,47 +1202,47 @@ msgstr "tidak aktif" msgid "labels.info" msgstr "Maklumat" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Fon yang dipasang" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Ralat Dalaman" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Jemputan" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Bahasa" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Perpustakaan & Templat" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Log masuk atau daftar" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Log keluar" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Pemasaran" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Ahli" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Ahli" @@ -1290,35 +1250,35 @@ msgstr "Ahli" msgid "labels.new-password" msgstr "Kata laluan baharu" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Seterusnya" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "" "Anda telah melihat semua yang baru! Pemberitahuan ulasan baharu akan " "dipaparkan di sini." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Tiada jemputan tertunda." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Alamak!" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "%s fail" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "%s papan" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "%s projek" @@ -1327,143 +1287,143 @@ msgstr[0] "%s projek" msgid "labels.old-password" msgstr "Kata laluan lama" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Lain-lain (nyatakan)" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Pemilik" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Kata laluan" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Belum selesai" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Sebelumnya" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Pengurus Produk atau Projek" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projek" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Nota keluaran" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Alih keluar" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Buang ahli" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Namakan semula" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Namakan semula pasukan" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Hantar semula jemputan" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Cuba semula" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Peranan" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Simpan" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Cari" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Cari fon" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Pilih pilihan" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Hantar" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Menghantar…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Kami sedang menjalankan penyelenggaraan berjadual pada sistem kami." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Perkhidmatan Tidak Tersedia" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Tetapan" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Kongsi" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Perpustakaan" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Paparkan semua komen" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Paparkan senarai komen" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Paparkan komen anda sahaja" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Mula" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Pelajar atau guru" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutorial" @@ -1471,23 +1431,23 @@ msgstr "Tutorial" msgid "labels.unpublish-multi-files" msgstr "Nyahterbitkan %s fail" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Kemas kini" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Kemas kini pasukan" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Muat naik" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Muat naik fon tersuai" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Memuat naik…" @@ -1495,11 +1455,11 @@ msgstr "Memuat naik…" msgid "labels.view-only" msgstr "LIHAT SAHAJA" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Penonton" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Cangkuk Web (Webhook)" @@ -1507,11 +1467,11 @@ msgstr "Cangkuk Web (Webhook)" msgid "labels.write-new-comment" msgstr "Tulis komen baharu" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(anda)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Akaun anda" @@ -1540,63 +1500,63 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Tambah “%s” sebagai Pustaka Kongsi" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Anjakan besar" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Sahkan e-mel baharu" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Kami akan menghantar e-mel ke e-mel semasa anda “%s” untuk pengesahan " "identiti." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "E-mel baharu" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Tukar e-mel" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Tukar e-mel anda" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Cipta cangkuk web (webhook)" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Cipta cangkuk web (webhook)" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL muatan" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Batal dan kekalkan akaun saya" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Ya, padam akaun saya" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "Dengan mengalih keluar akaun anda, anda akan kehilangan semua projek dan " "arkib semasa anda." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Adakah anda pasti mahu memadam akaun anda?" @@ -1646,31 +1606,31 @@ msgstr "Adakah anda pasti mahu memadamkan %s fail?" msgid "modals.delete-file-multi-confirm.title" msgstr "Memadamkan %s fail" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Adakah anda pasti mahu memadamkan gaya fon ini? Ia tidak akan dimuatkan " "jika digunakan dalam fail." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Memadam gaya fon" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Adakah anda pasti mahu memadamkan fon ini? Ia tidak akan dimuatkan jika " "digunakan dalam fail." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Memadam fon" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Adakah anda pasti mahu memadamkan halaman ini?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Padam halaman" @@ -1686,27 +1646,27 @@ msgstr "Adakah anda pasti mahu memadamkan projek ini?" msgid "modals.delete-project-confirm.title" msgstr "Padam projek" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Padam fail" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Ia tidak diaktifkan dalam mana-mana fail." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Pustaka ini diaktifkan di sini: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Adakah anda pasti mahu memadamkan fail ini?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Memadam fail" @@ -1725,160 +1685,160 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Memadam pasukan" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Padam ahli" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Adakah anda pasti mahu memadamkan ahli ini daripada pasukan?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Padam ahli pasukan" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" "Aset yang telah digunakan dalam fail ini akan kekal di sana (tiada reka " "bentuk akan rosak)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Padam cangkuk web (webhook)" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Adakah anda pasti mahu memadamkan cangkuk web (webhook) ini?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Memadam cangkuk web (webhook)" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Sunting cangkuk web (webhook)" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Sunting cangkuk web (webhook)" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Hantar jemputan" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-mel, dipisahkan dengan koma" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Sesetengah e-mel adalah daripada ahli pasukan semasa. Jemputan mereka tidak " "akan dihantar." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Jemput ahli ke pasukan" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Memandangkan anda satu-satunya ahli pasukan, pasukan ini akan dipadamkan " "bersama-sama dengan projek dan failnya." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Adakah anda pasti mahu meninggalkan pasukan %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Anda tidak boleh meninggalkan pasukan jika tiada ahli lain untuk dinaikkan " "pangkat kepada pemilik. Anda mungkin mahu memadamkan pasukan." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Anda adalah pemilik pasukan ini. Sila pilih ahli lain untuk dinaikkan " "pangkat kepada pemilik sebelum anda pergi." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Naik pangkat dan tinggalkan" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Pilih ahli untuk dinaikkan pangkat" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Sebelum anda pergi" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Tinggalkan pasukan" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Adakah anda pasti mahu meninggalkan pasukan ini?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Meninggalkan pasukan" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Jumlah anjakan" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Pindah hak milik" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Jika anda memindahkan pemilikan, anda akan menukar peranan anda kepada " "Pentadbir, kehilangan beberapa kebenaran ke atas pasukan ini. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Anda adalah pemilik semasa pasukan ini. Adakah anda pasti mahu menjadikan " "%s pemilik baharu pasukan?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Pemilik pasukan baru" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Terbitkan" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Pustaka anda kosong. Adakah anda pasti mahu menerbitkannya?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Terbitkan perpustakaan kosong" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Anjakan kecil" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Nyahterbit" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Adakah anda pasti mahu menyahterbitkan perpustakaan ini?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Nyahterbit pustaka" @@ -1905,25 +1865,25 @@ msgstr "Kemas kini komponen dalam pustaka kongsi" msgid "notifications.by-code.upgrade-version" msgstr "Versi baharu tersedia." -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Jemputan berjaya dihantar" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Pautan jemputan disalin" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Anda tidak boleh memadamkan profil anda. Tetapkan semula pasukan anda " "sebelum meneruskan." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil berjaya disimpan!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "E-mel pengesahan dihantar kepada %s. Semak e-mel anda!" @@ -1953,15 +1913,15 @@ msgstr "" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Cipta pasukan" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Jemput ahli" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Jemput dengan peranan:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Mari mulakan!" @@ -2007,43 +1967,43 @@ msgstr "Mula mereka bentuk" msgid "profile.recovery.go-to-login" msgstr "Pergi ke log masuk" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Tanggalkan" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Bercampur" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Komen" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Sejajarkan bahagian bawah" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Jajarkan tengah secara mendatar" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Jajar ke kiri" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Sejajar ke kanan" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Jajarkan atas" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Jajarkan tengah secara menegak" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Cipta papan daripada pemilihan" @@ -2051,43 +2011,43 @@ msgstr "Cipta papan daripada pemilihan" msgid "shortcuts.bold" msgstr "Togol tebal" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Perbezaan Boolean" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Pengecualian Boolean" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Persimpangan Boolean" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Kesatuan Boolean" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Hantar ke paling belakang" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Hantar ke belakang" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Bawa ke hadapan" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Bawa ke paling hadapan" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Kosongkan buat asal" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Salin" @@ -2095,35 +2055,35 @@ msgstr "Salin" msgid "shortcuts.create-new-project" msgstr "Buat baharu" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Potong" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Zum keluar" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Padam" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Padamkan nod" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Tanggalkan komponen" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Lengkung" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "elips" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Papan" @@ -2131,39 +2091,39 @@ msgstr "Papan" msgid "shortcuts.draw-nodes" msgstr "Lukis laluan" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Laluan" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Segi empat tepat" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Teks" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Pendua" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Batal" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Eksport bentuk" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Zum untuk muat semua" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Terbalikkan secara mendatar" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Terbalikkan secara menegak" @@ -2187,23 +2147,23 @@ msgstr "Pergi ke perpustakaan kongsi" msgid "shortcuts.go-to-search" msgstr "Cari" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Kumpulan" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Edarkan secara mendatar" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Tunjukkan / Sembunyikan UI" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Zum kedalam" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Masukkan gambar" @@ -2211,84 +2171,84 @@ msgstr "Masukkan gambar" msgid "shortcuts.italic" msgstr "Togol condong" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Sambung nod" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Asas" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Papan pemuka" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Penonton" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Ruang kerja" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Penjajaran" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Sunting" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Generik" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu utama" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Ubah suai lapisan" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigasi" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigasi" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigasi" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panel" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "laluan" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Bentuk" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Teks" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Alatan" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zum" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zum" diff --git a/frontend/translations/nb_NO.po b/frontend/translations/nb_NO.po index 4f052167bf..d0423d176d 100644 --- a/frontend/translations/nb_NO.po +++ b/frontend/translations/nb_NO.po @@ -15,7 +15,7 @@ msgstr "" msgid "auth.confirm-password" msgstr "Bekreft passord" -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Glemt passordet?" @@ -39,11 +39,11 @@ msgstr "Gjenopprett passord" msgid "auth.recovery-submit" msgstr "Endre passordet ditt" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Ingen konto enda?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Opprett konto" @@ -51,11 +51,11 @@ msgstr "Opprett konto" msgid "auth.register-title" msgstr "Opprett konto" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Legg til som delt bibliotek" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopi)" @@ -63,19 +63,19 @@ msgstr "(kopi)" msgid "dashboard.default-team-name" msgstr "Din Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Slett lag" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Inviter til team" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Forlat team" -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Delte biblioteker" @@ -91,15 +91,15 @@ msgstr "Flytt %s filer til" msgid "dashboard.move-to-other-team" msgstr "Flytt til annet team" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Ny fil" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nytt prosjekt" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s medlemmer" @@ -111,63 +111,63 @@ msgstr "Åpne fil i ny fane" msgid "dashboard.password-change" msgstr "Endre passord" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Fest/løsne" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Prosjekter" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Ønsker du å fjerne kontoen din?" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Søk …" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Şøker etter «%s» …" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Velg grensesnittsspråk" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Velg drakt" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Vis alle filer" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Teaminfo" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Teammedlemmer" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Lagprosjekter" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Utseende" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Din konto" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Ditt navn" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Avbryt" @@ -187,7 +187,7 @@ msgstr "Ugyldig farge" msgid "errors.invalid-recovery-token" msgstr "Gjenopprettelsessymbolet er ugyldig." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Ukjent symbol" @@ -211,15 +211,15 @@ msgstr "Skygge" msgid "inspect.attributes.typography" msgstr "Typografi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Skriftfamilie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Skriftstørrelse" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Skriftstil" @@ -247,11 +247,11 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Tekst" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Godta" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Avbryt" @@ -263,23 +263,23 @@ msgstr "Kode" msgid "labels.confirm-password" msgstr "Bekreft passord" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Opprett" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Opprett nytt lag" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Skriv inn nytt lagnavn" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Oversikt" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Slett" @@ -295,19 +295,19 @@ msgstr "Slett tråd" msgid "labels.delete-multi-files" msgstr "Slett %s filer" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Kladder" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Rediger" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Skriftfamilie" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Skrifter" @@ -315,19 +315,19 @@ msgstr "Skrifter" msgid "labels.info" msgstr "Info" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Installerte skrifter" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Språk" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Logg ut" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Medlemmer" @@ -339,75 +339,75 @@ msgstr "Nytt passord" msgid "labels.old-password" msgstr "Gammelt passord" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Eier" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Passord" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Prosjekter" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Fjern" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Gi nytt navn" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Prøv igjen" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rolle" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Lagre" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Søk etter skrift" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Send" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Sender …" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Innstillinger" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Delte bibliotek" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Vis alle kommentarer" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Last opp" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Last opp egendefinerte skrifter" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Laster opp …" @@ -423,11 +423,11 @@ msgstr "Laster inn bilde …" msgid "modals.add-shared-confirm.accept" msgstr "Legg til som delt bibliotek" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Bekreft ny e-postadresse" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Ja, slett kontoen min" @@ -447,7 +447,7 @@ msgstr "Slett fil" msgid "modals.delete-file-multi-confirm.accept" msgstr "Slett filer" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Slett side" @@ -455,15 +455,15 @@ msgstr "Slett side" msgid "modals.delete-project-confirm.accept" msgstr "Slett prosjekt" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Slett medlem" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Slett teammedlem" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Forlat team" @@ -471,15 +471,15 @@ msgstr "Forlat team" msgid "modals.update-remote-component.cancel" msgstr "Avbryt" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Invitasjon sendt" -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil lagret" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Innstillinger - Penpot" @@ -487,11 +487,11 @@ msgstr "Innstillinger - Penpot" msgid "title.settings.password" msgstr "Passord - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Innstillinger - %s - Penpot" @@ -499,11 +499,11 @@ msgstr "Innstillinger - %s - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Farger" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Komponenter" @@ -511,43 +511,43 @@ msgstr "Komponenter" msgid "workspace.assets.create-group" msgstr "Opprett en gruppe" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Slett" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Rediger" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Gruppe" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Gruppenavn" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Gi nytt navn" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Størrelse" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Skriftstil" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Vis regler" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Filbibliotek" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Nylige farger" @@ -555,46 +555,46 @@ msgstr "Nylige farger" msgid "workspace.libraries.colors.save-color" msgstr "Lagre fargestil" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Filbibliotek" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Komponent" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Eksporterer …" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Høyde" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Sett som forvalg" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Bruk forvalg" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Bredde" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Rader" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Lysne" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Skjerm" diff --git a/frontend/translations/nl.po b/frontend/translations/nl.po index 678e93c364..661cbe3cb8 100644 --- a/frontend/translations/nl.po +++ b/frontend/translations/nl.po @@ -1,17 +1,18 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:14+0000\n" -"Last-Translator: Stephan Paternotte \n" -"Language-Team: Dutch " -"\n" +"PO-Revision-Date: 2026-11-07 07:51+0000\n" +"Last-Translator: Stephan Paternotte \n" +"Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Heb je al een account?" @@ -33,7 +34,7 @@ msgstr "" "Dit is een DEMO-service, GEBRUIK DIT NIET voor echt werk, de projecten " "worden regelmatig gewist." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Wachtwoord vergeten?" @@ -41,37 +42,37 @@ msgstr "Wachtwoord vergeten?" msgid "auth.fullname" msgstr "Volledige naam" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Log in op mijn account" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Hier inloggen." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot is de gratis open-source ontwerptool voor samenwerking op het gebied " "van ontwerp en code" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -79,7 +80,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Typ een nieuw wachtwoord" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "Je maakt nu deel uit van %s" @@ -95,7 +96,7 @@ msgstr "Profiel is niet geverifieerd. Verifieer het profiel voordat je verder ga msgid "auth.notifications.recovery-token-sent" msgstr "Wachtwoordherstel-link is per e-mail naar je verzonden." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Succesvol toegetreden tot het team" @@ -127,7 +128,7 @@ msgstr "Wachtwoord vergeten?" msgid "auth.recovery-submit" msgstr "Wachtwoord wijzigen" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Nog geen account?" @@ -139,7 +140,7 @@ msgstr "Laat ons weten hoe we je moeten aanspreken op het dashboard en in e-mail msgid "auth.register-account-title" msgstr "Je naam" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Account aanmaken" @@ -152,7 +153,7 @@ msgstr "Account aanmaken" msgid "auth.terms-and-privacy-agreement" msgstr "Ik ga akkoord met de [servicevoorwaarden] (%s) en [privacybeleid] (%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Gebruiksvoorwaarden" @@ -174,135 +175,135 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Geen mensen gevonden voor @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publiceren" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Alle Penpot-gebruikers" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Weet je zeker dat je deze link wilt verwijderen? Als je dit doet, is het " "voor niemand meer beschikbaar" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(huidig)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Link verwijderen" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Link ophalen" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Link gekopieerd" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Rechten beheren" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 pagina gedeeld" msgstr[1] "%s pagina's gedeeld" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Kan commentaar geven" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Kan code inspecteren" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Iedereen met de link heeft toegang" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Gedeelde pagina's" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "De deelbare link zal hier verschijnen" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Alleen teamleden" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Prototypes delen" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Alles selecteren" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Publicatie ongedaan maken" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Teambeheer" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot is bedoeld voor teams. Nodig leden uit om samen te werken aan " "projecten en bestanden" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Werk samen!" #: src/app/main/ui/dashboard/file_menu.cljs:234 msgid "dashboard-restore-file-confirmation.description" -msgstr "Je gaat %1 herstellen." +msgstr "Je gaat %1 terugzetten." #: src/app/main/ui/dashboard/file_menu.cljs:233 msgid "dashboard-restore-file-confirmation.title" -msgstr "Bestand herstellen" +msgstr "Bestand terugzetten" #: src/app/main/ui/dashboard/placeholder.cljs:40 msgid "dashboard.add-file" msgstr "Bestand toevoegen" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Toevoegen als gedeelde bibliotheek" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "E-mailadres wijzigen" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "TEAM VERPLAATSEN" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Verplaatsen naar:" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "" "Projecten en bestanden blijven beschikbaar voor teamleden. Het team krijgt " "de configuratie van de nieuwe organisatie." -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Kies een organisatie" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "De organisatie van het team wijzigen" @@ -310,7 +311,7 @@ msgstr "De organisatie van het team wijzigen" msgid "dashboard.check-updates.available-message" msgstr "" "Nieuwere releases omvatten prestatieverbeteringen, nieuwe mogelijkheden en " -"oplossingen die uw installatie actueel houden." +"oplossingen die jouw installatie actueel houden." #: src/app/main/ui/dashboard/check_updates.cljs:309 msgid "dashboard.check-updates.available-title" @@ -360,23 +361,23 @@ msgstr "Volledig versielogboek weergeven" msgid "dashboard.check-updates.view-release-notes" msgstr "Uitgavenotities weergeven" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Prullenbak wissen" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopie)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Nieuwe org aanmaken" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Nieuw team aanmaken" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Deze versie vastmaken" @@ -384,7 +385,7 @@ msgstr "Deze versie vastmaken" msgid "dashboard.default-team-name" msgstr "Jouw Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Weet je zeker dat je al je verwijderde projecten en bestanden voor altijd " @@ -396,44 +397,44 @@ msgstr "" "Weet je zeker dat je %1 voor altijd wilt verwijderen? Dit is een niet " "omkeerbare actie." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s bestanden zijn met succes verwijderd." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Voor altijd verwijderen" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Project verwijderen" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Weet je zeker dat project %s voor altijd wilt verwijderen? Het project en " "alle bestanden die erin staan worden voor altijd verwijderd. Dit is een " "niet omkeerbare actie." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s is met succes verwijderd." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Team verwijderen" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "" -"Je afval is leeg. Verwijderde bestanden en projecten zullen hier " +"Je prullenbak is leeg. Verwijderde bestanden en projecten zullen hier " "verschijnen." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Wordt verwijderd op %1" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Penpot-bestand downloaden (.penpot)" @@ -445,19 +446,19 @@ msgstr "Dupliceren" msgid "dashboard.duplicate-multi" msgstr "%s bestanden dupliceren" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Zodra een projectlid een concept heeft gemaakt, wordt het hier weergegeven." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Nog geen concepten." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Zodra een projectlid een bestand heeft gemaakt, wordt het hier weergegeven." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Nog geen bestanden." @@ -505,23 +506,23 @@ msgstr "Importeer een .penpot-bestand" msgid "dashboard.empty-project.start" msgstr "Begin met het bouwen van geweldige dingen" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "Er is een fout opgetreden bij het verwijderen van de bestanden." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "Er is een fout opgetreden bij het verwijderen van het project %s." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "Er is een fout opgetreden bij het herstellen van het bestand %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "Er is een fout opgetreden bij het herstellen van de bestanden." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "" "Er is een fout opgetreden bij het herstellen van het project %s en zijn " @@ -531,37 +532,37 @@ msgstr "" msgid "dashboard.export-binary-multi" msgstr "%s Penpot-bestanden downloaden (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Borden exporteren als PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exporteren als PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s van %s elementen geselecteerd" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exporteren" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Je kunt exportinstellingen toevoegen aan elementen vanuit de " "ontwerpeigenschappen (onderaan de rechter zijbalk)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Info over het instellen van exports in Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Er zijn geen elementen met exportinstellingen." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Selectie exporteren" @@ -585,35 +586,35 @@ msgstr "Rasterweergave" msgid "dashboard.files-layout.list" msgstr "Lijstweergave" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Lettertype ontbreekt" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Alles negeren" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Aangepaste lettertypen die je uploadt, verschijnen hier." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Nog geen aangepaste lettertypen." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Zodra een projectlid een aangepast lettertype heeft geüpload, wordt het " "hier weergegeven." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 lettertype toegevoegd" msgstr[1] "%s lettertypes toegevoegd" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -622,7 +623,7 @@ msgstr "" "Lettertypen worden gegroepeerd op familienaam. Je kunt lettertypen uploaden " "met de volgende formaten: **TTF, OTF en WOFF** (slechts één formaat nodig)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -631,11 +632,11 @@ msgstr "" "Inhoudsrechten van [Penpot's Servicevoorwaarden](%s). Misschien wil je ook " "meer lezen over [lettertypelicenties](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Alles uploaden" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -646,23 +647,23 @@ msgstr "" "[Transfonter](https://transfonter.org/) te gebruiken om web-lettertypen te " "genereren en soortgelijke fouten op te lossen. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Ga naar het Beheerderspaneel" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "Laatst aangepast %s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importeer Penpot-bestanden" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Oeps! We konden dit bestand niet importeren" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "1 bibliotheek is automatisch op naam gekoppeld." @@ -672,121 +673,125 @@ msgstr[1] "%s bibliotheken zijn automatisch op naam gekoppeld." msgid "dashboard.import.bad-url" msgstr "Importeren is mislukt. De sjabloon-URL is onjuist" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "Bibliotheekkoppelingen bevestigen" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:814 +msgid "dashboard.import.connect-selected-libraries" +msgstr "Geselecteerde bibliotheken verbinden" + +#: src/app/main/ui/dashboard/import.cljs:288 +msgid "dashboard.import.file-name-label" +msgstr "Bestandsnaam" + +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "We konden dit bestand niet verifiëren." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Dit bestand lijkt beschadigd te zijn." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Niet alle bestanden zijn geïmporteerd" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "De volgende bestanden bevatten fouten:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Bestanden met fouten worden niet geüpload." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "Er ging iets mis bij de verwerking van dit bestand." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 bestand is geïmporteerd." msgstr[1] "%s bestanden zijn geïmporteerd." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Sommige bestanden bevatten ongeldige objecten die verwijderd zijn." +#: src/app/main/ui/dashboard/import.cljs:815 +msgid "dashboard.import.next-file" +msgstr "Volgende bestand" + #: src/app/main/ui/dashboard.cljs:265 msgid "dashboard.import.no-perms" msgstr "Je hebt geen toestemming om naar dit team te importeren" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Kleuren aan het verwerken" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Componenten aan het verwerken" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Media aan het verwerken" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Pagina aan het verwerkten: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Typografie verwerken" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Gegevens uploaden naar server (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Bestand aan het uploaden: %s" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "" "Sommige bibliotheken konden niet automatisch worden gekoppeld. Selecteer " "voor elke bibliotheek de juiste:" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "Controleer de bibliotheeklinks voordat je bevestigt:" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:447 +msgid "dashboard.import.resolve-libraries.connect-to" +msgstr "verbinden met" + +#: src/app/main/ui/dashboard/import.cljs:441 +msgid "dashboard.import.resolve-libraries.original-library" +msgstr "originele bibliotheek" + +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "Gekoppeld" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:517 +msgid "dashboard.import.summary.manually-linked" +msgstr "handmatig gekoppeld" + +#: src/app/main/ui/dashboard/import.cljs:525 +msgid "dashboard.import.summary.new" +msgstr "Nieuw" + +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "Geen bibliotheek geselecteerd" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/import.cljs:522 +msgid "dashboard.import.summary.original" +msgstr "Origineel" + +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Je gaat de uitnodigingen verwijderen op:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Je gaat de uitnodigingen opnieuw versturen:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Uitnodigingen verwijderen" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Uitnodigingen opnieuw versturen" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "Je hebt geen toestemming om mensen uit te nodigen om zich bij dit team aan " "te sluiten of om uitnodigingen te bewerken of te verwijderen." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Nodig mensen uit" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "Je bent niet gemachtigd om mensen uit te nodigen voor dit team" @@ -798,31 +803,31 @@ msgstr "Organisatie verlaten" msgid "dashboard.leave-organization.toast" msgstr "Je maakt niet langer deel uit van organisatie %s." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Team verlaten" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliotheken & sjablonen" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Hier heb je een aantal bibliotheken en sjablonen die je aan je project kunt " "toevoegen" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Ontdek er meer van en weet hoe je kunt bijdragen" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "" "Er is een probleem opgetreden bij het importeren van het sjabloon. Het " "sjabloon is niet geïmporteerd." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliotheken" @@ -830,7 +835,7 @@ msgstr "Bibliotheken" msgid "dashboard.loading-files" msgstr "bestanden laden …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "lettertypen laden …" @@ -850,61 +855,65 @@ msgstr "Verplaats %s bestanden naar" msgid "dashboard.move-to-other-team" msgstr "Verplaats naar ander team" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nieuw bestand" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Nieuw bestand" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nieuw project" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nieuw project" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Geen overeenkomsten gevonden voor \"%s\"" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "Je hebt geen toestemming om teams toe te voegen aan je organisaties." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 +msgid "dashboard.no-organizations-yet" +msgstr "Nog geen organisaties." + +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "Je mag geen teams aanmaken binnen organisatie %s. Neem voor meer informatie " "contact op met de eigenaar van de organisatie." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "Je mag geen teams verwijderen die deel uitmaken van organisatie %s. Neem " "voor meer informatie contact op met de eigenaar van de organisatie." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "Je mag geen teams verplaatsen die deel uitmaken van organisatie %s. Neem " "voor meer informatie contact op met de eigenaar van de organisatie." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Vastgemaakte projecten worden hier weergegeven" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Meldingen" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Je e-mailadres is succesvol bijgewerkt" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Je e-mailadres is geverifieerd" @@ -916,11 +925,11 @@ msgstr "Meldingsinstellingen bijgewerkt" msgid "dashboard.notifications.password-saved" msgstr "Wachtwoord succesvol opgeslagen!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Meldingen weergeven" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s leden" @@ -928,23 +937,23 @@ msgstr "%s leden" msgid "dashboard.open-in-new-tab" msgstr "Bestand openen in een nieuw tabblad" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opties" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Sortering op rol" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Sortering op status" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "De organisatie %s is verwijderd." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "Andere teams" @@ -968,11 +977,11 @@ msgstr "Je bent nu eigenaar van dit team." msgid "dashboard.permissions-change.viewer" msgstr "Je bent nu een lezer in dit team." -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "Persoonlijke projecten" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Vastzetten/losmaken" @@ -988,27 +997,27 @@ msgstr "Kan het plug-inmanifest niet verwerken" msgid "dashboard.plugins.try-plugin" msgstr "Probeer plugin: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "Bestanden verwijderen…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" -msgstr "Bestanden herstellen…" +msgstr "Bestanden terugzetten…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" -msgstr "Verwijdering onverwacht traag" +msgstr "Verwijdering gaat onverwacht traag" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" -msgstr "Herstel onverwacht traag" +msgstr "Terugzetten gaat onverwacht traag" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projecten" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Wil je je account verwijderen?" @@ -1016,114 +1025,114 @@ msgstr "Wil je je account verwijderen?" msgid "dashboard.removed-from-team" msgstr "Je maakt geen deel meer uit van het team “%s“." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" -msgstr "Je gaat al je projecten en bestanden herstellen. Dit kan even duren." +msgstr "Je gaat al je projecten en bestanden terugzetten. Dit kan even duren." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" -msgstr "Alle projecten en bestanden herstellen" +msgstr "Alle projecten en bestanden terugzetten" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" -msgstr "Alles herstellen" +msgstr "Alles terugzetten" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" -msgstr "%s bestanden zijn met succes hersteld." +msgstr "%s bestanden zijn met succes teruggezet." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" -msgstr "Project herstellen" +msgstr "Project terugzetten" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Je gaat het project %1 en alle daarin opgenomen bestanden herstellen." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" -msgstr "Project herstellen" +msgstr "Project terugzetten" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" -msgstr "%s is met succes hersteld." +msgstr "%s is met succes teruggezet." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Instellingen opslaan" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Zoeken…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Zoeken naar \"%s\"…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Organizaties" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "Teams" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" -msgstr "ORGANISATIE TOEVOEGEN" +msgstr "AAN ORGANISATIE TOEVOEGEN" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "Kies een organisatie:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" staat alleen organisatieleden toe om zich bij zijn teams aan te " "sluiten. Eventuele openstaande uitnodigingen aan mensen buiten de " "organisatie worden ingetrokken." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "" "Uitnodigingen aan externe gebruikers die nog in behandeling zijn worden " "geannuleerd." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "Hier vind je al je organisaties waar je teams mag aanmaken of toevoegen." -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "" "Hier bevinden zich de organisaties waar je deel van uitmaakt en waar je " "teams mag aanmaken of toevoegen." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "Kies een organisatie" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "Team toevoegen aan een organisatie" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Taal selecteren" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Thema selecteren" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Donker (standaard)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Licht" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Systeemthema" @@ -1187,11 +1196,11 @@ msgstr "Instellingen bijwerken" msgid "dashboard.settings.notifications.title" msgstr "Meldingen" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Alle bestanden tonen" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Versie-geschiedenis" @@ -1215,7 +1224,7 @@ msgstr[1] "Je bestanden zijn succesvol gedupliceerd" msgid "dashboard.success-duplicate-project" msgstr "Je project is succesvol gedupliceerd" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Je bestand is succesvol verplaatst" @@ -1227,184 +1236,184 @@ msgstr "Je bestanden zijn succesvol verplaatst" msgid "dashboard.success-move-project" msgstr "Je project is succesvol verplaatst" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s behoort nu tot %s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Team info" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Teamleden" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s behoort niet langer tot %s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Teamorganisatie" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Toevoegen aan een organisatie" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "" "Je hebt geen toestemming om dit team aan een van je organisaties toe te " "voegen." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Teamorganisatie wijzigen" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Alleen mensen binnen je organisatie kunnen worden uitgenodigd." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "Dit team maakt geen deel uit van een organisatie" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Team uit organisatie verwijderen" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Teamprojecten" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Toevoegen aan je project" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "UI thema" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Zoekresultaten" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Verwijderde bestanden blijven in de prullenbak voor" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s dagen. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "Daarna worden ze definitief verwijderd." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Als je van gedachten verandert, kun je ze herstellen of permanent " "verwijderen uit het menu van elk bestand." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Typ om te zoeken" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Publicatie Bibliotheek ongedaan maken" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Instellingen bijwerken" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "Je bent geen lid meer van de organisatie %s" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Beta" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "WebGL-weergave kan de prestaties verbeteren, maar bevindt zich in bèta en " "is mogelijk minder stabiel. Er kunnen enkele visuele verschillen optreden " "tussen het doek, de export (SVG/PDF) en de weergavemodus." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Uitgeschakeld" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Ingeschakeld" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Geef feedback" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Status" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "WebGL-weergave" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Is actief" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Wanneer deze hook wordt getriggerd, worden gebeurtenisdetails afgeleverd" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Je kunt alleen door jou gemaakte webhooks verwijderen of wijzigen." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Contenttype" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Webhook aanmaken" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook is succesvol aangemaakt." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhooks zijn een eenvoudige manier om andere websites en apps op de hoogte " "te stellen wanneer bepaalde gebeurtenissen bij Penpot plaatsvinden. We " "sturen een POST-verzoek naar elke URL die je opgeeft." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Druk op de knop \"Webhook aanmaken\" om er een toe te voegen." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Er zijn nog geen webhooks aangemaakt." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Jouw account" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-mail" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Naam" @@ -1420,11 +1429,11 @@ msgstr "Waarschuwing" msgid "ds.component-subtitle" msgstr "Componenten bijwerken:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Toestaan" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Annuleren" @@ -1444,7 +1453,7 @@ msgstr "Geen tokens van toepassing in actieve verzamelingen of thema's." msgid "ds.inputs.numeric-input.no-matches" msgstr "Geen overeenkomsten gevonden." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Lijst met tokens openen" @@ -1452,6 +1461,12 @@ msgstr "Lijst met tokens openen" msgid "ds.inputs.token-field.no-active-token-option" msgstr "Token {%s} zit niet in een actieve set of heeft een ongeldige waarde." +#: src/app/main/ui/auth/login.cljs:113 +msgid "errors.account-locked" +msgstr "" +"Te veel mislukte pogingen. Jouw account is voor %s minuten vergrendeld. " +"Probeer het later nog eens." + #: src/app/main/data/auth.cljs:344 msgid "errors.auth-provider-not-allowed" msgstr "Auth-provider is niet toegestaan voor dit profiel" @@ -1460,17 +1475,17 @@ msgstr "Auth-provider is niet toegestaan voor dit profiel" msgid "errors.auth-provider-not-configured" msgstr "Authenticatie-provider niet geconfigureerd." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "" "Het lijkt erop dat je niet geauthentiseerd bent of dat de sessie is " "verlopen." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Het lettertype %s kon niet geladen worden" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "De lettertypen %s konden niet geladen worden" @@ -1482,61 +1497,66 @@ msgstr "Kan het mediabestand niet uploaden." msgid "errors.character-limit-exceeded" msgstr "Tekenlimiet overschreden" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "" "Kan bibliotheek niet toevoegen: dit zou een circulaire afhankelijkheid " "creëren" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "Klembord-API is niet beschikbaar. Serveer Penpot via HTTPS om toegang tot " "het klembord mogelijk te maken" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Je browser kan deze functie niet uitvoeren" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" -"Toegang klembord geweigerd. Verleen klembordrechten aan uw browser om " +"Toegang klembord geweigerd. Verleen klembordrechten aan jouw browser om " "inhoud te plakken" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "Er is een fout opgetreden bij de opmerking" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:586 +msgid "errors.connection-error" +msgstr "" +"Kan de server niet bereiken Controleer je verbinding en probeer het opnieuw." + +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Sorry! Dit is een oud bestand dat gebruikmaakt van een verouderd type " "Penpot-activa en je niet kunt openen." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "zodat wij je kunnen helpen." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Hoewel Penpot dit soort activa niet meer ondersteunt, kun je wel" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "contact met ons opnemen" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "" "Een bovenliggende verzameling kan niet worden verwijderd naar een " "onderliggend pad." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E-mail is al in gebruik" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-mail is al gevalideerd." @@ -1548,30 +1568,30 @@ msgstr "Je kan je e-mail niet als wachtwoord gebruiken" msgid "errors.email-does-not-match-invitation" msgstr "E-mail komt niet overeen met de uitnodiging." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domein niet toegestaan" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Het emailadres «%s» heeft veel permanente bounce-rapporten." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "Het e-mailadres «%s» is gemeld als spam of permanent teruggestuurd." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Het lijkt erop dat je een bestand opent waarin de functie '%s' is " "ingeschakeld, maar jouw Penpot- versie ondersteunt dit niet of heeft het " "uitgeschakeld." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Functie '%s' wordt niet ondersteund." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Mag maximaal %s tekens bevatten." @@ -1579,7 +1599,7 @@ msgstr "Mag maximaal %s tekens bevatten." msgid "errors.field-min-length" msgstr "Moet minimaal 1 teken bevatten." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Leeg veld" @@ -1587,7 +1607,7 @@ msgstr "Leeg veld" msgid "errors.field-not-all-whitespace" msgstr "De naam moet een ander teken bevatten dan een spatie." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Het lijkt erop dat er een discrepantie bestaat tussen de ingeschakelde " @@ -1595,17 +1615,17 @@ msgstr "" "moeten migraties voor '%s' worden toegepast voordat het bestand kan worden " "geopend." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "De familienaam van het lettertype kan alleen letters, cijfers, spaties, " "koppeltekens, onderstrepingstekens en punten bevatten." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Er ging iets mis." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Er is iets mis gegaan met de webwerker." @@ -1621,7 +1641,7 @@ msgstr "Ongeldige gegevens" msgid "errors.invalid-email" msgstr "Voer een geldig e-mailadres in" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Bevestigingsmail moet overeenkomen" @@ -1655,11 +1675,11 @@ msgstr "Deze uitnodiging is mogelijk geannuleerd of verlopen." msgid "errors.ldap-disabled" msgstr "LDAP-authenticatie is uitgeschakeld." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Je hebt de limiet van '%s' bereikt. Neem contact op met support." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Het maximale (%s) aantal e-mails dat in één verzoek kan worden uitgenodigd, " @@ -1679,21 +1699,21 @@ msgstr "" msgid "errors.media-type-not-allowed" msgstr "Het lijkt erop dat dit geen geldige afbeelding is." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Het profiel dat je uitnodigt, heeft e-mails gedempt (spammeldingen of hoge " "bounces)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migratie aan de gang" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Alleen de maker van de versie kan het vergrendelen" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Alleen de maker van de versie kan het ontgrendelen" @@ -1707,7 +1727,7 @@ msgstr "" msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "De eigenaar van de organisatie kan de organisatie niet verlaten." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "Die organisatie bestaat niet" @@ -1719,7 +1739,7 @@ msgstr "Bevestigingswachtwoord moet overeenkomen" msgid "errors.password-too-short" msgstr "Wachtwoord moet minimaal 8 tekens lang zijn" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Ongeldige gegevens op klembord" @@ -1727,7 +1747,7 @@ msgstr "Ongeldige gegevens op klembord" msgid "errors.profile-blocked" msgstr "Het profiel is geblokkeerd" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "Je profiel heeft e-mails gedempt (spammeldingen of hoge bounces)." @@ -1735,28 +1755,32 @@ msgstr "Je profiel heeft e-mails gedempt (spammeldingen of hoge bounces)." msgid "errors.registration-disabled" msgstr "De registratie is momenteel uitgeschakeld." -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:700 msgid "errors.save-failed" msgstr "" -"Jouw laatste wijzigingen zijn niet bevestigd als opgeslagen. Het opslaan " -"kan niet automatisch worden hervat. Sluit of herlaad dit tabblad niet: je " -"kunt wijzigingen verliezen. Neem contact op met ondersteuning voor hulp." +"Autom. opslaan werkt niet vanwege een fout. Neem contact op met de " +"ondersteuning om de fout te melden en laad de pagina opnieuw om verder te " +"gaan vanaf je laatst opgeslagen versie." -#: src/app/main/errors.cljs:764 +#: src/app/main/data/persistence.cljs:447 +msgid "errors.save-retrying" +msgstr "Verbinding verbroken. Opnieuw proberen om jouw wijzigingen op te slaan." + +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG is ongeldig of misvormd" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Incompatibele functie '%s' gedetecteerd" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Onvoldoende leden om het team te verlaten, je kunt dit team maar beter " "verwijderen." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Het lid dat je probeert toe te wijzen, bestaat niet." @@ -1764,7 +1788,7 @@ msgstr "Het lid dat je probeert toe te wijzen, bestaat niet." msgid "errors.team-leave.only-owner-can-delete" msgstr "Alleen de eigenaar kan een team verwijderen." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "Eigenaar kan het team niet verlaten, je moet de rol van eigenaar eerst " @@ -1778,13 +1802,13 @@ msgstr "De teamnaam kan geen van de volgende tekens bevatten: '.', ':' of '/'" msgid "errors.token-set-already-exists" msgstr "Er bestaat al een verzameling met dezelfde naam" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "Kan sleepactie niet voltooien, een verzameling met dezelfde naam bestaat " "daar al." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Thema-optie met dezelfde naam bestaat" @@ -1796,13 +1820,13 @@ msgstr "Het thema verwijst naar enkele niet bestaande sets: %s" msgid "errors.tokens.circular-reference" msgstr "Token heeft een cirkelreferentie" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "Regelafstand is afhankelijk van de lettergrootte. Voeg een lettergrootte " "toe om de opgeloste waarde te verkrijgen." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "Dit veld mag niet leeg zijn" @@ -1822,11 +1846,11 @@ msgstr "Fout bij importeren:" msgid "errors.tokens.invalid-color" msgstr "Ongeldige kleurwaarde: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "Ongeldige tokenwaarde: je kunt alleen verwijzen naar een font-family-token" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Ongeldige gewichtswaarde lettertype: gebruik numerieke waarden (100-950) of " @@ -1848,41 +1872,41 @@ msgstr "" "Tokennamen mogen alleen letters en cijfers bevatten, gescheiden door . " "tekens en mogen niet beginnen met een $-teken." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "" "Ongeldig schaduwtype: alleen 'innerShadow' of 'dropShadow' worden " "geaccepteerd" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Ongeldige tokenwaarde: alleen none, Uppercase, Lowercase of Capitalize zijn " "toegestaan" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "" "Ongeldige tokenwaarde: alleen none, underline en strike-through zijn " "toegestaan" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "Ongeldige waarde: moet verwijzen naar een samengesteld schaduwtoken." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "Ongeldige waarde: moet verwijzen naar een samengesteld typografietoken." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Ongeldige tokenwaarde: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Ontbrekende tokenverwijzingen: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "" "Groepsnaam van %s is in conflict met een token met dezelfde naam in een " @@ -1896,31 +1920,31 @@ msgstr "Er zijn geen tokens, verzamelingen of thema's gevonden in dit bestand." msgid "errors.tokens.number-too-large" msgstr "Ongeldige tokenwaarde. De opgeloste waarde is te groot: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "De dekking moet tussen 0 en 100% of 0 en 1 zijn (bijv. 50% of 0,5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Token bevat cirkelverwijzing" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "Schaduwonscherpte moet groter zijn dan of gelijk zijn aan 0." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "Schaduwspreiding moet groter zijn dan of gelijk zijn aan 0." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "De dikte van de streek moet groter zijn dan of gelijk zijn aan 0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Ongeldige waarde: % is niet toegestaan." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Ongeldige waarde: Eenheden zijn niet toegestaan." @@ -1928,19 +1952,19 @@ msgstr "Ongeldige waarde: Eenheden zijn niet toegestaan." msgid "errors.unexpected-error" msgstr "Er is een onverwachte fout opgetreden." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Onbekend bewijsstuk" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Deze versie is al vergrendeld" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Deze versie is vergrendeld en kan niet door anderen worden verwijderd" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Bestand heeft een incompatibel versienummer" @@ -1973,41 +1997,41 @@ msgstr "Tenminste 1 grote letter" msgid "errors.weak-password.too-short" msgstr "Tenminste 8 tekens" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "" "WebGL is gestopt met werken. Laad de pagina opnieuw om deze opnieuw in te " "stellen" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Oeps! De context van het doek ging verloren" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Verbindingsfout, URL niet bereikbaar" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL komt niet door de validatie." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Laatste levering was niet succesvol." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Fout bij SSL-validatie." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Time-out" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Onverwachte fout bij het valideren" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Onverwachte status %s" @@ -2027,17 +2051,17 @@ msgstr "Omschrijving" msgid "feedback.description-placeholder" msgstr "Beschrijf de reden van je feedback" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "We zijn blij dat je er bent. Als je hulp nodig hebt, zoek dan eerst voordat " "je een nieuwe vraag stelt." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot-gemeenschap" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Andere manieren om contact met ons op te nemen" @@ -2061,11 +2085,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Contact opnemen" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Hier om te helpen met je technische vragen." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X-ondersteuningsaccount" @@ -2085,45 +2109,41 @@ msgstr "Idee" msgid "feedback.type.issue" msgstr "Probleem" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Bestanden downloaden" - -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "Gekoppelde bibliotheekactiva worden niet in het bestand opgenomen." -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "Activa als basisobjecten behandelen" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "" "Bestanden met gekoppelde bibliotheken worden in de export opgenomen, " "waarbij de koppeling behouden blijft. " -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "Bestand + bibliotheken exporteren" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "" "Wanneer geïmporteerd, kun je bestaande bibliotheken koppelen aan " "overeenkomende namen." -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "Overeenkomende bibliotheken koppelen bij import" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "" "Het bestand wordt geëxporteerd met bibliotheekactiva samengevoegd met de " "lokale bibliotheek." -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "Bibliotheekactiva insluiten in het bestand" @@ -2131,13 +2151,13 @@ msgstr "Bibliotheekactiva insluiten in het bestand" msgid "generic.error" msgstr "er is een fout opgetreden" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "Toepassen" #: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:102 msgid "import-shortcuts.diff-modal-text" -msgstr "De volgende sneltoetsen worden gewijzigd door de geïmporteerde configuratie." +msgstr "Deze sneltoetsen zullen veranderen:" #: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:98 msgid "import-shortcuts.diff-modal-title" @@ -2181,7 +2201,7 @@ msgstr "Voorbeeld van de vulafbeelding van de vorm" msgid "inspect.attributes.shadow" msgstr "Schaduw" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Grootte en positie" @@ -2189,43 +2209,31 @@ msgstr "Grootte en positie" msgid "inspect.attributes.stroke" msgstr "Streek" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Midden" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Binnenkant" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Buitenkant" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Typografie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Lettertype-familie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Lettergrootte" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Lettertype-stijl" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Letterafstand" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Regelafstand" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "Gemengde lettertypen Families" @@ -2235,17 +2243,17 @@ msgstr "Geen" #: src/app/main/ui/inspect/attributes/text.cljs:117 msgid "inspect.attributes.typography.text-decoration.strikethrough" -msgstr "" +msgstr "Doorhalen" #: src/app/main/ui/inspect/attributes/text.cljs:115 msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Onderstrepen" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Beginhoofdletters" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Kleine letters" @@ -2257,7 +2265,7 @@ msgstr "Geen" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Uitschakelen" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "HOOFDLETTERS" @@ -2305,6 +2313,10 @@ msgstr "Kopiëren" msgid "inspect.subtitle.main" msgstr "Hoofdcomponent" +#: src/app/main/ui/inspect/right_sidebar.cljs:176 +msgid "inspect.tabs-switcher-label" +msgstr "Inspectietabblad schakelen" + #: src/app/main/ui/inspect/right_sidebar.cljs:142 msgid "inspect.tabs.code.selected.circle" msgstr "Cirkel" @@ -2381,23 +2393,23 @@ msgstr "Opgeloste waarde:" msgid "inspect.tabs.styles.variants-panel" msgstr "Variant eigenschappen" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "Nieuw toegangstoken aanmaken" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "Druk op de knop \"Nieuw toegangstoken aanmaken\" om er een te genereren." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "Je hebt tot nu toe geen tokens." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "Persoonlijke toegangstokens" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "Persoonlijke toegangstokens functioneren als een alternatief voor ons " @@ -2482,53 +2494,53 @@ msgstr "De MCP-sleutel vervalt op %s" msgid "integrations.mcp-key.will-not-expire" msgstr "De MCP-sleutel heeft geen vervaldatum" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "Met de Penpot MCP-server kunnen MCP-clients rechtstreeks communiceren met " "Penpot-ontwerpbestanden." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "De configuratie van MCP-clients" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "Dit is de server-URL die je nodig hebt om je MCP-client te configureren " "voor de verbinding met de Penpot MCP-server." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "MCP-sleutel opnieuw genereren" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "MCP-sleutel" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "De MCP-sleutel is nodig voor het instellen van de MCP-client" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Status" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Uitgeschakeld" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Ingeschakeld" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "De MCP-sleutel voor de verbinding met de MCP-server is verlopen. Hierdoor " "kan de verbinding niet tot stand worden gebracht." -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "" "Genereer de MCP-sleutel opnieuw en pas je clientconfiguratie aan met de " @@ -2538,10 +2550,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "MCP-server" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "Bèta" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "Naam" @@ -2592,7 +2600,7 @@ msgstr "MCP-sleutel opnieuw genereren" msgid "integrations.regenerate-mcp-key.title.created" msgstr "MCP-sleutel opnieuw gegenereerd" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "Integraties" @@ -2610,47 +2618,47 @@ msgstr "Het token vervalt op %s" msgid "integrations.token.will-not-expire" msgstr "Het token heeft geen vervaldatum" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Alles als gelezen markeren" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Sneltoetsen" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Over Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Accepteren" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Actief" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Toevoegen" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Eigen lettertype toevoegen" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Toevoegen…" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Admin" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Alles" @@ -2658,17 +2666,17 @@ msgstr "Alles" msgid "labels.and" msgstr "en" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Terug" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Het lijkt erop dat je even moet wachten en het opnieuw moet proberen; we " "voeren klein onderhoud uit aan onze servers." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Bad Gateway" @@ -2680,23 +2688,23 @@ msgstr "Vervagen" msgid "labels.blur-effects" msgstr "Vervagingseffecten" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Annuleren" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "Controleren op updates" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "Controleren op updates…" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Sluiten" @@ -2704,7 +2712,7 @@ msgstr "Sluiten" msgid "labels.code" msgstr "Code" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Samenvouwen" @@ -2716,15 +2724,15 @@ msgstr "Kleur" msgid "labels.comment" msgstr "Commentaar" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Commentaar" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Gemeenschap" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Gemeenschap & bijdragen" @@ -2736,19 +2744,19 @@ msgstr "Berekend" msgid "labels.confirm-password" msgstr "Wachtwoord bevestigen" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Contact opnemen met ondersteuning" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Contact opnemen" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Doorgaan" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Je kunt doorgaan met een Penpot-account" @@ -2760,23 +2768,23 @@ msgstr "Kopie" msgid "labels.copy-color" msgstr "Kleur kopiëren" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Link kopiëren" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-heden" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Aanmaken" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Nieuw team aanmaken" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Nieuwe teamnaam" @@ -2784,7 +2792,7 @@ msgstr "Nieuwe teamnaam" msgid "labels.current" msgstr "Huidige" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Dashboard" @@ -2792,7 +2800,7 @@ msgstr "Dashboard" msgid "labels.default" msgstr "Standaard" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Verwijderen" @@ -2804,7 +2812,7 @@ msgstr "Commentaar verwijderen" msgid "labels.delete-comment-thread" msgstr "Thread verwijderen" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Uitnodiging verwijderen" @@ -2812,39 +2820,35 @@ msgstr "Uitnodiging verwijderen" msgid "labels.delete-multi-files" msgstr "%s bestanden verwijderen" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "Weet je zeker dat je je profielfoto wilt verwijderen?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "Profielfoto verwijderen" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Verwijderd" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Ontwikkeling" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Weggooien" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "%1 downloaden" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "Downloaden" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "Bestand downloaden…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Concepten" @@ -2852,19 +2856,19 @@ msgstr "Concepten" msgid "labels.duplicate" msgstr "Dupliceren" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Bewerken" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Redacteur" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Leeg" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Fout" @@ -2872,11 +2876,11 @@ msgstr "Fout" msgid "labels.exit" msgstr "Sluiten" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Verlopen" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exporteren" @@ -2888,7 +2892,7 @@ msgstr "Feedback uitgeschakeld" msgid "labels.feedback-sent" msgstr "Feedback verstuurd" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2896,63 +2900,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Vullen" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "Flex layout" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Lettertype-familie" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Stijlen" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Lettertypen" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "GitHub-repository" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Geef feedback" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Ga terug" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Grafisch ontwerp" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "Rasterlayout" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Helpcentrum" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Hulp & studie" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Verbergen" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Verwerkt commentaar verbergen" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importeren" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inactief" @@ -2960,53 +2964,53 @@ msgstr "Inactief" msgid "labels.info" msgstr "Info" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Geïnstalleerde lettertypen" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "Integraties" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Er is iets ergs gebeurd." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "" "Je kunt de bewerking opnieuw proberen of contact opnemen met de " "ondersteuning om de fout te melden." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Interne fout" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Uitnodigingen" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Taal" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Opmaak" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Studiecentrum" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Bibliotheken en sjablonen" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Laden…" @@ -3014,7 +3018,7 @@ msgstr "Laden…" msgid "labels.lock" msgstr "Vergrendelen" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Log in of meld je aan" @@ -3022,19 +3026,19 @@ msgstr "Log in of meld je aan" msgid "labels.login" msgstr "Inloggen" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Uitloggen" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Lid" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Leden" @@ -3046,43 +3050,43 @@ msgstr "Vermelden" msgid "labels.new-password" msgstr "Nieuw wachtwoord" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Volgende" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "" "Deze instantie heeft een installatiestap nodig die hoort bij de nieuwste " "versie." -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "Bekijk onze" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "Penpot kan nog niet starten." -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "technische handleiding" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "" "Penpot is tijdelijk niet beschikbaar vanwege een systeemprobleem. Probeer " "het even later nog eens." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Je bent helemaal bij! Nieuwe commentaarmeldingen verschijnen hier." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Geen openstaande uitnodigingen." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Verzamel je mensen en bouw samen geweldige dingen." @@ -3090,27 +3094,27 @@ msgstr "Verzamel je mensen en bouw samen geweldige dingen." msgid "labels.no-matches" msgstr "Geen overeenkomst" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Oeps!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Meldingen" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 bestand" msgstr[1] "%s bestanden" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 bord" msgstr[1] "%s borden" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 project" @@ -3120,43 +3124,43 @@ msgstr[1] "%s projecten" msgid "labels.old-password" msgstr "Huidig wachtwoord" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "Openen" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Opties" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Anders (namelijk…)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Anders" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Eigenaar" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Wachtwoord" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "In behandeling" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Penpot wijzigingen" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot-hub" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Vastgemaakte projecten" @@ -3164,31 +3168,31 @@ msgstr "Vastgemaakte projecten" msgid "labels.post" msgstr "Verzenden" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Vorige" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Product- of UX-ontwerp" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Product management" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profiel" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projecten" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Recent" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Referentie" @@ -3196,27 +3200,27 @@ msgstr "Referentie" msgid "labels.refresh" msgstr "Vernieuwen" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Release-opmerkingen" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Pagina's opnieuw laden" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Verwijderen" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Lid verwijderen" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Hernoemen" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Team hernoemen" @@ -3240,11 +3244,11 @@ msgstr "nieuwe reactie" msgid "labels.reply.thread" msgstr "Reageren" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Opnieuw versturen" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Uitnodiging opnieuw versturen" @@ -3252,43 +3256,43 @@ msgstr "Uitnodiging opnieuw versturen" msgid "labels.restore" msgstr "Herstellen" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Opnieuw proberen" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rol" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Opslaan" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Zoeken" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Lettertype zoeken" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Selecteer een optie" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Versturen" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Versturen…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "We zijn bezig met onderhoud van onze systemen." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Service niet beschikbaar" @@ -3296,70 +3300,74 @@ msgstr "Service niet beschikbaar" msgid "labels.sets" msgstr "Verzamelingen" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Instellingen" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Schaduw" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Delen" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibliotheek" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Weergeven" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Alle commentaar tonen" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Commentaarlijst tonen" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Alleen jouw vermeldingen weergeven" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Alleen eigen commentaar tonen" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/import.cljs:808 +msgid "labels.skip" +msgstr "Overslaan" + +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Bronnen" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "" "De aanmelding bij de identiteitsprovider van je organisatie is niet " "voltooid. Het kan zijn dat de provider niet beschikbaar is, of dat jouw " "account nog niet vermeld staat." -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "Opnieuw proberen" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "We konden je niet aanmelden bij %s" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Starten" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" @@ -3367,7 +3375,7 @@ msgstr "Status" msgid "labels.stroke" msgstr "Streek" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Student of docent" @@ -3379,27 +3387,31 @@ msgstr "Stijlen" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Schakelaar" +#: src/app/main/ui/dashboard/organization_team_switch.cljs:657 +msgid "labels.team-management" +msgstr "Team management" + #: src/app/main/ui/inspect/styles/style_box.cljs:30 msgid "labels.text" msgstr "Tekst" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Thema's" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Introductie" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Typografie" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Onbekende fout" @@ -3411,35 +3423,31 @@ msgstr "Ontgrendelen" msgid "labels.unpublish-multi-files" msgstr "Publicatie van %s bestanden ongedaan maken" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Bijwerken" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Team bijwerken" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Uploaden" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Eigen lettertypen uploaden" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Uploaden…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "Bestand uploaden…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variant" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Versie %s opmerkingen" @@ -3447,7 +3455,7 @@ msgstr "Versie %s opmerkingen" msgid "labels.view-only" msgstr "Alleen weergeven" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Kijker" @@ -3461,7 +3469,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s waarschuwing" msgstr[1] "%s waarschuwingen" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -3469,18 +3477,18 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Nieuw commentaar toevoegen" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(jij)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Jouw account" #: src/app/main/ui/ds/product/loader.cljs:21 msgid "loader.tips.01.message" msgstr "" -"Zorg ervoor dat uw ontwerpen consistent zijn en eenvoudig kunnen worden " +"Zorg ervoor dat jouw ontwerpen consistent zijn en eenvoudig kunnen worden " "bijgewerkt in verschillende projecten." #: src/app/main/ui/ds/product/loader.cljs:20 @@ -3619,7 +3627,7 @@ msgstr "" "Je bent het enige lid van een aantal van de teams. Deze teams worden samen " "met de projecten en bestanden verwijderd." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "VOORDAT JE DE ORGANISATIE VERLAAT" @@ -3627,82 +3635,82 @@ msgstr "VOORDAT JE DE ORGANISATIE VERLAAT" msgid "modals.before-leave-organization.warning" msgstr "Elk team waarvan je het enige lid bent, wordt verwijderd." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Grote verschuiving" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Nieuw e-mailadres bevestigen" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "We sturen je een e-mail naar je huidige e-mailadres \"%s\" om je identiteit " "te verifiëren." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Nieuw e-mailadres" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "E-mailadres wijzigen" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Je e-mailadres wijzigen" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Webhook aanmaken" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Webhook aanmaken" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Payload-URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://voorbeeld.nl/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Annuleer en behoud mijn account" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Ja, verwijder mijn account" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "Als je je account verwijdert, verlies je al je huidige projecten en " "archieven." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "Deze actie is onomkeerbaar. Je account, abonnement en organisaties worden " "onmiddellijk verwijderd. Je verliest ook de toegang tot je huidige " "projecten en bestanden." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organisaties die zullen worden verwijderd" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s leden" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s teams" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Weet je zeker dat je je account wilt verwijderen?" @@ -3762,23 +3770,23 @@ msgstr "Weet je zeker dat je %s bestanden wilt verwijderen?" msgid "modals.delete-file-multi-confirm.title" msgstr "Verwijderen van %s bestanden" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Weet je zeker dat je deze lettertypestijl wilt verwijderen? Het wordt dan " "niet meer geladen als het in een bestand wordt gebruikt." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Lettertypestijl verwijderen" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Weet je zeker dat je dit lettertype wilt verwijderen? Het wordt dan niet " "meer geladen als het in een bestand wordt gebruikt." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Lettertype verwijderen" @@ -3788,19 +3796,19 @@ msgstr "" "Weet je zeker dat je dit team dat deel uitmaakt van organisatie %s wil " "verwijderen?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Weet je zeker dat je deze pagina wilt verwijderen?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Pagina verwijderen" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "Weet je zeker dat je de geselecteerde pagina's wilt verwijderen?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "Pagina's verwijderen" @@ -3816,31 +3824,31 @@ msgstr "Weet je zeker dat je dit project wilt verwijderen?" msgid "modals.delete-project-confirm.title" msgstr "Project verwijderen" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Bestand verwijderen" msgstr[1] "Bestanden verwijderen" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Het is in geen enkel bestand geactiveerd." msgstr[1] "Ze zijn in geen enkel bestand geactiveerd." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Deze bibliotheek wordt hier geactiveerd: " msgstr[1] "Deze bibliotheken worden hier geactiveerd: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Weet je zeker dat je dit bestand wilt verwijderen?" msgstr[1] "Weet je zeker dat je deze bestanden wilt verwijderen?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Bestand verwijderen" @@ -3860,19 +3868,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Team verwijderen" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Lid verwijderen" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Weet je zeker dat je dit lid van het team wilt verwijderen?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Teamlid verwijderen" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3882,23 +3890,23 @@ msgstr[1] "" "Activa die al in die bestanden zijn gebruikt, blijven daar staan (er wordt " "geen ontwerp verbroken)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Webhook verwijderen" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Weet je zeker dat je deze webhook wilt verwijderen?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Webhook verwijderen" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Webhook bewerken" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Webhook bewerken" @@ -3916,15 +3924,15 @@ msgstr "Tokens importeren" msgid "modals.import-library-tokens.title" msgstr "Tokens importeren uit bibliotheek?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Uitnodiging versturen" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-mailadressen, kommagescheiden" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "Sommige leden zijn al lid van het team. We nodigen de rest wel uit." @@ -3942,102 +3950,102 @@ msgstr "Er zijn geen uitnodigingen verzonden" msgid "modals.invite-restricted-members.all-organization-members-in-team" msgstr "" "Alleen leden van de organisatie \"%s\" kunnen voor dit team worden " -"uitgenodigd en ze zijn allemaal al teamleden. Als u meer informatie nodig " +"uitgenodigd en ze zijn allemaal al teamleden. Als je meer informatie nodig " "heeft, neem dan contact op met de eigenaar van de organisatie." -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "Adressen die geen uitnodiging ontvangen" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "Annuleren" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "Alleen bestaande leden van de organisatie kunnen worden uitgenodigd." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "Uitnodigingen versturen" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "Enkele uitnodigingen kunnen niet worden verzonden" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "Sommige mensen op de lijst ontvangen de uitnodiging niet." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Je kunt leden uitnodigen voor het team zodat ze toegang hebben tot dit " "bestand en alle teambestanden." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Leden voor het team uitnodigen" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Aangezien je het enige lid van dit team bent, wordt het team samen met de " "projecten en bestanden verwijderd." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Weet je zeker dat je het %s team wilt verlaten?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Je kunt het team niet verlaten als er geen ander lid is om tot eigenaar te " "promoveren. Je zou het team kunnen verwijderen." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Jij bent de eigenaar van dit team. Selecteer een ander lid om tot eigenaar " "te maken voordat je vertrekt." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promoveren en verlaten" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Selecteer een lid om te promoveren" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Voordat je gaat" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Team verlaten" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Weet je zeker dat je dit team wilt verlaten?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Team verlaten" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Jij bent de eigenaar van de teams van sommige organisaties. Promoveer een " "ander lid om eigenaar te worden." -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Je bent het enige lid van een aantal van de teams. Deze teams worden samen " "met de projecten en bestanden verwijderd." -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "Bovendien ben jij de eigenaar van de teams van sommige organisaties. " @@ -4057,57 +4065,57 @@ msgstr "" msgid "modals.leave-organization-confirm.title" msgstr "Organisatie %s verlaten?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Verplaatsen" msgstr[1] "Verplaatsen" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Weet je zeker dat je deze bibliotheek wilt verplaatsen?" msgstr[1] "Weet je zeker dat je deze bibliotheken wilt verplaatsen?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Bibliotheek verplaatsen" msgstr[1] "Bibliotheken verplaatsen" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Verschuiving" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Eigendom overdragen" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Als je het eigendom overdraagt, verander je je rol in beheerder en verlies " "je enkele machtigingen voor dit team. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Je bent de huidige eigenaar van dit team. Weet je zeker dat je %s de nieuwe " "eigenaar van het team wilt maken?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Nieuwe teameigenaar" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publiceren" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Je bibliotheek is leeg. Weet je zeker dat je het wilt publiceren?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Lege bibliotheek publiceren" @@ -4129,17 +4137,17 @@ msgstr "Weet je zeker dat je het team '%s' uit de organisatie '%s' wilt verwijde msgid "modals.remove-team-organization.title" msgstr "TEAM UIT DE ORGANISATIE VERWIJDEREN" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Kleine verschuiving" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Publicatie ongedaan maken" msgstr[1] "Publicaties ongedaan maken" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Weet je zeker dat je de publicatie van deze bibliotheek ongedaan wilt maken?" @@ -4147,7 +4155,7 @@ msgstr[1] "" "Weet je zeker dat je de publicatie van deze bibliotheken ongedaan wilt " "maken?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Publicatie bibliotheek ongedaan maken" @@ -4204,11 +4212,11 @@ msgstr "" #: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:121 msgid "nitrate.code-activation.footer-after" -msgstr " en neem contact met ons op:" +msgstr "Stuur het bestand naar" #: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:126 msgid "nitrate.code-activation.footer-before" -msgstr "Een code nodig? Download je " +msgstr "en wij sturen je je code." #: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:119 msgid "nitrate.code-activation.footer-download" @@ -4238,38 +4246,38 @@ msgstr "Activeren" msgid "nitrate.code-activation.title" msgstr "Nitrate inschakelen" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "Neem contact op met Verkoop" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "" "Neem contact op met ons verkoopteam om over te schakelen naar dit " "abonnement. Wij helpen je jouw abonnement bij te werken en ervoor te zorgen " "dat alles correct wordt ingesteld." -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Je organisatie wordt verwijderd." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "" "Je totale opslag, automatische versiegeschiedenis en bestandsherstelperiode " "zijn beperkt." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "" "De teams, projecten en bestanden zullen geen deel meer uitmaken van een " "organisatie maar blijven wel beschikbaar." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "Wanneer je overgaat naar een mindere abonnementsvorm:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "Overschakelen naar abonnement %s?" @@ -4287,17 +4295,17 @@ msgstr "Neem contact met ons op om Enterprise 14 dagen uit te proberen:" msgid "nitrate.form.contact-upgrade" msgstr "Neem contact met ons op om te upgraden naar Enterprise:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "" "Neem contact met ons op om het Enterprise-abonnement 14 dagen uit te " "proberen:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "Neem contact met ons op om te upgraden naar het Enterprise-abonnement:" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "" "Er worden momenteel geen kosten in rekening gebracht. De betaling wordt aan " @@ -4318,7 +4326,7 @@ msgstr "" msgid "nitrate.form.enterprise-feature-3" msgstr "Volledige open-sourcevrijheid, met het bestuur dat past bij jouw organisatie" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "Alles wat jouw teams nodig hebben om veilig op scale%s te werken" @@ -4326,7 +4334,7 @@ msgstr "Alles wat jouw teams nodig hebben om veilig op scale%s te werken" msgid "nitrate.form.enterprise.price" msgstr "per deelnemer per maand" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "Probeer het 14 dagen gratis" @@ -4342,7 +4350,7 @@ msgstr "Aan de slag met Enterprise" msgid "nitrate.form.subscribe-with-code" msgstr "Abonneren met een activeringscode" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "Enterprise-functies vrijgeven" @@ -4354,7 +4362,7 @@ msgstr "Probeer het 14 dagen gratis" msgid "nitrate.modal-success.title" msgstr "Welkom bij Enterprise!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "Actief tot %s" @@ -4362,23 +4370,23 @@ msgstr "Actief tot %s" msgid "nitrate.subscription.renew-success" msgstr "Je Nitrate abonnement is verlengd." -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "Abonnement annuleren" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "Om je Nitrate-abonnement te annuleren, kun je contact met ons opnemen via:" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "Verlengen met activeringscode" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Deze pagina bestaat niet" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 fout" @@ -4396,11 +4404,11 @@ msgstr "We hebben een herstel-e-mail gestuurd naar" msgid "not-found.login.sent-recovery-check" msgstr "Controleer je e-mail en klik op de link om een nieuw wachtwoord te maken." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Gratis aanmelden" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "En begin binnen enkele seconden met Penpot!" @@ -4408,71 +4416,71 @@ msgstr "En begin binnen enkele seconden met Penpot!" msgid "not-found.made-with-love" msgstr "Gemaakt met LIEFDE en Open Source" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Je hebt al toegang tot dit bestand aangevraagd." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Je hebt al toegang gevraagd tot dit bestand of andere bestanden of " "projecten van dit team." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Je hebt al toegang gevraagd tot dit project of andere projecten of " "bestanden van dit team." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Je hebt al toegang tot dit project aangevraagd." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "TOEGANG VRAGEN" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "" "Onthoud dat als de eigenaar het toestaat, je wordt uitgenodigd voor het " "team." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Jouw verzoek is correct verzonden!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Je hebt geen toegang tot dit bestand." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Ga naar je Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Als de eigenaar het toestaat, word je uitgenodigd voor het team." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot-bestand" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Je hebt geen toegang tot dit project." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJECT" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "" "Om toegang te krijgen tot dit bestand, kun je dit aan de teameigenaar " "vragen." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "" "Om toegang te krijgen tot dit project, kun je dit aan de teameigenaar " @@ -4488,33 +4496,33 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Er is een nieuwe versie beschikbaar." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "Deze uitnodiging is niet meer beschikbaar." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Uitnodiging met succes verwijderd" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Uitnodiging succesvol verstuurd" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Uitnodigingslink gekopieerd" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Je kunt je profiel niet verwijderen. Wijs je teams opnieuw toe voordat je " "verder gaat." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profiel is opgeslagen!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Verificatie-e-mail verzonden naar %s. Controleer je e-mail!" @@ -4552,7 +4560,7 @@ msgstr "Teamnaam" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Team aanmaken" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Leden uitnodigen" @@ -4562,98 +4570,98 @@ msgstr "" "Ontwikkelaars, ontwerpers, managers... iedereen is welkom. Later kun je nog " "meer uitnodigen." -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Uitnodigen met rol:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Laten we beginnen!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Echte code genereren vanuit ontwerpen" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Design Systems creëren" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototype" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Het ontwerp van de UI/UX van een app" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Draadmodellering" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Waarvoor ga jij Penpot gebruiken?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Laat ons iets over je weten om ons te helpen Penpot voor jou te laten " "werken. Je antwoorden helpen ons bij het prioriteren van nieuwe functies en " "wijzen je in de goede richting om te beginnen." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Help ons je te leren kennen" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Welke van deze hulpmiddelen gebruik je het meest?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Wat voor werk doe je?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Wat is je bedrijfsgrootte?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Waar wil je aan de slag?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1.001 - 5.000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 - 500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 - 100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 - 1.000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "Alleen ik" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5.001+" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Onderwijs" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Persoonlijk" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Werk" @@ -4725,7 +4733,7 @@ msgstr "Actie" #: src/app/main/ui/settings/restore_shortcuts_modal.cljs:94 msgid "restore-shortcuts.modal-text" -msgstr "De volgende sneltoetsen worden teruggezet naar hun standaardwaarden." +msgstr "Deze sneltoetsen worden teruggezet naar standaard:" #: src/app/main/ui/settings/restore_shortcuts_modal.cljs:90 msgid "restore-shortcuts.modal-title" @@ -4735,11 +4743,11 @@ msgstr "Standaardconfiguratie herstellen" msgid "restore-shortcuts.restore" msgstr "Herstellen" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Ontkoppelen" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Gemengd" @@ -4751,35 +4759,35 @@ msgstr "Kleur verwijderen" msgid "settings.select-this-color" msgstr "Selecteer items in deze stijl" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Commentaar" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Onderaan uitlijnen" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Horizontaal centreren" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Links uitlijnen" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Rechts uitlijnen" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Bovenaan uitlijnen" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Verticaal centreren" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Maak bord van selectie" @@ -4787,55 +4795,55 @@ msgstr "Maak bord van selectie" msgid "shortcuts.bold" msgstr "Vet in/uitschakelen" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Aftrekken (Booleaans verschil)" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Uitsluiting" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Booleaanse kruising" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Booleaanse vereniging" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Naar de achtergrond" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Naar achteren" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Naar voren" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Naar de voorgrond" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Ongedaan maken wissen" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Kopiëren" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Link naar klembord kopiëren" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Eigenschappen kopiëren" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Component/variant aanmaken" @@ -4843,63 +4851,63 @@ msgstr "Component/variant aanmaken" msgid "shortcuts.create-new-project" msgstr "Nieuw project aanmaken" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Knippen" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Uitzoomen" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Verwijderen" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Knooppunt verwijderen" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "Knooppunt en segmenten verwijderen" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "Selectie opheffen" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Component losmaken" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "Sneltoets uitschakelen" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "Uitgeschakeld" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "Uitgeschakelde sneltoets" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "Pijl" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Kromme" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Ellips" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Bord" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "Lijn" @@ -4907,65 +4915,65 @@ msgstr "Lijn" msgid "shortcuts.draw-nodes" msgstr "Pad tekenen" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Pad" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rechthoek" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Tekst" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Dupliceren" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "Combinatie toegewezen aan \"%s\". Opslaan zal het daar verwijderen." -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "Als je opslaat, neemt deze sneltoets de oorspronkelijke waarde aan:" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "" "Deze toetsencombinatie is beschikbaar en kan zonder conflicten worden " "toegewezen." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "Aanpassen op instellingen" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Annuleren" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Vormen exporteren" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "Zoeken" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "Zoeken en Vervangen" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Passend zoomen" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Horizontaal spiegelen" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Verticaal spiegelen" @@ -4989,27 +4997,27 @@ msgstr "Ga naar gedeelde bibliotheek" msgid "shortcuts.go-to-search" msgstr "Zoeken" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Groeperen" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Horizontaal verdelen" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "UI tonen/verbergen" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "Import/Export" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Inzoomen" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Afbeelding invoegen" @@ -5017,11 +5025,11 @@ msgstr "Afbeelding invoegen" msgid "shortcuts.italic" msgstr "Cursief in/uitschakelen" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Knooppunten verbinden" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "Druk op de toetsencombinatie" @@ -5029,39 +5037,39 @@ msgstr "Druk op de toetsencombinatie" msgid "shortcuts.line-through" msgstr "Doorstrepen in/uitschakelen" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Hoek maken" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Kromme maken" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Maskeren" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Knooppunten samenvoegen" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Verplaatsen" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Snel naar beneden verplaatsen" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Snel naar links verplaatsen" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Snel naar rechts verplaatsen" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Snel naar boven verplaatsen" @@ -5069,379 +5077,379 @@ msgstr "Snel naar boven verplaatsen" msgid "shortcuts.move-nodes" msgstr "Knooppunt verplaatsen" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Naar beneden verplaatsen" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Naar links verplaatsen" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Naar rechts verplaatsen" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Naar boven verplaatsen" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Volgend bord" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" -msgstr "Er zijn geen uitgeschakelde sneltoetsen" +msgstr "Geen uitgeschakelde sneltoetsen." -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "Ga naar Alles om te beginnen met het aanpassen van je sneltoetsen." -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" -msgstr "Er zijn geen sneltoetsen die overeenkomen met de zoekopdracht" +msgstr "Geen sneltoetsen die overeenkomen met de zoekopdracht." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" -msgstr "Geen sneltoets gevonden" +msgstr "Geen sneltoetsen die overeenkomen met de zoekopdracht." -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Dekking 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Dekking 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Dekking 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Dekking 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Dekking 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Dekking 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Dekking 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Dekking 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Dekking 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Dekking 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Kleurkiezer" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Ga naar het commentaargedeelte van de kijker" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Ga naar dashboard" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Ga naar de sectie voor het inspecteren van kijkers" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Ga naar de kijkersinteracties-sectie" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Ga naar de kijkersinteracties-sectie" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Ga naar werkruimte" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " of " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "Deze sneltoets is opnieuw toegewezen aan %s" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "Sneltoetsenpagina" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Plakken" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Eigenschappen plakken" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "Plakken en vervangen" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "Gepersonaliseerd" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Beheer van plug-ins" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Vorig bord" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Opnieuw doen" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "" "Herlaad na het bewerken van snelkoppelingen alle andere geopende " "Penpot-tabbladen om de wijzigingen toe te passen." -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Hernoemen" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "Terugzetten naar standaard" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Zoom herstellen" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Schaal" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Basis" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Dashboard" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Bekijker" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Werkruimte" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Alles selecteren" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Volgende laag selecteren" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Bovenliggende laag selecteren" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Vorige laag selecteren" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Knooppunten loskoppelen" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "Toon metingen" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Pixelraster tonen/verbergen" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Sneltoetsen tonen/verbergen" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Uitlijnen op knooppunten" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Uitlijnen op pixelraster" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Start met bewerken" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Uitlijning" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Bewerken" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Algemeen" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Hoofdmenu" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Lagen wijzigen" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigatie" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigatie" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigatie" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panelen" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Paden" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Vormen" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Teksten" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Hulpmiddelen" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoomen" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoomen" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Miniaturen instellen" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Sneltoetsen" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Dynamisch uitlijnen in/uitschakelen" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Activa wisselen" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Kleurenpalet in/uitschakelen" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "Zichtbaarheid van commentaar wisselen" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Focusmodus in/uitschakelen" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Volledig scherm in/uitschakelen" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Hulplijnen tonen/verbergen" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Geschiedenis in/uitschakelen" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Lagen in/uitschakelen" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Flex-layout toevoegen/verwijderen" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Raster-layout toevoegen/verwijderen" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Vergrendelen/ontgrendelen" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Proporties vergrendelen" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Linialen tonen/verbergen" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Uitlijnen op hulpijnen" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Uitlijnen op liniaal-hulplijnen" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Tekstpalet in/uitschakelen" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Thema veranderen" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Tonen/verbergen" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Zoomstijl wisselen" @@ -5449,31 +5457,31 @@ msgstr "Zoomstijl wisselen" msgid "shortcuts.underline" msgstr "Onderstrepen in/uitschakelen" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Ongedaan maken" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Groep opheffen" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Masker verwijderen" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Verticaal verdelen" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Zoomlens verkleinen" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Zoomlens vergroten" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zoomen naar selectie" @@ -5492,7 +5500,7 @@ msgstr "Enterprise in actie bekijken" msgid "subscription.current-plan.enterprise" msgstr "Enterprise" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Enterprise" @@ -5504,7 +5512,7 @@ msgstr "Enterprise Proefperiode" msgid "subscription.current-plan.professional" msgstr "Professional" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "Jouw abonnement" @@ -5633,281 +5641,281 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Mensen uitnodigen bij het Unlimited-abonnement" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "" "Betaling is niet voltooid. Probeer het opnieuw wanneer je er klaar voor " "bent." -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "" "De betaling is niet voltooid. Probeer het alsjeblieft nog eens. Als het " "probleem aanhoudt, neem dan contact met ons op: support@penpot.app." -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Abonnement" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "Activatiecode invoeren" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Voeg een betaalmethode toe om door te gaan na de proefperiode" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "Admin Console" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Alle voordelen van het Professional-abonnement en:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Alle voordelen van het Unlimited-abonnement en:" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "Alle voordelen van het Unlimited-abonnement, plus:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s editors)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise (proefperiode)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90-dagen automatische opslag van versies en bestandsherstel" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Gemaximeerde maandelijkse rekening" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "Geavanceerde controle:" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "Enterprise-beveiliging:" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Multi-Org-beheer:" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "" "Native SAML/SSO en volledige Audit Trail-registratie voor " "nalevingsrapportage." -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "" "Granulaire whitelisting van plug-ins, inclusief onze veilige, geïsoleerde " "MCP Server voor AI-connectiviteit." -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "" "Ondersteuning voor complexe teamhiërarchieën en afzonderlijke " "suborganisaties." -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Onbeperkte opslag" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" -msgstr "Beheer uw abonnement" +msgstr "Abonnement beheren" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Betalingsgegevens toevoegen" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Voeg nu je betalingsgegevens toe om je abonnement na de proefperiode soepel " "te laten verlopen en ons open-sourceproject te blijven ondersteunen. Er " "worden nog geen kosten in rekening gebracht." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Nu overslaan en proefperiode starten" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Help ons groeien en maak je proefperiode eenvoudiger" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Momenteel heb je % s persoon in je teams die kan bewerken." msgstr[1] "Momenteel heb je % s persoen in je teams die kunnen bewerken." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Let op: overstappen naar een lager abonnement betekent minder opslagruimte " "en kortere back-ups en versiegeschiedenis." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Editors" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(Eigenaren, beheerder en redacteuren. Kijkers tellen niet als Editors)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "Je kunt niet minder editors instellen dan nu. Wijzig de rol (editor/admin " "naar viewer) voor mensen die bestanden niet daadwerkelijk bewerken in de " "teaminstellingen." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "Berekend na proefperiode. Nu geen creditcard nodig." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s** per maand" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "%s toepassen op je teams" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Tip: Je kunt het aantal gebruikers nu verhogen om uitnodigingen te blijven " "doen. Bij 25+ editors van verschillende teams geniet je van een vaste prijs " "van $175/maand." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" -msgstr "Lid van Penpot sinds: %s" +msgstr "Lid van Penpot sinds: %s." -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Meer informatie" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "org deelnemer / maand" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Overige Penpot-abonnementen" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "organisatie per maand" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "gebruiker / maand" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professional" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7-dagen automatische versies en bestandsherstel" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "Alle ontwerp- en prototypingfuncties met ondersteuning door de gemeenschap." -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "Volledige controle over data en infrastructuur." -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "Onbeperkte gebruikers, teams en bestanden." -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10GB opslag" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Onbeperkte teams. Maximaal 8 editors in je eigen teams." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Aanbevolen" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Jouw abonnement" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Start gratis proefperiode" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Neem een abonnement" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "Je bent op het %s-plan sinds %s." -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Je kunt je abonnement op elk gewenst moment aanpassen via de pagina " "'Abonnement' in je accountgegevens." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Veel plezier met je abonnement!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Bedankt voor het kiezen van het Penpot %s-abonnement!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Je bent %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Probeer het 14 dagen gratis" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Unlimited" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unlimited (proef)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30-dagen automatisch opgeslagen versies en bestandsherstel" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Maandelijkse rekening van max. $175" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GB opslag" @@ -5934,45 +5942,45 @@ msgstr "" "Als je deze limiet wilt verhogen, [pas dan je abonnement " "aan|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 uitnodiging geselecteerd" msgstr[1] "%s uitnodigingen geselecteerd" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Lettertypeaanbieders - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Lettertypen - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projecten - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Zoeken - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Gedeelde bibliotheken - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Ontwerpvrijheid voor teams" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Feedback geven - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "Integraties - Penpot" @@ -5980,7 +5988,7 @@ msgstr "Integraties - Penpot" msgid "title.settings.notifications" msgstr "Meldingen - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Instellingen - Penpot" @@ -5988,27 +5996,27 @@ msgstr "Instellingen - Penpot" msgid "title.settings.password" msgstr "Wachtwoord - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profiel - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Uitnodigingen - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Leden - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Instellingen - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Weergavemodus - Penpot" @@ -6020,51 +6028,51 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Token loskoppelen" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Geen borden gevonden op de pagina." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Bord niet gevonden." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Commentaar (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Interacties niet tonen" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Volledig scherm" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspecteren (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interacties" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interacties (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Link kopiëren" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Interacties tonen" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Interacties tonen bij aanklikken" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Sitemap" @@ -6101,67 +6109,67 @@ msgstr "Volg onze " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "WebGL-gids voor probleemoplossing" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "WebGL-weergave uitgeschakeld" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "WebGL-weergave ingeschakeld" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "WebGL-context ging verloren" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "WebGL-context is hersteld" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "De laatste levering was succesvol." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Horizontaal centreren (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Horizontaal verdelen (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Links uitlijnen (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Rechts uitlijnen (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Onderkant uitlijnen (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Verticaal centreren (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Verticaal verdelen (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Bovenkant uitlijnen (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Bibliotheek toevoegen" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Alle activa" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Kleuren" @@ -6173,7 +6181,7 @@ msgstr "Kleur toevoegen" msgid "workspace.assets.component-group-options" msgstr "Componentgroep opties" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Componenten" @@ -6181,7 +6189,7 @@ msgstr "Componenten" msgid "workspace.assets.components.add-component" msgstr "Component toevoegen" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s varianten" @@ -6189,11 +6197,11 @@ msgstr "%s varianten" msgid "workspace.assets.create-group" msgstr "Groep aanmaken" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Je items krijgen automatisch de naam \"groepsnaam / itemnaam\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Verwijderen" @@ -6201,11 +6209,11 @@ msgstr "Verwijderen" msgid "workspace.assets.delete-group" msgstr "Groep verwijderen" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "Pagina's verwijderen" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Dupliceren" @@ -6213,39 +6221,39 @@ msgstr "Dupliceren" msgid "workspace.assets.duplicate-main" msgstr "Hoofdcomponent dupliceren" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Bewerken" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filteren" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Rasterweergave" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Groeperen" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Groepsnaam" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Lijstweergave" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "Lokale bibliotheek" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Bibliotheek beheren" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Geen activa gevonden" @@ -6253,7 +6261,7 @@ msgstr "Geen activa gevonden" msgid "workspace.assets.open-library" msgstr "Open Bibliotheek-bestand" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Hernoemen" @@ -6261,11 +6269,11 @@ msgstr "Hernoemen" msgid "workspace.assets.rename-group" msgstr "Groep hernoemen" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Activa zoeken" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Gedeelde bibliotheek" @@ -6275,43 +6283,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 component" msgstr[1] "%s componenten" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Sorteren" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Typografie" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Typografie toevoegen" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Grootte" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Lettertype-stijl" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Ga naar het stijl-bibliotheekbestand om te bewerken" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Letterafstand" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Regelafstand" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Tekst transformeren" @@ -6319,101 +6327,105 @@ msgstr "Tekst transformeren" msgid "workspace.assets.ungroup" msgstr "Groep opheffen" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "Afbeelding kopiëren…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "Afbeelding gekopieerd naar het klembord" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "Fout bij kopiëren van afbeelding" +#: src/app/main/ui/workspace/colorpicker.cljs:447 +msgid "workspace.colorpicker.color-picker" +msgstr "Kies kleur van scherm" + #: src/app/main/ui/workspace/colorpicker.cljs:439, src/app/main/ui/workspace/colorpicker.cljs:454 msgid "workspace.colorpicker.color-tokens" msgstr "Kleurtokens" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Componenten kunnen niet in zichzelf worden genest." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "Sommige exemplaren konden niet worden verwisseld. Componenten kunnen niet " "in zichzelf worden genest." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Gebied aanmaken" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "Kolommen kopiëren" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "Rijen kopiëren" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Bord aanmaken" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Cellen samenvoegen" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "Plakken" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Rechts 1 kolom toevoegen" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Links 1 kolom toevoegen" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Kolom verwijderen" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Kolom en vormen verwijderen" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Kolom dupliceren" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Onder 1 rij toevoegen" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Boven 1 rij toevoegen" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Rij verwijderen" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Rij en vormen verwijderen" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Rij dupliceren" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "Hulplijnkleur" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "Hulplijn verwijderen" @@ -6421,15 +6433,15 @@ msgstr "Hulplijn verwijderen" msgid "workspace.debug.title" msgstr "Hulpmiddelen voor foutopsporing" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Focusmodus" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Focus uit" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Focus aan" @@ -6441,243 +6453,247 @@ msgstr "Lineair verloop" msgid "workspace.gradients.radial" msgstr "Radiaal verloop" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Dynamische uitlijning uitschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Proportionele schaal uitschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Uitlijnen op hulplijnen uitschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Uitlijnen op pixel uitschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Uitlijnen op liniaal-hulplijnen deactiveren" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "WebGL-weergave (bèta) uitschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Dynamische uitlijning inschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Proportionele schaal inschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Uitlijnen op hulplijnen" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Uitlijnen op pixel" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Uitlijnen op liniaal-hulplijnen" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "WebGL-weergave (bèta) inschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "Zoeken" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "Zoeken en Vervangen" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Bordnamen verbergen" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "Commentaar verbergen" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Hulplijnen verbergen" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Kleurenpalet verbergen" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Pixelraster verbergen" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Linialen verbergen" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Lettertype-palet verbergen" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "Hulplijnen vergrendelen" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "Verbinding maken" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "Verbinding verbreken" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "Beheren (status: uitgeschakeld)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "Beheren (status: ingeschakeld)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Bewerken" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Bestand" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Hulp & informatie" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "MCP-server" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Voorkeuren" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Beeld" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Opnieuw doen" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Alles selecteren" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Bordnamen tonen" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "Commentaar weergeven" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Hulplijnen weergeven" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Kleurenpalet tonen" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Pixelraster tonen" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Linialen tonen" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Lettertype-palet tonen" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Donker thema inschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Licht thema inschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Systeemthema inschakelen" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Ongedaan maken" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "Hulplijnen ontgrendelen" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Herstellen" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:133 +msgid "workspace.header.retrying" +msgstr "Opnieuw proberen op te slaan" + +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Fout tijdens opslaan" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Opgeslagen" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Opslaan" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Delen" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Weergavemodus (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoomen" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Vullen - Schalen om te vullen" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Passend maken - Verkleinen om te passen" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Passend zoomen" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Volledig scherm" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Naar selectie zoomen" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Raster bewerken" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Sluiten" @@ -6685,7 +6701,7 @@ msgstr "Sluiten" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Vulling aan de onderkant" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "4-zijdige opvulopties weergeven" @@ -6721,37 +6737,37 @@ msgstr "Klaar" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Lokaliseren" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Raster-layout tonen" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 kleur" msgstr[1] "%s kleuren" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "Kleur toevoegen aan bilbliotheek" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Er zijn nog geen kleurstijlen in je bibliotheek" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "Er zijn nog geen recente kleuren" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Er zijn nog geen typografiestijlen in je bibliotheek" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Bestandsbibliotheek" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Recente kleuren" @@ -6763,75 +6779,99 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Kleurstijl opslaan" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "Kleurenpalet wisselen" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 component" msgstr[1] "%s componenten" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:864 +msgid "workspace.libraries.connected-libraries" +msgstr "Verbonden bibliotheek" + +#: src/app/main/ui/workspace/libraries.cljs:235 +msgid "workspace.libraries.connected-through" +msgstr "Verbinden door" + +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Verbonden met" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Of voeg er enkele toe om te proberen:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:834 +msgid "workspace.libraries.empty.no-assets" +msgstr "Nog geen activa" + +#: src/app/main/ui/workspace/libraries.cljs:918 +msgid "workspace.libraries.empty.no-connected-libraries" +msgstr "Nog geen verbonden bibliotheken." + +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "Er zijn geen Gedeelde Bibliotheken bij jouw team, je kunt zoeken naar" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "enkele sjablonen hier" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Bestandsbibliotheek" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 afbeelding" msgstr[1] "%s afbeeldingen" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTHEKEN IN DIT BESTAND" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTHEKEN" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "BIBLIOTHEEK-UPDATES" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Laden…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Er zijn geen gedeelde bibliotheken die moeten worden bijgewerkt" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Geen resultaten gevonden voor “%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Gedeelde bibliotheken zoeken" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:849, src/app/main/ui/workspace/libraries.cljs:897 +msgid "workspace.libraries.set-as-tokens-source" +msgstr "Instellen als tokensbron" + +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "GEDEELDE BIBLIOTHEKEN" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:674 +msgid "workspace.libraries.shared-library-added" +msgstr "Toegevoegd" + +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Bibliotheek verbinden" @@ -6855,25 +6895,47 @@ msgstr "Alle activa ontkoppelen" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "Alle tokens loskoppelen" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:219, src/app/main/ui/workspace/libraries.cljs:263 +msgid "workspace.libraries.token-sets" +msgid_plural "workspace.libraries.token-sets" +msgstr[0] "1 set" +msgstr[1] "%s sets" + +#: src/app/main/ui/workspace/libraries.cljs:222, src/app/main/ui/workspace/libraries.cljs:267 +msgid "workspace.libraries.token-themes" +msgid_plural "workspace.libraries.token-themes" +msgstr[0] "1 thema" +msgstr[1] "%s thema's" + +#: src/app/main/ui/workspace/libraries.cljs:216, src/app/main/ui/workspace/libraries.cljs:259 +msgid "workspace.libraries.tokens" +msgid_plural "workspace.libraries.tokens" +msgstr[0] "1 token" +msgstr[1] "%s tokens" + +#: src/app/main/ui/workspace/libraries.cljs:842, src/app/main/ui/workspace/libraries.cljs:889 +msgid "workspace.libraries.tokens-source" +msgstr "Tokensbron" + +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 typografie" msgstr[1] "%s typografiën" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Bibliotheekverbinding verbreken" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Bijwerken" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "alle wijzigingen bekijken" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "UPDATES" @@ -6935,121 +6997,125 @@ msgstr "Vervaging wisselen" msgid "workspace.options.blur-options.toggle-more-options" msgstr "Meer opties tonen/verbergen" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "Annuleren" #: src/app/main/ui/workspace/sidebar/options/page.cljs:78, src/app/main/ui/workspace/sidebar/options/page.cljs:86 msgid "workspace.options.canvas-background" -msgstr "Canvasachtergrond" +msgstr "Doekachtergrond" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:75 +msgid "workspace.options.canvas-background-section" +msgstr "Doekachtergrond-sectie" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Content bijsnijden" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Component" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Aantekening" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Kopiëren" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Aantekening maken" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Aantekening bewerken" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Hoofd" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Component uitwisselen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Er zijn nog geen activa in deze bibliotheek" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Niet gekoppeld" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Ga naar het hoofdonderdeel" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Dit onderdeel kent conflicterende varianten. Zorg ervoor dat elke variatie " "een unieke set eigenschapswaarden heeft." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Gedupliceerde varianten lokaliseren" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Sommige varianten hebben identieke eigenschappen en waarden" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "Deze varianten hebben identieke eigenschappen en waarden." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Deze variant heeft identieke eigenschappen en waarden als een andere " "variant." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "Sommige van deze varianten hebben identieke eigenschappen en waarden." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Dit onderdeel heeft varianten met ongeldige namen. Zorg ervoor dat elke " "variant de juiste structuur volgt." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Ongeldige varianten localiseren" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Sommigen varianten hebben een ongeldige naam" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Ongeldige varianten zoeken" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Deze varianten hebben een ongeldige naam." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Deze variant heeft een ongeldige naam." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Sommige van deze varianten hebben een ongeldige naam." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[eigenschap]=[waarde], [eigenschap]=[waarde]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Gebruik de volgende structuur:" @@ -7088,7 +7154,7 @@ msgstr "Heben hetzelfde hiërarchieniveau." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Groepen, borden en lay-outs worden als gelijkwaardig beschouwd." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Hoe varianten verbonden blijven" @@ -7136,11 +7202,11 @@ msgstr "Boven- en onderkant" msgid "workspace.options.convert-to-typography" msgstr "Typografiestijl maken" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Ontwerp" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exporteren" @@ -7148,53 +7214,53 @@ msgstr "Exporteren" msgid "workspace.options.export-multiple" msgstr "Selectie exporteren" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "1 Element exporteren" msgstr[1] "%s Elementen exporteren" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Export toevoegen" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Export verwijderen" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Achtervoegsel" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "Exportservice is druk, probeer het later opnieuw" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "Export geannuleerd" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "Annuleren…" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Export klaar" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Exporteren…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Export mislukt" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Export onverwacht traag" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "Wachten…" @@ -7214,7 +7280,7 @@ msgstr "Vulling verwijderen" msgid "workspace.options.fill.section" msgstr "Sectie vullen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Grootte van bord aanpassen aan inhoud" @@ -7234,71 +7300,77 @@ msgstr "Stroomdiagram-startpunt" msgid "workspace.options.flows.flow-starts" msgstr "Stroomdiagram-startpunten" +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:300, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 +msgid "workspace.options.font-not-available" +msgstr "" +"Het lettertype dat door deze tekst wordt gebruikt (%s) is niet langer " +"beschikbaar. Upload het lettertype opnieuw om een typografiestijl te maken" + #: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:34 msgid "workspace.options.grid.auto" msgstr "Automatisch" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Kolommen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Kleur" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Tussenruimte" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Hoogte" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Marge" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Als standaard instellen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Onderkant" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Midden" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Links" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Rechts" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Uitrekken" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Bovenkant" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Standaard gebruiken" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Breedte" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Rijen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Vierkant" @@ -7310,7 +7382,7 @@ msgstr "Groep vullen" msgid "workspace.options.group-stroke" msgstr "Groep-streek" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Hulplijn toevoegen" @@ -7318,19 +7390,19 @@ msgstr "Hulplijn toevoegen" msgid "workspace.options.guides.remove-guide" msgstr "Hulplijn verwijderen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Hulplijnen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Hulplijn wisselen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Hoogte" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspecteren" @@ -7546,115 +7618,119 @@ msgstr "Interacties" msgid "workspace.options.interactions.add-interaction" msgstr "Interactie toevoegen" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Kleur" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Kleur versterken" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Kleur verminderen" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Donkerder maken" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Verschil" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Uitsluiting" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Hard licht" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Tint" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Lichter maken" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Helderheid" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Vermenigvuldigen" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normaal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Overlay" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Verzadiging" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Scherm" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Zacht licht" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "Menusectie laag" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Laagzichtbaarheid wisselen" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Max.Hoogte" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Max.Breedte" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Min.Hoogte" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Min.Breedte" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Meer kleuren" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Meer bibliotheekkleuren" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "Met kleurtokens" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "Geen overeenkomende maatvoorinstelling" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Dekking" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:99, src/app/main/ui/workspace/sidebar/options/page.cljs:106 +msgid "workspace.options.pixel-grid-color" +msgstr "Pixelrasterkleur" + +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototype" @@ -7690,23 +7766,23 @@ msgstr "Randradius van sectie" msgid "workspace.options.radius.show-single-corners" msgstr "Onafhankelijke radius weergeven" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Recent" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Opnieuw proberen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotatie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Lettertype zoeken" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "Groottevoorinstelling zoeken" @@ -7716,11 +7792,11 @@ msgstr "" "Selecteer een vorm, bord of groep om d.m.v. slepen een verbinding met een " "ander bord te maken." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Geselecteerde kleuren" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "Kleurselectie van sectie" @@ -7788,55 +7864,55 @@ msgstr "Schaduw wisselen" msgid "workspace.options.show-fill-on-export" msgstr "In exports tonen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "In weergavemodus tonen" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Grootte" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Groottevoorinstellingen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Vaste verhoudingen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Vrije verhoudingen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Cirkel" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Ruit" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Pijl" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Geen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Rond" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Vierkant" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rechthoek" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Driehoek" @@ -7844,31 +7920,31 @@ msgstr "Driehoek" msgid "workspace.options.stroke-color" msgstr "Streekkleur" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "Streepjes" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "Ruimte" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Streekdikte" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "Onder (blok-einde)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "Links (inline-start)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "Links (inline-einde)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "Boven (blok-start)" @@ -7876,49 +7952,49 @@ msgstr "Boven (blok-start)" msgid "workspace.options.stroke.add-stroke" msgstr "Streekkleur toevoegen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Gecentreerd" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Gestreept" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Gestippeld" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Binnen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Gemengd" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Buiten" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "Streken per zijde" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "" "De functie Streken per zijde is alleen beschikbaar in de nieuwe render. " "Wissel de Render om het te gebruiken." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Streek verwijderen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Solide" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "Streek wisselen" @@ -7954,11 +8030,11 @@ msgstr "Automatische breedte" msgid "workspace.options.text-options.grow-fixed" msgstr "Vast" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Letterafstand" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Regelafstand" @@ -8006,150 +8082,154 @@ msgstr "Onderstrepen (%s)" msgid "workspace.options.use-play-button" msgstr "Gebruik de afspeelknop in de koptekst om de prototypeweergave uit te voeren." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Breedte" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X-as" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y-as" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Knooppunten tekenen (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "Uitgelijnd" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "Onafhankelijk" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "Gelijk" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "Handlergedrag" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Knooppunten verbinden (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Naar hoek (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Naar kromme (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Knooppunten samenvoegen (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Knooppunten verplaatsen (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Knooppunten loskoppelen (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Uitlijnen op knooppunten (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Openen" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Ontdekken [meer plug-ins](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Nog geen plug-ins geïnstalleerd" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Het plug-in-manifest is onjuist." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Je moet een redacteur zijn om deze plug-in te gebruiken" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/data/plugins.cljs:160 +msgid "workspace.plugins.error.unreachable" +msgstr "De URL van de plugin kon niet worden bereikt." + +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "De plug-in bestaat niet of de URL is niet correct." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Installeren" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Geïnstalleerde plug-ins" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Plug-in-beheer" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Plug-ins" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "DEZE PLUGIN UPDATEN" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "De plug-in is gewijzigd sinds je hem voor het laatst hebt geopend. Het wil " "nu ook toegang krijgen tot:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Bestandsdownloads starten." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Gegevens opslaan in de browser." -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "Lees de inhoud van je klembord." -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "Lees en schrijf naar je klembord." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Jouw opmerkingen en antwoorden lezen." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Lees en wijzig jouw opmerkingen en antwoord in jouw naam." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Lees de inhoud van bestanden waartoe gebruikers toegang hebben." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Lees en wijzig de inhoud van bestanden waartoe gebruikers toegang hebben." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Houd er rekening mee dat deze plug-in is gemaakt door een externe partij, " @@ -8157,74 +8237,74 @@ msgstr "" "van jouw gegevens en beveiliging zijn belangrijk voor ons. Als je je zorgen " "maakt, neem dan contact op met ondersteuning." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Jouw bibliotheken en activa bekijken." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Jouw bibliotheken en activa lezen en aanpassen." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "PLUG-IN '%s' WIL TOEGANG TOT:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Lees de profielinformatie van de huidige gebruiker." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Plug-in-lijst" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Plug-in verwijderen" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "De URL van een plug-in invoeren" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Plug-ins" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "NIET NU" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Wil je even kijken? Het wordt geopend in een nieuw concept voor je huidige " "team. (Zo niet, dan vindt je het altijd in de geïnstalleerde plug-ins van " "elk bestand.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "PLUG-IN '%s' IS GEÏNSTALLEERD VOOR JE GEBRUIKER!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "PLUG-IN UITPROBEREN" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Flex-indeling toevoegen" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Rasterindeling toevoegen" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Lay-out toevoegen" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Variant aanmaken" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Nieuwe eigenschap toevoegen" @@ -8236,11 +8316,11 @@ msgstr "Naar achtergrond verplaatsen" msgid "workspace.shape.menu.backward" msgstr "Naar achteren verplaatsen" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "Bordhulplijnen wissen" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Combineren als varianten" @@ -8248,7 +8328,7 @@ msgstr "Combineren als varianten" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Componenten moeten op dezelfde pagina staan" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Kopiëren" @@ -8292,27 +8372,27 @@ msgstr "Kopiëren als tekst" msgid "workspace.shape.menu.create-annotation" msgstr "Aantekening maken" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Selectie naar bord" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Component aanmaken" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Meerdere componenten aanmaken" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Knippen" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Verwijderen" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Stroomdiagram-start verwijderen" @@ -8324,7 +8404,7 @@ msgstr "Instantie losmaken" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Instanties losmaken" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Verschil" @@ -8332,15 +8412,15 @@ msgstr "Verschil" msgid "workspace.shape.menu.duplicate" msgstr "Dupliceren" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Bewerken" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Uitsluiten" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Afvlakken" @@ -8352,7 +8432,7 @@ msgstr "Horizontaal spiegelen" msgid "workspace.shape.menu.flip-vertical" msgstr "Verticaal spiegelen" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Stroomdiagram-start" @@ -8364,31 +8444,31 @@ msgstr "Naar voren brengen" msgid "workspace.shape.menu.front" msgstr "Naar voorgrond brengen" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Groep" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Verbergen" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Gebruikersinterface tonen/verbergen" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Doorsnede" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Vergrendelen" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Masker" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Plakken" @@ -8396,27 +8476,27 @@ msgstr "Plakken" msgid "workspace.shape.menu.paste-props" msgstr "Eigenschappen plakken" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Pad" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Flex-indeling verwijderen" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Raster-layout verwijderen" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Lay-out verwijderen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Eigenschap verwijderen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "Variant moet ten minste één eigenschap hebben" @@ -8440,11 +8520,11 @@ msgstr "Variant herstellen" msgid "workspace.shape.menu.select-layer" msgstr "Laag selecteren" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Tonen" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "In paneel Activa tonen" @@ -8452,7 +8532,7 @@ msgstr "In paneel Activa tonen" msgid "workspace.shape.menu.show-main" msgstr "Hoofdcomponent tonen" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "Streek naar pad" @@ -8464,19 +8544,19 @@ msgstr "Miniatuur verwijderen" msgid "workspace.shape.menu.thumbnail-set" msgstr "Instellen als miniatuur" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Groep opheffen" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Vereniging" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Ontgrendelen" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Masker wegnemen" @@ -8492,79 +8572,79 @@ msgstr "Zijbalk inklappen" msgid "workspace.sidebar.expand" msgstr "Zijbalk uitklappen" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Geschiedenis" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Lagen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Componenten" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Filter" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Borden" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Groepen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Afbeeldingen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Maskers" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "Geen overeenkomst" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "Vervangen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "Alles vervangen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "Vervangen door…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Zoeken…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "Zoeken en vervangen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "Tekstinhoud" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "Laagnamen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Vormen" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Teksten" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Geïmporteerde SVG-kenmerken" @@ -8576,7 +8656,7 @@ msgstr "Pagina's" msgid "workspace.sidebar.sitemap.add-page" msgstr "Pagina toevoegen" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Sitemap" @@ -8596,6 +8676,10 @@ msgstr "%s actieve thema's" msgid "workspace.tokens.add-new-theme" msgstr "Nieuw thema toevoegen" +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:106 +msgid "workspace.tokens.add-set" +msgstr "Set toevoegen" + #: src/app/main/ui/workspace/tokens/sets/context_menu.cljs:62 msgid "workspace.tokens.add-set-to-group" msgstr "Verzameling aan deze groep toevoegen" @@ -8604,7 +8688,7 @@ msgstr "Verzameling aan deze groep toevoegen" msgid "workspace.tokens.add-token" msgstr "Token toevoegen: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Toegepast op" @@ -8624,7 +8708,11 @@ msgstr "Basis lettergrootte" msgid "workspace.tokens.base-font-size.error" msgstr "De basislettergrootte moet een waarde zijn in pixels of zonder eenheid." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:98 +msgid "workspace.tokens.change-token-source" +msgstr "Tokenbron wijzigen" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Kleur" @@ -8636,7 +8724,7 @@ msgstr "Tokenpad kopiëren" msgid "workspace.tokens.create-new-theme" msgstr "Maak nu je eerste thema aan." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Maak er een aan." @@ -8644,6 +8732,10 @@ msgstr "Maak er een aan." msgid "workspace.tokens.create-token" msgstr "Nieuw %s token aanmaken" +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:101 +msgid "workspace.tokens.current-tokens-source-tooltip" +msgstr "Dit is de huidige tokensbron" + #: src/app/main/ui/workspace/tokens/management/context_menu.cljs:437 msgid "workspace.tokens.delete" msgstr "Token verwijderen" @@ -8660,7 +8752,7 @@ msgstr "Token dupliceren" msgid "workspace.tokens.duplicate-group" msgstr "Tokensgroep dupliceren" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "kopie" @@ -8684,7 +8776,7 @@ msgstr "%s token bewerken" msgid "workspace.tokens.enter-token-name" msgstr "Voer de tokennaam %s in" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "" "Tokens kunnen niet worden toegepast tijdens het bewerken van tekst. " @@ -8714,7 +8806,7 @@ msgstr "Voorbeeld:" msgid "workspace.tokens.export.single-file" msgstr "Enkel bestand" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Lettergrootte of {alias}" @@ -8724,7 +8816,7 @@ msgstr "" "Fout bij instellen van lettertype gewicht/stijl. Deze lettertypestijl " "bestaat niet in het huidige lettertype" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Voer een waarde in (300, vet, normaal cursief...) of een {alias}" @@ -8762,7 +8854,7 @@ msgstr "" "In één JSON-bestand moeten de sleutels op het eerste niveau de namen van de " "tokenset zijn." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Tokens importeren" @@ -8772,17 +8864,17 @@ msgstr "" "Als je tokens importeert, worden al je huidige tokens, sets en thema's " "overschreven." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Uitgeschakeld" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Deze verzameling is niet ingeschakeld. Verander het thema of activeer deze " "verzameling om wijzigingen in de viewport te zien" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Individuele tokens gebruiken" @@ -8802,11 +8894,11 @@ msgstr "Thema" msgid "workspace.tokens.label.theme-placeholder" msgstr "Een thema toevoegen (bijv. Licht)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Letterafstand of {alias} toevoegen" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Voer de regelafstand in — vermenigvuldigingsfactor, px, % of {alias}" @@ -8822,11 +8914,11 @@ msgstr "Max. grootte" msgid "workspace.tokens.min-size" msgstr "Min. grootte" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Ontbrekende verwijzing" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Klik met de rechtermuisknop om opties te zien" @@ -8842,7 +8934,7 @@ msgstr "Geen thema actief" msgid "workspace.tokens.no-permisions-set" msgstr "Je moet een redacteur zijn om verzamelingen in/uit te schakelen" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Je moet een redacteur zijn om thema's te gebruiken" @@ -8854,7 +8946,7 @@ msgstr "Er zijn nog geen verzamelingen gedefinieerd. Maak er eerst een aan." msgid "workspace.tokens.no-sets-yet" msgstr "Er zijn nog geen verzamelingen." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Er zijn geen thema's." @@ -8866,11 +8958,25 @@ msgstr "Je hebt momenteel geen thema's." msgid "workspace.tokens.not-remap" msgstr "Niet opnieuw toewijzen" +#: src/app/main/data/workspace/libraries.cljs:848 +msgid "workspace.tokens.notifications.source-sets-or-themes-updated" +msgstr "Tokensbronnensets of thema's zijn aangepast" + #: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:132 msgid "workspace.tokens.num-active-sets" msgstr "%s actieve verzamelingen" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/colorpicker.cljs:438, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:112 +msgid "workspace.tokens.only-first-fill-or-stroke" +msgstr "" +"Ontwerptokens kunnen alleen worden toegepast op de eerste vulling of streek " +"in de lijst." + +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:104 +msgid "workspace.tokens.open-source-new-tab" +msgstr "Open de tokensbron in een nieuw tabblad" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Oorspronkelijke waarde: %s" @@ -8882,15 +8988,15 @@ msgstr "Vulling" msgid "workspace.tokens.radius" msgstr "Radius" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "Verwijzing in {%s} is niet geldig of staat niet in een actieve set." -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Voer een alias voor tokentypografie in" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Voer een alias voor een schaduwtoken in" @@ -8932,7 +9038,7 @@ msgstr "Tokensgroep hernoemen" msgid "workspace.tokens.rename-group-name-hint" msgstr "Je tokens worden automatisch hernoemd naar % s.(suffix).(tokenName)" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Opgeloste waarde: %s" @@ -8965,34 +9071,34 @@ msgstr "Thema bewerken en verzamelingen beheren" #: src/app/main/ui/workspace/tokens/settings/menu.cljs:90 msgid "workspace.tokens.setting-description" msgstr "" -"Hier kunt u de basislettergrootte configureren, die de waarde van 1rem " +"Hier kunt je de basislettergrootte configureren, die de waarde van 1rem " "definieert:" #: src/app/main/ui/workspace/tokens/settings/menu.cljs:83 msgid "workspace.tokens.settings" msgstr "Instellingen voor tokens" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Schaduw toevoegen" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Vervanging" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Inzet" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Schaduw verwijderen" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Spreiding" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "Vervaging kan niet negatief zijn" @@ -9000,11 +9106,11 @@ msgstr "Vervaging kan niet negatief zijn" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "Spreiding kan niet negatief zijn" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -9012,11 +9118,19 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "Grootte" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:75 +msgid "workspace.tokens.source" +msgstr "Bron:" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:311, src/app/main/ui/workspace/tokens/management/context_menu.cljs:335 +msgid "workspace.tokens.stroke-width" +msgstr "Streekdikte" + +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize of {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through of {alias}" @@ -9030,35 +9144,39 @@ msgstr "" msgid "workspace.tokens.themes-list" msgstr "Lijst met thema's" +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:80 +msgid "workspace.tokens.this-file" +msgstr "Dit bestand" + #: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:334, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:335 msgid "workspace.tokens.token-description" msgstr "Beschrijving" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Lettertypefamilie selecteren" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Lettertypefamilie" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Lettertypefamilie of lijst met lettertypen gescheiden door komma (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Naam" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "Er bestaat al een token op het pad: %s of op een voorvoegsel daarvan." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "Naam moet minimaal 1 teken zijn" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " is geen geldige tokennaam.\n" @@ -9069,11 +9187,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Waarde" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Voer een waarde of alias in met {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENS - %s" @@ -9092,15 +9210,15 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "Typ '%s' wordt niet ondersteund (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Referentie gebruiken" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "De waarde is niet geldig" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "Pijl (%s)" @@ -9108,75 +9226,75 @@ msgstr "Pijl (%s)" msgid "workspace.toolbar.assets" msgstr "Activa" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Kleurenpalet (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Commentaar (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Kromme (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Ellips (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Bord (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Afbeelding (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:362 +msgid "workspace.toolbar.label" +msgstr "Tekenhulpmiddelen" + +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "Lijn (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "Hier verbinden" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP verbonden" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Verplaatsen (%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "Paletbalk" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Pad (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Plug-ins (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rechthoek (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Tekst (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Typografie (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Werkbalk wisselen" @@ -9197,11 +9315,11 @@ msgstr "Weergave niet beschikbaar. Vernieuwen om de bewerking te herstellen." msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "Vernieuwen" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Er zijn tot nu toe geen wijzigingen in de geschiedenis" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "door %s" @@ -9341,19 +9459,19 @@ msgstr "typografie asset" msgid "workspace.undo.entry.unknown" msgstr "Voortgang van %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Afwijzen" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Meer info" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Er zijn updates in gedeelde bibliotheken" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Bijwerken" @@ -9443,3 +9561,448 @@ msgstr "" #: src/app/main/ui/workspace/sidebar/versions.cljs:462 msgid "workspace.versions.warning.text" msgstr "Automatisch opgeslagen versies worden %s dagen bewaard." + +#: src/app/main/ui/dashboard/sidebar.cljs:228 +msgid "dashboard.clear-search" +msgstr "Zoekopdracht wissen" + +#: src/app/main/ui/dashboard/fonts.cljs:442, src/app/main/ui/dashboard/fonts.cljs:443 +msgid "dashboard.fonts.delete-variant" +msgstr "Lettertypestijl verwijderen" + +#: src/app/main/ui/dashboard/import.cljs:625 +msgid "dashboard.import.check.message" +msgstr "Zorgen dat elk bestand geïmporteerd kan worden. Dit kan even duren." + +#: src/app/main/ui/dashboard/import.cljs:624 +msgid "dashboard.import.check.title" +msgstr "Bestanden controleren…" + +#: src/app/main/ui/dashboard/import.cljs:600 +msgid "dashboard.import.importing.done" +msgid_plural "dashboard.import.importing.done" +msgstr[0] "1 klaar" +msgstr[1] "%s klaar" + +#: src/app/main/ui/dashboard/import.cljs:637 +msgid "dashboard.import.importing.message" +msgstr "Opnieuw bouwen van pagina's, componenten en activa in jouw project." + +#: src/app/main/ui/dashboard/import.cljs:598 +msgid "dashboard.import.importing.title" +msgid_plural "dashboard.import.importing.title" +msgstr[0] "1 bestand importeren" +msgstr[1] "%s bestanden importeren" + +#: src/app/main/ui/dashboard/import.cljs:634 +msgid "dashboard.import.queued.message" +msgstr "Bestanden worden geüpload. De import start zodra een positie vrij is." + +#: src/app/main/ui/dashboard/import.cljs:328, src/app/main/ui/exports/files.cljs:103 +#, unused +msgid "jobs.queued" +msgstr "Wachtrijpositie" + +#: src/app/main/ui/dashboard/import.cljs:633 +msgid "dashboard.import.queued.title" +msgstr "Wachten om te beginnen…" + +#: src/app/main/ui/dashboard/import.cljs:628 +msgid "dashboard.import.ready.message" +msgstr "Bestanden kunnen worden geïmporteerd. Ga door met het uploaden ervan." + +#: src/app/main/ui/dashboard/import.cljs:627 +msgid "dashboard.import.ready.title" +msgstr "Bestanden gereed" + +#: src/app/main/ui/dashboard/import.cljs:331 +msgid "dashboard.import.status.ready" +msgstr "Gereed" + +#: src/app/main/ui/dashboard/import.cljs:568 +msgid "dashboard.import.steps.check" +msgstr "Controleren" + +#: src/app/main/ui/dashboard/import.cljs:570 +msgid "dashboard.import.steps.import" +msgstr "Importeren" + +#: src/app/main/ui/dashboard/import.cljs:573 +msgid "dashboard.import.steps.label" +msgstr "Stappen importeren" + +#: src/app/main/ui/dashboard/import.cljs:569 +msgid "dashboard.import.steps.upload" +msgstr "Uploaden" + +#: src/app/main/ui/dashboard/import.cljs:1130 +msgid "dashboard.import.title" +msgstr "Bestand importeren" + +#: src/app/main/ui/dashboard/import.cljs:631 +msgid "dashboard.import.upload.message" +msgstr "" +"Bestanden verzenden. Houd dit dialoogvenster open totdat de uploaden " +"voltooid is." + +#: src/app/main/ui/dashboard/import.cljs:630 +msgid "dashboard.import.upload.title" +msgstr "Bestanden uploaden…" + +#: src/app/main/data/exports/assets.cljs:64, src/app/main/data/exports/assets.cljs:93 +msgid "errors.export-failed" +msgstr "De export is mislukt. Probeer het zo nog eens." + +#: src/app/main/data/exports/assets.cljs:372 +msgid "errors.export-too-large" +msgstr "Te groot om te exporteren, probeer een kleinere schaal" + +#: src/app/main/errors.cljs:886 +msgid "errors.profile-props-too-large" +msgstr "" +"Je profielinstellingen zijn te groot om op te slaan. Verwijder enkele " +"plugins en probeer het opnieuw." + +#: src/app/main/errors.cljs:691 +msgid "errors.save-still-retrying" +msgstr "" +"Jouw wijzigingen worden nog niet opgeslagen omdat Penpot de server niet kan " +"bereiken. Ze blijven behouden en Penpot blijft proberen ze op te slaan. Houd " +"dit tabblad open totdat ze zijn opgeslagen." + +#: src/app/main/data/workspace/tokens/errors.cljs:62 +msgid "errors.tokens.division-by-zero" +msgstr "Ongeldige tokenwaarde: deling door nul" + +#: src/app/main/data/workspace/tokens/errors.cljs:74 +msgid "errors.tokens.function-argument-out-of-range" +msgstr "Ongeldige tokenwaarde: het argument van %s() ligt buiten bereik" + +#: src/app/main/data/workspace/tokens/errors.cljs:66 +msgid "errors.tokens.incompatible-units" +msgstr "Ongeldige tokenwaarde: de eenheden %s kunnen niet worden gecombineerd" + +#: src/app/main/data/workspace/tokens/errors.cljs:70 +msgid "errors.tokens.unknown-function" +msgstr "Ongeldige tokenwaarde: onbekende functie %s" + +#: src/app/main/ui/exports/files.cljs:392 +msgid "files-download-modal.unsaved-changes" +msgstr "" +"Sommige van uw laatste wijzigingen zijn nog niet opgeslagen, dus deze " +"download bevat deze niet." + +#: src/app/main/ui/exports/files.cljs:467 +msgid "files-export-modal.cancel-export" +msgstr "Export annuleren" + +#: src/app/main/ui/exports/files.cljs:471 +msgid "files-export-modal.done" +msgstr "Klaar" + +#: src/app/main/ui/exports/files.cljs:210 +msgid "files-export-modal.export-error" +msgstr "Kon niet exporteren: %s" + +#: src/app/main/ui/exports/files.cljs:246 +msgid "files-export-modal.exported.message" +msgstr "Controleer je downloadmap." + +#: src/app/main/ui/exports/files.cljs:239 +msgid "files-export-modal.exported.title" +msgid_plural "files-export-modal.exported.title" +msgstr[0] "1 bestand geëxporteerd" +msgstr[1] "%s bestanden geëxporteerd" + +#: src/app/main/ui/exports/files.cljs:245 +msgid "files-export-modal.exporting.message" +msgstr "Elk bestand wordt gedownload zodra het klaar is." + +#: src/app/main/ui/exports/files.cljs:238 +msgid "files-export-modal.exporting.title" +msgid_plural "files-export-modal.exporting.title" +msgstr[0] "1 bestand exporteren" +msgstr[1] "%s bestanden exporteren" + +#: src/app/main/ui/exports/files.cljs:179 +msgid "files-export-modal.status.exported" +msgstr "Geëxporteerd" + +#: src/app/main/ui/exports/files.cljs:186 +msgid "files-export-modal.status.waiting" +msgstr "Wachten…" + +#: src/app/main/ui/exports/files.cljs:381 +msgid "files-export-modal.title" +msgstr "Bestand exporteren" + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:113 +msgid "import-shortcuts.imported" +msgstr "Geïmporteerd" + +#: src/app/main/ui/settings/shortcuts.cljs:511, src/app/main/ui/settings/shortcuts.cljs:513 +msgid "import-shortcuts.invalid-file" +msgstr "Dit bestand is geen geldige export van snelkoppelingen." + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:153 +msgid "import-shortcuts.skipped-entries" +msgstr "" +"Deze vermeldingen kunnen niet worden geïmporteerd: ze komen niet overeen met " +"een Penpot-snelkoppeling en gebruiken geen gereserveerde sleutelcombinatie." + +#: src/app/main/ui/inspect/code.cljs:322, src/app/main/ui/inspect/code.cljs:365 +msgid "inspect.code.expand-panel" +msgstr "Paneel uitbreiden" + +#: src/app/main/ui/exports/files.cljs:223 +msgid "jobs.export-cancelled" +msgstr "Export geannuleerd" + +#: src/app/main/data/imports.cljs:376 +msgid "jobs.import-cancelled" +msgstr "Import geannuleerd" + +#: src/app/main/ui/jobs/progress.cljs:26 +msgid "jobs.progress.stage.colors" +msgstr "Kleuren verwerken" + +#: src/app/main/ui/jobs/progress.cljs:25 +msgid "jobs.progress.stage.components" +msgstr "Componenten verwerken" + +#: src/app/main/ui/jobs/progress.cljs:21 +msgid "jobs.progress.stage.files" +msgstr "Bestanden verwerken" + +#: src/app/main/ui/jobs/progress.cljs:32 +msgid "jobs.progress.stage.manifest" +msgstr "Pakket lezen" + +#: src/app/main/ui/jobs/progress.cljs:23 +msgid "jobs.progress.stage.media" +msgstr "Media verwerken" + +#: src/app/main/ui/jobs/progress.cljs:31 +msgid "jobs.progress.stage.metadata" +msgstr "Metagegevens verwerken" + +#: src/app/main/ui/jobs/progress.cljs:22 +msgid "jobs.progress.stage.pages" +msgstr "Pagina's verwerken" + +#: src/app/main/ui/jobs/progress.cljs:30 +msgid "jobs.progress.stage.relations" +msgstr "Bibliotheekverwijzingen verwerken" + +#: src/app/main/ui/jobs/progress.cljs:29 +msgid "jobs.progress.stage.storage-objects" +msgstr "Opslagobjecten verwerken" + +#: src/app/main/ui/jobs/progress.cljs:24 +msgid "jobs.progress.stage.thumbnails" +msgstr "Miniaturen verwerken" + +#: src/app/main/ui/jobs/progress.cljs:28 +msgid "jobs.progress.stage.tokens" +msgstr "Tokens verwerken" + +#: src/app/main/ui/jobs/progress.cljs:27 +msgid "jobs.progress.stage.typographies" +msgstr "Typografieën verwerken" + +#: src/app/main/ui/jobs/progress.cljs:34 +msgid "jobs.progress.stage.unknown" +msgstr "Aan het werk" + +#: src/app/main/ui/jobs/progress.cljs:33 +msgid "jobs.progress.stage.upload" +msgstr "Bestand uploaden…" + +#: src/app/main/ui/viewer/thumbnails.cljs:96 +msgid "labels.expand" +msgstr "Uitbreiden" + +#: src/app/main/ui/mcp_authorize.cljs:155 +msgid "mcp.oauth.access" +msgstr "" +"Deze client kan ontwerpen lezen en wijzigen via MCP-verbindingen die vanuit " +"Penpot zijn opgezet." + +#: src/app/main/ui/mcp_authorize.cljs:153 +msgid "mcp.oauth.account" +msgstr "Je bent aangemeld als %s." + +#: src/app/main/ui/mcp_authorize.cljs:162 +msgid "mcp.oauth.approve" +msgstr "Toegang verlenen" + +#: src/app/main/ui/mcp_authorize.cljs:133 +msgid "mcp.oauth.cancelled" +msgstr "Verbinding geannuleerd" + +#: src/app/main/ui/mcp_authorize.cljs:157 +msgid "mcp.oauth.enable" +msgstr "" +"Toegang verlenen maakt de MCP-integratie mogelijk en maakt indien nodig jouw " +"MCP-token aan." + +#: src/app/main/ui/mcp_authorize.cljs:137 +msgid "mcp.oauth.failed" +msgstr "" +"Dit verzoek is verlopen, werd al gebruikt of kon niet worden voltooid. Start " +"de verbinding opnieuw vanaf jouw MCP-client." + +#: src/app/main/ui/mcp_authorize.cljs:149 +msgid "mcp.oauth.request" +msgstr "%s wil verbinding maken met Penpot." + +#: src/app/main/ui/mcp_authorize.cljs:129 +msgid "mcp.oauth.title" +msgstr "Verbinding maken met Penpot MCP" + +#: src/app/main/ui/shortcuts.cljs:714 +msgid "shortcuts.edit" +msgstr "%s bewerken" + +#: src/app/main/ui/shortcuts.cljs:771 +msgid "shortcuts.edit-modal.reserved" +msgstr "" +"Deze combinatie is gereserveerd door Penpot en kan niet worden toegewezen." + +#: src/app/main/ui/settings/shortcuts.cljs:53 +msgid "shortcuts.reset-all" +msgstr "Alles herstellen" + +#: src/app/main/ui/settings/shortcuts.cljs:44, src/app/main/ui/workspace/sidebar/shortcuts.cljs:143 +msgid "shortcuts.search" +msgstr "Sneltoetsen zoeken" + +#: src/app/main/ui/settings/subscription.cljs:724 +msgid "subscription.settings.trial-ends-on" +msgstr "Je gratis proefperiode eindigt op %s." + +#: src/app/main/ui/viewer.cljs:127 +msgid "viewer.go-to-first-board" +msgstr "Ga naar het eerste bord" + +#: src/app/main/ui/viewer/thumbnails.cljs:70 +msgid "viewer.thumbnails.scroll-left" +msgstr "Links bladeren" + +#: src/app/main/ui/viewer/thumbnails.cljs:76 +msgid "viewer.thumbnails.scroll-right" +msgstr "Rechts bladeren" + +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:297 +msgid "workspace.assets.typography.multiple-texts-selected" +msgstr "Selecteer één tekst om een typografiestijl aan te maken" + +#: src/app/main/data/exports/assets.cljs:47 +msgid "workspace.export.unsaved-changes" +msgstr "" +"Sommige laatste wijzigingen zijn nog niet opgeslagen en kunnen ontbreken aan " +"deze export." + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1050 +msgid "workspace.layout-grid.editor.columns" +msgstr "Kolommen" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1051 +msgid "workspace.layout-grid.editor.rows" +msgstr "Rijen" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:994 +msgid "workspace.layout-grid.editor.select-column" +msgstr "Kolom selecteren" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:995 +msgid "workspace.layout-grid.editor.select-row" +msgstr "Rij selecteren" + +#: src/app/main/ui/workspace/viewport/grid_layout_editor.cljs:926 +msgid "workspace.layout-grid.editor.track-options" +msgstr "Opties voor bijhouden" + +#: src/app/main/ui/workspace/libraries.cljs:921 +msgid "workspace.libraries.add-shared-library" +msgstr "Gedeelde bibliotheek toevoegen" + +#: src/app/main/ui/workspace/libraries.cljs:823 +msgid "workspace.libraries.assets-in-this-file" +msgstr "ACTIVA IN DIT BESTAND" + +#: src/app/main/ui/workspace/libraries.cljs:1159 +msgid "workspace.libraries.this-file" +msgstr "Dit bestand" + +#: src/app/main/data/workspace/notifications.cljs:282 +msgid "workspace.notifications.file-repaired" +msgstr "" +"We hebben een aantal problemen in dit bestand opgelost. In de " +"versiegeschiedenis kun je een kopie van de originele versie vinden." + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:44 +msgid "workspace.options.grid.params.guide-defaults" +msgstr "Standaardhulplijnen" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:516 +msgid "workspace.options.no-font-results" +msgstr "Geen overeenkomende lettertypen" + +#: src/app/main/ui/workspace/plugins.cljs:344 +msgid "workspace.plugins.permissions.allow-global" +msgstr "" +"Actief houden terwijl je tussen het dashboard en bestanden beweegt, en jouw " +"projecten en bestanden doorzoeken en openen." + +#: src/app/main/ui/workspace/plugins.cljs:365 +msgid "workspace.plugins.permissions.manage-delete" +msgstr "Jouw bestanden en projecten verwijderen." + +#: src/app/main/ui/workspace/plugins.cljs:351 +msgid "workspace.plugins.permissions.manage-projects" +msgstr "Jouw projecten aanmaken, hernoemen, vastmaken en dupliceren." + +#: src/app/main/ui/workspace/plugins.cljs:358 +msgid "workspace.plugins.permissions.manage-teams" +msgstr "" +"Jouw teams weergeven, aanmaken en hernoemen, en projecten daartussen " +"verplaatsen." + +#: src/app/main/ui/workspace/plugins.cljs:188 +msgid "workspace.plugins.remove-confirmation.message" +msgstr "" +"Weet je zeker dat je de plugin %s wilt verwijderen? Je kunt het op elk " +"gewenst moment opnieuw installeren." + +#: src/app/main/ui/workspace/plugins.cljs:187 +msgid "workspace.plugins.remove-confirmation.title" +msgstr "Plugin verwijderen" + +#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:53 +msgid "workspace.sidebar.options.svg-attrs.remove" +msgstr "Kenmerk verwijderen" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:253, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:226 +msgid "workspace.tokens.detach-reference" +msgstr "Verwijzing losmaken" + +#: src/app/main/ui/components/mcp_menu.cljs:136 +msgid "workspace.toolbar.mcp-copy-session-id" +msgstr "Sessie-ID kopiëren" + +#: src/app/main/ui/components/mcp_menu.cljs:33 +msgid "workspace.toolbar.mcp-session-copied" +msgstr "Sessie-ID gekopieerd" + +#: src/app/main/ui/components/mcp_menu.cljs:132 +msgid "workspace.toolbar.mcp-session-id" +msgstr "Sessie-ID: %s" + +#: src/app/main/ui/workspace/palette.cljs:211 +msgid "workspace.toolbar.palette-options" +msgstr "Opties voor het palet" + +#: src/app/main/ui/workspace/sidebar/history.cljs:371 +msgid "workspace.undo.entry.show-details" +msgstr "Details weergeven" diff --git a/frontend/translations/pl.po b/frontend/translations/pl.po index 352c7e9eec..fc538a9f05 100644 --- a/frontend/translations/pl.po +++ b/frontend/translations/pl.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: Radek Sawicki \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Polish " "\n" "Language: pl\n" @@ -10,9 +10,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && " "(n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Posiadasz już konto?" @@ -30,7 +30,7 @@ msgstr "" "To jest usługa DEMO, NIE UŻYWAJ jej do prawdziwej pracy, projekty będą " "okresowo usuwane." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Zapomniałeś hasła?" @@ -38,27 +38,27 @@ msgstr "Zapomniałeś hasła?" msgid "auth.fullname" msgstr "Imię i nazwisko" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Zaloguj się tutaj" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "Gitlab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -78,7 +78,7 @@ msgstr "Profil nie został zweryfikowany, zweryfikuj profil przed kontynuacją." msgid "auth.notifications.recovery-token-sent" msgstr "Link do odzyskiwania hasła został wysłany na Twój email." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Pomyślnie dołączyłeś do zespołu" @@ -110,11 +110,11 @@ msgstr "Zapomniałeś hasła?" msgid "auth.recovery-submit" msgstr "Zmień hasło" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Nie masz jeszcze konta?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Utwórz konto" @@ -122,114 +122,114 @@ msgstr "Utwórz konto" msgid "auth.register-title" msgstr "Utwórz konto" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Warunki świadczenia usług" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Opublikuj" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Wszyscy użytkownicy Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Czy na pewno chcesz usunąć ten link? Jeśli to zrobisz, nie będzie już " "dostępny dla nikogo" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(aktualne)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Usuń link" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Uzyskaj link" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Link skopiowano pomyślnie" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Zarządzaj uprawnieniami" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 udostępniona strona" msgstr[1] "%s udostępnione strony" msgstr[2] "%s udostępnionych stron" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Może komentować" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Może sprawdzać kod" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Każdy, kto ma link, będzie miał dostęp" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Udostępnione strony" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Tutaj pojawi się link do udostępniania" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Tylko członkowie zespołu" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Udostępnij prototypy" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Wybierz wszystko" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Cofnij publikację" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Zarządzanie zespołem" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot jest przeznaczony dla zespołów. Zaproś członków do wspólnej pracy " "nad projektami i plikami" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Połącz siły!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Dodaj jako Udostępnioną Bibliotekę" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Zmień email" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(skopiuj)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Utwórz nowy zespół" @@ -237,11 +237,11 @@ msgstr "Utwórz nowy zespół" msgid "dashboard.default-team-name" msgstr "Twój Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Usuń zespół" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Pobierz plik Penpot (.penpot)" @@ -257,60 +257,60 @@ msgstr "Zduplikuj %s pliki" msgid "dashboard.export-binary-multi" msgstr "Pobierz %s plików Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Eksportuj obszary kompozycji jako PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Eksportuj jako PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s z %s elementów wybranych" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Eksportuj" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Możesz dodać ustawienia eksportu do elementów z właściwości projektu (na " "dole prawego paska bocznego)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informacje jak ustawić eksport w Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Nie ma elementów z ustawieniami eksportu." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Eksportuj wybrane" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Czcionka usunięta" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Odrzuć wszystko" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Niestandardowe czcionki, które prześlesz, pojawią się tutaj." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 czcionka dodana" msgstr[1] "%s czcionki dodane" msgstr[2] "%s czcionki dodane" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -320,7 +320,7 @@ msgstr "" "czcionek**. Możesz przesyłać czcionki w następujących formatach: **TTF, OTF " "i WOFF** (potrzebny będzie tylko jeden)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -330,75 +330,47 @@ msgstr "" "Możesz też przeczytać o [licencjonowaniu " "czcionek](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Prześlij wszystko" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importuj pliki Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Kurza stopa! Nie udało się zaimportować tego pliku" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgstr "%s plików zostało pomyślnie zaimportowanych." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Niektóre pliki zawierały nieprawidłowe obiekty, które zostały usunięte." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Przetwarzanie kolorów" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Przetwarzanie komponentów" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Przetwarzanie mediów" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Przetwarzanie strony: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Przetwarzanie typografii" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Przesyłanie danych na serwer (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Przesyłanie pliku: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Zaproś ludzi" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Opuść zespół" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Biblioteki i szablony" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Poznaj więcej z nich i dowiedz się, jak pomóc" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Wystąpił problem z importem szablonu. Szablon nie został zaimportowany." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Biblioteki współdzielone" @@ -406,7 +378,7 @@ msgstr "Biblioteki współdzielone" msgid "dashboard.loading-files" msgstr "wczytywanie Twoich plików…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "wczytywanie Twoich czcionek…" @@ -422,35 +394,35 @@ msgstr "Przenieś %s pliki do" msgid "dashboard.move-to-other-team" msgstr "Przenieś do innego zespołu" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Nowy plik" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Nowy plik" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nowy projekt" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nowy projekt" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Nie znaleziono dopasowań dla “%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Tutaj pojawią się przypięte projekty" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Twój adres email został pomyślnie zaktualizowany" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Twój adres email został pomyślnie zweryfikowany" @@ -458,7 +430,7 @@ msgstr "Twój adres email został pomyślnie zweryfikowany" msgid "dashboard.notifications.password-saved" msgstr "Hasło zostało pomyślnie zapisane!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s członków" @@ -466,7 +438,7 @@ msgstr "%s członków" msgid "dashboard.open-in-new-tab" msgstr "Otwórz plik w nowej zakładce" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opcje" @@ -474,39 +446,39 @@ msgstr "Opcje" msgid "dashboard.password-change" msgstr "Zmień hasło" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Przypnij/Odepnij" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projekty" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Czy chesz usunąć swoje konto?" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Zapisz ustawienia" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Szukaj…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Wyszukiwanie “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Wybierz język interfejsu" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Wybierz temat" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Pokaż wszystkie pliki" @@ -518,7 +490,7 @@ msgstr "Twój projekt został pomyślnie usunięty" msgid "dashboard.success-duplicate-project" msgstr "Twój projekt został pomyślnie zduplikowany" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Twój plik został pomyślnie przeniesiony" @@ -530,82 +502,82 @@ msgstr "Twoje pliki został pomyślnie przeniesione" msgid "dashboard.success-move-project" msgstr "Twój projekt został pomyślnie przeniesiony" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Informacje o zespole" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Członkowie zespołu" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Projekty zespołu" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Temat interfejsu" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Wyniki wyszukiwania" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Wpisz, aby wyszukać wyniki" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Cofnij publikację biblioteki" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Aktualizuj ustawienia" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Jest aktywny" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Po wywołaniu tego haka zostaną dostarczone szczegóły zdarzenia" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Typ treści" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Utwórz webhooka" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Pomyślnie utworzono webhook." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhook to prosty sposób na powiadamianie innych stron internetowych i " "aplikacji o określonych zdarzeniach w Penpot. Wyślemy żądanie POST do " "każdego podanego przez Ciebie adresu URL." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Naciśnij przycisk „Dodaj webhook”, aby go dodać." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Do tej pory nie utworzono żadnych webhooków." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Twoje konto" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Email" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Twoje imię" @@ -621,7 +593,7 @@ msgstr "Uwaga" msgid "ds.component-subtitle" msgstr "Komponenty do aktualizacji:" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Anuluj" @@ -637,27 +609,27 @@ msgstr "Jesteś pewien?" msgid "errors.auth-provider-not-configured" msgstr "Dostawca uwierzytelniania nie został skonfigurowany." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Wygląda na to, że nie jesteś uwierzytelniony lub sesja wygasła." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Nie można załadować czcionki %s" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Nie można załadować czcionek %s" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Twoja przeglądarka nie może wykonać tej operacji" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Email jest już używany" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-mail został już zweryfikowany." @@ -665,25 +637,25 @@ msgstr "E-mail został już zweryfikowany." msgid "errors.email-as-password" msgstr "Nie możesz użyć swojego adresu email jako hasła" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Email «%s» zawiera wiele stałych raportów o odrzuceniu." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "Email «%s» został zgłoszony jako spam lub trwale odrzucony." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Wygląda na to, że otwierasz plik, który ma włączoną funkcję '%s'. Twój " "interfejs użytkownika penpota jej nie obsługuje lub ma ją wyłączoną." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Funkcja '%s' nie jest obsługiwana." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Coś poszło nie tak." @@ -695,7 +667,7 @@ msgstr "Nieprawidłowy kolor" msgid "errors.invalid-email" msgstr "Podaj prawidłowy adres e-mail" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "E-mail potwierdzający musi być zgodny" @@ -715,7 +687,7 @@ msgstr "To zaproszenie może być anulowane lub wygasło." msgid "errors.ldap-disabled" msgstr "Uwierzytelnianie LDAP jest wyłączone." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Osiągnąłeś limit '%s'. Skontaktuj się z pomocą techniczną." @@ -731,7 +703,7 @@ msgstr "Wygląda na to, że zawartość obrazu nie pasuje do rozszerzenia pliku. msgid "errors.media-type-not-allowed" msgstr "Wygląda, że to nie jest prawidłowy obraz." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "W zapraszanym profilu powiadomienia email są wyciszone (zgłoszenia spamu " @@ -749,7 +721,7 @@ msgstr "Hasło powinno zawierać co najmniej 8 znaków" msgid "errors.profile-blocked" msgstr "Profil jest zablokowany" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Twój profil ma wyciszone powiadomienia email (zgłoszenia spamu lub wysokie " @@ -759,17 +731,17 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Rejestracja jest obecnie wyłączona." -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Niewystarczająca liczba członków, aby opuścić zespół, prawdopodobnie chcesz " "go usunąć." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Członek, którego próbujesz przypisać, nie istnieje." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "Właściciel nie może opuścić zespołu, musisz ponownie przypisać rolę " @@ -779,35 +751,35 @@ msgstr "" msgid "errors.unexpected-error" msgstr "Wystąpił nieoczekiwany błąd." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Nieznany token" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Błąd połączenia, adres URL nieosiągalny" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "Adres URL nie przechodzi weryfikacji." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Ostatnia wysyłka nie powiodła się." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Błąd podczas sprawdzania poprawności SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Koniec czasu" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Nieoczekiwany błąd podczas sprawdzania poprawności" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Nieoczekiwany status %s" @@ -823,13 +795,13 @@ msgstr "Stare hasło jest nieprawidłowe" msgid "feedback.description" msgstr "Opis" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Cieszymy się, że tu jesteś. Jeśli potrzebujesz pomocy, poszukaj jej zanim " "napiszesz." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Społeczność Penpot" @@ -843,11 +815,11 @@ msgstr "" "Opisz powód swojego e-maila, określając, czy jest to problem, pomysł czy " "wątpliwość. Członek naszego zespołu odpowie tak szybko, jak to możliwe." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Służymy pomocą w kwestiach technicznych." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Konto wsparcia na Xze" @@ -879,7 +851,7 @@ msgstr "Pobierz obraz źródłowy" msgid "inspect.attributes.shadow" msgstr "Cień" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Rozmiar i położenie" @@ -887,39 +859,27 @@ msgstr "Rozmiar i położenie" msgid "inspect.attributes.stroke" msgstr "Obrys" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Środek" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Wewnątrz" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Zewnątrz" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Typografia" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Rodzina czcionek" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Rozmiar czcionki" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Styl czcionki" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Rozstaw liter" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Wysokość linii" @@ -931,11 +891,11 @@ msgstr "Brak" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Podkreślenie" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Kapitaliki" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "małe litery" @@ -943,7 +903,7 @@ msgstr "małe litery" msgid "inspect.attributes.typography.text-transform.none" msgstr "Brak" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "DUŻE LITERY" @@ -1005,27 +965,27 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Tekst" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Skróty" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Akceptuj" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Aktywny" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Dodaj niestandardową czcionkę" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Admin" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Wszystko" @@ -1033,25 +993,25 @@ msgstr "Wszystko" msgid "labels.and" msgstr "i" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Cofnij" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Wygląda na to, że musisz trochę poczekać i spróbować ponownie; wykonujemy " "drobne prace konserwacyjne naszych serwerów." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Bad Gateway" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Anuluj" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Zamknij" @@ -1059,11 +1019,11 @@ msgstr "Zamknij" msgid "labels.code" msgstr "Kod" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Komentarze" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Społeczność" @@ -1071,35 +1031,35 @@ msgstr "Społeczność" msgid "labels.confirm-password" msgstr "Potwierdź hasło" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Kontynuuj" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Możesz kontynuować z kontem Penpot" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Kopiuj link" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Stwórz" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Stwórz nowy zespół" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Wprowadź nazwę nowego zespołu" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Kokpit" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Usuń" @@ -1111,7 +1071,7 @@ msgstr "Usuń komentarz" msgid "labels.delete-comment-thread" msgstr "Usuń wątek" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Usuń zaproszenie" @@ -1119,23 +1079,23 @@ msgstr "Usuń zaproszenie" msgid "labels.delete-multi-files" msgstr "Usuń %s plików" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Szkice" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Edytuj" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Edytor" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Wygasły" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Eksportuj" @@ -1147,39 +1107,39 @@ msgstr "Opinie wyłączone" msgid "labels.feedback-sent" msgstr "Opinia wysłana" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Rodzina czcionek" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Style" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Czcionki" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Repozytorium Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Dodaj opinię" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Wróć" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Centrum Pomocy" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Ukryj rozwiązane komentarze" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Nieaktywny" @@ -1187,39 +1147,39 @@ msgstr "Nieaktywny" msgid "labels.info" msgstr "Informacje" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Zainstalowane czcionki" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Błąd wewnętrzny" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Zaproszenia" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Język" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Biblioteki i szablony" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Zaloguj się lub zarejestruj" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Wyloguj" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Członek" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Członkowie" @@ -1227,35 +1187,35 @@ msgstr "Członkowie" msgid "labels.new-password" msgstr "Nowe hasło" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "" "Wszyscy jesteście na bieżąco! Tutaj będą pojawiać się powiadomienia o " "nowych komentarzach." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Brak oczekujących zaproszeń." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Kurza stopa!" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 plik" msgstr[1] "%s pliki" msgstr[2] "%s plików" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 artboard" msgstr[1] "%s artboardy" msgstr[2] "%s artboardów" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 projekt" @@ -1266,107 +1226,107 @@ msgstr[2] "%s projektów" msgid "labels.old-password" msgstr "Stare hasło" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Właściciel" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Hasło" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Oczekiwanie" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projekty" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Uwagi do wydania" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Usuń" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Usuń członka zespołu" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Zmień nazwę" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Zmień nazwę zespołu" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Wyślij ponownie zaproszenie" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Ponów" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rola" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Zapisz" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Szukaj czcionki" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Wyślij" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Wysyłanie…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Zajmujemy się planowaną konserwacją naszych systemów." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Serwis niedostępny" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Ustawienia" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Biblioteki Współdzielone" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Pokaż wszystkie komentarze" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Pokaż listę komentarzy" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Pokaż tylko swoje komentarze" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Samouczki" @@ -1374,23 +1334,23 @@ msgstr "Samouczki" msgid "labels.unpublish-multi-files" msgstr "Cofnij publikację plików %s" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Aktualizuj" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Zaktualizuj zespół" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Wgraj" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Wgraj niestandardowe czcionki" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Wgrywanie…" @@ -1398,11 +1358,11 @@ msgstr "Wgrywanie…" msgid "labels.view-only" msgstr "TYLKO PODGLĄD" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Widz" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooki" @@ -1410,11 +1370,11 @@ msgstr "Webhooki" msgid "labels.write-new-comment" msgstr "Napisz nowy komentarz" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(Ty)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Twoje konto" @@ -1436,61 +1396,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Dodaj “%s” jako Bibliotekę Współdzieloną" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Duże przesunięcie" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Zweryfikuj nowy email" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Wyślemy Ci wiadomość na Twój aktualny adres email “%s”, żeby zweryfikować " "Twoją tożsamość." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Nowy email" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Zmień email" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Zmień swój email" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Utwórz webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Utwórz webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Ładowność URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Anuluj i zachowaj moje konto" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Tak, usuń moje konto" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Usuwając konto, stracisz wszystkie swoje bieżące i archiwalne projekty." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Czy na pewno chcesz usunąć swoje konto?" @@ -1532,31 +1492,31 @@ msgstr "Czy na pewno chcesz usunąć %s pliki?" msgid "modals.delete-file-multi-confirm.title" msgstr "Usuwanie %s plików" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Czy na pewno chcesz usunąć ten styl czcionki? Nie zostanie załadowany, " "jeśli jest używany w pliku." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Usuwanie stylu czcionki" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Czy na pewno chcesz usunąć tę czcionkę? Nie zostanie załadowana, jeśli jest " "używana w pliku." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Usuwanie czcionki" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Czy na pewno chcesz usunąć tę stronę?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Usuń stronę" @@ -1572,21 +1532,21 @@ msgstr "Czy na pewno chcesz usunąć ten projekt?" msgid "modals.delete-project-confirm.title" msgstr "Usuń projekt" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Usuń plik" msgstr[1] "Usuń pliki" msgstr[2] "Usuń pliki" -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Czy na pewno chcesz usunąć ten plik?" msgstr[1] "Czy na pewno chcesz usunąć te pliki?" msgstr[2] "Czy na pewno chcesz usunąć te pliki?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Usuwanie pliku" @@ -1607,139 +1567,139 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Usuwanie zespołu" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Usuń członka" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Czy na pewno chcesz usunąć tego członka z zespołu?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Usuń członka zespołu" -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Usuń webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Czy na pewno chcesz usunąć ten webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Usuwanie webhooka" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Edytuj webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Edytuj webhook" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Wyślij zaproszenie" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Emaile, oddzielone przecinkami" -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Zaproś członków do zespołu" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Ponieważ jesteś jedynym członkiem tego zespołu, zespół zostanie usunięty " "wraz z projektami i plikami." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Czy na pewno chcesz opuścić zespół %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Nie możesz opuścić zespołu, jeśli nie ma innego członka, którego można by " "awansować na właściciela. Możesz chcieć usunąć zespół." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Jesteś właścicielem tego zespołu. Wybierz innego członka, którego chcesz " "awansować na właściciela, zanim odejdziesz." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Awansuj i odejdź" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Wybierz członka do awansu" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Zanim opuścisz" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Opuść zespół" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Czy na pewno chcesz opuścić ten zespół?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Opusczanie zespołu" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Ilość przesunięcia" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Przeniesienie własności" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Jeśli przeniesiesz własność, zmienisz swoją rolę na administratora, tracąc " "niektóre uprawnienia w tym zespole. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Jesteś obecnym właścicielem tego zespołu. Czy na pewno chcesz, aby %s " "został nowym właścicielem zespołu?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Nowy właściciel zespołu" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Małe przesunięcie" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Cofnij publikację" msgstr[1] "Cofnij publikacje" msgstr[2] "Cofnij publikacje" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Czy na pewno chcesz cofnąć publikację tej biblioteki?" msgstr[1] "Czy na pewno chcesz cofnąć publikację tych bibliotek?" msgstr[2] "Czy na pewno chcesz cofnąć publikację tych bibliotek?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Cofnij publikację biblioteki" @@ -1764,25 +1724,25 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Zaktualizuj komponent w bibliotece współdzielonej" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Zaproszenie wysłane pomyślnie" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Link do zaproszenia został skopiowany" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Nie możesz usunąć swojego profilu. Zmień przypisanie swoich zespołów, zanim " "przejdziesz dalej." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil został pomyślnie zapisany!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Email weryfikacyjny został wysłany do %s. Sprawdź swój email!" @@ -1798,11 +1758,11 @@ msgstr "Witamy w Penpot!" msgid "onboarding.choice.team-up.create-team-desc" msgstr "Po nazwaniu swojego zespołu będziesz mógł zapraszać osoby do dołączenia." -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Zaproś członków" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Zaproś z rolą:" @@ -1848,79 +1808,79 @@ msgstr "Zacznij projektować" msgid "profile.recovery.go-to-login" msgstr "Przejdź do logowania" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Mieszane" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Komentarze" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Wyrównaj do dołu" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Wyrównaj do środka w poziomie" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Wyrównaj do lewej" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Wyrównaj do prawej" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Wyrównaj do góry" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Wyrównaj do środka w pionie" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Utwórz obszar kompozycji z wybranych" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Różnica logiczna" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Wykluczenie logiczne" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Logiczny punkt przecięcia" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Związek logiczny" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Przesuń na spód" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Przesuń niżej" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Przesuń wyżej" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Przesuń na wierzch" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Wyczyść cofnięcia" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Kopiuj" @@ -1928,35 +1888,35 @@ msgstr "Kopiuj" msgid "shortcuts.create-new-project" msgstr "Stwórz nowy" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Wytnij" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Oddal" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Usuń" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Usuń węzeł" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Odłącz komponent" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Krzywa" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipsa" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Obszar kompozycji" @@ -1964,39 +1924,39 @@ msgstr "Obszar kompozycji" msgid "shortcuts.draw-nodes" msgstr "Rysuj ścieżkę" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Ścieżka" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Prostokąt" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Tekst" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplikuj" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Anuluj" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Eksportuj kształty" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Powiększ, aby dopasować wszystko" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Obróć poziomo" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Obróć pionowo" @@ -2012,63 +1972,63 @@ msgstr "Idź do bibliotek współdzielonych" msgid "shortcuts.go-to-search" msgstr "Szukaj" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Grupa" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Rozłóż w poziomie" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Pokaż/ukryj UI" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Przybliż" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Wstaw obraz" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Połącz węzły" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Zrób narożnik" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Zrób krzywą" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Maska" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Scal węzły" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Przesuń" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Szybko w dół" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Szybko w lewo" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Szybko w prawo" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Szybko w górę" @@ -2076,329 +2036,329 @@ msgstr "Szybko w górę" msgid "shortcuts.move-nodes" msgstr "Przesuń węzeł" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "W dół" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "W lewo" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "W prawo" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "W górę" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Następny obszar kompozycji" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Nie znaleziono skrótów" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Ustaw krycie na 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Ustaw krycie na 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Ustaw krycie na 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Ustaw krycie na 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Ustaw krycie na 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Ustaw krycie na 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Ustaw krycie na 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Ustaw krycie na 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Ustaw krycie na 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Ustaw krycie na 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Próbnik kolorów" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Przejdź do sekcji komentarzy widzów" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Idź do kokpitu" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Przejdź do sekcji kontroli widzów" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Idź do sekcji interakcji widza" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Idż do sekcji interakcji widza" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Idż do obszaru roboczego" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " lub " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Wklej" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Poprzedni artboard" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Ponów" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Resetuj powiększenie" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Podstawy" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Kokpit" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Widz" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Obszar roboczy" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Wybierz wszystko" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Oddziel węzły" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Pokaż/ukryj siatkę pikseli" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Pokaż/ukryj skróty" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Przyciągaj do węzłów" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Przyciągaj do siatki pikseli" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Rozpocznij edycję" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Wyrównanie" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Edytuj" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Ogólny" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu główne" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modyfikuj warstwy" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Nawigacja" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Nawigacja" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Nawigacja" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panele" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Ścieżki" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Kształty" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Narzędzia" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Przybliżenie" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Przybliżenie" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Ustaw miniaturki" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Skróty klawiaturowe" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Przełącz dynamiczne wyrównanie" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Przełącz zasoby" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Przełącz paletę kolorów" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Przełącz tryb skupienia" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Przełącz tryb pełnoekranowy" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Przełącz historię" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Przełącz warstwy" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Dodaj/usuń flex układu" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Zablokuj wybrane" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Zablokuj proporcje" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Przełącz paletę tekstu" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Przełącz sposób powiększania" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Cofnij" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Rozgrupuj" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Usuń maskę" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Rozłóż w pionie" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Przybliż wybrane" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Dostawcy Czcionek - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Czcionki - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projekty - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Szukaj - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Biblioteki Współdzielone - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot — swoboda projektowania dla zespołów" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Przekaż opinię - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Ustawienia - Penpot" @@ -2406,27 +2366,27 @@ msgstr "Ustawienia - Penpot" msgid "title.settings.password" msgstr "Hasło - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Zaproszenia - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Członkowie - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Ustawienia - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooki - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Tryb wyświetlania - Penpot" @@ -2434,99 +2394,99 @@ msgstr "%s - Tryb wyświetlania - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Na stronie nie znaleziono obszarów kompozycji." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Obszar kompozycji nie znaleziony." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Komentarze (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Nie pokazuj interakcji" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Pełny ekran" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Sprawdź (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interakcje" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interakcje (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Kopiuj link" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Pokaż interakcje" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Pokaż interakcje po kliknięciu" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Mapa strony" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Ostatnia wysyłka przebiegła pomyślnie." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Wyrównaj do środka w poziomie (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Rozłóż odstępy w poziomie (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Wyrównaj do lewej (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Wyrównaj do prawej (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Wyrównaj do dołu (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Wyrównaj do środka w pionie (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Rozłóż odstępy w pionie (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Wyrównaj do góry (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Wszystkie zasoby" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Kolory" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Komponenty" @@ -2534,41 +2494,41 @@ msgstr "Komponenty" msgid "workspace.assets.create-group" msgstr "Utwórz grupę" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Twoje elementy zostaną automatycznie nazwane jako „nazwa grupy / nazwa " "elementu”" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Usuń" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplikuj" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Edytuj" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Grupa" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nazwa grupy" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "lokalna biblioteka" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Nie znaleziono zasobów" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Zmień nazwę" @@ -2576,43 +2536,43 @@ msgstr "Zmień nazwę" msgid "workspace.assets.rename-group" msgstr "Zmień nazwę grupy" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Szukaj zasobów" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Typografia" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Rozmiar" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Styl czcionki" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Przejdź do pliku biblioteki stylów, żeby edytować" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Odstępy między literami" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Wysokość Linii" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformacja tekstu" @@ -2620,15 +2580,15 @@ msgstr "Transformacja tekstu" msgid "workspace.assets.ungroup" msgstr "Rozgrupuj" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Tryb skupienia" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Tryb skupienia wyłączony" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Tryb skupienia włączony" @@ -2640,139 +2600,139 @@ msgstr "Gradient liniowy" msgid "workspace.gradients.radial" msgstr "Gradient radialny" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Wyłącz wyrównanie dynamiczne" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Wyłącz przyciąganie do prowadnic" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Wyłącz przyciąganie do piksela" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Włącz dynamiczne wyrównanie" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Przyciągaj do prowadnic" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Włącz przyciąganie do piksela" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Ukryj nazwy obszarów kompozycji" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Ukryj paletę kolorów" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Ukryj siatkę pikseli" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Ukryj linijki" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Ukryj paletę czcionek" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Edytuj" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Plik" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Pomoc i info" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Ustawienia" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Widok" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Zaznacz wszystko" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Pokaz nazwy obszarów kompozycji" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Pokaż paletę kolorów" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Pokaż siatkę pikseli" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Pokaż linijki" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Pokaż paletę czcionek" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Resetuj" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Błąd przy zapisywaniu" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Zapisano" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Zapisywanie" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Tryb widoku (%s)" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Wypełnij — skaluj, aby wypełnić" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Dopasuj — zmniejsz, aby dopasować" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Powiększ, aby dopasować wszystko" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Pełny ekran" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Powiększ wybrane" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Biblioteka plików" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Bieżące kolory" @@ -2784,39 +2744,39 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Zapisz styl koloru" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Biblioteka plików" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTEKI W TYM PLIKU" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTEKI" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Nie ma Bibliotek Współdzielonych, które wymagają aktualizacji" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Nie znaleziono dopasowań dla “%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Przeszukaj Biblioteki Współdzielone" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "BIBLIOTEKI WSPÓŁDZIELONE" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Aktualizuj" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "AKTUALIZACJE" @@ -2836,11 +2796,11 @@ msgstr "Rozmycie zaznaczenia" msgid "workspace.options.canvas-background" msgstr "Tło płótna" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Przytnij treść" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Komponent" @@ -2884,11 +2844,11 @@ msgstr "Góra" msgid "workspace.options.constraints.topbottom" msgstr "Góra i dół" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Projektowanie" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Eksport" @@ -2896,30 +2856,30 @@ msgstr "Eksport" msgid "workspace.options.export-multiple" msgstr "Eksportuj zaznaczone" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Eksportuj 1 element" msgstr[1] "Eksportuj %s elementy" msgstr[2] "Eksportuj %s elementów" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Przyrostek" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Eksportowanie zakończone" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Eksportowanie…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Eksport nie powiódł się" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Eksportowanie niespodziewanie powolne" @@ -2943,67 +2903,67 @@ msgstr "Początek flow" msgid "workspace.options.grid.auto" msgstr "Auto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Kolumny" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Kolor" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Rowek" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Wysokość" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margines" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Ustaw jako domyślne" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Dół" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Środek" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Lewo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Prawo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Rozciągnij" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Góra" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Użyj domyślnych" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Szerokość" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Rzędy" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Kwadrat" @@ -3015,11 +2975,11 @@ msgstr "Wypełnienie grupy" msgid "workspace.options.group-stroke" msgstr "Obrys grupy" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Wysokość" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Sprawdź" @@ -3231,99 +3191,99 @@ msgstr "Przy naciśnięciu" msgid "workspace.options.interactions" msgstr "Interakcje" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Kolor" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Ściemnianie" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Rozjaśnienie" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Ciemniej" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Różnica" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Wykluczenie" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Ostre światło" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Barwa" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Jaśniej" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Jasność" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Mnożenie" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normalny" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Nakładka" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Nasycenie" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Ekran" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Łagodne światło" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Maks.Wysokość" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Maks.Szerokość" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Min.Wysokość" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Min.Szerokość" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Więcej kolorów" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Więcej kolorów z biblioteki" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Nieprzezroczystość" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototyp" @@ -3347,19 +3307,19 @@ msgstr "Górne lewo" msgid "workspace.options.radius-top-right" msgstr "Górne prawo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Bieżące" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Ponów" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Obrót" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Szukaj czcionki" @@ -3369,7 +3329,7 @@ msgstr "" "Wybierz kształt, obszar roboczy lub grupę, aby przeciągnąć połączenie do " "innego obszaru kompozycji." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Wybrane kolory" @@ -3425,27 +3385,27 @@ msgstr "Cień zaznaczenia" msgid "workspace.options.show-fill-on-export" msgstr "Pokaż przy eksporcie" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Pokaż w trybie przeglądania" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Rozmiar" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Wstępne ustawienia rozmiaru" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Brak" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Okrągły" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Kwadratowy" @@ -3453,35 +3413,35 @@ msgstr "Kwadratowy" msgid "workspace.options.stroke-color" msgstr "Kolor obrysu" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Szerokość obrysu" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Środek" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Przerywany" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Kropkowany" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "W środku" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Mieszany" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Na zewnątrz" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Ciągły" @@ -3517,11 +3477,11 @@ msgstr "Automatyczna szerokość" msgid "workspace.options.text-options.grow-fixed" msgstr "Stały" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Odstępy między literami" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Wysokość linii" @@ -3565,51 +3525,51 @@ msgstr "Podkreślenie (%s)" msgid "workspace.options.use-play-button" msgstr "Użyj przycisku odtwarzania w nagłówku, aby uruchomić widok prototypu." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Szerokość" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Oś X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Oś Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Rysuj węzły (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Połącz węzły (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Do rogu (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Do krzywej (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Połącz węzły (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Przenieś węzły (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Rozłącz węzły (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Przyciągnij węzły (%s)" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Dodaj układ flex" @@ -3621,27 +3581,27 @@ msgstr "Przesuń na spód" msgid "workspace.shape.menu.backward" msgstr "Przesuń do tyłu" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Kopiuj" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Zaznaczone do obszaru kompozycji" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Utwórz komponent" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Wytnij" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Usuń" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Usuń początek flow" @@ -3653,7 +3613,7 @@ msgstr "Odłącz instancję" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Odłącz instancje" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Różnica" @@ -3661,15 +3621,15 @@ msgstr "Różnica" msgid "workspace.shape.menu.duplicate" msgstr "Duplikuj" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Edytuj" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Wyklucz" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Spłaszcz" @@ -3681,7 +3641,7 @@ msgstr "Odwróć w poziomie" msgid "workspace.shape.menu.flip-vertical" msgstr "Odwróć w pionie" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Początek flow" @@ -3693,39 +3653,39 @@ msgstr "Przesuń do góry" msgid "workspace.shape.menu.front" msgstr "Przesuń na wierzch" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Grupuj" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Ukryj" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Pokaż/Ukryj UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Przecięcie" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Zablokuj" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Maskuj" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Wklej" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Ścieżka" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Usuń układ flex" @@ -3741,11 +3701,11 @@ msgstr "Przywróć główny komponent" msgid "workspace.shape.menu.select-layer" msgstr "Zaznacz warstwę" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Pokaż" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Pokaż w panelu zasobów" @@ -3761,19 +3721,19 @@ msgstr "Usuń miniaturę" msgid "workspace.shape.menu.thumbnail-set" msgstr "Ustaw jako miniaturę" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Rozgrupuj" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Złącz" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Odblokuj" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Odmaskuj" @@ -3789,47 +3749,47 @@ msgstr "Zwiń pasek boczny" msgid "workspace.sidebar.expand" msgstr "Rozwiń pasek boczny" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Historia" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Warstwy" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Komponenty" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Obszary kompozycji" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Grupy" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Obrazy" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Maski" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Przeszukaj warstwy" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Kształty" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Teksty" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Importowane atrybuty SVG" @@ -3837,7 +3797,7 @@ msgstr "Importowane atrybuty SVG" msgid "workspace.sidebar.sitemap" msgstr "Strony" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Mapa strony" @@ -3845,51 +3805,51 @@ msgstr "Mapa strony" msgid "workspace.toolbar.assets" msgstr "Zasoby" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Palety kolorów (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Komentarze (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Krzywa (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elipsa (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Obszar kompozycji (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Obraz (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Przenieś (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Ścieżka (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Prostokąt (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Tekst (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Typografie (%s)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Jak dotąd nie ma zmian w historii" @@ -4029,14 +3989,14 @@ msgstr "zasób typograficzny" msgid "workspace.undo.entry.unknown" msgstr "Operacja ponad %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Odrzuć" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "W bibliotekach współdzielonych są aktualizacje" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Aktualizuj" diff --git a/frontend/translations/pt.po b/frontend/translations/pt.po index 03e6cb88a7..8d4d4a3223 100644 --- a/frontend/translations/pt.po +++ b/frontend/translations/pt.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 2026.6.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Já tem uma conta?" @@ -29,7 +29,7 @@ msgstr "" "Este é um serviço DEMONSTRAÇÃO, NÃO USE para trabalho real, os projetos " "serão apagados periodicamente." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Esqueceu sua senha?" @@ -37,37 +37,37 @@ msgstr "Esqueceu sua senha?" msgid "auth.fullname" msgstr "Nome completo" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Entrar na minha conta" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Faça login aqui." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot é a ferramenta de design de código aberto e gratuita para " "colaboração em design e código" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -89,7 +89,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "Link de recuperação de senha enviado para o seu e-mail." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Entrou para a equipe com sucesso" @@ -121,7 +121,7 @@ msgstr "Esqueceu sua senha?" msgid "auth.recovery-submit" msgstr "Altere sua senha" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Ainda não tem uma conta?" @@ -133,7 +133,7 @@ msgstr "Informe como deseja ser chamado no painel e nos e-mails." msgid "auth.register-account-title" msgstr "Seu nome" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Criar uma conta" @@ -146,7 +146,7 @@ msgstr "Criar uma conta" msgid "auth.terms-and-privacy-agreement" msgstr "Concordo com os [termos de serviço](%s) e a [política de privacidade](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Termos de serviço" @@ -158,93 +158,93 @@ msgstr "E-mail profissional" msgid "comments.mentions.not-found" msgstr "Nenhuma pessoa encontrada para @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publicar" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Todos os usuários do Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Tem certeza de que deseja remover este link? Se fizer isso, ele não estará " "mais disponível para ninguém" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(atual)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Excluir link" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Obter link" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Link copiado com sucesso" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Gerenciar permissões" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "Página compartilhada" msgstr[1] "%s páginas compartilhadas" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Pode comentar" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Pode inspecionar o código" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Qualquer pessoa com o link terá acesso" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Páginas compartilhadas" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "O link compartilhável aparecerá aqui" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Somente membros da equipe" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Compartilhar protótipos" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Selecionar tudo" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Despublicar" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Gestão da equipe" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "O Penpot foi feito para equipes. Convide membros para trabalharem juntos em " "projetos e arquivos" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Monte sua equipe!" @@ -252,18 +252,18 @@ msgstr "Monte sua equipe!" msgid "dashboard.add-file" msgstr "Adicionar arquivo" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Adicionar como Biblioteca Compartilhada" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Alterar e-mail" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copiar)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Criar nova equipe" diff --git a/frontend/translations/pt_BR.po b/frontend/translations/pt_BR.po index 0847bf55bf..4690e87f8f 100644 --- a/frontend/translations/pt_BR.po +++ b/frontend/translations/pt_BR.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:17+0000\n" -"Last-Translator: Renan Mayrinck \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Portuguese (Brazil) " "\n" "Language: pt_BR\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Já tem uma conta?" @@ -29,7 +29,7 @@ msgstr "" "Este é um serviço DEMONSTRATIVO, NÃO USE para trabalho real. Os projetos " "serão apagados periodicamente." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Esqueceu sua senha?" @@ -37,37 +37,37 @@ msgstr "Esqueceu sua senha?" msgid "auth.fullname" msgstr "Nome completo" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Entrar na minha conta" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Fazer Login." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot é uma ferramenta de código aberto gratuíto para colaboração em " "Design e código" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -87,7 +87,7 @@ msgstr "Perfil não verificado. Por favor, verifique o perfil antes de continuar msgid "auth.notifications.recovery-token-sent" msgstr "Link de recuperação de senha foi enviado para sua caixa de entrada." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Entrou para a equipe com sucesso" @@ -119,7 +119,7 @@ msgstr "Esqueceu sua senha?" msgid "auth.recovery-submit" msgstr "Mudar a sua senha" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Ainda não tem uma conta?" @@ -131,7 +131,7 @@ msgstr "Informe-nos sobre como devemos te chamar no painel e nos e-mails." msgid "auth.register-account-title" msgstr "Seu nome" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Criar uma conta" @@ -144,7 +144,7 @@ msgstr "Criar uma conta" msgid "auth.terms-and-privacy-agreement" msgstr "Concordo com os [termos de serviço](%s) e as [políticas de privacidade](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Termos de serviço" @@ -162,93 +162,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Ninguém encontrado com @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publicar" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Todos os usuários do Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Tem certeza de que deseja remover este link? Se você fizer isso, ele não " "estará mais disponível para ninguém" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(atual)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Destruir link" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Obter link" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Link copiado com sucesso" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Gerenciar Permissões" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 página compartilhada" msgstr[1] "%s páginas compartilhadas" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Pode comentar" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Pode inspecionar o código" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Qualquer pessoa com o link terá acesso" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Páginas compartilhadas" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "O link compartilhável aparecerá aqui" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Apenas membros da equipe" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Compartilhar protótipos" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Selecionar todos" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Cancelar publicação" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Gerenciamento de equipe" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "O Penpot é destinado a equipes. Convide pessoas para trabalhar junto com " "você em projetos e arquivos" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Junte o time!" @@ -256,23 +256,23 @@ msgstr "Junte o time!" msgid "dashboard.add-file" msgstr "Adicionar arquivo" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Adicionar a biblioteca compartilhada" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Alterar e-mail" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copiar)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Criar nova equipe" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Fixar esta versão" @@ -280,11 +280,11 @@ msgstr "Fixar esta versão" msgid "dashboard.default-team-name" msgstr "Seu Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Deletar equipe" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Baixar arquivo Penpot (.penpot)" @@ -296,19 +296,19 @@ msgstr "Duplicar" msgid "dashboard.duplicate-multi" msgstr "Duplicar %s arquivos" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Quando um membro do projeto criar um rascunho, ele será exibido aqui." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Nenhum rascunho ainda." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Quando um membro do projeto criar um arquivo, ele será exibido aqui." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Não há arquivos ainda." @@ -360,69 +360,69 @@ msgstr "Comece a criar coisas incríveis" msgid "dashboard.export-binary-multi" msgstr "Baixar %s arquivos Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exportar boards em PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exportar como arquivo PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s de %s elementos selecionados" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exportar" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Você pode adicionar configurações de exportação em elementos nas " "propriedades de design (na parte inferior da barra lateral direita)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informações sobre como configurar exportações no Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Não há elementos com configurações de exportação." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Exportar seleção" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Fonte deletada" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Retirar todas" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Fontes customizadas que você adicionar vão aparecer aqui." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Ainda não há fontes personalizadas." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Assim que um membro do projeto enviar uma fonte personalizada, ela será " "exibida aqui." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 fonte adicionada" msgstr[1] "%s fontes adicionadas" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -432,7 +432,7 @@ msgstr "" "como uma **única família de fontes**. Você pode fazer upload de fontes com " "os seguintes formatos: **TTF, OTF e WOFF** (apenas uma será necessária)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -441,11 +441,11 @@ msgstr "" "Serviço do Penpot](%s). Caso queira saber mais, veja esta leitura sobre " "[licenciamento de fontes](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Enviar tudo" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -456,11 +456,11 @@ msgstr "" "utilizar o [Transfonter](https://transfonter.org/) para gerar webfonts e " "corrigir erros. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importar arquivos" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Ops! Não conseguimos importar este arquivo" @@ -468,23 +468,23 @@ msgstr "Ops! Não conseguimos importar este arquivo" msgid "dashboard.import.bad-url" msgstr "Falhou ao importar. A URL do modelo está incorreta" -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Nem todos os arquivos foram importados" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Os seguintes arquivos apresentam erros:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Arquivos com erros não puderam ser enviados." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgstr "%s arquivos foram importados com sucesso." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Alguns arquivos continham objetos inválidos que foram removidos." @@ -492,77 +492,49 @@ msgstr "Alguns arquivos continham objetos inválidos que foram removidos." msgid "dashboard.import.no-perms" msgstr "Você não tem permissão para importar para esta equipe" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Processando as cores" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Processando os componentes" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Processando as mídias" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Processando página: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Processando as fontes" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Carregando dados para o servidor (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Enviando arquivo: %s" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Você vai excluir os convites para:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Você vai reenviar os convites para:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Exclua convites" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Reenviar convites" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Convidar pessoas" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Sair da equipe" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliotecas e Modelos" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Aqui você tem algumas bibliotecas e modelos que você pode adicionar ao seu " "projeto" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Explore mais e aprenda como contribuir" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Ocorreu um problema ao importar o modelo. O modelo não foi importado." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliotecas de ativos" @@ -570,7 +542,7 @@ msgstr "Bibliotecas de ativos" msgid "dashboard.loading-files" msgstr "carregando seus arquivos…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "carregando suas fontes …" @@ -590,39 +562,39 @@ msgstr "Mover %s arquivos para" msgid "dashboard.move-to-other-team" msgstr "Mover para outra equipe" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Novo arquivo" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Novo arquivo" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Novo projeto" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Novo projeto" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Não achamos nada com “%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Projetos fixados aparecerão aqui" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notificações" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Seu endereço de e-mail foi atualizado com sucesso" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Seu endereço de e-mail foi verificado com sucesso" @@ -634,11 +606,11 @@ msgstr "As configurações de notificações foram atualizadas" msgid "dashboard.notifications.password-saved" msgstr "Senha salva com sucesso!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Ver notificações" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s membros" @@ -646,15 +618,15 @@ msgstr "%s membros" msgid "dashboard.open-in-new-tab" msgstr "Abrir arquivo em uma nova guia" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opções" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Ordenar por função" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Ordenar por status" @@ -678,7 +650,7 @@ msgstr "Agora você é o proprietário desta equipe." msgid "dashboard.permissions-change.viewer" msgstr "Agora você é um espectador desta equipe." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Fixar/Desafixar" @@ -694,11 +666,11 @@ msgstr "Não é possível avaliar o manifesto do plugin" msgid "dashboard.plugins.try-plugin" msgstr "Experimente o plugin: " -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projetos" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Quer remover sua conta?" @@ -706,31 +678,31 @@ msgstr "Quer remover sua conta?" msgid "dashboard.removed-from-team" msgstr "Você não faz mais parte do time \"%s\"." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Salvar configurações" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Pesquisar…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Pesquisando por “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Selecionar idioma da Interface" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Selecionar tema" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Dark (padrão)" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Tema do sistema" @@ -794,11 +766,11 @@ msgstr "Atualizar configurações" msgid "dashboard.settings.notifications.title" msgstr "Notificações" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Mostrar todos os arquivos" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Histórico de versões" @@ -822,7 +794,7 @@ msgstr[1] "Seus arquivos foram duplicados com sucesso" msgid "dashboard.success-duplicate-project" msgstr "Seu projeto foi duplicado com sucesso" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Seu arquivo foi movido com sucesso" @@ -834,90 +806,90 @@ msgstr "Seus arquivos foram movidos com sucesso" msgid "dashboard.success-move-project" msgstr "Seu projeto foi movido com sucesso" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Informação da equipe" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Membros da equipe" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Projetos da equipe" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Adicionar ao seu projeto" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema da Interface" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Resultados da pesquisa" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Digite para pesquisar nos resultados" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Cancelar publicação da biblioteca" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Atualizar configurações" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Está ativo" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Quando esse hook é invocado detalhes de eventos vão ser enviados" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Você só pode excluir ou modificar os webhooks criados por você." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Tipo de conteúdo" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Criar webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook criado com sucesso." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhooks são uma forma simples de permitir que outros websites e " "aplicativos sejam notificados quando certos eventos acontecem no Penpot. " "Nós enviaremos uma solicitação POST para todas as URLs que você digite." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Clique em \"Adicionar Webhook\" para adicionar um Webhook." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Nenhum Webhook criado ainda." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Sua conta" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-mail" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Seu nome" @@ -933,11 +905,11 @@ msgstr "Atenção" msgid "ds.component-subtitle" msgstr "Componentes para atualizar:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Permissão" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Cancelar" @@ -957,7 +929,7 @@ msgstr "Nenhum token aplicável nos conjuntos ou temas ativos." msgid "ds.inputs.numeric-input.no-matches" msgstr "Nenhum resultado encontrado." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Abrir lista de tokens" @@ -969,15 +941,15 @@ msgstr "Provedor de autenticação não permitido para este perfil" msgid "errors.auth-provider-not-configured" msgstr "Provedor de autenticação não configurado." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Parece que você não está autenticado ou a sessão expirou." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "A fonte %s não pôde ser carregada" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "As fontes %s não puderam ser carregadas" @@ -989,43 +961,43 @@ msgstr "Não é possível carregar o arquivo de mídia." msgid "errors.character-limit-exceeded" msgstr "Limite de caracteres excedido" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Seu navegador não pode fazer esta operação" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "Houve um erro com o comentário" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Desculpe! Este é um arquivo antigo que utiliza um tipo obsoleto de recursos " "do Penpot e você não pode abri-lo." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "para que possamos ajudar você." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "" "Embora o Penpot não seja mais compatível com este tipo de recurso, você " "pode" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "contate-nos" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Não é possível remover a definição principal dentro de um caminho filho." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E-mail já utilizado" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-mail já validado." @@ -1037,30 +1009,30 @@ msgstr "Você não pode usar seu e-mail como senha" msgid "errors.email-does-not-match-invitation" msgstr "O e-mail não corresponde ao convite." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domínio não permitido" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "O e-mail «%s» tem muitos relatórios de devolução permanentes." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "O e-mail «%s» foi denunciado como spam ou devolvido permanentemente." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Parece que você está abrindo um arquivo que tem a funcionalidade '%s' " "habilitada, mas o seu frontend no penpot não a suporta ou ela está " "desabilitada." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Recurso '%s' não é suportado." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Deve conter no máximo %s caracteres." @@ -1068,7 +1040,7 @@ msgstr "Deve conter no máximo %s caracteres." msgid "errors.field-min-length" msgstr "Deve conter pelo menos 1 caractere." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Campo vazio" @@ -1076,18 +1048,18 @@ msgstr "Campo vazio" msgid "errors.field-not-all-whitespace" msgstr "O nome deve conter algum caractere diferente de espaço." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Parece que há uma incompatibilidade entre os recursos habilitados e os " "recursos do arquivo que você está tentando abrir. Migrações para '%s' " "precisam ser aplicadas antes que o arquivo possa ser aberto." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Algo errado aconteceu." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Algo de errado aconteceu com o web worker." @@ -1103,7 +1075,7 @@ msgstr "Data inválida" msgid "errors.invalid-email" msgstr "Por favor, insira um email válido" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "E-mail de confirmação deve ser o mesmo" @@ -1127,11 +1099,11 @@ msgstr "Este convite pode ter sido cancelado ou expirou." msgid "errors.ldap-disabled" msgstr "Autenticação por LDAP está desativada." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Você atingiu a cota de '%s'. Entre em contato com o suporte." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "O número máximo (%s) de e-mails que podem ser convidados em uma única " @@ -1149,21 +1121,21 @@ msgstr "Parece que o conteúdo da imagem não corresponde a extensão do arquivo msgid "errors.media-type-not-allowed" msgstr "Parece que esta não é uma imagem válida." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "O perfil que você está convidando tem e-mails silenciados (relatos de spam " "ou altas devoluções)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migração em progresso" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Somente o criador da versão pode bloqueá-la" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Somente o criador da versão pode desbloqueá-la" @@ -1175,7 +1147,7 @@ msgstr "A senha de confirmação deve ser a mesma" msgid "errors.password-too-short" msgstr "A senha deve ter pelo menos 8 caracteres" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Dados inválidos na área de transferência" @@ -1183,7 +1155,7 @@ msgstr "Dados inválidos na área de transferência" msgid "errors.profile-blocked" msgstr "O perfil está bloqueado" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "Seu perfil tem e-mails silenciados (relatos de spam ou altas devoluções)." @@ -1191,25 +1163,25 @@ msgstr "Seu perfil tem e-mails silenciados (relatos de spam ou altas devoluçõe msgid "errors.registration-disabled" msgstr "O registro de contas está desativado no momento." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG inválido ou malformado" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Recurso incompatível '%s' detectado" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Membros insuficientes para deixar a equipe, você provavelmente deseja " "excluí-la." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "O membro que você tentou atribuir não existe." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "O proprietário não pode sair da equipe, você deve reatribuir a função de " @@ -1219,13 +1191,13 @@ msgstr "" msgid "errors.token-set-already-exists" msgstr "Já existe uma definição com o mesmo nome" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "Não é possível concluir a remoção, uma definição com o mesmo nome já existe " "no caminho." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Existe uma opção de tema com o mesmo nome" @@ -1233,47 +1205,47 @@ msgstr "Existe uma opção de tema com o mesmo nome" msgid "errors.unexpected-error" msgstr "Um erro inesperado ocorreu." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Token desconhecido" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Esta versão já está bloqueada" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Esta versão está bloqueada e não pode ser excluída por outras pessoas" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "O arquivo tem um número de versão incompatível" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Erro de conexão, URL não acessível" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "A URL não passa na validação." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "O último envio não foi bem-sucedido." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Erro na validação SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "O tempo se esgotou" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Erro inesperado durante a validação" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Status inesperado %s" @@ -1289,13 +1261,13 @@ msgstr "A senha antiga está incorreta" msgid "feedback.description" msgstr "Descrição" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Estamos felizes em ter você aqui. Se precisar de ajuda com algo, pesquise " "antes de postar." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Comunidade do Penpot" @@ -1310,11 +1282,11 @@ msgstr "" "uma ideia ou uma dúvida. Um membro de nossa equipe responderá o mais rápido " "possível." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Precisa de ajuda com dúvidas mais técnicas? Veja o nosso X." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Conta de suporte no X" @@ -1350,7 +1322,7 @@ msgstr "Baixar imagem original" msgid "inspect.attributes.shadow" msgstr "Sombra" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Tamanho e posição" @@ -1358,39 +1330,27 @@ msgstr "Tamanho e posição" msgid "inspect.attributes.stroke" msgstr "Traçado" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centro" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Dentro" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Fora" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografia" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Família tipográfica" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Tamanho da fonte" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Estilo da fonte" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Espaçamento entre letras" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Altura da linha" @@ -1402,11 +1362,11 @@ msgstr "Nenhum" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Sublinhado" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Iniciais em maiúsculas" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Minúsculo" @@ -1418,7 +1378,7 @@ msgstr "Nenhuma" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Não definido" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Maiúsculo" @@ -1530,43 +1490,43 @@ msgstr "Valor resolvido:" msgid "inspect.tabs.styles.variants-panel" msgstr "Propriedades da variante" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Marcar todos como lidos" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Atalhos" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Sobre o Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Aceitar" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Ativo" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Adicionar" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Adicionar fonte personalizada" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Adicionando..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Administrador" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Todos" @@ -1574,17 +1534,17 @@ msgstr "Todos" msgid "labels.and" msgstr "e" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Voltar" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Parece que você precisa esperar um pouco e tentar de novo; estamos " "realizando pequenas manutenções em nossos servidores." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Erro do servidor (Bad Gateway)" @@ -1592,11 +1552,11 @@ msgstr "Erro do servidor (Bad Gateway)" msgid "labels.blur" msgstr "Desfoque" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Cancelar" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Fechar" @@ -1604,7 +1564,7 @@ msgstr "Fechar" msgid "labels.code" msgstr "Código" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Recolher" @@ -1616,15 +1576,15 @@ msgstr "Cor" msgid "labels.comment" msgstr "Comentário" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Comentários" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Comunidade" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Comunidade e contribuições" @@ -1636,11 +1596,11 @@ msgstr "Calculado" msgid "labels.confirm-password" msgstr "Confirmar senha" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continuar" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Você pode continuar com uma conta do Penpot" @@ -1652,27 +1612,27 @@ msgstr "Copiar" msgid "labels.copy-color" msgstr "Copiar cor" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Copiar link" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Criar" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Criar nova equipe" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Insira o nome da nova equipe" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Painel" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Excluir" @@ -1684,7 +1644,7 @@ msgstr "Excluir comentário" msgid "labels.delete-comment-thread" msgstr "Excluir tópico" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Excluir convite" @@ -1692,23 +1652,23 @@ msgstr "Excluir convite" msgid "labels.delete-multi-files" msgstr "Excluir %s arquivos" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Rascunhos" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Editar" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Editor" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Expirado" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exportar" @@ -1720,39 +1680,39 @@ msgstr "Feedback desativado" msgid "labels.feedback-sent" msgstr "Feedback enviado" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Família tipográfica" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Estilos" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fontes" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Repositório do Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Enviar feedback" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Voltar" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Central de Ajuda" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Ocultar comentários resolvidos" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inativo" @@ -1760,43 +1720,43 @@ msgstr "Inativo" msgid "labels.info" msgstr "Informação" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Fontes instaladas" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Erro interno" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Convites" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Linguagem" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Bibliotecas & Modelos" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Entre ou cadastre-se" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Sair" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Membro" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Membros" @@ -1808,39 +1768,39 @@ msgstr "Menção" msgid "labels.new-password" msgstr "Nova senha" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Próximo" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Nada de novo por aqui! Novas notificações de comentários irão aparecer aqui." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Não há convites pendentes." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Ops!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notificações" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 arquivo" msgstr[1] "%s arquivos" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 tela" msgstr[1] "%s telas" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 projeto" @@ -1850,31 +1810,31 @@ msgstr[1] "% projetos" msgid "labels.old-password" msgstr "Senha antiga" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Opções" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Outra (específica)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Outra" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Proprietário" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Senha" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Pendente" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Projetos fixados" @@ -1882,23 +1842,23 @@ msgstr "Projetos fixados" msgid "labels.post" msgstr "Publicar" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Anterior" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Produto ou UX Design" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Gestão de Produtos" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Perfil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projetos" @@ -1906,23 +1866,23 @@ msgstr "Projetos" msgid "labels.refresh" msgstr "Recarregar" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Notas de atualização" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Remover" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Remover membro" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Renomear" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Renomear equipe" @@ -1946,7 +1906,7 @@ msgstr "nova resposta" msgid "labels.reply.thread" msgstr "Resposta" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Reenviar convite" @@ -1954,75 +1914,75 @@ msgstr "Reenviar convite" msgid "labels.restore" msgstr "Restaurar" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Tentar novamente" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Cargo" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Salvar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Buscar" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Buscar fonte" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Selecione uma opção" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Enviar" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Enviando…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Estamos em manutenção programada de nossos sistemas." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Serviço indisponível" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Configurações" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibliotecas" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Mostrar todos os comentários" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Mostrar lista de comentários" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Mostrar apenas seus comentários" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Recursos" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutorial" @@ -2030,23 +1990,23 @@ msgstr "Tutorial" msgid "labels.unpublish-multi-files" msgstr "Despublicar arquivos %s" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Atualizar" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Atualizar equipe" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Carregar" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Enviar fontes personalizadas" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Enviando…" @@ -2054,11 +2014,11 @@ msgstr "Enviando…" msgid "labels.view-only" msgstr "SOMENTE VISUALIZAÇÃO" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Visualizador" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -2066,11 +2026,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Escrever um novo comentário" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(você)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Sua conta" @@ -2097,61 +2057,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Adicionar “%s” na Biblioteca Compartilhada" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Grande deslocamento" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Verificar o novo e-mail" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Enviaremos a você um e-mail para seu e-mail atual “%s” para verificar sua " "identidade." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Novo e-mail" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Alterar e-mail" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Alterar seu e-mail" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Criar webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Criar webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL do payload" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://exemplo.com.br/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Cancelar e manter minha conta" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Sim, exclua minha conta" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Ao excluir sua conta, você perderá todos os seus projetos e arquivos atuais." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Tem certeza que deseja excluir sua conta?" @@ -2193,31 +2153,31 @@ msgstr "Tem certeza de que deseja excluir %s arquivos?" msgid "modals.delete-file-multi-confirm.title" msgstr "Excluindo %s arquivos" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Tem certeza de que deseja excluir este estilo de fonte? Ele não será " "carregado se for usado em um arquivo." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Excluindo estilo de fonte" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Tem certeza que deseja excluir essa fonte? Ela não será carregada se for " "utilizada em um arquivo." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Excluindo fonte" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Tem certeza de que deseja excluir esta página?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Excluir página" @@ -2233,19 +2193,19 @@ msgstr "Tem certeza de que deseja excluir este projeto?" msgid "modals.delete-project-confirm.title" msgstr "Excluir projeto" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Excluir arquivo" msgstr[1] "Excluir arquivos" -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Tem certeza de que deseja excluir este arquivo?" msgstr[1] "Tem certeza de que deseja excluir estes arquivos?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Excluindo arquivo" @@ -2265,135 +2225,135 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Excluindo equipe" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Excluir membro" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Tem certeza de que deseja excluir este membro da equipe?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Excluir membro da equipe" -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Excluir webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Tem certeza que deseja excluir este webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Excluindo webhook" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Editar webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Editar webhook" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Enviar convite" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-mails, separados por vírgulas" -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Convide membros para a equipe" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Como você é o único membro desta equipe, a equipe será excluída junto com " "seus projetos e arquivos." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Tem certeza de que deseja sair da equipe %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Você não pode deixar a equipe se não houver outro membro para promover a " "proprietário. Você talvez queira excluir a equipe." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "Você é o proprietário de %s." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promover e sair" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Selecione um membro para promover" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Selecione um membro para promover" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Sair da equipe" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Tem certeza de que deseja sair da equipe?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Saindo da equipe" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Quantidade de deslocamento" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Transferir equipe" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Se você transferir a propriedade, mudará sua função para Admin, perdendo " "algumas permissões sobre essa equipe. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Você é o atual proprietário da equipe. Tem certeza de que deseja promover " "este usuário a proprietário?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Novo proprietário da equipe" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Pequeno deslocamento" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Despublicar" msgstr[1] "Despublicar" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Tem certeza de que deseja cancelar a publicação desta biblioteca?" msgstr[1] "Tem certeza de que deseja cancelar a publicação dessas bibliotecas?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Cancelar publicação da biblioteca" @@ -2417,25 +2377,25 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Atualizar componente em uma biblioteca compartilhada" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Convite enviado com sucesso" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Link do convite copiado" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Você não pode deletar seu perfil. Determine um novo proprietário para suas " "equipes antes de continuar." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Perfil salvo com sucesso!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "E-mail de verificação enviado para %s. Verifique seu e-mail!" @@ -2453,11 +2413,11 @@ msgstr "Bem-vindo ao Penpot!" msgid "onboarding.choice.team-up.create-team-desc" msgstr "Depois de nomear sua equipe, você poderá convidar pessoas para participar." -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Convide membros" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Convide com a função:" @@ -2503,79 +2463,79 @@ msgstr "Começar a criar" msgid "profile.recovery.go-to-login" msgstr "Ir para a página de login" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Misto" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Comentários" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Alinhar a base" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Centralizar horizontalmente" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Alinhar a esquerda" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Alinhar a direita" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Alinhar ao topo" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Centralizar verticalmente" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Criar tela a partir da seleção" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Subtrair" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Exclusão" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Interseccionar" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "União" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Mover ao fundo" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Mover para trás" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Mover para a frente" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Mover ao topo" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Limpar desfazer" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copiar" @@ -2583,35 +2543,35 @@ msgstr "Copiar" msgid "shortcuts.create-new-project" msgstr "Criar novo" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Recortar" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Reduzir zoom" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Apagar" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Apagar ponto" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Quebrar componente" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Curva" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Tela" @@ -2619,39 +2579,39 @@ msgstr "Tela" msgid "shortcuts.draw-nodes" msgstr "Desenhar caminho" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Caminho" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Retângulo" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Texto" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplicar" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Cancelar" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Exportar formas" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Ajustar para ver tudo" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Refletir horizontalmente" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Refletir verticalmente" @@ -2667,63 +2627,63 @@ msgstr "Ir para as bibliotecas compartilhadas" msgid "shortcuts.go-to-search" msgstr "Pesquisar" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Agrupar" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribuir horizontalmente" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Mostrar/ocultar interface" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Mais zoom" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Inserir imagem" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Unir pontos" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Arredondar canto" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Criar curva" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Máscara" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Unir pontos" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Mover" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Mover para baixo rápido" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Mover para a esquerda rápido" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Mover para a direita rápido" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Mover para cima rápido" @@ -2731,293 +2691,293 @@ msgstr "Mover para cima rápido" msgid "shortcuts.move-nodes" msgstr "Mover ponto" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Mover para baixo" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Mover para a esquerda" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Mover para a direita" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Mover para cima" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Próximo quadro" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Não foram encontrados atalhos" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Definir opacidade para 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Definir opacidade para 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Definir opacidade para 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Definir opacidade para 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Definir opacidade para 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Definir opacidade para 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Definir opacidade para 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Definir opacidade para 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Definir opacidade para 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Definir opacidade para 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Seletor de Cores" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Ir para seção de comentários" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Ir para o dashboard" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Ir para seção de inspeção" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Ir para seção de interação" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Ir para seção de interação" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Ir para o workspace" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " ou " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Colar" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Tela anterior" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Refazer" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Redefinir zoom" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Fundamentos" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Painel" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Espectador" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Espaço de trabalho" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Selecionar todos" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Separar pontos" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Mostrar/ocultar grade de pixels" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Mostrar/ocultar atalhos" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Aderir aos pontos" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Ajustar a grade de Pixels" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Começar a editar" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Alinhamento" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Editar" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Geral" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu principal" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modificar camadas" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navegação" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navegação" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navegação" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Painéis" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Curvas" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formas" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Ferramentas" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Definir como miniatura" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Atalhos do teclado" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Alternar alinhamento dinâmico" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Mostrar/Esconder ativos" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Mostrar/Esconder paleta de cores" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Entrar/Sair do modo de foco" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Entrar/Sair da tela cheia" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Mostrar/Esconder histórico" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Mostrar/Esconder camadas" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Adicionar/remover Flex Layout" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Bloquear selecionado" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Fixar proporções" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Mostrar/Esconder paleta de tipografias" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Alternar estilo de zoom" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Desfazer" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Desagrupar" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Retirar máscara" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribuir verticalmente" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Ajustar zoom a seleção" @@ -3101,125 +3061,125 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Convidando pessoas enquanto estiver no plano Ilimitado" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Assinatura" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Adicione um método de pagamento para continuar após o seu teste" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Todos os benefícios do plano Profissional e:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Todos os benefícios do plano Ilimitado e:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s editores)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Empresarial" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Empresarial (teste)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "Versões salvas automaticamente e recuperação de arquivos por 90 dias" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Fatura mensal fixa" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Armazenamento ilimitado" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Gerencie sua assinatura" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Atualmente, você tem %s pessoa em suas equipes que pode editar." msgstr[1] "Atualmente, você tem %s pessoas em suas equipes que podem editar." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Atenção: mudar para um plano inferior significa menos armazenamento e " "backups e histórico de versões mais curtos." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Editores" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "" "(Proprietários, Administradores e Editores. Visualizadores não contam como " "Editores)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "Você não pode definir menos editores do que tem agora. Altere o papel " "(editor/administrador para visualizador) para pessoas que realmente não " "editam arquivos nas configurações da equipe." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "Cobrado após o período de teste. Nenhum cartão de crédito necessário agora." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/mês" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Aplicado %s às suas equipes" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Provedores de fonte - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Fontes - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projetos - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Pesquisar - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Bibliotecas Compartilhadas - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Liberdade de design para equipes" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Dê sua opinião - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Configurações - Penpot" @@ -3227,27 +3187,27 @@ msgstr "Configurações - Penpot" msgid "title.settings.password" msgstr "Senha - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Perfil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Convites - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Membros - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Configurações - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Modo protótipo - Penpot" @@ -3259,99 +3219,99 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Desvincular token" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Não foram encontradas telas nesta página." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Tela não encontrada." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Comentários (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Não mostrar interações" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Tela cheia" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspeção (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interações" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interações (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Copiar link" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Mostrar interações" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Mostrar interações ao clicar" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Mapa do site" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "O último envio foi bem-sucedido." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Centralizar horizontalmente (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribuir espaçamento horizontal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Alinhar a esquerda (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Alinhar a direita (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Alinhar a base (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Centralizar verticalmente (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribuir espaçamento vertical (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Alinhar ao topo (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Todos os ativos" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Cores" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Componentes" @@ -3359,41 +3319,41 @@ msgstr "Componentes" msgid "workspace.assets.create-group" msgstr "Criar grupo" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Seus itens serão nomeados automaticamente como \"nome do grupo / nome do " "item\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Excluir" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplicar" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Editar" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Agrupar" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nome do grupo" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "biblioteca local" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Nenhum ativo encontrado" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Renomear" @@ -3401,39 +3361,39 @@ msgstr "Renomear" msgid "workspace.assets.rename-group" msgstr "Renomear grupo" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Procurar nos ativos" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Tipografias" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Tamanho" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Estilo da fonte" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Ir para biblioteca de estilo para editar" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Espaçamento de letra" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Altura da linha" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformações de texto" @@ -3441,15 +3401,15 @@ msgstr "Transformações de texto" msgid "workspace.assets.ungroup" msgstr "Desagrupar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Modo de foco" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Foco desligado" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Foco ligado" @@ -3461,139 +3421,139 @@ msgstr "Gradiente linear" msgid "workspace.gradients.radial" msgstr "Gradiente radial" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Desabilitar alinhamento dinâmico" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Desativar aderência as réguas" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Desativar aderência a grade de pixels" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Habilitar alinhamento dinâmico" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Aderir as réguas" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Habilitar aderência a grade de pixels" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Esconder nomes das telas" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Esconder paleta de cores" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Ocultar grade de pixels" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Ocultar réguas" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Ocultar paleta de tipografias" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Editar" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Arquivo" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Ajuda e informações" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferências" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Visualizar" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Selecionar tudo" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Mostrar nomes das telas" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Mostrar paleta de cores" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Mostrar grade de pixels" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Mostrar réguas" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Mostrar paleta de tipografias" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Redefinir" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Erro ao salvar" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Salvo" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Salvando" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Ver protótipo (%s)" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Ajustar até preencher" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Ajustar até encaixar" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Ajustar a janela" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Tela cheia" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zoom na seleção" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Biblioteca do arquivo" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Cores recentes" @@ -3605,39 +3565,39 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Salvar estilo de cor" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Biblioteca de arquivos" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTECAS NESTE ARQUIVO" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTECAS" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Não há bibliotecas compartilhadas para atualizar" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Não foram encontrados resultados para “%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Procurar bibliotecas compartilhadas" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "BIBLIOTECAS COMPARTILHADAS" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Atualizar" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "ATUALIZAÇÔES" @@ -3657,11 +3617,11 @@ msgstr "Desfoque da seleção" msgid "workspace.options.canvas-background" msgstr "Fundo da tela" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Cortar conteúdo" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Componente" @@ -3705,11 +3665,11 @@ msgstr "Cima" msgid "workspace.options.constraints.topbottom" msgstr "Cima e Baixo" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exportar" @@ -3717,23 +3677,23 @@ msgstr "Exportar" msgid "workspace.options.export-multiple" msgstr "Exportar seleção" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Sufixo" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Exportação completa" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Exportando…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Exportação falhou" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "A exportação está inesperadamente lenta" @@ -3757,67 +3717,67 @@ msgstr "Início de fluxo" msgid "workspace.options.grid.auto" msgstr "Automático" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Colunas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Cor" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Espaçamento" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Altura" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margem" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Definir como padrão" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Inferior" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Esquerda" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Direita" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Esticar" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Superior" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Usar padrão" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Largura" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Linhas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Quadrado" @@ -3829,11 +3789,11 @@ msgstr "Preenchimento do grupo" msgid "workspace.options.group-stroke" msgstr "Traçado do grupo" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Altura" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspecionar" @@ -4045,99 +4005,99 @@ msgstr "Enquanto clica" msgid "workspace.options.interactions" msgstr "Interações" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Cor" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Queimar cor" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Subexposição de cores" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Escurecer" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Diferença" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Exclusão" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Luz direta" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Matiz" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Clarear" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminusidade" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Multiplicação" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Sobreposição" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturação" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Tela" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Luz difusa" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Altura Máx." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Largura Máx." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Altura Min." -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Altura Min." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Mais cores" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Mais cores da biblioteca" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacidade" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Protótipo" @@ -4161,19 +4121,19 @@ msgstr "Superior (a esquerda)" msgid "workspace.options.radius-top-right" msgstr "Superior (a direita)" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Recente" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Repetir" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotação" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Procurar fonte" @@ -4183,7 +4143,7 @@ msgstr "" "Selecione uma forma, tela ou grupo para arrastar uma conexão para outra " "tela." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Cores selecionadas" @@ -4239,27 +4199,27 @@ msgstr "Sombras da seleção" msgid "workspace.options.show-fill-on-export" msgstr "Mostrar nas exportações" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Mostrar no modo protótipo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Tamanho" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Padrões de tamanho" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Nenhum" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Redonda" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Quadrada" @@ -4267,35 +4227,35 @@ msgstr "Quadrada" msgid "workspace.options.stroke-color" msgstr "Cor do traçado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Largura do traçado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Tracejada" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Pontilhada" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Dentro" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Misturado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Fora" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Sólido" @@ -4331,11 +4291,11 @@ msgstr "Largura automática" msgid "workspace.options.text-options.grow-fixed" msgstr "Fixo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Espaçamento entre letras" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Altura de linha" @@ -4379,51 +4339,51 @@ msgstr "Sublinhado (%s)" msgid "workspace.options.use-play-button" msgstr "Use o botão play no cabeçalho para visualizar o protótipo." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Largura" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "eixo X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "eixo Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Desenhar ponto (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Unir pontos (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Transformar em retas (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Transformar em Curvas (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Juntar pontos (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Mover pontos (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Separar pontos (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Aderir aos pontos (%s)" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Adicionar Flex Layout" @@ -4435,27 +4395,27 @@ msgstr "Enviar para o fundo" msgid "workspace.shape.menu.backward" msgstr "Enviar para trás" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Copiar" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Agrupar em uma tela" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Criar componente" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Cortar" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Remover" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Remover início de fluxo" @@ -4467,7 +4427,7 @@ msgstr "Quebrar instância" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Quebrar instâncias" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Diferença" @@ -4475,15 +4435,15 @@ msgstr "Diferença" msgid "workspace.shape.menu.duplicate" msgstr "Duplicar" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Editar" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Excluir" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Achatar" @@ -4495,7 +4455,7 @@ msgstr "Inverter horizontalmente" msgid "workspace.shape.menu.flip-vertical" msgstr "Inverter verticalmente" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Início do fluxo" @@ -4507,39 +4467,39 @@ msgstr "Trazer para a frente" msgid "workspace.shape.menu.front" msgstr "Trazer para o topo" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Agrupar" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Esconder" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Mostrar/ocultar Interface" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Interseção" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Bloquear" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Máscara" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Colar" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Caminho" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Excluir Flex Layout" @@ -4555,11 +4515,11 @@ msgstr "Restaurar componente principal" msgid "workspace.shape.menu.select-layer" msgstr "Selecionar camada" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Mostrar" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Mostrar no painel de ativos" @@ -4575,19 +4535,19 @@ msgstr "Remover miniatura" msgid "workspace.shape.menu.thumbnail-set" msgstr "Definir como miniatura" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Desagrupar" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Unir" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Desbloquear" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Remover máscara" @@ -4603,47 +4563,47 @@ msgstr "Recolher a barra lateral" msgid "workspace.sidebar.expand" msgstr "Expandir a barra lateral" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Histórico" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Camadas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Componentes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Telas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Grupos" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Imagens" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Máscaras" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Pesquisar nas camadas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Formas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Textos" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Atributos SVG importados" @@ -4655,7 +4615,7 @@ msgstr "Páginas" msgid "workspace.sidebar.sitemap.add-page" msgstr "Adicionar página" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Mapa do site" @@ -4667,7 +4627,7 @@ msgstr "Voltar a listagem de temas" msgid "workspace.tokens.create-new-theme" msgstr "Crie seu primeiro tema agora." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Criar um." @@ -4695,7 +4655,7 @@ msgstr "Editar token" msgid "workspace.tokens.token-description" msgstr "Descrição" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nome" @@ -4703,7 +4663,7 @@ msgstr "Nome" msgid "workspace.tokens.token-value" msgstr "Valor" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "" @@ -4711,7 +4671,7 @@ msgstr "" msgid "workspace.tokens.tools" msgstr "Ferramentas" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "O valor não é válido" @@ -4719,51 +4679,51 @@ msgstr "O valor não é válido" msgid "workspace.toolbar.assets" msgstr "Ativos" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Paleta de cores (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Comentários (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Ferramenta Curvas (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Ferramenta Elipse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Ferramenta Tela (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Importar Imagem (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Ferramenta Seleção/Mover (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Ferramenta Caneta (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Ferramenta Retângulo (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Ferramenta Texto (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Tipografias (%s)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Não há mudanças no histórico até agora" @@ -4903,19 +4863,19 @@ msgstr "ativo de tipografia" msgid "workspace.undo.entry.unknown" msgstr "Operação em %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Dispensar" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Mais informações" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Existem atualizações nas bibliotecas compartilhadas" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Atualizar" diff --git a/frontend/translations/pt_PT.po b/frontend/translations/pt_PT.po index 57f7cbd423..296cab8bc6 100644 --- a/frontend/translations/pt_PT.po +++ b/frontend/translations/pt_PT.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: TheScientistPT \n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Portuguese (Portugal) " "\n" "Language: pt_PT\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Já tens uma conta?" @@ -29,7 +29,7 @@ msgstr "" "Este é um serviço de DEMONSTRAÇÃO, NÃO UTILIZES para trabalhos reais. Os " "projetos serão eliminados periodicamente." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Esqueceste a tua palavra-passe?" @@ -37,37 +37,37 @@ msgstr "Esqueceste a tua palavra-passe?" msgid "auth.fullname" msgstr "Nome completo" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Entrar na minha conta" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Inicia a sessão aqui." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "O Penpot é a ferramenta de desenho gratuita e de código aberto para " "colaboração entre Design e Código" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID Connect" @@ -87,7 +87,7 @@ msgstr "O perfil não está verificado, por favor valida-o antes de continuar." msgid "auth.notifications.recovery-token-sent" msgstr "Link de recuperação da palavra-passe enviado para o teu e-mail." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Entraste na equipa com sucesso" @@ -119,7 +119,7 @@ msgstr "Não te lembras da tua palavra-passe?" msgid "auth.recovery-submit" msgstr "Altera a palavra-passe" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Não tens conta?" @@ -131,7 +131,7 @@ msgstr "Diz-nos como te devemos tratar no painel principal e em emails." msgid "auth.register-account-title" msgstr "O teu nome" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Criar conta" @@ -144,7 +144,7 @@ msgstr "Cria uma conta" msgid "auth.terms-and-privacy-agreement" msgstr "Aceito os [termos de serviço](%s) e a [política de privacidade](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Termos de serviço" @@ -152,93 +152,93 @@ msgstr "Termos de serviço" msgid "auth.work-email" msgstr "Email profissional" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publicar" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Todos os utilizadores Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Tens a certeza de que queres remover este link? Se o fizeres, deixa de " "ficar disponível para ninguém" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(atual)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Eliminar link" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Obter link" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Link copiado com sucesso" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Gerir permissões" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 página partilhada" msgstr[1] "%s páginas partilhadas" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Pode comentar" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Pode inspecionar o código" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Qualquer pessoa com o link terá acesso" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Páginas partilhadas" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "O link partilhável será apresentado aqui" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Apenas membros da equipa" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Partilha protótipos" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Seleciona tudo" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Cancelar publicação" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Gestão da equipa" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "O Penpot é destinado a equipas. Convida colegas para colaborarem em " "projetos e ficheiros" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Trabalho de equipa!" @@ -246,23 +246,23 @@ msgstr "Trabalho de equipa!" msgid "dashboard.add-file" msgstr "Adicionar ficheiro" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Adicionar como biblioteca partilhada" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Alterar e-mail" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copiar)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "+ Criar nova equipa" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Afixar esta versão" @@ -270,11 +270,11 @@ msgstr "Afixar esta versão" msgid "dashboard.default-team-name" msgstr "O teu Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Eliminar equipa" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Descarregar ficheiro Penpot (.penpot)" @@ -286,19 +286,19 @@ msgstr "Duplicar" msgid "dashboard.duplicate-multi" msgstr "Duplicar %s ficheiros" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Assim que um membro do projeto crie um rascunho, será apresentado aqui." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Ainda não existem rascunhos." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Assim que um membro do projeto criar um ficheiro, será apresentado aqui." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Ainda não existem ficheiros." @@ -346,59 +346,59 @@ msgstr "Começa a criar coisas incríveis" msgid "dashboard.export-binary-multi" msgstr "Descarrega %s ficheiros Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exportar pranchetas para PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exportar para PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s de %s elementos selecionados" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exportar" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Podes adicionar definições de exportação em elementos a partir das " "propriedades de design (na parte inferior da barra lateral direita)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informações sobre como definir exportações no Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Não existem elementos com definições de exportação." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Exportar seleção" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Tipo de letra eliminado" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Ignorar todas" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Ainda não tens tipos de letra personalizados instalados." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 tipo de letra adicionado" msgstr[1] "%s tipos de letra adicionados" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -408,7 +408,7 @@ msgstr "" "carregar tipos de letra com os seguintes formatos: **TTF, OTF e WOFF** " "(apenas um será necessário)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -417,11 +417,11 @@ msgstr "" "serviço do Penpot](%s). Podes também ler mais sobre [licenciamento de " "fontes](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Carregar tudo" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -431,81 +431,53 @@ msgstr "" "além disso, recomendamos o uso do [Transfonter](https://transfonter.org/) " "para gerar fontes web e corrigir erros. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importar ficheiros Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Oops! Não conseguimos importar este ficheiro" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 ficheiro foi importado com sucesso." msgstr[1] "%s ficheiros foram importados com sucesso." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Alguns ficheiros continham objetos inválidos que foram removidos." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Processando cores" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Processando componentes" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Processando media" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Processando página: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Processando tipografias" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "A carregar dados para o servidor (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "A carregar ficheiro: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Convidar para a equipa" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Sair da equipa" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliotecas e Templates" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Aqui estão algumas Bibliotecas e templates que podes adicionar ao teu " "projeto" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Explora mais e sabe como contribuir" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "" "Ocorreu um problema com a importação do template. O template não foi " "importado." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliotecas" @@ -513,7 +485,7 @@ msgstr "Bibliotecas" msgid "dashboard.loading-files" msgstr "a carregar os teus ficheiros …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "a carregar as tuas fontes …" @@ -533,39 +505,39 @@ msgstr "Mover %s ficheiros para" msgid "dashboard.move-to-other-team" msgstr "Mover para outra equipa" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Novo Ficheiro" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Novo Ficheiro" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Novo projeto" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Novo Projeto" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Não há resultados para \"%s\"" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Projetos afixados aparecerão aqui" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notificações" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "O teu endereço de e-mail foi atualizado com sucesso" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "O teu endereço de e-mail foi verificado com sucesso" @@ -577,11 +549,11 @@ msgstr "Definições de notificação atualizadas" msgid "dashboard.notifications.password-saved" msgstr "Palavra-passe guardada com sucesso!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Ver notificações" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s membros" @@ -589,7 +561,7 @@ msgstr "%s membros" msgid "dashboard.open-in-new-tab" msgstr "Abrir ficheiro num novo separador" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opções" @@ -609,7 +581,7 @@ msgstr "Passaste a ser editor(a) nesta equipa." msgid "dashboard.permissions-change.owner" msgstr "Passaste a ser dono(a) nesta equipa." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Afixar/Desafixar" @@ -625,11 +597,11 @@ msgstr "Não foi possível analisar as definições do plugin" msgid "dashboard.plugins.try-plugin" msgstr "Experimentar o plugin: " -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projetos" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Queres remover a tua conta?" @@ -637,23 +609,23 @@ msgstr "Queres remover a tua conta?" msgid "dashboard.removed-from-team" msgstr "Já não pertences à equipa “%s“." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Guardar definições" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Pesquisar…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Pesquisar por \"%s\"…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Selecionar idioma da Interface" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Selecionar tema" @@ -709,11 +681,11 @@ msgstr "Atualizar definições" msgid "dashboard.settings.notifications.title" msgstr "Notificações" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Mostrar todos os ficheiros" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Histórico de versões" @@ -737,7 +709,7 @@ msgstr[1] "Os teus ficheiros foram duplicados com sucesso" msgid "dashboard.success-duplicate-project" msgstr "O teu projeto foi duplicado com sucesso" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "O teu ficheiro foi movido com sucesso" @@ -749,90 +721,90 @@ msgstr "Os teus ficheiros foram movidos com sucesso" msgid "dashboard.success-move-project" msgstr "O teu projeto foi movido com sucesso" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Informação da equipa" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Membros da equipa" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Projetos da equipa" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Adicionar ao teu projeto" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema da interface" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Resultados da pesquisa" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Escreve para pesquisar resultados" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Cancelar publicação da Biblioteca" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Atualizar definições" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Ativo" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Quando este webhook for ativado serão enviados detalhes do evento" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Só podes apagar ou modificar webhooks que criaste." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Tipo de conteúdo" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Criar webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook criado com sucesso." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Os webhooks são uma forma simples de permitir a outros sites e aplicações " "serem notificados quando acontecem certas ações no Penpot. Enviaremos um " "pedido POST para cada um dos URLs que forneceres." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Clica no botão \"Criar webhook\" para adicionar um." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Ainda não há nenhum webhook criado." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "A tua conta" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-mail" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "O teu nome" @@ -848,11 +820,11 @@ msgstr "Atenção" msgid "ds.component-subtitle" msgstr "Componentes para atualizar:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Permitir" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Cancelar" @@ -872,15 +844,15 @@ msgstr "O provedor de autenticação não é permitido neste perfil" msgid "errors.auth-provider-not-configured" msgstr "Provedor de autenticação não configurado." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Parece que não estás autenticado ou a sessão expirou." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "A fonte %s não pôde ser carregada" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "As fontes %s não puderam ser carregadas" @@ -892,15 +864,15 @@ msgstr "Não foi possível carregar o ficheiro." msgid "errors.character-limit-exceeded" msgstr "Limite de caracteres excedido" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "O teu browser não pode fazer esta operação" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E-mail já utilizado" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-mail já validado." @@ -908,40 +880,40 @@ msgstr "E-mail já validado." msgid "errors.email-as-password" msgstr "Não podes utilizar o teu e-mail como palavra-passe" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domínio não permitido" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "O e-mail «%s» tem muitos relatórios de rejeição permanentes." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "O e-mail «%s» foi denunciado como spam ou devolvido permanentemente." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Parece que estás a abrir um ficheiro que tem a funcionalidade '%s' ativada, " "mas a versão do teu Penpot não a suporta, ou está desativada." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "A funcionalidade '%s' não é suportada." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Campo vazio" -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Parece que existem discrepâncias entre as funcionalidades ativadas e as " "funcionalidades do ficheiro que estás a tentar abrir. Será necessário " "aplicar migrações para '%s' antes de poder abrir o ficheiro." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Ocorreu algo inesperado." @@ -957,7 +929,7 @@ msgstr "Dados inválidos" msgid "errors.invalid-email" msgstr "Por favor introduz um email válido" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "O e-mail de confirmação deve combinar" @@ -981,7 +953,7 @@ msgstr "Este convite pode ter sido cancelado ou expirado." msgid "errors.ldap-disabled" msgstr "Autenticação LDAP está desativada." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Alcançou o máximo da quota '%s'. Contacte o suporte técnico." @@ -997,13 +969,13 @@ msgstr "Parece que o conteúdo da imagem não corresponde à extensão do fichei msgid "errors.media-type-not-allowed" msgstr "Parece que esta não é uma imagem válida." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "O perfil que estás a convidar tem e-mails silenciados (relatórios de spam " "ou devoluções altas)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migração em progresso" @@ -1015,7 +987,7 @@ msgstr "A palavra-passe de confirmação tem de corresponder" msgid "errors.password-too-short" msgstr "A palavra-passe deverá conter no mínimo 8 caracteres" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Dados inválidos na área de transferência" @@ -1023,7 +995,7 @@ msgstr "Dados inválidos na área de transferência" msgid "errors.profile-blocked" msgstr "O perfil está bloqueado" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "O teu perfil tem e-mails silenciados (relatórios de spam ou devoluções " @@ -1033,19 +1005,19 @@ msgstr "" msgid "errors.registration-disabled" msgstr "A criação de contas está atualmente desativada." -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Funcionalidade incompatível '%s' detetada" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "Membros insuficientes para deixar a equipa, provavelmente queres eliminá-la." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "O membro que tentas atribuir não existe." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "O proprietário não pode sair da equipa, deverás retribuir a função de " @@ -1055,39 +1027,39 @@ msgstr "" msgid "errors.unexpected-error" msgstr "Ocorreu um erro inesperado." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Token desconhecido" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "O ficheiro tem um número de versão incompatível" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Erro de conexão, não foi possível alcançar o URL" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "O URL não passou na validação." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Último envio sem sucesso." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Erro na validação SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Timeout" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Erro inesperado ao validar" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Estado inesperado %s" @@ -1103,13 +1075,13 @@ msgstr "Palavra-passe antiga está incorreta" msgid "feedback.description" msgstr "Descrição" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Estamos felizes em ter-te aqui. Se precisares de ajuda, por favor pesquisa " "antes de publicar." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Comunidade Penpot" @@ -1124,11 +1096,11 @@ msgstr "" "uma ideia, ou uma dúvida. Um membro da nossa equipa tentará responder o " "mais rápido possível." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Aqui para ajudar com as tuas dúvidas técnicas." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Conta de suporte no X" @@ -1160,7 +1132,7 @@ msgstr "Descarregar imagem original" msgid "inspect.attributes.shadow" msgstr "Sombra" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Tamanho e posição" @@ -1168,39 +1140,27 @@ msgstr "Tamanho e posição" msgid "inspect.attributes.stroke" msgstr "Traço" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centro" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Interior" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Exterior" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografia" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Família da Fonte" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Tamanho da Fonte" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Estilo da Fonte" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Espaço entre caracteres" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Altura da Linha" @@ -1212,11 +1172,11 @@ msgstr "Nenhum" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Sublinhado" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Capitalização de Título" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Minúsculas" @@ -1228,7 +1188,7 @@ msgstr "Nenhum" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Sem atribuição" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Maiúsculas" @@ -1294,43 +1254,43 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Texto" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Marcar todas como lidas" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Atalhos" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Aceitar" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Ativo" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Adicionar" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Adicionar fonte personalizada" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "A adicionar..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Administração" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Todos" @@ -1338,29 +1298,29 @@ msgstr "Todos" msgid "labels.and" msgstr "e" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Voltar" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Parece que tens de aguardar um pouco e tentar novamente; estamos a realizar " "pequenas manutenções nos nossos servidores." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Error de Servidor (Bad Gateway)" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Cancelar" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Fechar" @@ -1368,7 +1328,7 @@ msgstr "Fechar" msgid "labels.code" msgstr "Código" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Colapsar" @@ -1376,11 +1336,11 @@ msgstr "Colapsar" msgid "labels.comment" msgstr "Comentário" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Comentários" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Comunidade" @@ -1388,11 +1348,11 @@ msgstr "Comunidade" msgid "labels.confirm-password" msgstr "Confirmar palavra-passe" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continuar" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Podes continuar com uma conta Penpot" @@ -1400,27 +1360,27 @@ msgstr "Podes continuar com uma conta Penpot" msgid "labels.copy-color" msgstr "Copiar cor" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Copiar link" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Criar" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Criar equipa nova" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Introduz o nome da nova equipa" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Painel" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Eliminar" @@ -1432,7 +1392,7 @@ msgstr "Eliminar comentário" msgid "labels.delete-comment-thread" msgstr "Eliminar tópico" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Eliminar convite" @@ -1440,31 +1400,31 @@ msgstr "Eliminar convite" msgid "labels.delete-multi-files" msgstr "Eliminar %s ficheiros" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Desenvolvimento" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Descartar" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Rascunhos" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Editar" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Editor" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Expirado" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exportar" @@ -1476,55 +1436,55 @@ msgstr "Feedback desativado" msgid "labels.feedback-sent" msgstr "Feedback enviado" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Família da Fonte" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Variantes" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fontes" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Repositório Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Dá feedback" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Voltar atrás" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Design gráfico" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Centro de Ajuda" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Ocultar" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Ocultar comentários resolvidos" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importar" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inativo" @@ -1532,35 +1492,35 @@ msgstr "Inativo" msgid "labels.info" msgstr "Informação" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Fontes instaladas" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Erro interno" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Convites" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Idioma" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Bibliotecas e Templates" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "A carregar…" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Iniciar sessão ou registar" @@ -1568,19 +1528,19 @@ msgstr "Iniciar sessão ou registar" msgid "labels.login" msgstr "Iniciar sessão" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Terminar sessão" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Membro" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Membros" @@ -1588,39 +1548,39 @@ msgstr "Membros" msgid "labels.new-password" msgstr "Palavra-passe nova" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Seguinte" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Não tens notificações de comentários pendentes." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Não há convites." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Ups!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notificações" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 ficheiro" msgstr[1] "%s ficheiros" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 prancheta" msgstr[1] "%s pranchetas" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 projeto" @@ -1630,67 +1590,67 @@ msgstr[1] "%s projetos" msgid "labels.old-password" msgstr "Palavra-passe antiga" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Opções" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Outra (especificar)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Outra" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Proprietário" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Palavra-passe" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Pendente" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Anterior" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Design de produtos ou UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Product Management" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Perfil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projetos" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Notas de versões" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Remover" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Remover membro" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Renomear" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Renomear equipa" @@ -1714,7 +1674,7 @@ msgstr "nova resposta" msgid "labels.reply.thread" msgstr "Responder" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Reenviar convite" @@ -1722,99 +1682,99 @@ msgstr "Reenviar convite" msgid "labels.restore" msgstr "Restaurar" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Tentar novamente" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Cargo" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Salvar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Pesquisar" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Pesquisar font" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Escolhe uma opção" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Enviar" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Enviando…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Estamos numa manutenção programada dos nossos sistemas." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Serviço Indisponível" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Definições" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Partilhar" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibliotecas" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Mostrar todos os comentários" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Mostrar lista de comentários" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Mostrar apenas as tuas menções" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Mostrar apenas os teus comentários" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Começar" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Estado" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Estudante ou professor(a)" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Temas" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutoriais" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Erro desconhecido" @@ -1822,23 +1782,23 @@ msgstr "Erro desconhecido" msgid "labels.unpublish-multi-files" msgstr "Despublicar %s ficheiros" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Atualizar" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Atualizar equipa" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Carregar" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Carregar fontes personalizadas" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Carregando…" @@ -1846,11 +1806,11 @@ msgstr "Carregando…" msgid "labels.view-only" msgstr "Somente leitura" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Visualizador" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -1858,11 +1818,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Escrever novo comentário" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(tu)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "A tua conta" @@ -1992,61 +1952,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Adicionar \"%s\" como Biblioteca Partilhada" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Grande deslocamento" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Verificar o novo e-mail" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Vamos enviar um e‑mail para o teu endereço atual \"%s\" para verificar a " "tua identidade." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Novo e-mail" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Alterar e-mail" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Altera o teu e-mail" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Criar webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Criar webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL do payload" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Cancelar e manter a minha conta" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Sim, elimina a minha conta" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Ao remover a tua conta vais perder todos os projetos e ficheiros." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Tens a certeza de que queres eliminar a tua conta?" @@ -2096,31 +2056,31 @@ msgstr "Tens a certeza de que pretendes eliminar %s ficheiros?" msgid "modals.delete-file-multi-confirm.title" msgstr "Eliminando %s ficheiros" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Tens a certeza de que pretendes eliminar este estilo de fonte? Não " "carregará se estiver a ser utilizado num ficheiro." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Eliminando estilo de fonte" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Tens a certeza de que pretendes eliminar esta fonte? Não carregará se " "estiver a ser utilizada num ficheiro." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Eliminando fonte" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Tens a certeza de que pretendes eliminar esta página?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Eliminar página" @@ -2136,31 +2096,31 @@ msgstr "Tens a certeza de que pretendes eliminar este projeto?" msgid "modals.delete-project-confirm.title" msgstr "Eliminar projeto" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Eliminar ficheiro" msgstr[1] "Eliminar ficheiros" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Não está ativa em nenhum ficheiro." msgstr[1] "Não estão ativas em nenhum ficheiro." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Esta biblioteca está ativa aqui: " msgstr[1] "Estas bibliotecas estão ativas aqui: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Tens a certeza de que pretendes eliminar este ficheiro?" msgstr[1] "Tens a certeza de que pretendes eliminar estes ficheiros?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Eliminando ficheiro" @@ -2180,19 +2140,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Eliminando equipa" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Eliminar membro" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Tens a certeza de que pretendes eliminar este membro da equipa?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Eliminar membro da equipa" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -2202,167 +2162,167 @@ msgstr[1] "" "Os recursos que estiverem a ser usados nestes ficheiros continuarão " "presentes (nenhum design será afetado)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Apagar webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Tens a certeza que queres apagar este webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "A apagar webhook" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Editar webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Editar webhook" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Enviar convite" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-mails, separados por vírgulas" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Alguns endereços de email pertencem a membros atuais da equipa. Não serão " "enviados convites para estes endereços." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Podes convidar membros para a equipa para que estes possam ter acesso a " "este ficheiro e todos os ficheiros da equipa." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Convidar membros para a equipa" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Como és o único membro desta equipa, a equipa será eliminado juntamente com " "os projetos e ficheiros." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Tens a certeza de que pretendes sair da equipa %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Não pode deixar a equipa se não houver outro membro a promover a dono. Pode " "querer eliminar a equipa." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "És o proprietário desta equipa. Por favor seleciona outro membro para " "promover para proprietário antes de saíres." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promover e sair" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Seleciona um membro para promover" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Antes de saires" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Sair da equipa" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Tens a certeza de que pretendes sair desta equipa?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Saindo da equipa" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Mover" msgstr[1] "Mover" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Tens a certeza que queres mover esta biblioteca?" msgstr[1] "Tens a certeza que queres mover estas bibliotecas?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Mover biblioteca" msgstr[1] "Mover bibliotecas" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Montante de deslocamento" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Transferir propriedade" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Se transferires a propriedade, vais alterar a tua função para " "Administrador, perdendo algumas permissões sobre esta equipa. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "És o proprietário atual desta equipa. Tens a certeza de que pretendes " "promover %s a proprietário da equipa?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Novo proprietário de equipa" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publicar" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "A tua biblioteca está vazia. Tens a certeza que queres publicá-la?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publicar biblioteca vazia" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Pequeno deslocamento" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Cancelar publicação" msgstr[1] "Cancelar publicações" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Tens a certeza de que queres cancelar a publicação desta biblioteca?" msgstr[1] "Tens a certeza de que queres cancelar a publicação destas bibliotecas?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Cancelar publicação da biblioteca" @@ -2386,11 +2346,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Atualizar componente numa biblioteca partilhada" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Esta página não existe" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Erro 404" @@ -2408,11 +2368,11 @@ msgstr "Enviámos um email de recuperação para" msgid "not-found.login.sent-recovery-check" msgstr "Verifica o teu email e clica no link para criares uma nova palavra-passe." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Inscreve-te gratuitamente" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "E começa a usar o Penpot em poucos segundos!" @@ -2420,41 +2380,41 @@ msgstr "E começa a usar o Penpot em poucos segundos!" msgid "not-found.made-with-love" msgstr "Feito com AMOR e código aberto" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Já solicitaste acesso a este ficheiro." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Já solicitaste acesso a este ficheiro, ou outros ficheiros, ou projetos " "desta equipa." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Já solicitaste acesso a este projeto." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "PEDIR ACESSO" -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "O teu pedido foi enviado com sucesso!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Não tens acesso a este ficheiro." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Ir para o teu Penpot" -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Ficheiro do Penpot" -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJETO" @@ -2468,25 +2428,25 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "Está disponível uma nova versão." -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Convite enviado com sucesso" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Link de convite copiado" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Não podes eliminar o teu perfil. Atribui um proprietário às tuas equipas " "antes de avançar." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Perfil salvo com sucesso!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "E-mail de verificação enviado para %s. Verifica o teu e-mail!" @@ -2516,78 +2476,78 @@ msgstr "Depois de nomeares a tua equipa, poderás convidar pessoas para entrar." msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Criar equipa" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Convida membros" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Convidar com a função:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Vamos começar!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Gerar código a partir de designs" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Criar sistemas de design" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototipagem" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Desenhar o UI/UX de uma aplicação" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Para que fim vais usar o Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Conta-nos um pouco sobre ti para que possamos fazer com que o Penpot se " "adapte a ti. As tuas respostas ajudar-nos-ão a priorizar novas " "funcionalidades e indicar-te o melhor caminho para começar." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Ajuda-nos a conhecer-te" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Qual destas ferramentas usas mais?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Que tipo de trabalho fazes?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Fala-nos do teu trabalho" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Por onde é que gostarias de começar?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Educação" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Uso pessoal" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Trabalho" @@ -2637,11 +2597,11 @@ msgstr "Começa a desenhar" msgid "profile.recovery.go-to-login" msgstr "Ir para login" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Desacoplar" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Misturado" @@ -2653,35 +2613,35 @@ msgstr "Remover cor" msgid "settings.select-this-color" msgstr "Selecionar itens que usem este estilo" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Comentários" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Alinhar abaixo" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Alinhar ao centro horizontalmente" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Alinhar à esquerda" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Alinhar à direita" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Alinhar topo" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Alinhar ao centro verticalmente" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Criar prancheta a partir da seleção" @@ -2689,47 +2649,47 @@ msgstr "Criar prancheta a partir da seleção" msgid "shortcuts.bold" msgstr "Alternar negrito" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Diferença booleana" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Exclusão Booleana" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Intersecção Booleana" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "União Booleana" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Enviar para trás" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Mover para trás" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Mover para a frente" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Enviar para a frente" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Limpar historial" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copiar" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Copiar ligação para a área de transferência" @@ -2737,35 +2697,35 @@ msgstr "Copiar ligação para a área de transferência" msgid "shortcuts.create-new-project" msgstr "Criar novo" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Recortar" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Menos zoom" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Eliminar" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Eliminar nó" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Desanexar componente" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Curva" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipse" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Quadro" @@ -2773,39 +2733,39 @@ msgstr "Quadro" msgid "shortcuts.draw-nodes" msgstr "Desenhar caminho" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Caminho" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rectângulo" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Texto" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplicar" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Cancelar" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Exportar formas" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Ajustar tudo à janela" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Virar horizontalmente" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Virar verticalmente" @@ -2829,23 +2789,23 @@ msgstr "Ir para bibliotecas partilhadas" msgid "shortcuts.go-to-search" msgstr "Pesquisa" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Grupo" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribuir horizontalmente" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Mostrar/ocultar interface" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Mais zoom" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Inserir imagem" @@ -2853,7 +2813,7 @@ msgstr "Inserir imagem" msgid "shortcuts.italic" msgstr "Alternar itálico" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Unir nós" @@ -2861,39 +2821,39 @@ msgstr "Unir nós" msgid "shortcuts.line-through" msgstr "Alternar texto rasurado" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Fazer canto" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Fazer curva" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Máscara" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Fundir nós" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Mover" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Mover para baixo rápido" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Mover para a esquerda rápido" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Mover para a direita rápido" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Mover para cima rápido" @@ -2901,325 +2861,325 @@ msgstr "Mover para cima rápido" msgid "shortcuts.move-nodes" msgstr "Mover nó" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Mover para baixo" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Mover para a esquerda" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Mover para a direita" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Mover para cima" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Próximo quadro" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Nenhum atalho encontrado" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Definir opacidade a 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Definir opacidade a 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Definir opacidade a 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Definir opacidade a 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Definir opacidade a 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Definir opacidade a 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Definir opacidade a 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Definir opacidade a 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Definir opacidade a 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Definir opacidade a 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Selector de cores" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Ir para secção de comentários" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Ir para painel" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Ir para secção de inspeção" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Ir para secção de interações" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Ir para secção de interações" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Ir para a área de trabalho" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " ou " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Colar" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Quadro anterior" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Refazer" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Renomear" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Redefinir zoom" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Redimensionar" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Básicos" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Painel" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Visualizador" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Área de Trabalho" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Selecionar tudo" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Selecionar camada seguinte" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Selecionar camada pai" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Selecionar camada anterior" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Separar nós" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Mostrar/ocultar grade de píxeis" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Mostrar/ocultar atalhos" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Ajustar aos nós" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Ajustar à grade de píxeis" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Iniciar edição" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Alinhamento" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Editar" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Genérico" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Menu Principal" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modificar camadas" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navegação" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navegação" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navegação" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Painéis" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Caminhos" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Formas" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Textos" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Ferramentas" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Definir imagem destaque" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Atalhos do teclado" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Alternar alinhamento dinâmico" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Alternar recursos" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Alternar selector de cores" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Alternar modo de foco" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Alternar tela cheia" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Mostrar / Ocultar guias" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Alternar histórico" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Alternar camadas" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Adicionar / Remover layout flex" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Adicionar/remover layout em grelha" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Bloquear selecionado" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Bloquear proporções" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Mostrar / Ocultar réguas" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Ajustar às guias" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Ajustar às guias de régua" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Alternar paleta de texto" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Mudar tema" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Mostrar / Ocultar" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Alternar estilo de zoom" @@ -3227,63 +3187,63 @@ msgstr "Alternar estilo de zoom" msgid "shortcuts.underline" msgstr "Alternar sublinhado" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Desfazer" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Desagrupar" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Retirar máscara" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribuir verticalmente" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Reduzir zoom na lupa" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Aumentar zoom na lupa" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zoom para selecionados" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Provedores de fonte - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Fontes - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projetos - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Pesquisa - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Bibliotecas partilhadas - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Liberdade de Design para Equipas" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Dá feedback - Penpot" @@ -3291,7 +3251,7 @@ msgstr "Dá feedback - Penpot" msgid "title.settings.notifications" msgstr "Notificações - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Definições - Penpot" @@ -3299,27 +3259,27 @@ msgstr "Definições - Penpot" msgid "title.settings.password" msgstr "Palavra-passe - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Perfil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Convites - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Membros - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Definições - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Modo visualizador - Penpot" @@ -3327,99 +3287,99 @@ msgstr "%s - Modo visualizador - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Não foram encontrados quadros na página." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Quadro não encontrado." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Comentários (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Não mostrar interações" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Tela Cheia" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspecionar (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interações" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interações (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Copiar link" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Mostrar interações" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Mostrar interações com click" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Mapa do site" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Último envio com sucesso." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Alinhar horizontal ao centro (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribuir espaçamento horizontal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Alinhar à esquerda (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Alinhar à direita (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Alinhar abaixo (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Alinhar ao centro verticalmente (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribuir espaçamento vertical (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Alinhar ao topo (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Adicionar biblioteca" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Todos os recursos" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Cores" @@ -3427,7 +3387,7 @@ msgstr "Cores" msgid "workspace.assets.colors.add-color" msgstr "Adicionar cor" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Componentes" @@ -3439,17 +3399,17 @@ msgstr "Adicionar componente" msgid "workspace.assets.create-group" msgstr "Criar grupo" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Os teus itens serão nomeados automaticamente como \"nome do grupo / nome do " "item\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Eliminar" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplicar" @@ -3457,27 +3417,27 @@ msgstr "Duplicar" msgid "workspace.assets.duplicate-main" msgstr "Duplicar principal" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Editar" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtrar" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Agrupar" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nome do grupo" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "biblioteca local" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Recursos não encontrados" @@ -3485,7 +3445,7 @@ msgstr "Recursos não encontrados" msgid "workspace.assets.open-library" msgstr "Abrir ficheiro de biblioteca" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Renomear" @@ -3493,11 +3453,11 @@ msgstr "Renomear" msgid "workspace.assets.rename-group" msgstr "Renomear grupo" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Pesquisar recursos" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Biblioteca partilhada" @@ -3507,43 +3467,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 componente" msgstr[1] "%s componentes" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Ordenar" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Tipografias" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Adicionar tipografia" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Tamanho" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Estilo da Fonte" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Ir para ficheiro da biblioteca de estilos para editar" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Espaço entre letras" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Altura da Linha" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformar Texto" @@ -3551,67 +3511,67 @@ msgstr "Transformar Texto" msgid "workspace.assets.ungroup" msgstr "Desagrupar" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Criar área" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Criar prancheta" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Unir células" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Adicionar 1 coluna à direita" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Adicionar 1 coluna à esquerda" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Eliminar coluna" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Apagar coluna e conteúdos" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Duplicar coluna" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Adicionar 1 fila abaixo" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Adicionar 1 fila acima" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Apagar fila" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Apagar fila e conteúdos" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Duplicar fila" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Modo de foco" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Foco desativado" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Foco ativo" @@ -3623,191 +3583,191 @@ msgstr "Gradiente linear" msgid "workspace.gradients.radial" msgstr "Gradiente radial" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Desativar alinhamento dinâmico" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Desativar escala proporcional" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Desativar ajuste às guias" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Desativar ajuste ao pixel" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Desativar ajuste às guias de régua" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Ativar alinhamento dinâmico" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Ativar escala proporcional" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Ajustar às guias" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Ativar ajuste ao pixel" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Ajustar às guias de régua" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Ocultar nome das pranchetas" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Ocultar guias" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Ocultar paleta de cor" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Ocultar grade de píxeis" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Ocultar réguas" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Ocultar paleta de texto" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Editar" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Ficheiro" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Ajuda e Informações" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferências" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Visualização" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Refazer" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Selecionar tudo" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Mostrar nomes das pranchetas" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Mostrar guias" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Mostrar paleta de cor" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Mostrar grade de píxeis" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Mostrar réguas" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Mostrar paleta de texto" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Alterar para tema escuro" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Mudar para tema claro" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Desfazer" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Ampliar em 100%" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Erro ao salvar" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Guardado" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "A salvar" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Partilhar" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Modo de visualização (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoom" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Ajustar para preencher" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Ajustar para encaixar" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Ajustar para mostrar tudo" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Tela cheia" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Aumentar para seleção" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Editar grelha" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Sair" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Mostrar 4 opções de preenchimento de lados" @@ -3823,23 +3783,23 @@ msgstr "Feito" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Localizar" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Localizar layout em grelha" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Ainda não existem estilos de cor na tua biblioteca" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Ainda não existem tipografias na tua biblioteca" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Biblioteca de ficheiros" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Cores recentes" @@ -3851,59 +3811,59 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Guardar estilo de cor" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Biblioteca de ficheiros" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTECAS NESTE FICHEIRO" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTECAS" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "ATUALIZAÇÕES DE BIBLIOTECAS" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "A carregar…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Não há bibliotecas partilhadas que precisem de atualização" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Não há resultados para \"%s\"" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Pesquisar bibliotecas partilhadas" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "BIBLIOTECAS PARTILHADAS" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Conectar biblioteca" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Desconectar biblioteca" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Atualizar" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "ver todas as alterações" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "ATUALIZAÇÕES" @@ -3931,39 +3891,39 @@ msgstr "Desfoque de seleção" msgid "workspace.options.canvas-background" msgstr "Plano de fundo" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Recorte do conteúdo" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Componente" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Nota" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Cópia" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Criar uma nota" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Editar uma nota" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Principal" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Trocar de componente" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Ainda não existem recursos nesta biblioteca" @@ -4007,11 +3967,11 @@ msgstr "Topo" msgid "workspace.options.constraints.topbottom" msgstr "Topo e Abaixo" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exportar" @@ -4019,33 +3979,33 @@ msgstr "Exportar" msgid "workspace.options.export-multiple" msgstr "Exportar seleção" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Exportar 1 elemento" msgstr[1] "Exportar %s elementos" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Eliminar exportação" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Sufixo" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Exportação completa" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "A exportar…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "A exportação falhou" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Exportação inesperadamente lenta" @@ -4061,7 +4021,7 @@ msgstr "Adicionar cor de preenchimento" msgid "workspace.options.fill.remove-fill" msgstr "Remover preenchimento" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Redimensionar para ajustar ao conteúdo" @@ -4085,67 +4045,67 @@ msgstr "Início de fluxo" msgid "workspace.options.grid.auto" msgstr "Automático" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Colunas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Cor" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Espaço" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Altura" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margem" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Definir como padrão" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Abaixo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Esquerda" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Direita" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Esticar" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Topo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Utilizar padrão" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Largura" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Linhas" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Quadrado" @@ -4157,7 +4117,7 @@ msgstr "Preenchimento de grupo" msgid "workspace.options.group-stroke" msgstr "Traço de grupo" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Adicionar guia" @@ -4165,15 +4125,15 @@ msgstr "Adicionar guia" msgid "workspace.options.guides.remove-guide" msgstr "Remover guia" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Guias" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Altura" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspecionar" @@ -4389,99 +4349,99 @@ msgstr "Interações" msgid "workspace.options.interactions.add-interaction" msgstr "Adicionar interação" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Cor" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Superexposição de cor" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Subexposição Linear (Adicionar)" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Escurecer" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Diferença" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Exclusão" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Luz direta" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Matiz" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Clarear" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminosidade" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Multiplicação" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Sobrepor" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturação" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Tela" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Luz indireta" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Altura.Máx" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Largura.Máx" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Altura.Min" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Largura.Min" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Mais cores" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Mais bibliotecas de cor" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacidade" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Protótipo" @@ -4505,19 +4465,19 @@ msgstr "Superior esquerdo" msgid "workspace.options.radius-top-right" msgstr "Superior direito" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Recente" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Repetir" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotação" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Pesquisar fonte" @@ -4527,7 +4487,7 @@ msgstr "" "Selecionar a forma, prancheta ou grupo para arrastar uma conexão para outra " "prancheta." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Cores selecionadas" @@ -4591,47 +4551,47 @@ msgstr "Seleção de sombras" msgid "workspace.options.show-fill-on-export" msgstr "Mostrar na exportação" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Mostrar no modo de visualização" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Tamanho" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Tamanho pré-definido" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Círculo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamante" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Seta" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Nenhum" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Arredondado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Quadrado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Retângulo" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triângulo" @@ -4639,7 +4599,7 @@ msgstr "Triângulo" msgid "workspace.options.stroke-color" msgstr "Cor do traço" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Largura do traço" @@ -4647,35 +4607,35 @@ msgstr "Largura do traço" msgid "workspace.options.stroke.add-stroke" msgstr "Adicionar cor de traço" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centro" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Tracejado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Pontilhado" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Interior" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Misto" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Exterior" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Remover traço" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Sólido" @@ -4711,11 +4671,11 @@ msgstr "Largura automática" msgid "workspace.options.text-options.grow-fixed" msgstr "Fixo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Espaço entre letras" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Altura entre linhas" @@ -4761,152 +4721,152 @@ msgstr "" "Utiliza o botão de play no cabeçalho para iniciar a visualização do " "protótipo." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Largura" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Eixo X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Desenhar nós (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Unir nós (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Em cantos (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Em curvas (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Fundir nós (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Mover nós (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Separar nós (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Ajustar nós (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Abrir" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Descobre [mais plugins](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Sem plugins instalados" -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Precisas de ser um(a) editor(a) para usar este plugin" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "O plugin não existe ou o URL está incorreto." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Instalar" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Plugins instalados" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Gestor de plugins" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Plugins" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "ATUALIZAR ESTE PLUGIN" -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Ler os teus comentários e respostas." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Ler e modificar os teus comentários, e enviar respostas em teu nome." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Ler informações sobre as tuas bibliotecas e recursos." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Ler e modificar as tuas bibliotecas e recursos." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "O PLUGIN '%s' SOLICITA ACESSO A:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Ler as informações de perfil do utilizador atual." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Lista de plugins" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Remover plugin" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Introduz um URL de plugin" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Plugins" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "AGORA NÃO" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "O PLUGIN '%s' FOI INSTALADO PARA O TEU UTILIZADOR!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "TESTAR PLUGIN" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Adicionar layout flex" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Adicionar layout em grelha" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Adicionar layout" @@ -4918,7 +4878,7 @@ msgstr "Enviar para trás" msgid "workspace.shape.menu.backward" msgstr "Mover para trás" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Copiar" @@ -4946,27 +4906,27 @@ msgstr "Copiar como texto" msgid "workspace.shape.menu.create-annotation" msgstr "Criar nota" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Seleção para a prancheta" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Criar componente" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Criar múltiplos componentes" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Recortar" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Eliminar" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Eliminar início de fluxo" @@ -4978,7 +4938,7 @@ msgstr "Desprender instância" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Desprender instâncias" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Diferença" @@ -4986,15 +4946,15 @@ msgstr "Diferença" msgid "workspace.shape.menu.duplicate" msgstr "Duplicar" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Editar" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Excluir" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Achatar" @@ -5006,7 +4966,7 @@ msgstr "Virar na horizontal" msgid "workspace.shape.menu.flip-vertical" msgstr "Virar na vertical" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Início de fluxo" @@ -5018,31 +4978,31 @@ msgstr "Mover para a frente" msgid "workspace.shape.menu.front" msgstr "Enviar para a frente" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Agrupar" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Ocultar" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Mostrar/Ocultar interface" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Interseção" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Bloquear" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Máscara" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Colar" @@ -5050,19 +5010,19 @@ msgstr "Colar" msgid "workspace.shape.menu.paste-props" msgstr "Colar propriedades" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Curvas" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Remover layout flex" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Remover layout em grelha" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Remover layout" @@ -5082,11 +5042,11 @@ msgstr "Restaurar componente principal" msgid "workspace.shape.menu.select-layer" msgstr "Selecionar camada" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Mostrar" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Mostrar recursos no painel" @@ -5102,19 +5062,19 @@ msgstr "Remover miniatura" msgid "workspace.shape.menu.thumbnail-set" msgstr "Definir como miniatura" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Desagrupar" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "União" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Desbloquear" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Retirar máscara" @@ -5130,47 +5090,47 @@ msgstr "Fechar barra lateral" msgid "workspace.sidebar.expand" msgstr "Expandir barra lateral" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Histórico" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Camadas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Componentes" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Pranchetas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Grupos" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Imagens" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Máscaras" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Pesquisar camadas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Formas" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Textos" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Importar atributos do SVG" @@ -5182,7 +5142,7 @@ msgstr "Páginas" msgid "workspace.sidebar.sitemap.add-page" msgstr "Adicionar página" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Mapa do site" @@ -5190,7 +5150,7 @@ msgstr "Mapa do site" msgid "workspace.tokens.active-themes" msgstr "%s temas ativos" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Aplicado a" @@ -5198,7 +5158,7 @@ msgstr "Aplicado a" msgid "workspace.tokens.back-to-themes" msgstr "Voltar à lista de temas" -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Cor" @@ -5230,7 +5190,7 @@ msgstr "Tamanho máx." msgid "workspace.tokens.min-size" msgstr "Tamanho mín." -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Valor original: %s" @@ -5254,7 +5214,7 @@ msgstr "Lista de temas" msgid "workspace.tokens.token-description" msgstr "Descrição" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nome" @@ -5266,7 +5226,7 @@ msgstr "Valor" msgid "workspace.tokens.tools" msgstr "Ferramentas" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "O valor não é válido" @@ -5274,55 +5234,55 @@ msgstr "O valor não é válido" msgid "workspace.toolbar.assets" msgstr "Recursos" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Paleta de cores (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Comentários (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Curvas (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elipse (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Prancheta (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Imagem (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Mover (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Caminho (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Plugins (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rectângulo (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Texto (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Tipografias (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Alternar barra de ferramentas" @@ -5335,7 +5295,7 @@ msgstr "Feito" msgid "workspace.top-bar.view-only" msgstr "**A inspecionar código** (Somente leitura)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Não há histórico de mudanças até agora" @@ -5475,19 +5435,19 @@ msgstr "recurso tipográfico" msgid "workspace.undo.entry.unknown" msgstr "Operação sobre %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Ignorar" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Mais informações" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Existem atualizações nas bibliotecas partilhadas" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Atualizar" diff --git a/frontend/translations/ro.po b/frontend/translations/ro.po index 2581de553f..6bfc49ace1 100644 --- a/frontend/translations/ro.po +++ b/frontend/translations/ro.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:18+0000\n" -"Last-Translator: AlexTECPlayz \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Romanian " "\n" "Language: ro\n" @@ -10,9 +10,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Ai deja un cont?" @@ -30,7 +30,7 @@ msgstr "" "Acesta este un serviciu DEMO, NU ÎL UTILIZA pentru lucrări reale, întrucât " "proiectele vor fi șterse periodic." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Ai uitat parola?" @@ -38,37 +38,37 @@ msgstr "Ai uitat parola?" msgid "auth.fullname" msgstr "Numele complet" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Intru în contul meu" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Intră în cont aici." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot este unealta gratuită cu cod sursă liber pentru colaborare în design " "și cod" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -90,7 +90,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "Un link de recuperare a parolei s-a trimis pe e-mail." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Te-ai alăturat echipei cu success" @@ -122,7 +122,7 @@ msgstr "Ai uitat parola?" msgid "auth.recovery-submit" msgstr "Schimbă parola" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Nu ai încă un cont?" @@ -136,7 +136,7 @@ msgstr "" msgid "auth.register-account-title" msgstr "Numele dvs." -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Creează un cont" @@ -151,7 +151,7 @@ msgstr "" "Sunt de acord cu [termenii și condițiile](%s) și [politica de " "confidențialitate](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Termenii serviciului" @@ -169,94 +169,94 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Nicio persoană găsită pentru @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publică" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Toți utilizatorii Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Sigur dorești să elimini acest link? Dacă o faci, nu mai este disponibil " "pentru nimeni" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(curent)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Distruge linkul" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Obține link" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Link copiat cu succes" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Gestionează permisiunile" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 pagină partajată" msgstr[1] "%s pagini partajate" msgstr[2] "%s de pagini partajate" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Poate comenta" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Poate inspecta codul" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Oricine cu linkul va avea acces" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Pagini partajate" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Linkul care poate fi partajat va apărea aici" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Doar membrii echipei" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Distribuie prototipuri" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Selectează Tot" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Anulează publicarea" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Managementul echipei" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot este destinat echipelor. Invită membrii să lucrați împreună la " "proiecte și fișiere" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Faceți echipă!" @@ -264,23 +264,23 @@ msgstr "Faceți echipă!" msgid "dashboard.add-file" msgstr "Adaugă filă" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Adaugă ca bibliotecă partajată" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Schimbă adresa de e-mail" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(copiază)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Creează o nouă echipă" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Fixează această versiune" @@ -288,11 +288,11 @@ msgstr "Fixează această versiune" msgid "dashboard.default-team-name" msgstr "Contul Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Șterge echipa" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Descarcă fișierul Penpot (.penpot)" @@ -304,19 +304,19 @@ msgstr "Duplicat" msgid "dashboard.duplicate-multi" msgstr "Duplică %s fișiere" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Odată ce un membru de proiect creează o ciornă, aceasta va fi afișată aici." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Nu ai încă ciorne." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Odată ce un membru de proiect creează un fișier, acesta va fi afișat aici." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Nu ai încă fișiere." @@ -368,70 +368,70 @@ msgstr "Începe să creezi lucruri uimitoare" msgid "dashboard.export-binary-multi" msgstr "Descarcă %s fișiere Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exportă table ca PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exportă ca PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s din %s elemente selectate" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exportă" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Poți adăuga setări de export la elemente din proprietățile designului (în " "partea de jos a barei laterale din dreapta)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Informații despre cum să setezi exporturile în Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Nu există elemente cu setări de export." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Exportă selecția" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Font șters" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Anulează tot" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Fonturile personalizate pe care le încarci vor apărea aici." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Nu ai încă fonturi personalizate." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Odată ce un membru de proiect încarcă un font personalizat. acesta va fi " "afișat aici." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 font adăugat" msgstr[1] "%s fonturi adăugate" msgstr[2] "%s de fonturi adăugate" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -440,7 +440,7 @@ msgstr "" "familie de font-uri**. Tipurile de fişiere acceptate: **TTF, OTF și WOFF** " "(se poate urca doar un singur tip)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -449,11 +449,11 @@ msgstr "" "și Condițiile Penpot](%s). De asemenea, îți recomandăm să citești și despre " "[licențierea fonturilor](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Încarcă toate" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -464,11 +464,11 @@ msgstr "" "utilizezi [Transfonter](https://transfonter.org/) pentru a genera fonturi " "web și a remedia erorile. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importă fișiere Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Hopa! Nu am putut importa acest fișier" @@ -476,26 +476,26 @@ msgstr "Hopa! Nu am putut importa acest fișier" msgid "dashboard.import.bad-url" msgstr "Importarea a eșuat. URL-ul șablonului este incorect" -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Nu toate fișierele au fost importate" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Următoarele fișiere au erori:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Fișierele cu erori nu vor fi încărcate." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 fișier a fost importat cu succes." msgstr[1] "% fișiere au fost importate cu succes." msgstr[2] "% de fișiere au fost importate cu succes." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Unele fișiere conțineau obiecte invalide care au fost eliminate." @@ -503,77 +503,49 @@ msgstr "Unele fișiere conțineau obiecte invalide care au fost eliminate." msgid "dashboard.import.no-perms" msgstr "Nu ai permisiunea pentru a importa în această echipă" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Procesarea culorilor" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Procesarea componentelor" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Procesarea fișierelor media" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Procesarea paginii: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Procesarea tipografiilor" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Încărcarea datelor pe server (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Încărcarea fișierului: %s" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Vei șterge invitațiile pentru:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Vei retrimite invitațiile pentru:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Șterge invitațiile" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Retrimite invitațiile" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Invită persoane" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Părăsește echipa" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Biblioteci & Șabloane" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Aici sunt unele dintre bibliotecile și modelele pe care le poți adăuga la " "proiectul tău" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Explorează mai multe dintre ele și află cum să contribui" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "A apărut o problemă la importarea șablonului. Șablonul nu a fost importat." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Biblioteci Partajate" @@ -581,7 +553,7 @@ msgstr "Biblioteci Partajate" msgid "dashboard.loading-files" msgstr "încărcarea fișierelor …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "se încarcă fonturile tale…" @@ -601,39 +573,39 @@ msgstr "Mută %s fișiere la" msgid "dashboard.move-to-other-team" msgstr "Mută la o altă echipă" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Fișier nou" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Fișer nou" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Proiect nou" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Proiect nou" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Nu există rezultate pentru “%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Proiectele fixate vor apărea aici" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notificări" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Adresa ta de email a fost actualizată cu success" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Adresa ta de email este confirmată" @@ -645,11 +617,11 @@ msgstr "Setările notificărilor au fost actualizate" msgid "dashboard.notifications.password-saved" msgstr "Parolă actualizată cu success!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Afișează notificările" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s membrii" @@ -657,15 +629,15 @@ msgstr "%s membrii" msgid "dashboard.open-in-new-tab" msgstr "Deschide fișier într-o pagină nouă" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Opțiuni" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Sortează după rol" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Sortează după status" @@ -689,7 +661,7 @@ msgstr "Deții acum această echipă." msgid "dashboard.permissions-change.viewer" msgstr "Ești acum un cititor în această echipă." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Prinde/Desprinde" @@ -705,11 +677,11 @@ msgstr "Nu s-a putut citi registrul extensiei" msgid "dashboard.plugins.try-plugin" msgstr "Încearcă extensia: " -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Proiecte" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Dorești să îți ștergi contul?" @@ -717,35 +689,35 @@ msgstr "Dorești să îți ștergi contul?" msgid "dashboard.removed-from-team" msgstr "Nu mai faci parte din echipa \"%s\"." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Salvează setările" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Căutare…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Căutare pentru “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Selectează limbajul interfeței" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Selectează o temă" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Întunecat (implicit)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Luminos" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Temă sistem" @@ -809,11 +781,11 @@ msgstr "Actualizează setările" msgid "dashboard.settings.notifications.title" msgstr "Notificări" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Afișează toate fișierele" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Istoric versiuni" @@ -839,7 +811,7 @@ msgstr[2] "Fișierele dumneavoastră au fost duplicate cu succes" msgid "dashboard.success-duplicate-project" msgstr "Proiectul s-a duplicat cu succes" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Fișierul a fost mutat cu succes" @@ -851,67 +823,67 @@ msgstr "Fișerele au fost mutate cu succes" msgid "dashboard.success-move-project" msgstr "Proiectul a fost mutat cu succes" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Informațiile echipei" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Membrii echipei" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Proiectele echipei" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Adaugă la proiectul tău" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Interfață temă" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Rezultatele căutării" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Scrie pentru a începe căutarea" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Anulează publicarea bibliotecii" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Actualizează setările" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Este activ" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Când acest hook este declanșat, se vor furniza detalii despre eveniment" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Poți șterge sau modifica doar webhook-urile create de tine." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Tip de conținut" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Creează webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook creat cu succes." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhook-urile sunt o modalitate simplă de a permite altor site-uri web și " @@ -919,23 +891,23 @@ msgstr "" "Penpot. Vom trimite o solicitare POST la fiecare dintre adresele URL pe " "care le furnizezi." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Apasă butonul \"Adaugă webhook\" pentru a adăuga unul." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Niciun webhook creat până acum." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Contul tău" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Email" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Numele tău" @@ -951,11 +923,11 @@ msgstr "Atenţie" msgid "ds.component-subtitle" msgstr "Componente de actualizat:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Permite" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Anulează" @@ -975,7 +947,7 @@ msgstr "Nici un token nu este aplicabil în seturile active sau temele." msgid "ds.inputs.numeric-input.no-matches" msgstr "Nu a fost găsit nimic." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Deschide lista de token-uri" @@ -987,15 +959,15 @@ msgstr "Furnizor de autentificare neautorizat pentru acest profil" msgid "errors.auth-provider-not-configured" msgstr "Furnizorul de autentificare nu este configurat." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Se pare că nu ești autentificat sau sesiunea a expirat." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Fontul %s nu a putut fi încărcat" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Fonturile %s nu au putut fi încărcate" @@ -1007,41 +979,41 @@ msgstr "Fișierul media nu s-a putut încărca." msgid "errors.character-limit-exceeded" msgstr "Limită de caractere depășită" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Bowser-ul tău nu permite clipboard" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "A apărut o eroare în urma acestui comentariu" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Ne pare rău! Acesta este un fișier vechi care folosește un tip de obiecte " "Penpot vechi și nu îl poți deschide." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "pentru a te putea ajuta." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Deși Penpot nu mai suportă acest tip de obiecte, poți" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "să ne contactezi" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Nu poți muta un set părinte în propria cale al unuia dintre copii." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Email deja trimis" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Adresa de email este deja validată." @@ -1053,29 +1025,29 @@ msgstr "Nu poți folosi e-mailul ca parolă" msgid "errors.email-does-not-match-invitation" msgstr "Adresa de e-mail nu corespunde invitației." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domeniu neautorizat" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Adresa de email «%s» are multe rapoarte permanente de respingere." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "E-mailul «%s» a fost raportat ca spam sau respins permanent." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Se pare că vrei să deschizi un fișier care are activată funcția \"%s\", dar " "versiunea curentă a Penpot nu o acceptă sau o are dezactivată." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Funcția \"%s\" nu este acceptată." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Necesită cel mult 1 caracter." @@ -1083,7 +1055,7 @@ msgstr "Necesită cel mult 1 caracter." msgid "errors.field-min-length" msgstr "Necesită cel puțin 1 caracter." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Câmp gol" @@ -1091,18 +1063,18 @@ msgstr "Câmp gol" msgid "errors.field-not-all-whitespace" msgstr "Numele necesită un alt caracter decât spațiu." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Se pare că există o discrepanță între caracteristicile activate și " "caracteristicile fișierului pe care încerci să îl deschizi. Migrații pentru " "'%s' trebuie să fie aplicate înainte ca fișierul să poată fi deschis." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "S-a întâmplat ceva în neregulă." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "A apărut o problemă cu web worker-ul." @@ -1118,7 +1090,7 @@ msgstr "Date invalide" msgid "errors.invalid-email" msgstr "Te rugăm să introduci un e-mail valid" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "E-mailul de confirmare trebuie să se potrivească" @@ -1142,11 +1114,11 @@ msgstr "Se poate ca această invitație să fie anulată sau expirată." msgid "errors.ldap-disabled" msgstr "Autentificarea cu LDAP este dezactivată." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Ai ajuns la cota \"%s\". Ia legătura cu serviciul de asistență." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Numărul maxim de e-mailuri (%s) care pot fi invitate într-o singură cerere " @@ -1164,21 +1136,21 @@ msgstr "Se pare că conținutul imaginii nu se potrivește cu extensia de fișie msgid "errors.media-type-not-allowed" msgstr "Se pare că aceasta nu este o imagine validă." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Profilul pe care încerci să îl inviți este dezactivat (din cauza " "rapoartelor spam sau inactivitate)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migrație în progres" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Doar creatorul versiunii poate bloca" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Doar creatorul versiunii poate debloca" @@ -1190,7 +1162,7 @@ msgstr "Parola de confirmare trebuie să se potrivească" msgid "errors.password-too-short" msgstr "Parola trebuie să conțină cel puțin 8 caractere" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Date invalide în clipboard" @@ -1198,7 +1170,7 @@ msgstr "Date invalide în clipboard" msgid "errors.profile-blocked" msgstr "Profilul este blocat" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Profilul tău conține adrese de email dezactivate (rapoarte spam sau " @@ -1208,25 +1180,25 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Înregistrarea este dezactivată în prezent." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG este invalid sau malformat" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Funcție incompatibilă detectată '%s'" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Nu există suficienți membri pentru a părăsi echipa, probabil că dorești să " "o ștergi." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Membrul pe care încerci să-l atribui nu există." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Proprietarul nu poate părăsi echipa, trebuie reatribuit rolul de proprietar." @@ -1234,15 +1206,15 @@ msgstr "Proprietarul nu poate părăsi echipa, trebuie reatribuit rolul de propr msgid "errors.token-set-already-exists" msgstr "Un set cu același nume există deja" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "Nu se poate completa mutarea, un set cu același nume există deja în cale." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "O opțiune de temă cu același nume există deja" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "Înălțimea liniei depinde de dimensiunea fontului. Adaugă o dimensiune " @@ -1264,7 +1236,7 @@ msgstr "Eroare import:" msgid "errors.tokens.invalid-color" msgstr "Valoare culoare invalidă: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Valoare greutate font invalidă: folosește valori numerice (100-950) sau " @@ -1286,27 +1258,27 @@ msgstr "" "Numele token-urilor trebuie să conțină doar litere și cifre separate de " "caracterul . și nu trebuie să înceapă cu un semn $." -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Valoare token invalidă: doar none, Uppercase, Lowercase sau Capitalize sunt " "acceptate" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "Valoare token invalidă: doar none, underline și strike-trough sunt acceptate" -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "" "Valoare invalidă: trebuie să facă referință la un token de tipografie " "compozită." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Valoare token invalidă: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Referințe token lipsă: " @@ -1318,23 +1290,23 @@ msgstr "Nu s-au găsit token-uri, seturi sau teme în acest fișier." msgid "errors.tokens.number-too-large" msgstr "Valoare token invalidă. Valoarea rezultată este prea mare: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "Opacitatea trebuie să fie între 0 și 100% sau 0 și 1 (ex: 50% sau 0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Token-ul își face referință singur" -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "Lățimea conturului trebuie să fie mai mare sau egală cu 0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Valoare invalidă: % nu este permis." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Valoare invalidă: Unitățile nu sunt permise." @@ -1342,47 +1314,47 @@ msgstr "Valoare invalidă: Unitățile nu sunt permise." msgid "errors.unexpected-error" msgstr "A apărut o eroare neașteptată." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Cod necunoscut" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Această versiune este blocată deja" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Această versiune este blocată și nu poate fi ștearsă de către alții" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Fișierul are un număr incompatibil de versiune" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Eroare de conexiune, URL-ul nu poate fi accesat" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL-ul nu a trecut validarea." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Ultima livrare nu a avut succes." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Eroare la validarea SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Timeout" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Eroare neașteptată la validare" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Stare neașteptată %s" @@ -1398,13 +1370,13 @@ msgstr "Parola veche este incorectă" msgid "feedback.description" msgstr "Descriere" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Ne bucurăm să te avem aici. Dacă ai nevoie de ajutor, te rog să cauți " "înainte de a posta." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Comunitatea Penpot" @@ -1418,11 +1390,11 @@ msgstr "" "Descrie motivul pentru care ne scrii, specificând eventuale probleme, idei " "sau nelămuriri. Un membru al echipei noastre îți va răspunde în scurt timp." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Aici pentru a te ajuta cu întrebările tehnice." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Cont de asistență X" @@ -1458,7 +1430,7 @@ msgstr "Descarcă imaginea sursă" msgid "inspect.attributes.shadow" msgstr "Umbră" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Dimensiune și poziție" @@ -1466,39 +1438,27 @@ msgstr "Dimensiune și poziție" msgid "inspect.attributes.stroke" msgstr "Contur" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Centru" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Interior" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Exterior" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Familie de Fonturi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Dimensiune Font" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Stil Font" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Spațiere între litere" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Înălțimea rândului" @@ -1510,11 +1470,11 @@ msgstr "Niciunul" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Subliniat" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Majuscule Titlu" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Minuscule" @@ -1526,7 +1486,7 @@ msgstr "Niciunul" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Ne-setează" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Majuscule" @@ -1638,47 +1598,47 @@ msgstr "Valoare rezultată:" msgid "inspect.tabs.styles.variants-panel" msgstr "Proprietățile variantei" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Marchează tot ca fiind citit" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Comenzi rapide" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Despre Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Acceptă" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Activ" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Adaugă" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Adaugă font personalizat" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Adăugare..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Administrator" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Toate" @@ -1686,15 +1646,15 @@ msgstr "Toate" msgid "labels.and" msgstr "și" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Înapoi" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "Momentan serverele noastre sunt în mentenanță. Revino în scurt timp." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Eroare de Server" @@ -1702,15 +1662,15 @@ msgstr "Eroare de Server" msgid "labels.blur" msgstr "Estompare" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Anulează" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Închide" @@ -1718,7 +1678,7 @@ msgstr "Închide" msgid "labels.code" msgstr "Cod" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Închidere" @@ -1730,15 +1690,15 @@ msgstr "Culoare" msgid "labels.comment" msgstr "Comentariu" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Comentarii" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Comunitate" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Comunitate și contribuții" @@ -1750,11 +1710,11 @@ msgstr "Calculat" msgid "labels.confirm-password" msgstr "Confirmă parola" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Continuă" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Poți continua cu un cont Penpot" @@ -1766,27 +1726,27 @@ msgstr "Copiază" msgid "labels.copy-color" msgstr "Copiază culoarea" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Copiază link-ul" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Creează" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Creează o echipă" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Introdu noul nume al echipei" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Administrare" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Șterge" @@ -1798,7 +1758,7 @@ msgstr "Șterge comentariu" msgid "labels.delete-comment-thread" msgstr "Șterge discuția" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Șterge invitația" @@ -1806,15 +1766,15 @@ msgstr "Șterge invitația" msgid "labels.delete-multi-files" msgstr "Șterge %s fișiere" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Dezvoltare" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Anulează" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Drafturi" @@ -1822,27 +1782,27 @@ msgstr "Drafturi" msgid "labels.duplicate" msgstr "Duplică" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Editează" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Editor" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Gol" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Eroare" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Expirat" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exportă" @@ -1854,7 +1814,7 @@ msgstr "Feedback dezactivat" msgid "labels.feedback-sent" msgstr "Feedback trimis" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -1862,55 +1822,55 @@ msgstr "Figma" msgid "labels.fill" msgstr "Umple" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Familie de Fonturi" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Stiluri" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fonturi" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Repozitoriu Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Lasă un feedback" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Întoarce-te" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Design grafic" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Centru Ajutor" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Ajutor și educație" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Ascunde" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Ascunde comentariile rezolvate" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importă" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inactiv" @@ -1918,39 +1878,39 @@ msgstr "Inactiv" msgid "labels.info" msgstr "Info" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Fonturi instalate" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Eroare internă" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Invitații" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Limbă" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Aspect" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Centrul de învățare" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Biblioteci & Șabloane" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Se încarcă…" @@ -1958,7 +1918,7 @@ msgstr "Se încarcă…" msgid "labels.lock" msgstr "Blochează" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Conectează-te sau înregistrează-te" @@ -1966,19 +1926,19 @@ msgstr "Conectează-te sau înregistrează-te" msgid "labels.login" msgstr "Intră în cont" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Deconectare" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marketing" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Membru" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Membri" @@ -1990,45 +1950,45 @@ msgstr "Mențiune" msgid "labels.new-password" msgstr "Parolă nouă" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Următor" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Ești la zi! Notificările de comentarii noi vor apărea aici." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Nu există invitații în așteptare." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Adună-ți echipa și construiți lucruri grozave împreună." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Opa!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notificări" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 fişier" msgstr[1] "%s fişiere" msgstr[2] "%s fişiere" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 planșă" msgstr[1] "%s planșe" msgstr[2] "%s de planșe" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 proiecte" @@ -2039,39 +1999,39 @@ msgstr[2] "%s proiecte" msgid "labels.old-password" msgstr "Parola veche" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Opțiuni" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Alta (specifică)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Altele" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Autor" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Parola" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "În curs" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Registrul de schimbări ale Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Hub-ul Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Proiecte fixate" @@ -2079,27 +2039,27 @@ msgstr "Proiecte fixate" msgid "labels.post" msgstr "Postează" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Anterior" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Design produs sau UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Management de proiecte" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Proiecte" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Referință" @@ -2107,23 +2067,23 @@ msgstr "Referință" msgid "labels.refresh" msgstr "Actualizează" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Mențiuni" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Elimină" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Elimină membru" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Redenumire" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Modifică numele echipei" @@ -2147,11 +2107,11 @@ msgstr "răspuns nou" msgid "labels.reply.thread" msgstr "Răspunde" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Retrimite" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Retrimite invitația" @@ -2159,43 +2119,43 @@ msgstr "Retrimite invitația" msgid "labels.restore" msgstr "Restaurează" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Încearcă din nou" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rol" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Salvează" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Caută" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Caută font" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Selectează opțiunea" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Trimitere" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Se trimite…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Momentan suntem în mentenanță." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Serviciul nu este disponibil" @@ -2203,55 +2163,55 @@ msgstr "Serviciul nu este disponibil" msgid "labels.sets" msgstr "Seturi" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Setări" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Umbră" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Partajează" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Biblioteci distribuite" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Afișează" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Afișează toate comentariile" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Arată lista de comentarii" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Afișează doar răspunsurile tale" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Afișează doar comentariile mele" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Surse" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Începeți" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Stare" @@ -2259,7 +2219,7 @@ msgstr "Stare" msgid "labels.stroke" msgstr "Contur" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Student sau profesor" @@ -2275,19 +2235,19 @@ msgstr "SVG" msgid "labels.text" msgstr "Text" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Teme" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Tutoriale" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Tipografie" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Eroare necunoscută" @@ -2299,31 +2259,31 @@ msgstr "Deblochează" msgid "labels.unpublish-multi-files" msgstr "Anulează publicarea a %s fișiere" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Actualizare" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Actualizare echipă" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Încărcare" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Încarcă fonturi personalizate" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Se încarcă…" -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variantă" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Versiune note %s" @@ -2331,7 +2291,7 @@ msgstr "Versiune note %s" msgid "labels.view-only" msgstr "NUMAI VIZUALIZARE" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Vizitator" @@ -2339,7 +2299,7 @@ msgstr "Vizitator" msgid "labels.visibility" msgstr "Vizibilitate" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhook-uri" @@ -2347,11 +2307,11 @@ msgstr "Webhook-uri" msgid "labels.write-new-comment" msgstr "Scrie un comentariu" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(tu)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Contul tău" @@ -2488,59 +2448,59 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Adaugă “%s” la Colecții Distribuite" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Ghiont mare" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Verifică-ți adresa de e-mail" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "Îți vom trimite un email pe adresa “%s” pentru identificare." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Mail nou" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Schimbă adresa de e-mail" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Schimbă-ți adresa de E-mail" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Creează webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Creează webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL a încărcăturii" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Anulează ștergerea contului" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Confirm ștergerea contului" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Prin ștergerea contului, se vor șterge toate proiectele și arhivele tale." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Ești sigur că dorești ștergerea contului?" @@ -2590,31 +2550,31 @@ msgstr "Ești sigur că dorești să ștergi aceste %s fișiere?" msgid "modals.delete-file-multi-confirm.title" msgstr "Ștergere %s fișeiere în curs" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Sigur doriți să ștergeți acest stil de font? Nu se va încărca dacă este " "folosit într-un fișier." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Ștergerea stilului fontului" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Ești sigur că dorești să ștergi acest font? O dată șters acesta nu se va " "mai încărca în proiectele tale." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Ștergere font" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Ești sigur că dorești să ștergi această pagină?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Șterge pagină" @@ -2630,35 +2590,35 @@ msgstr "Ești sigur că dorești să ștergi acest proiect?" msgid "modals.delete-project-confirm.title" msgstr "Șterge proiect" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Șterge fișier" msgstr[1] "Șterge fișiere" msgstr[2] "Șterge fișierele" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Nu este activat în niciun fișier." msgstr[1] "Nu sunt activate în niciun fișier." msgstr[2] "Nu sunt activate în niciun fișier." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Această librărie este activată aici: " msgstr[1] "Aceste librării sunt activate aici: " msgstr[2] "Aceste librării sunt activate aici: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Ești sigur că vrei sa ștergi acest fișier?" msgstr[1] "Ești sigur că vrei sa ștergi aceste fișiere?" msgstr[2] "Ești sigur că vrei sa ștergi aceste fișiere?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Se șterge fișierul" @@ -2679,19 +2639,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Ștergere echipă în curs" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Elimină membru" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Ești sigur că dorești să elimini acest membru din echipă?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Elimină un membru al echipei" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -2704,168 +2664,168 @@ msgstr[2] "" "Obiectele care au fost folosite deja în aceste fișiere vor rămâne acolo " "(niciun design nu va fi stricat)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Ștergeți webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Ești sigur că vrei să ștergi acest webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Ștergerea webhook-ului" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Editează webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Editează webhook" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Trimite invitație" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-mailuri, separate prin virgulă" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Unele e-mailuri provin de la membri actuali ai echipei. Invitațiile lor nu " "vor fi trimise." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Poți invita membri în echipă pentru a putea accesa acest fișier și toate " "fișierele echipei." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Invită membri în echipă" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Deoarece ești singurul membru al acestei echipe, echipa va fi ștearsă " "împreună cu proiectele și fișierele sale." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Sigur doriți să părăsiți echipa %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Nu puteţi părăsi echipa dacă nu există un alt membru care să devină " "administrator. Aţi putea şterge echipa." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "Ești administratorul echipei %s." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Promovează şi părăseşte echipa" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Selectează un membru pentru promovare" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Selectează un membru pentru promovare" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Părăsește echipa" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Ești sigur că dorești să părăsești această echipă?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Părăsire echipă în curs" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Mută" msgstr[1] "Mută" msgstr[2] "Mută" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Ești sigur că vrei să muți această bibliotecă?" msgstr[1] "Ești sigur că vrei să muți aceste biblioteci?" msgstr[2] "Ești sigur că vrei să muți aceste biblioteci?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Mută biblioteca" msgstr[1] "Mută bibliotecile" msgstr[2] "Mută bibliotecile" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Suma înghiontului" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Promovează" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Dacă transferi dreptul de proprietate, îți vei schimba rolul în " "Administrator, pierzând unele permisiuni asupra acestei echipe. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "Ești sigur că dorești să promovezi acest utilizator ca deținător al echipei?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Confirmare promovare" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publică" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Biblioteca ta este goală. Ești sigur că vrei să o publici?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publică o bibliotecă goală" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Înghiont mic" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Anulează publicarea" msgstr[1] "Anulează publicarea" msgstr[2] "Anulează publicarea" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Sigur dorești să anulezi publicarea acestei biblioteci?" msgstr[1] "Sigur dorești să anulezi publicarea acestor biblioteci?" msgstr[2] "Sigur dorești să anulezi publicarea acestor biblioteci?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Anulează publicarea bibliotecii" @@ -2890,11 +2850,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Actualizaţi o componentă dintr-o colecţie distribuită" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Această pagină nu există" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Eroare 404" @@ -2914,11 +2874,11 @@ msgstr "" "Verifică-ți adresa de e-mail și apasă pe linkul primit pentru a crea o nouă " "parolă." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Înscrie-te gratuit" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "Și începe să folosești Penpot în secunde!" @@ -2926,67 +2886,67 @@ msgstr "Și începe să folosești Penpot în secunde!" msgid "not-found.made-with-love" msgstr "Creat cu iubire și cod sursă deschis" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Ai solicitat deja acces la acest fișier." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Ai solicitat deja acces la acest fișier sau alte fișiere sau proiecte din " "această echipă." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Ai solicitat deja acces la acest proiect sau alte proiecte sau fișiere din " "această echipă." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Ai solicitat deja acces la acest proiect." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "SOLICITĂ ACCES" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Reține că, dacă proprietarul acceptă, vei fi invitat în echipă." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Cererea ta a fost trimisă corect!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Nu ai acces la acest fișier." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Du te la pagina ta de acasă în Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Dacă proprietarul acceptă, vei fi invitat în echipă." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Fișier Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Nu ai acces la acest proiect." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROIECT" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Pentru a accesa acest fișier, poți întreba proprietarul echipei." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Pentru a accesa acest proiect, poți întreba proprietarul echipei." @@ -3000,27 +2960,27 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "O versiune nouă este valabilă." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Invitație ștearsă cu succes" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Invitaţie trimisă cu succes" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Link de invitație copiat" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "Nu poți șterge profilul. Reatribuie echipele tale înainte de a continua." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil salvat cu succes!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Un e-mail de verificare a fost trimis la %s. Verifică-ţi adresa de e-mail!" @@ -3048,78 +3008,78 @@ msgstr "După ce îți denumești echipa, vei putea invita oameni să se alătur msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Creează echipă" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Invită membri" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Invită cu rolul:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Să începem!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Generez cod real din design-uri" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Creez sisteme de design" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototipuri" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Design UI/UX pentru o aplicație" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Pentru ce vei folosi Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Spune-ne puțin despre tine pentru a ne ajuta să facem Penpot să se adapteze " "pentru tine. Răspunsurile tale ne vor ajuta să prioritizăm funcționalități " "noi și să te îndreptăm în direcția bună pentru a începe." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Ajută-ne să te cunoaștem mai bine" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Care dintre aceste unelte folosești cel mai mult?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Ce fel de muncă faceți?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Spune-ne despre jobul tău" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Unde ai vrea să începi?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Educație" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Personal" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Muncă" @@ -3169,11 +3129,11 @@ msgstr "Începe să proiectezi" msgid "profile.recovery.go-to-login" msgstr "Mergi la autentificare" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Detașează" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Mixt" @@ -3185,35 +3145,35 @@ msgstr "Elimină culoarea" msgid "settings.select-this-color" msgstr "Alege elemente folosind acest stil" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Comentarii" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Aliniază jos" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Aliniază centrul orizontal" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Aliniază stânga" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Aliniază dreapta" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Aliniază sus" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Aliniază centrul vertical" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Creează tablă din selecție" @@ -3221,55 +3181,55 @@ msgstr "Creează tablă din selecție" msgid "shortcuts.bold" msgstr "Comutare bold" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Diferența booleană" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Exclude boolean" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Intersecție boolean" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Uniune boolean" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Trimite la spate" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Trimite în spate" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Aduce în față" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Aduce în față" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Ștergeți anularea" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Copiază" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Copiază link" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Copiază proprietățile" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Creează componentă / variantă" @@ -3277,35 +3237,35 @@ msgstr "Creează componentă / variantă" msgid "shortcuts.create-new-project" msgstr "Creează nou" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Taie" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Micșorează" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Șterge" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Șterge nod" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Detașează componentă" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Curbă" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elipsă" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Tablă" @@ -3313,39 +3273,39 @@ msgstr "Tablă" msgid "shortcuts.draw-nodes" msgstr "Trasează drum" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Drum" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Dreptunghi" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Text" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplică" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Anulează" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Exportă formele" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Măriți pentru a încăpea tot" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Întoarceți pe orizontală" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Întoarceți pe verticală" @@ -3369,23 +3329,23 @@ msgstr "Accesează bibliotecile partajate" msgid "shortcuts.go-to-search" msgstr "Caută" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Grup" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribuiți pe orizontală" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Arată/ascunde interfața" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Măriți" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Inserează imagine" @@ -3393,7 +3353,7 @@ msgstr "Inserează imagine" msgid "shortcuts.italic" msgstr "Comutare cursiv" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Uniți noduri" @@ -3401,39 +3361,39 @@ msgstr "Uniți noduri" msgid "shortcuts.line-through" msgstr "Comutare tăiere" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Faceți colț" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Faceți o curbă" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Mască" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Îmbină nodurile" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Mută" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Mută mai jos rapid" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Mută în stânga rapid" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Mută în dreapta rapid" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Mută mai sus rapid" @@ -3441,333 +3401,333 @@ msgstr "Mută mai sus rapid" msgid "shortcuts.move-nodes" msgstr "Mută noduri" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Mută mai jos" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Mută în stânga" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Mută în dreapta" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Mută mai sus" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Următoarea tablă" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Nu s-au găsit comenzi rapide" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Setează opacitatea la 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Setează opacitatea la 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Setează opacitatea la 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Setează opacitatea la 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Setează opacitatea la 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Setează opacitatea la 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Setează opacitatea la 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Setează opacitatea la 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Setează opacitatea la 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Setează opacitatea la 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Selector de culoare" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Accesează secțiunea de comentarii ale spectatorului" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Accesează tabloul de bord" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Mergeți la secțiunea de inspecție a vizualizatorului" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Accesează secțiunea de interacțiuni ale spectatorului" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Accesează secțiunea de interacțiuni ale spectatorului" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Accesează spațiul de lucru" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " sau " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Lipește" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Lipește proprietățile" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Manager extensii" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Tabla anterioară" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Refaceți" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Redenumire" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Resetează zoom" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Scală" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Bazele" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Bord" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Vizualizator" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Spațiu de lucru" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Selectează tot" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Selectare strat următor" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Selectează strat părinte" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Selectare strat anterior" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Noduri separate" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Afișează / Ascunde grila de pixeli" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Afișează / Ascunde comenzile rapide" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Fixare la noduri" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Fixare la grila de pixeli" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Începeți editarea" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Aliniere" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Editează" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Generic" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Meniul principal" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Modifică straturi" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigație" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigație" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigație" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Panouri" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Drumuri" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Forme" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Texte" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Unelte" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Setează miniaturile" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Comenzi rapide de la tastatură" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Comută alinierea dinamică" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Comută obiectele" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Comută paleta de culori" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Comută modul de focus" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Comută la ecran complet" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Afișează / Ascunde reperele de aliniere" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Comută istoricul" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Comută straturile" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Adăugare/eliminare aspect flexibil" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Adaugă/Elimină aspectul de grilă" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Blochează selecția" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Blochează proporțiile" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Afișează / Ascunde reperele de aliniere" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Aliniază la repere" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Aliniază la reperele liniarului" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Comută paleta de text" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Schimbă tema" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Afișează / Ascunde" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Comută stilul zoomului" @@ -3775,31 +3735,31 @@ msgstr "Comută stilul zoomului" msgid "shortcuts.underline" msgstr "Comutare subliniere" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Anulează" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Degrupează" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Demască" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribuiți vertical" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Reducere obiectiv de zoom" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Creștere obiectiv de zoom" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Mărește la selecție" @@ -3884,183 +3844,183 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Invitarea de persoane când folosiți planul Nelimitat" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Abonament" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Adaugă o metodă de plată pentru a continua cu perioada de probă" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Toate beneficiile planului Profesional și:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Toate beneficiile planului Nelimitat și:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s editori)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise (probă)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "Versiuni auto-salvate timp de 90 de zile și recuperare de fișiere" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Factură lunară fixă" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Stocare nelimitată" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Gestionează-ți abonamentul" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "În acest moment, ai %s persoană în echipele tale care poate edita." msgstr[1] "În acest moment, ai %s persoane în echipele tale care pot edita." msgstr[2] "În acest moment, ai %s de persoane în echipele tale care pot edita." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Atenție: dacă te muți la un plan mai inferior vei primi mai puțină stocare, " "mai puține copii de rezervă și un istoric mai scurt de versiuni." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Editori" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "" "(Proprietari, Administratori și Editori. Vizualizorii nu sunt numărați ca " "Editori)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "Nu poți pune mai puțini editori de câți ai acum. Schimbă rolul " "(editor/admin la vizualizor) pentru persoanele care nu edită fișiere în " "setările echipei." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "Vei plăti după perioada de probă. Nu este nevoie să adaugi un card acum." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/lună" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Aplică %s la echipele tale" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Pont: Poți crește numărul de locuri acum pentru a fi pregătit pentru " "invitații. La peste 25 de editori în echipe, vei beneficia de prețul fix de " "$175/lună." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Membru Penpot din: %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Mai multe informații" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Alte abonamente Penpot" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "organizație per lună" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Profesional" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "Versiuni auto-salvate timp de 7 zile și recuperare de fișiere" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10GB de stocare" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Echipe nelimitate. Până la 8 editori în echipele deținute de tine." -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Abonamentul tău" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Începe perioada de probă gratuit" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Abonează-te" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Poți să îți modifici abonamentul oricând din pagina 'Abonament' în " "detaliile contului." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Bucură-te de abonament!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Mulțumim pentru că ai ales planul Penpot %s!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Ești %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Încearcă gratuit pentru 14 zile" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Nelimitat" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Nelimitat (probă)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "Versiuni de 30 de zile cu auto-salvare și recuperare de fișiere" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Factura lunară limitată la $175" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GB de stocare" @@ -4085,35 +4045,35 @@ msgstr "" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "Dacă vrei să crești această limită, [schimbă-ți abonamentul|target:self](%s)" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Furnizori de Fonturi - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Fonturi - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Proiecte - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Caută - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Biblioteci Distribuite - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Libertate în Design pentru Echipe" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Oferă feedback - Penpot" @@ -4121,7 +4081,7 @@ msgstr "Oferă feedback - Penpot" msgid "title.settings.notifications" msgstr "Notificări - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Setări - Penpot" @@ -4129,27 +4089,27 @@ msgstr "Setări - Penpot" msgid "title.settings.password" msgstr "Parolă - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Invitații -%s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Membri - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Setări - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Vizualizare - Penpot" @@ -4161,99 +4121,99 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Detașează tokenul" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Nu există ferestre disponibile pe această pagină." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Fereastra nu există." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Comentarii (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Nu afişa interacţiunile" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Ecran complet" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspectă (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interacţiunile" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interacțiuni (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Copiază link" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Afişează interacţiunile" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Afişează interacţiunile la click" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Harta site-ului" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Ultima livrare a fost cu succes." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Aliniază orizontal (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Introdu spaţierea orizontală (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Aliniază la stânga (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Aliniază la dreapta (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Aliniază jos (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Aliniază vertical (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Introdu spaţierea pe verticală (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Aliniază sus (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Adaugă bibliotecă" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Toate obiectele" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Culori" @@ -4265,7 +4225,7 @@ msgstr "Adaugă culoare" msgid "workspace.assets.component-group-options" msgstr "Opțiuni grup de componente" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Componente" @@ -4273,7 +4233,7 @@ msgstr "Componente" msgid "workspace.assets.components.add-component" msgstr "Adaugă componentă" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s variante" @@ -4281,15 +4241,15 @@ msgstr "%s variante" msgid "workspace.assets.create-group" msgstr "Creează grup" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Obiectele vor fi numite automat ca \"nume grup / nume obiect\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Şterge" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplică" @@ -4297,39 +4257,39 @@ msgstr "Duplică" msgid "workspace.assets.duplicate-main" msgstr "Duplicare principală" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Editează" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtru" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Vizualizare grilă" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Grup" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Nume grup" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Vizualizare listă" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "biblioteca locală" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Gestionează biblioteca" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Nu au fost găsite obiecte" @@ -4337,7 +4297,7 @@ msgstr "Nu au fost găsite obiecte" msgid "workspace.assets.open-library" msgstr "Deschideți fișier librărie" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Redenumeşte" @@ -4345,11 +4305,11 @@ msgstr "Redenumeşte" msgid "workspace.assets.rename-group" msgstr "Redenumiți grupul" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Caută obiecte" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Librărie partajată" @@ -4360,43 +4320,43 @@ msgstr[0] "1 componentă" msgstr[1] "%s componente" msgstr[2] "%s de componente" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Sortează" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Tipografii" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Adaugă tipografie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Dimensiune" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Stil Font" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Editează fişierul în Colecţia de stiluri" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Spaţiere Litere" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Înălţime linie" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Transformare Text" @@ -4408,77 +4368,77 @@ msgstr "Degrupează" msgid "workspace.colorpicker.color-tokens" msgstr "Token-uri de culoare" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Componentele nu pot fi incluse în aceleași componente." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "Unele copii nu au putut fi comutate. Componentele nu pot fi incluse în ele " "singure." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Creează zonă" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Creează tablou" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Unește celulele" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Adaugă 1 coloană la dreapta" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Adaugă 1 coloană la stânga" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Șterge coloana" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Șterge coloana și formele" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Duplică coloana" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Adaugă 1 rând sub" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Adaugă 1 rând deasupra" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Șterge rândul" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Șterge rândul și formele" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Duplică rândul" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Mod focus" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Focus oprit" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Focus pornit" @@ -4490,191 +4450,191 @@ msgstr "Gradient liniar" msgid "workspace.gradients.radial" msgstr "Gradient radial" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Dezactivează alinierea dinamică" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Dezactivare scară proporțională" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Dezactivează fixarea la ghiduri" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Dezactivează fixarea la pixel" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Dezactivează alinierea automată la reperele liniarului" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Aliniere dinamică" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Activare scară proporțională" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Fixare la ghiduri" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Activează fixarea la pixel" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Aliniază la reperele liniarului" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Ascundeți numele tablelor" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Ascunde reperele de aliniere" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Ascunde paleta de culori" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Ascundeți grila de pixeli" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Ascunde ghidul liniar" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Ascundeți paleta de fonturi" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Editează" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Fișier" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Ajutor & info" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Preferințe" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Vezi" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Refacere" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Selectează tot" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Afișează numele tablelor" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Afișează reperele de aliniere" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Afişează paleta de culori" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Afișează grila de pixeli" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Afişează Liniarul" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Afișează paleta de fonturi" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Treci la tema întunecată" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Treci la tema luminoasă" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Treci la tema de sistem" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Anulare" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Resetează" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Eroare în timpul salvării" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Salvat" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Salvare în curs" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Partajează" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Vizualizare (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoom" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Umplere - Scalare pentru a umple" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Potrivire - Scalare în jos a se potrivi" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Zoom pentru a potrivi tot" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Ecran complet" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zoom la selecție" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Modifică grila" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Ieși" @@ -4682,7 +4642,7 @@ msgstr "Ieși" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Margine interioară jos" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Afișează opțiuni pentru toate 4 marginile interioare" @@ -4718,30 +4678,30 @@ msgstr "Terminat" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Localizează" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Localizează aspectul grilei" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 culoare" msgstr[1] "%s culori" msgstr[2] "%s de culori" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Nu există stiluri de culori în librăria dvs. încă" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Nu există stiluri de tipografie în librăria dvs. încă" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Bibliotecă de fișiere" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Culori recente" @@ -4753,96 +4713,96 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Salvează stilul culorii" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 componentă" msgstr[1] "%s componente" msgstr[2] "%s de componente" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Conectat la" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Sau adaugă unele dintre acestea pentru a încerca:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "Echipa ta nu are biblioteci partajate, poți căuta pentru" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "unele șabloane aici" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Bibliotecă de fișiere" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 obiect grafic" msgstr[1] "%s obiecte grafice" msgstr[2] "%s de obiecte grafice" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTECILE FIŞIERULUI" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTECI" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "ACTUALIZĂRI LIBRĂRII" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Se încarcă…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Nu există Biblioteci Distribuite ce necesită update" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Nu au fost găsite asemănări pentru “%s“" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Caută în bibliotecile distribuite" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "BIBLIOTECI DISTRIBUITE" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Conectează biblioteca" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 tipografie" msgstr[1] "%s tipografii" msgstr[2] "%s de tipografii" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Deconectează biblioteca" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Actualizează" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "vedeți toate schimbările" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "ACTUALIZĂRI" @@ -4878,117 +4838,117 @@ msgstr "Comută estompare" msgid "workspace.options.canvas-background" msgstr "Fundal canvas" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Conținutul clipului" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Componentă" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Notă" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Copiază" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Creează o notă" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Editează o notă" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Principală" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Înlocuiește componentă" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Această bibliotecă nu are încă obiecte" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Deconectat" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Localizează variantele în conflict" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Această componentă are variante în conflict. Asigură-te că fiecare variantă " "are un set unic de valori ale proprietăților." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Localizează variantele duplicate" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Unele variante au proprietăți și valori identice" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Aceste variante au proprietăți și valori identice. Ajustează valorile " "pentru a putea fi citite." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Această variantă are proprietăți și valori identice cu o altă variantă. " "Ajustează valorile pentru a putea fi citite." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Unele dintre aceste variante au proprietăți și valori identice. Ajustează " "valorile pentru a putea fi citite." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Această componentă are variante cu nume invalide. Asigură-te că fiecare " "variantă urmează structura corectă." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Localizează variantele invalide" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Unele variante au nume invalide" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Localizează variantele invalide" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Aceste variante au nume invalide." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Această variantă are un nume invalid." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Unele dintre aceste variante au nume invalide." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[proprietate]=[valoare], [proprietate]=[valoare]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Încearcă să folosești următoarea structură:" @@ -5027,7 +4987,7 @@ msgstr "Au același nivel ierarhic." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Grupurile, tablourile și aspectele sunt considerate echivalente." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Cum variantele rămân conectate" @@ -5071,11 +5031,11 @@ msgstr "Sus" msgid "workspace.options.constraints.topbottom" msgstr "Sus & Jos" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exportă" @@ -5083,38 +5043,38 @@ msgstr "Exportă" msgid "workspace.options.export-multiple" msgstr "Exportă selecția" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Exportă 1 element" msgstr[1] "Exportă %s elemente" msgstr[2] "Exportă %s elemente" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Adaugă export" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Elimină export" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Sufix" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Export finalizat" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Se exportă…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Export eșuat" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Export neașteptat de lent" @@ -5130,7 +5090,7 @@ msgstr "Adaugă umplere" msgid "workspace.options.fill.remove-fill" msgstr "Elimină umplere" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Ajustează dimensiunea tabloului pentru a încăpea tot conținutul" @@ -5154,67 +5114,67 @@ msgstr "Fluxul începe" msgid "workspace.options.grid.auto" msgstr "Auto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Coloane" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Culoare" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Spaţiere" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Înălţime" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Margine" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Setează ca predefinit" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Jos" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Centru" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Stânga" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Dreapta" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Întinde" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Sus" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Foloseşte default" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Lăţime" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Rânduri" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Pătrat" @@ -5226,7 +5186,7 @@ msgstr "Umplere grup" msgid "workspace.options.group-stroke" msgstr "Conturare grup" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Adaugă reper" @@ -5234,19 +5194,19 @@ msgstr "Adaugă reper" msgid "workspace.options.guides.remove-guide" msgstr "Elimină reper" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Repere" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Comută reperele" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Înălțime" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspectă" @@ -5462,103 +5422,103 @@ msgstr "Interacțiuni" msgid "workspace.options.interactions.add-interaction" msgstr "Adaugă interacțiune" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Culoare" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Arderea culorii" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Evitare culoare" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Întunecat" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Diferență" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Excludere" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Lumină Puternică" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Nuanță" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Luminat" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminozitate" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Multiplică" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Strat" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Saturaţie" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Screen" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Lumină slabă" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Comută vizibilitatea straturilor" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Înălțime maximă" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Lățime maximă" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Înălțime minimă" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Lățime minimă" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Mai multe culori" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Mai multe culori de bibliotecă" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacitate" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototip" @@ -5590,19 +5550,19 @@ msgstr "Ascunde razele independente" msgid "workspace.options.radius.show-single-corners" msgstr "Afișează razele independente" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Recente" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Reîncearcă" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotaţie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Caută font" @@ -5610,7 +5570,7 @@ msgstr "Caută font" msgid "workspace.options.select-a-shape" msgstr "Selectează o formă, o planşă sau grupează pentru a conecta o altă planşă." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Culori selectate" @@ -5678,55 +5638,55 @@ msgstr "Comută umbrele" msgid "workspace.options.show-fill-on-export" msgstr "Afișează în exporturi" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Afișare în modul de vizualizare" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Mărime" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Dimensiuni presetate" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Blochează proporțiile" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Deblochează proporțiile" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Cerc" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamant" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Săgeată" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Nimic" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Rotund" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Pătrat" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Dreptunghi" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triunghi" @@ -5734,7 +5694,7 @@ msgstr "Triunghi" msgid "workspace.options.stroke-color" msgstr "Culoare contur" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Lățimea conturului" @@ -5742,35 +5702,35 @@ msgstr "Lățimea conturului" msgid "workspace.options.stroke.add-stroke" msgstr "Adaugă culoare contur" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Centru" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Întrerupt" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Punctat" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Interior" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Mixt" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Exterior" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Elimină contur" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Solid" @@ -5806,11 +5766,11 @@ msgstr "Lăţime auto" msgid "workspace.options.text-options.grow-fixed" msgstr "Fix" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Spaţiere Litere" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Înălţime linii" @@ -5854,126 +5814,126 @@ msgstr "Subliniază (%s)" msgid "workspace.options.use-play-button" msgstr "Foloseşte butonul play din header pentru a porni vizualizarea prototip." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Lăţime" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Axa X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Axa Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Desenează noduri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Adaugă noduri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "În colţ (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "În curbă (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Uneşte noduri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Mută noduri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Separă noduri (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Trage noduri (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Deschide" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Descoperă [mai multe extensii](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Nu ai încă extensii instalate" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Registrul extensiei este incorect." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Trebuie să fii un editor pentru a folosi această extensie" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Extensia nu există sau URL incorect." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Instalează" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Extensii instalate" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Manager extensii" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Extensii" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "ACTUALIZEAZĂ ACEASTĂ EXTENSIE" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Această extensie a fost modificată de când ai deschis-o ultima dată. Acum " "vrea să acceseze:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Începe descărcarea de fișiere." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Stochează datele în browser." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Citește comentariile și răspunsurile tale." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Citește și modifică comentariile tale și răspunde în numele tău." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Citește conținutul fișierelor la care au acces utilizatorii." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Citește și modifică conținutul fișierelor la care au acces utilizatorii." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Ține cont că această extensie este creată de o terță parte, deci asigură-te " @@ -5981,74 +5941,74 @@ msgstr "" "confidențialitatea datelor tale sunt importante nouă. dacă ai vreo " "nelămurire, te rugăm să contactezi suportul." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Citește bibliotecile și obiectele tale." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Citește și modifică bibliotecile și obiectele tale." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "EXTENSIA '%s' VREA ACCES LA:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Citește informațiile profilului utilizatorului curent." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Lista de extensii" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Elimină extensie" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Introdu un URL de extensie" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Extensii" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "NU ACUM" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Vrei să arunci o privire? Va deschide într-o schiță pentru echipa ta " "curentă. (Dacă nu, poți oricând să o găsești în lista de extensii instalate " "a oricărui fișier.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "EXTENSIE '%s' ESTE INSTALATĂ PENTRU UTILIZATORUL TĂU!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "ÎNCEARCĂ EXTENSIA" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Adaugă aspect flexibil" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Adaugă aspect grilă" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Adaugă aspect" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Creează variantă" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Adaugă o nouă proprietate" @@ -6060,7 +6020,7 @@ msgstr "Trimite înapoi" msgid "workspace.shape.menu.backward" msgstr "Trimite în urmă" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Combină ca variante" @@ -6068,7 +6028,7 @@ msgstr "Combină ca variante" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Componentele trebuie să fie în aceeași pagină" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Copiază" @@ -6104,27 +6064,27 @@ msgstr "Copiază ca text" msgid "workspace.shape.menu.create-annotation" msgstr "Creează notă" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Selecție la planșă" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Creează componentă" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Creează componente multiple" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Taie" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Şterge" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Ștergeți începutul fluxului" @@ -6136,7 +6096,7 @@ msgstr "Detaşează instanţă" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Detașează instanțele" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Diferență" @@ -6144,15 +6104,15 @@ msgstr "Diferență" msgid "workspace.shape.menu.duplicate" msgstr "Duplică" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Editează" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Excludeți" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Aplatizează" @@ -6164,7 +6124,7 @@ msgstr "Întoarce pe orizontală" msgid "workspace.shape.menu.flip-vertical" msgstr "Întoarce pe verticală" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Pornirea fluxului" @@ -6176,31 +6136,31 @@ msgstr "Aduceţi înainte" msgid "workspace.shape.menu.front" msgstr "Aduceţi în faţă" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Grupează" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Ascunde" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Afișează / Ascunde UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Intersecție" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Blochează" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Maschează" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Lipeşte" @@ -6208,27 +6168,27 @@ msgstr "Lipeşte" msgid "workspace.shape.menu.paste-props" msgstr "Lipește proprietățile" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Drum" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Îndepărtează aspect flexibil" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Elimină aspectul grilă" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Elimină aspect" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Elimină proprietatea" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "Varianta trebuie să aibă cel puțin o proprietate" @@ -6252,11 +6212,11 @@ msgstr "Restaurează varianta" msgid "workspace.shape.menu.select-layer" msgstr "Selectează stratul" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Afişează" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Afișează în panoul de obiecte" @@ -6272,19 +6232,19 @@ msgstr "Elimină miniatura" msgid "workspace.shape.menu.thumbnail-set" msgstr "Setează ca miniatură" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Degrupează" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Uniune" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Deblochează" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Demaschează" @@ -6300,47 +6260,47 @@ msgstr "Restrângeți bara laterală" msgid "workspace.sidebar.expand" msgstr "Extindeți bara laterală" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Istoric" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Layere" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Componente" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Plăci" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Grupuri" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Imagini" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Măști" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Caută straturi" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Forme" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Texte" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Atribute SVG importate" @@ -6352,7 +6312,7 @@ msgstr "Pagini" msgid "workspace.sidebar.sitemap.add-page" msgstr "Adaugă pagină" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Harta site-ului" @@ -6372,7 +6332,7 @@ msgstr "Adaugă set la acest grup" msgid "workspace.tokens.add-token" msgstr "Adaugă token: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Aplicat la" @@ -6394,7 +6354,7 @@ msgstr "" "Dimensiunea de bază a fontului trebuie să fie o valoare în pixeli sau fără " "unități." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Culoare" @@ -6402,7 +6362,7 @@ msgstr "Culoare" msgid "workspace.tokens.create-new-theme" msgstr "Creează prima ta temă acum." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Creează unul." @@ -6422,7 +6382,7 @@ msgstr "Șterge temă" msgid "workspace.tokens.duplicate" msgstr "Duplică token" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "copie" @@ -6476,7 +6436,7 @@ msgstr "" "Eroare la setarea stilului/greutății fontului. Acest stil de font nu există " "în fontul curent" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Introdu o valoare (300, Bold, Regular, Italic...) sau un {alias}" @@ -6514,7 +6474,7 @@ msgstr "" "Într-un singur fișier JSON, cheile primului nivel ar trebui să fie numele " "setului de token-uri." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Importă token-uri" @@ -6524,17 +6484,17 @@ msgstr "" "Importul de token-uri va înlocui toate token-urile, seturile și temele " "curente." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Inactiv" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Acest set este inactiv. Schimbă tema sau activează acest set pentru a vedea " "schimbările în viewport" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Folosește token-uri individuale" @@ -6554,11 +6514,11 @@ msgstr "Temă" msgid "workspace.tokens.label.theme-placeholder" msgstr "Adaugă o temă (ex: Luminoasă)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Adaugă spațiere între litere sau {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Introdu o înălțime de linie - multiplier, px, % sau {alias}" @@ -6574,7 +6534,7 @@ msgstr "Dimensiune maximă" msgid "workspace.tokens.min-size" msgstr "Dimensiune minimă" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Clic dreapta pentru a vedea opțiuni" @@ -6590,7 +6550,7 @@ msgstr "Nu sunt teme active" msgid "workspace.tokens.no-permisions-set" msgstr "Trebuie să fii un editor pentru a activa / dezactiva seturi" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Trebuie să fii un editor pentru a folosi temele" @@ -6602,7 +6562,7 @@ msgstr "Nu există seturi definite. Creează unul mai întâi." msgid "workspace.tokens.no-sets-yet" msgstr "Nu există încă seturi." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Nu există teme." @@ -6614,7 +6574,7 @@ msgstr "În prezent nu ai teme." msgid "workspace.tokens.num-active-sets" msgstr "%s seturi active" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Valoare originală: %s" @@ -6626,11 +6586,11 @@ msgstr "Margini interioare" msgid "workspace.tokens.radius" msgstr "Raze" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Introdu un alias de token tipografic" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Valoare rezultată: %s" @@ -6670,11 +6630,11 @@ msgstr "Setări token-uri" msgid "workspace.tokens.size" msgstr "Dimensiune" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "Introdu: none | uppercase | lowercase | capitalize sau {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "Introdu decorațiune text: none | underline | strike-through" @@ -6692,23 +6652,23 @@ msgstr "Lista de teme" msgid "workspace.tokens.token-description" msgstr "Descriere" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Selectează familia fontului" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Familie de fonturi" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "O familie de fonturi sau o listă de fonturi separate de virgulă (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Nume" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " nu este un nume valid pentru token.\n" @@ -6719,11 +6679,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Valoare" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Introdu o valoare sau alias cu {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKEN-URI- %s" @@ -6742,11 +6702,11 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "Tipul '%s' nu este suportat (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Folosește ca referință" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Valoarea este invalidă" @@ -6754,55 +6714,55 @@ msgstr "Valoarea este invalidă" msgid "workspace.toolbar.assets" msgstr "Obiecte" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Paletă de culori (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Comentarii (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Curbe (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elipsă (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Planşă de lucru (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Imagine (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Poziţionează (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Cale (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Extensii (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Dreptunghi (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Text (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Tipografii (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Comută bara de utilități" @@ -6815,7 +6775,7 @@ msgstr "Terminat" msgid "workspace.top-bar.view-only" msgstr "**Inspecție cod** (Doar vizualizare)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Nu sunt modificări în istoric" @@ -6955,19 +6915,19 @@ msgstr "obiect tipografic" msgid "workspace.undo.entry.unknown" msgstr "Operaţiune terminată %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Renunţă" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Mai multe informații" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Există actualizări în bibliotecile distribuite" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Actualizează" diff --git a/frontend/translations/ru.po b/frontend/translations/ru.po index 24fcebf98a..bee45f04bb 100644 --- a/frontend/translations/ru.po +++ b/frontend/translations/ru.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:13+0000\n" -"Last-Translator: Anonymous \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Russian " "\n" "Language: ru\n" @@ -9,12 +9,16 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Уже есть аккаунт?" +#: src/app/main/ui/auth/recovery_request.cljs:111, src/app/main/ui/auth/register.cljs:250 +msgid "auth.check-email" +msgstr "Проверьте почту!" + #: src/app/main/ui/auth/recovery.cljs:83 msgid "auth.confirm-password" msgstr "Подтвердите пароль" @@ -29,7 +33,7 @@ msgstr "" "Это ДЕМОНСТРАЦИЯ, НЕ ИСПОЛЬЗУЙТЕ для работы, проекты будут периодически " "удаляться." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Забыли пароль?" @@ -37,37 +41,37 @@ msgstr "Забыли пароль?" msgid "auth.fullname" msgstr "Полное имя" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Войдите в свой аккаунт" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Войти здесь." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot — это бесплатный инструмент для дизайна с открытым исходным кодом, " "созданный для совместной работы с дизайном и кодом" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -75,6 +79,10 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Введите новый пароль" +#: src/app/main/ui/auth/verify_token.cljs:54 +msgid "auth.notifications.organization-invitation-accepted" +msgstr "Теперь Вы входите в %s" + #: src/app/main/ui/auth/recovery.cljs:52 msgid "auth.notifications.password-changed-successfully" msgstr "Пароль успешно изменён" @@ -87,7 +95,7 @@ msgstr "Профиль не подтверждён, пожалуйста, сна msgid "auth.notifications.recovery-token-sent" msgstr "Ссылка для восстановления пароля отправлена на почту." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Приглашение в команду принято" @@ -119,7 +127,7 @@ msgstr "Забыли пароль?" msgid "auth.recovery-submit" msgstr "Изменить пароль" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Ещё нет аккаунта?" @@ -133,7 +141,7 @@ msgstr "" msgid "auth.register-account-title" msgstr "Ваше имя" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Создать аккаунт" @@ -148,10 +156,14 @@ msgstr "" "Я согласен с [условиями обслуживания](%s) и [политикой " "конфиденциальности](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Условия использования" +#: src/app/main/ui/auth/register.cljs:251 +msgid "auth.verification-sent-email" +msgstr "Мы отправили письмо с подтверждением на адрес:" + #: src/app/main/ui/auth/login.cljs:185, src/app/main/ui/auth/recovery_request.cljs:77, src/app/main/ui/auth/register.cljs:182 msgid "auth.work-email" msgstr "Рабочая эл. почта" @@ -166,93 +178,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Людей не найдено для @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Опубликовать" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Все пользователи Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Вы точно хотите удалить эту ссылку? После этого она перестанет быть " "доступной" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(текущая)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Удалить ссылку" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Получить ссылку" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Ссылка скопирована" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Управлять разрешениями" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 общая страница" msgstr[1] "%s общих страниц" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Могут комментировать" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Могут проверять код" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Доступ открыт для получателей ссылки" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Общие страницы" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Ссылка общего доступа появится здесь" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Только участники команды" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Поделиться прототипами" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Выбрать все" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Снять с публикации" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Управление командой" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot предназначен для команд. Приглашайте участников к совместной работе " "над проектами и файлами" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Объединяйтесь!" @@ -268,31 +280,107 @@ msgstr "Восстановить файл" msgid "dashboard.add-file" msgstr "Добавить файл" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Добавить как общую библиотеку" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Изменить эл. почту" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/data/nitrate.cljs:614 +msgid "dashboard.change-organization-modal.accept" +msgstr "ПЕРЕМЕСТИТЬ КОМАНДУ" + +#: src/app/main/data/nitrate.cljs:612 +msgid "dashboard.change-organization-modal.choose" +msgstr "Переместить в:" + +#: src/app/main/data/nitrate.cljs:611 +msgid "dashboard.change-organization-modal.description" +msgstr "" +"Проекты и файлы останутся доступны участникам команды. Команда получит " +"настройки новой организации." + +#: src/app/main/data/nitrate.cljs:613 +msgid "dashboard.change-organization-modal.select" +msgstr "Выберите организацию" + +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 +msgid "dashboard.change-organization-modal.title" +msgstr "Смена организации команды" + +#: src/app/main/ui/dashboard/check_updates.cljs:334 +msgid "dashboard.check-updates.available-message" +msgstr "" +"Новые выпуски улучшают скорость работы, добавляют возможности и " +"исправления, чтобы Ваша установка не устаревала." + +#: src/app/main/ui/dashboard/check_updates.cljs:309 +msgid "dashboard.check-updates.available-title" +msgstr "ДОСТУПНА НОВАЯ ВЕРСИЯ PENPOT" + +#: src/app/main/ui/dashboard/check_updates.cljs:341 +msgid "dashboard.check-updates.highlights-title" +msgstr "Что нового — главное" + +#: src/app/main/ui/dashboard/check_updates.cljs:318 +msgid "dashboard.check-updates.installed-version" +msgstr "Установленная версия" + +#: src/app/main/ui/dashboard/check_updates.cljs:326 +msgid "dashboard.check-updates.latest-version" +msgstr "Последняя версия" + +#: src/app/main/ui/dashboard/check_updates.cljs:235 +msgid "dashboard.check-updates.try-again" +msgstr "Попробовать снова" + +#: src/app/main/ui/dashboard/check_updates.cljs:230 +msgid "dashboard.check-updates.unable-hint" +msgstr "Проверьте сеть и настройки экземпляра и попробуйте снова." + +#: src/app/main/ui/dashboard/check_updates.cljs:226 +msgid "dashboard.check-updates.unable-message" +msgstr "Этой установке не удалось связаться со службой выпусков Penpot." + +#: src/app/main/ui/dashboard/check_updates.cljs:215 +msgid "dashboard.check-updates.unable-title" +msgstr "НЕ УДАЛОСЬ ПРОВЕРИТЬ ОБНОВЛЕНИЯ" + +#: src/app/main/ui/dashboard/check_updates.cljs:265 +msgid "dashboard.check-updates.uptodate-message" +msgstr "У Вас последняя стабильная версия:" + +#: src/app/main/ui/dashboard/check_updates.cljs:254 +msgid "dashboard.check-updates.uptodate-title" +msgstr "PENPOT ОБНОВЛЁН" + +#: src/app/main/ui/dashboard/check_updates.cljs:371 +msgid "dashboard.check-updates.view-changelog" +msgstr "Открыть полный список изменений" + +#: src/app/main/ui/dashboard/check_updates.cljs:374 +msgid "dashboard.check-updates.view-release-notes" +msgstr "Открыть примечания к выпуску" + +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Очистить корзину" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(копия)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Создать новую организацию" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Создать новую команду" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Закрепить эту версию" @@ -300,7 +388,7 @@ msgstr "Закрепить эту версию" msgid "dashboard.default-team-name" msgstr "Ваш Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Вы уверены, что хотите навсегда удалить все удаленные проекты и файлы? Это " @@ -310,41 +398,41 @@ msgstr "" msgid "dashboard.delete-file-forever-confirmation.description" msgstr "Вы уверены, что хотите навсегда удалить %s? Это необратимое действие." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s файлов успешно удалено." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Удалить навсегда" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Удалить проект" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Вы уверены, что хотите удалить проект %s навсегда? Вы собираетесь удалить " "его и все находящиеся в нем файлы. Это необратимое действие." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s успешно удален." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Удалить команду" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "Ваша корзина пуста. Удаленные файлы и проекты появятся здесь." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Будет удален %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Скачать файл Penpot (.penpot)" @@ -356,19 +444,19 @@ msgstr "Дублировать" msgid "dashboard.duplicate-multi" msgstr "Дублировать файлы (%s)" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Как только участник проекта создаст черновик, он отобразится здесь." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Черновиков пока нет." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Как только участник проекта создаст файл, он отобразится здесь." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Файлов пока нет." @@ -416,23 +504,23 @@ msgstr "Импорт .penpot файл" msgid "dashboard.empty-project.start" msgstr "Начать создавать удивительные вещи" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "Произошла ошибка во время удаления файлов." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "Произошла ошибка во время удаления проекта %s." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "Произошла ошибка во время восстановления файла %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "Произошла ошибка во время восстановления файлов." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "Произошла ошибка во время восстановления проекта %s и его файлов." @@ -440,37 +528,37 @@ msgstr "Произошла ошибка во время восстановлен msgid "dashboard.export-binary-multi" msgstr "Скачать файлы Penpot (.penpot) (%s)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" -msgstr "Экспорт кадров в PDF" +msgstr "Экспорт досок в PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Экспорт в формате PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "Выбрано %s из %s элементов" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Экспорт" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Вы можете добавить настройки экспорта элементам из свойств дизайна (в " "нижней части правой боковой панели)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Информация о настройке экспорта в Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Нет элементов с настройками экспорта." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Выбор экспорта" @@ -486,35 +574,43 @@ msgid_plural "dashboard.file-menu.restore-files-option" msgstr[0] "Восстановить файл" msgstr[1] "Восстановить файлы" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/dashboard/layout_toggle.cljs:32 +msgid "dashboard.files-layout.grid" +msgstr "Вид сетки" + +#: src/app/main/ui/dashboard/layout_toggle.cljs:28 +msgid "dashboard.files-layout.list" +msgstr "Вид списка" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Шрифт удалён" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Отменить все" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Пользовательские шрифты, которые вы загружаете, будут отображаться здесь." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Пользовательских шрифтов пока нет." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Как только участник проекта загрузит собственный шрифт, он будет отображен " "здесь." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "Шрифт добавлен" msgstr[1] "Шрифты добавлены (%s)" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -524,21 +620,20 @@ msgstr "" "загрузки допустимы следующие форматы: **TTF, OTF и WOFF** (используйте один " "из них)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" "Вам следует загружать только собственные шрифты, или у которых есть " "лицензия на использование в Penpot. Больше информации в разделе \"Content " -"rights\" в [Условиях использования Penpot](https://penpot.app/terms.html). " -"Также можете прочитать о [лицензировании " -"шрифтов](https://www.typography.com/faq)." +"rights\" в [Условиях использования Penpot](%s). Также можете прочитать о " +"[лицензировании шрифтов](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Загрузить все" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -549,115 +644,185 @@ msgstr "" "использовать [Transfonter](https://transfonter.org/) для создания " "веб-шрифтов и исправления ошибок. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 +msgid "dashboard.go-to-admin-console" +msgstr "Перейти в Admin Console" + +#: src/app/main/ui/dashboard/grid.cljs:253 +msgid "dashboard.grid.last-modified-at" +msgstr "Последнее изменение %s" + +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Импорт файлов Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Ой! Не вышло импортировать этот файл" +#: src/app/main/ui/dashboard/import.cljs:698 +msgid "dashboard.import.auto-linked-libraries" +msgid_plural "dashboard.import.auto-linked-libraries" +msgstr[0] "1 библиотека была автоматически связана по имени." +msgstr[1] "%s библиотек было автоматически связано по имени." + #: src/app/main/ui/dashboard.cljs:264 msgid "dashboard.import.bad-url" msgstr "Импорт не удался. URL-адрес шаблона неверен" -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:838 +msgid "dashboard.import.confirm-library-links" +msgstr "Подтвердите связи библиотек" + +#: src/app/main/ui/dashboard/import.cljs:814 +msgid "dashboard.import.connect-selected-libraries" +msgstr "Свяжите выбранные библиотеки" + +#: src/app/main/ui/dashboard/import.cljs:288 +msgid "dashboard.import.file-name-label" +msgstr "Имя файла" + +#: src/app/main/ui/dashboard/import.cljs:721 +msgid "dashboard.import.import-error.check-error" +msgstr "Не удалось проверить этот файл." + +#: src/app/main/ui/dashboard/import.cljs:725 +msgid "dashboard.import.import-error.corrupt-file" +msgstr "Похоже, этот файл повреждён." + +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Не все файлы были импортированы" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "В следующих файлах есть ошибки:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Файлы с ошибками загружены не будут." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:728 +msgid "dashboard.import.import-error.unknown-error" +msgstr "Что-то пошло не так при обработке файла." + +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 файл был успешно импортирован." msgstr[1] "%s файлов было успешно импортировано." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Некоторые файлы содержали неверные объекты, которые были удалены." +#: src/app/main/ui/dashboard/import.cljs:815 +msgid "dashboard.import.next-file" +msgstr "Следующий файл" + #: src/app/main/ui/dashboard.cljs:265 msgid "dashboard.import.no-perms" msgstr "У вас нет разрешения на импорт в эту команду" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Обработка цветов" +#: src/app/main/ui/dashboard/import.cljs:431 +msgid "dashboard.import.resolve-libraries" +msgstr "" +"Некоторые библиотеки не удалось связать автоматически. Выберите правильную " +"библиотеку для каждой:" -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Обработка компонентов" +#: src/app/main/ui/dashboard/import.cljs:553 +msgid "dashboard.import.resolve-libraries-summary" +msgstr "Проверьте связи библиотек перед подтверждением:" -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Обработка медиа" +#: src/app/main/ui/dashboard/import.cljs:447 +msgid "dashboard.import.resolve-libraries.connect-to" +msgstr "связать с" -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Обработка страницы: %s" +#: src/app/main/ui/dashboard/import.cljs:441 +msgid "dashboard.import.resolve-libraries.original-library" +msgstr "исходная библиотека" -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Обработка типографики" +#: src/app/main/ui/dashboard/import.cljs:509 +msgid "dashboard.import.summary.linked" +msgstr "связана автоматически" -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Загрузка данных на сервер (%s/%s)" +#: src/app/main/ui/dashboard/import.cljs:517 +msgid "dashboard.import.summary.manually-linked" +msgstr "связана вручную" -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Загрузка файла: %s" +#: src/app/main/ui/dashboard/import.cljs:525 +msgid "dashboard.import.summary.new" +msgstr "Новая" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/import.cljs:546 +msgid "dashboard.import.summary.no-selection" +msgstr "Библиотека не выбрана" + +#: src/app/main/ui/dashboard/import.cljs:522 +msgid "dashboard.import.summary.original" +msgstr "Оригинал" + +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Вы собираетесь удалить приглашения в:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Вы собираетесь повторно разослать приглашения:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Удалить приглашения" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Повторно отправить приглашения" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 +msgid "dashboard.invitations.no-permission" +msgstr "" +"У Вас нет разрешения приглашать людей в эту команду, а также изменять или " +"удалять приглашения." + +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Пригласить людей" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/team.cljs:136 +msgid "dashboard.invite-profile-disabled" +msgstr "У Вас нет разрешения приглашать людей в эту команду" + +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:235, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:266 +msgid "dashboard.leave-organization" +msgstr "Покинуть организацию" + +#: src/app/main/data/nitrate.cljs:259 +msgid "dashboard.leave-organization.toast" +msgstr "Вы больше не входите в организацию %s." + +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Покинуть команду" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Библиотеки и шаблоны" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Здесь вы найдете библиотеки и шаблоны, которые вы можете добавить в свой " "проект" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Узнайте больше о них и о том, как внести свой вклад" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Возникла проблема с импортом шаблона. Шаблон не был импортирован." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Библиотеки" @@ -665,7 +830,7 @@ msgstr "Библиотеки" msgid "dashboard.loading-files" msgstr "загрузка ваших файлов …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "загрузка ваших шрифтов …" @@ -685,39 +850,65 @@ msgstr "Переместить файлы (%s)" msgid "dashboard.move-to-other-team" msgstr "Перевести в другую команду" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Новый файл" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Новый файл" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Новый проект" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Новый проект" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Совпадений для “%s“ не найдено" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/team_form.cljs:162 +msgid "dashboard.no-organization-allows-create-team.message" +msgstr "У Вас нет разрешения добавлять команды ни в одну из Ваших организаций." + +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 +msgid "dashboard.no-organizations-yet" +msgstr "Организаций пока нет." + +#: src/app/main/ui/dashboard/team_form.cljs:156 +msgid "dashboard.no-permission-create-team.message" +msgstr "" +"Вам нельзя создавать команды в организации %s. Если нужна информация, " +"свяжитесь с владельцем организации." + +#: src/app/main/ui/dashboard/team_form.cljs:158 +msgid "dashboard.no-permission-delete-team.message" +msgstr "" +"Вам нельзя удалять команды, входящие в организацию %s. Если нужна " +"информация, свяжитесь с владельцем организации." + +#: src/app/main/ui/dashboard/team_form.cljs:164 +msgid "dashboard.no-permission-move-team.message" +msgstr "" +"Вам нельзя перемещать команды, входящие в организацию %s. Если нужна " +"информация, свяжитесь с владельцем организации." + +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Закреплённые проекты будут здесь" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Уведомления" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Ваш адрес эл. почты был успешно обновлён" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Ваш адрес эл. почты был успешно подтверждён" @@ -729,11 +920,11 @@ msgstr "Настройки уведомлений обновлены" msgid "dashboard.notifications.password-saved" msgstr "Пароль успешно сохранён!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Просмотреть уведомления" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "Участников: %s" @@ -741,18 +932,26 @@ msgstr "Участников: %s" msgid "dashboard.open-in-new-tab" msgstr "Открыть в новой вкладке" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Опции" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Сортировать по роли" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Сортировать по статусу" +#: src/app/main/data/dashboard.cljs:721 +msgid "dashboard.organization-deleted" +msgstr "Организация %s удалена." + +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 +msgid "dashboard.other-teams" +msgstr "Другие команды" + #: src/app/main/ui/settings/password.cljs:112, src/app/main/ui/settings/password.cljs:126 msgid "dashboard.password-change" msgstr "Изменить пароль" @@ -773,7 +972,11 @@ msgstr "Теперь вы владелец этой команды." msgid "dashboard.permissions-change.viewer" msgstr "Теперь вы наблюдатель в этой команде." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 +msgid "dashboard.personal-projects" +msgstr "Личные проекты" + +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Закрепить/Открепить" @@ -789,27 +992,27 @@ msgstr "Невозможно проанализировать манифест msgid "dashboard.plugins.try-plugin" msgstr "Попробуйте плагин: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "Удаление файлов…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "Восстановление файлов…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "Удаление идет медленнее ожидаемого" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "Восстановление идет неожиданно медленно" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Проекты" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Хотите удалить свой аккаунт?" @@ -817,69 +1020,115 @@ msgstr "Хотите удалить свой аккаунт?" msgid "dashboard.removed-from-team" msgstr "Вы больше не являетесь частью команды \"%s\"." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "" "Вы собираетесь восстановить все ваши проекты и файлы. Это может занять " "некоторое время." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Восстановить все проекты и файлы" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Восстановить все" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "файлы %s были успешно восстановлены." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Восстановить проект" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Вы собираетесь восстановить проект %s и все файлы в нем." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Восстановить проект" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s был успешно восстановлен." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Сохранить настройки" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Поиск…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Поиск \"%s\"…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 +msgid "dashboard.section.organizations" +msgstr "Организации" + +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 +msgid "dashboard.section.teams" +msgstr "Команды" + +#: src/app/main/data/nitrate.cljs:529 +msgid "dashboard.select-organization-modal.accept" +msgstr "ДОБАВИТЬ В ОРГАНИЗАЦИЮ" + +#: src/app/main/data/nitrate.cljs:527 +msgid "dashboard.select-organization-modal.choose" +msgstr "Выберите организацию:" + +#: src/app/main/ui/dashboard/team.cljs:1042 +msgid "dashboard.select-organization-modal.external-invitations-warning" +msgstr "" +"«%s» разрешает вступать в команды только своим участникам. Все ожидания " +"приглашений для посторонних будут отозваны." + +#: src/app/main/ui/dashboard/team.cljs:1038 +msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" +msgstr "Ожидающие приглашения для внешних пользователей будут отменены." + +#: src/app/main/data/nitrate.cljs:595 +msgid "dashboard.select-organization-modal.permission-info" +msgstr "" +"Здесь организации, в которые Вы входите и где Вам разрешено перемещать " +"команду." + +#: src/app/main/data/nitrate.cljs:519 +msgid "dashboard.select-organization-modal.permission-info-add" +msgstr "" +"Здесь организации, в которые Вы входите и где Вам разрешено создавать и " +"добавлять команды." + +#: src/app/main/data/nitrate.cljs:528 +msgid "dashboard.select-organization-modal.select" +msgstr "Выберите организацию" + +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 +msgid "dashboard.select-organization-modal.title" +msgstr "Добавить команду в организацию" + +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Выберите язык интерфейса" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Выберите тему" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Темный Penpot (по умолчанию)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Светлый Penpot" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Системная тема" @@ -943,11 +1192,11 @@ msgstr "Обновить настройки" msgid "dashboard.settings.notifications.title" msgstr "Уведомления" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Показать все файлы" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "История версий" @@ -971,7 +1220,7 @@ msgstr[1] "Ваши файлы были успешно продублирова msgid "dashboard.success-duplicate-project" msgstr "Ваш проект продублирован" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Ваш файл успешно перемещён" @@ -983,108 +1232,182 @@ msgstr "Ваши файлы успешно перемещены" msgid "dashboard.success-move-project" msgstr "Ваш проект перемещён" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/data/common.cljs:268 +#, backend +msgid "dashboard.team-belong-organization" +msgstr "Эта команда теперь входит в организацию %s" + +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "О команде" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Участники команды" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/data/common.cljs:269 +#, backend +msgid "dashboard.team-no-longer-belong-organization" +msgstr "Эта команда больше не входит в организацию %s" + +#: src/app/main/ui/dashboard/team.cljs:1689 +msgid "dashboard.team-organization" +msgstr "Организация команды" + +#: src/app/main/ui/dashboard/team.cljs:1724 +msgid "dashboard.team-organization.add" +msgstr "Добавить в организацию" + +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 +msgid "dashboard.team-organization.add.no-valid-organizations" +msgstr "У Вас нет разрешения добавлять эту команду ни в одну из Ваших организаций." + +#: src/app/main/ui/dashboard/team.cljs:1713 +msgid "dashboard.team-organization.change" +msgstr "Сменить организацию команды" + +#: src/app/main/ui/dashboard/team.cljs:946 +msgid "dashboard.team-organization.disabled-organization-tooltip" +msgstr "Приглашать можно только людей из Вашей организации." + +#: src/app/main/ui/dashboard/team.cljs:1720 +msgid "dashboard.team-organization.none" +msgstr "Эта команда не входит ни в одну организацию" + +#: src/app/main/ui/dashboard/team.cljs:1716 +msgid "dashboard.team-organization.remove" +msgstr "Убрать команду из организации" + +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Командные проекты" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Добавить в свой проект" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Тема интерфейса пользователя" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Результаты поиска" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Удаленные файлы останутся в корзине в течение" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s дней. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "После этого они будут удалены навсегда." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Если вы передумаете, вы можете восстановить их или удалить навсегда через " "меню каждого файла." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Введите для поиска" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Снять библиотеку с публикации" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Обновить настройки" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/data/dashboard.cljs:689 +#, backend +msgid "dashboard.user-no-longer-belong-organization" +msgstr "Вы больше не входите в организацию %s" + +#: src/app/main/ui/settings/options.cljs:115 +msgid "dashboard.webgl-switch.beta" +msgstr "Бета" + +#: src/app/main/ui/settings/options.cljs:116 +msgid "dashboard.webgl-switch.description" +msgstr "" +"Рендеринг WebGL может ускорить работу, но он в стадии бета и менее " +"стабилен. Возможны расхождения между холстом, экспортом (SVG/PDF) и режимом " +"просмотра." + +#: src/app/main/ui/settings/options.cljs:119 +msgid "dashboard.webgl-switch.disabled" +msgstr "Отключено" + +#: src/app/main/ui/settings/options.cljs:119 +msgid "dashboard.webgl-switch.enabled" +msgstr "Включено" + +#: src/app/main/ui/settings/options.cljs:122 +msgid "dashboard.webgl-switch.feedback" +msgstr "Отправить отзыв" + +#: src/app/main/ui/settings/options.cljs:118 +msgid "dashboard.webgl-switch.status" +msgstr "Статус" + +#: src/app/main/ui/settings/options.cljs:114 +msgid "dashboard.webgl-switch.title" +msgstr "Рендеринг WebGL" + +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Активен" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Когда этот хук сработает, будут доставлены подробные сведения о событии" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Вы можете удалять или изменять только созданные вами webhook'и." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Тип контента" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Создать вебхук" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Вебхук успешно создан." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Вебхуки - это простой способ уведомлять другие веб-сайты и приложения о тех " "или иных событиях в Penpot. Мы отправим POST-запрос на каждый из указанных " "вами URL-адресов." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Нажмите кнопку «Добавить вебхук», чтобы добавить его." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Вебхуки пока не созданы." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Ваш аккаунт" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Эл. почта" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Ваше имя" @@ -1100,11 +1423,11 @@ msgstr "Внимание" msgid "ds.component-subtitle" msgstr "Компоненты для обновления:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Разрешить" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Отмена" @@ -1124,10 +1447,20 @@ msgstr "В активных наборах или темах нет примен msgid "ds.inputs.numeric-input.no-matches" msgstr "Совпадений не найдено." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Открыть список токенов" +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:55, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:54, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:118, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:134 +msgid "ds.inputs.token-field.no-active-token-option" +msgstr "Токен {%s} пропал из активных наборов или неверен." + +#: src/app/main/ui/auth/login.cljs:113 +msgid "errors.account-locked" +msgstr "" +"Слишком много неудачных попыток. Ваш аккаунт заблокирован на %s минут. " +"Попробуйте позже." + #: src/app/main/data/auth.cljs:344 msgid "errors.auth-provider-not-allowed" msgstr "Поставщик аутентификации не разрешён для этого профиля" @@ -1136,15 +1469,15 @@ msgstr "Поставщик аутентификации не разрешён д msgid "errors.auth-provider-not-configured" msgstr "Провайдер аутентификации не настроен." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Кажется, сеанс истёк. Войдите снова." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Шрифт %s не может быть загружен" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Шрифты %s не могут быть загружены" @@ -1156,41 +1489,61 @@ msgstr "Невозможно загрузить медиафайл." msgid "errors.character-limit-exceeded" msgstr "Превышен лимит символов" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/libraries.cljs:1778 +msgid "errors.circular-library-reference" +msgstr "Не удаётся добавить библиотеку: возникнет циклическая зависимость" + +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 +msgid "errors.clipboard-api-unavailable" +msgstr "" +"Clipboard API недоступен. Откройте Penpot по HTTPS, чтобы разрешить доступ " +"к буферу обмена" + +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Ваш браузер не поддерживает эту операцию" -#: src/app/main/errors.cljs:773 +#: src/app/util/clipboard.cljs:271 +msgid "errors.clipboard-permission-denied" +msgstr "" +"Доступ к буферу обмена запрещён. Разрешите его в настройках браузера, чтобы " +"вставлять содержимое" + +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "В комментарии произошла ошибка" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:586 +msgid "errors.connection-error" +msgstr "Нет связи с сервером. Проверьте соединение и попробуйте снова." + +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Извините! Это старый файл, использующий устаревший тип ресурсов Penpot, и " "вы не можете его открыть." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "чтобы мы могли вам помочь." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Хоть Penpot больше не поддерживает этот тип Ресурсов, вы можете" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "Связаться с нами" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Невозможно переместить родительский набор в собственный дочерний путь." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Такая эл. почта уже используется" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Эл. почта уже подтверждена." @@ -1202,37 +1555,37 @@ msgstr "Нельзя указывать в качестве пароля адр msgid "errors.email-does-not-match-invitation" msgstr "Адрес электронной почты не соответствует приглашению." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Домен не разрешён" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Эл. почта «%s» постоянно недоступна." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "Эл. почта «%s» была отмечена как спам или постоянно недоступна." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Похоже, вы открываете файл, в котором включена функция '%s', но текущая " "версия penpot её не поддерживает или она отключена." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Функция «%s» не поддерживается." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" -msgstr "Должен содержать не более 1 символа." +msgstr "Должен содержать не более %s символов." #: ../common/src/app/common/schema.cljc:1068 msgid "errors.field-min-length" msgstr "Должен содержать не менее 1 символа." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Пустое поле" @@ -1240,18 +1593,24 @@ msgstr "Пустое поле" msgid "errors.field-not-all-whitespace" msgstr "Имя должно содержать какой-либо символ, отличный от пробела." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Похоже, что существует несоответствие между включенными функциями и " "функциями файла, который вы пытаетесь открыть. Прежде чем файл можно будет " "открыть, необходимо применить миграцию для «%s»." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/ui/dashboard/fonts.cljs:358 +msgid "errors.font-family-invalid-chars" +msgstr "" +"Имя семейства шрифтов может содержать только буквы, цифры, пробелы, дефисы, " +"подчёркивания и точки." + +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Что-то пошло не так." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Произошла ошибка в работе веб-воркера." @@ -1267,7 +1626,7 @@ msgstr "Неверные данные" msgid "errors.invalid-email" msgstr "Пожалуйста, введите действительный адрес электронной почты" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Эл. почта для подтверждения должна совпадать" @@ -1279,6 +1638,16 @@ msgstr "Неверный код восстановления." msgid "errors.invalid-text" msgstr "Неверный текст" +#: src/app/main/ui/static.cljs:88 +msgid "errors.invite-email-mismatch" +msgstr "" +"Это приглашение для другой почты. Выйдите и войдите под приглашённым " +"аккаунтом или попросите владельца команды прислать новое приглашение." + +#: src/app/main/ui/static.cljs:92 +msgid "errors.invite-expired" +msgstr "Срок приглашения истёк. Попросите владельца команды прислать Вам новое." + #: src/app/main/ui/static.cljs:95 msgid "errors.invite-invalid" msgstr "Приглашение недействительно" @@ -1291,11 +1660,11 @@ msgstr "Возможно, это приглашение отменено или msgid "errors.ldap-disabled" msgstr "Вход c LDAP отключён." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Вы достигли указанного значения «%s». Обратитесь в службу поддержки." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Достигнуто максимальное (%s) количество адресов электронной почты, которые " @@ -1313,22 +1682,34 @@ msgstr "Формат медиа не соответствует расширен msgid "errors.media-type-not-allowed" msgstr "Неверное медиа." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "Невозможно пригласить адресата с недоступным адресом электронной почты." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Миграция в процессе" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Только создатель версии может ее заблокировать" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Разблокировать версию может только ее создатель" +#: src/app/main/data/nitrate.cljs:209 +msgid "errors.organization-leave.no-valid-teams" +msgstr "Не удалось покинуть организацию. Попробуйте снова." + +#: src/app/main/data/nitrate.cljs:210 +msgid "errors.organization-leave.organization-owner-cannot-leave" +msgstr "Владелец не может покинуть организацию." + +#: src/app/main/ui/auth/verify_token.cljs:98 +msgid "errors.organization-not-found" +msgstr "Такой организации не существует" + #: src/app/main/ui/auth/recovery.cljs:25, src/app/main/ui/settings/password.cljs:73 msgid "errors.password-invalid-confirmation" msgstr "Пароль для подтверждения должен совпадать" @@ -1337,7 +1718,7 @@ msgstr "Пароль для подтверждения должен совпад msgid "errors.password-too-short" msgstr "Пароль должен быть минимум 8 символов" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Недопустимые данные в буфере обмена" @@ -1345,7 +1726,7 @@ msgstr "Недопустимые данные в буфере обмена" msgid "errors.profile-blocked" msgstr "Профиль заблокирован" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "Ваш адрес электронной почты не доступен." @@ -1353,97 +1734,270 @@ msgstr "Ваш адрес электронной почты не доступе msgid "errors.registration-disabled" msgstr "Регистрация сейчас отключена." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:700 +msgid "errors.save-failed" +msgstr "" +"Автосохранение не работает из-за ошибки. Сообщите о ней в поддержку и " +"перезагрузите страницу, чтобы продолжить с последней сохранённой версии." + +#: src/app/main/data/persistence.cljs:447 +msgid "errors.save-retrying" +msgstr "Соединение потеряно. Пробуем сохранить Ваши изменения." + +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG недействителен или имеет неправильный формат" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Обнаружена несовместимая функция «%s»" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Недостаточно участников, чтобы покинуть команду, вероятно, вы хотите её " "удалить." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Участник, которого вы пытаетесь назначить, не существует." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:191 +msgid "errors.team-leave.only-owner-can-delete" +msgstr "Удалить команду может только её владелец." + +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Нужно переназначить роль владельца перед тем, как покинуть команду." +#: ../common/src/app/common/types/team.cljc:27 +msgid "errors.team-name-invalid-chars" +msgstr "Название команды не может содержать символы: «.», «:» или «/»" + #: src/app/main/ui/workspace/tokens/sets/helpers.cljs:25, src/app/main/ui/workspace/tokens/sets/helpers.cljs:46, ../common/src/app/common/files/tokens.cljc:291 msgid "errors.token-set-already-exists" msgstr "Набор с таким именем уже существует" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "Невозможно завершить перенос, набор с таким именем уже существует по " "указанному пути." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Вариант темы с таким же названием уже существует" +#: ../common/src/app/common/files/tokens.cljc:351 +msgid "errors.token-theme-not-existing-sets" +msgstr "Тема ссылается на несуществующие наборы: %s" + +#: src/app/main/data/workspace/tokens/errors.cljs:50, ../common/src/app/common/files/tokens.cljc:230 +msgid "errors.tokens.circular-reference" +msgstr "У токена циклическая ссылка" + +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 +msgid "errors.tokens.composite-line-height-needs-font-size" +msgstr "" +"Line Height зависит от Font Size. Добавьте Font Size — и значение " +"посчитается." + +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 +msgid "errors.tokens.empty-field" +msgstr "Поле не может быть пустым" + +#: src/app/main/data/workspace/tokens/errors.cljs:42, ../common/src/app/common/files/tokens.cljc:34 +msgid "errors.tokens.empty-input" +msgstr "Значение токена не может быть пустым" + +#: src/app/main/data/workspace/tokens/errors.cljs:16 +msgid "errors.tokens.error-parse" +msgstr "Ошибка импорта: не удалось разобрать JSON." + +#: src/app/main/data/workspace/tokens/errors.cljs:33, src/app/main/data/workspace/tokens/errors.cljs:38 +msgid "errors.tokens.import-error" +msgstr "Ошибка импорта:" + +#: src/app/main/data/workspace/tokens/errors.cljs:54 +msgid "errors.tokens.invalid-color" +msgstr "Неверный цвет: %s" + +#: src/app/main/data/workspace/tokens/errors.cljs:124 +msgid "errors.tokens.invalid-font-family-token-value" +msgstr "Неверное значение: только ссылка на токен font-family" + +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 +msgid "errors.tokens.invalid-font-weight-token-value" +msgstr "" +"Неверная насыщенность: числа (100–950) или имена (thin, light, regular, " +"bold и так далее), после — «Italic»" + +#: src/app/main/data/workspace/tokens/errors.cljs:24 +msgid "errors.tokens.invalid-json" +msgstr "Ошибка импорта: неверные данные токенов в JSON." + +#: src/app/main/data/workspace/tokens/errors.cljs:28 +msgid "errors.tokens.invalid-json-token-name" +msgstr "Ошибка импорта: неверное имя токена в JSON." + +#: src/app/main/data/workspace/tokens/errors.cljs:29 +msgid "errors.tokens.invalid-json-token-name-detail" +msgstr "" +"«%s» — неверное имя токена.\n" +"Имена токенов — только буквы и цифры через «.» и не с «$» в начале." + +#: src/app/main/data/workspace/tokens/errors.cljs:136 +msgid "errors.tokens.invalid-shadow-type-token-value" +msgstr "Неверный тип тени: только «innerShadow» или «dropShadow»" + +#: src/app/main/data/workspace/tokens/errors.cljs:112 +msgid "errors.tokens.invalid-text-case-token-value" +msgstr "Неверное значение: только none, Uppercase, Lowercase или Capitalize" + +#: src/app/main/data/workspace/tokens/errors.cljs:116 +msgid "errors.tokens.invalid-text-decoration-token-value" +msgstr "Неверное значение: только none, underline и strike-through" + +#: src/app/main/data/workspace/tokens/errors.cljs:148 +msgid "errors.tokens.invalid-token-value-shadow" +msgstr "Неверное значение: нужна ссылка на составной токен тени." + +#: src/app/main/data/workspace/tokens/errors.cljs:128 +msgid "errors.tokens.invalid-token-value-typography" +msgstr "Неверное значение: нужна ссылка на составной токен типографики." + +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 +msgid "errors.tokens.invalid-value" +msgstr "Неверное значение токена: %s" + +#: src/app/main/data/workspace/tokens/errors.cljs:88 +msgid "errors.tokens.missing-references" +msgstr "Нет ссылок на токены: " + +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +msgid "errors.tokens.name-collision" +msgstr "Группа %s совпадает по имени с токеном в другом активном наборе." + +#: src/app/main/data/workspace/tokens/errors.cljs:20 +msgid "errors.tokens.no-token-files-found" +msgstr "В файле нет токенов, наборов и тем." + +#: src/app/main/data/workspace/tokens/errors.cljs:58 +msgid "errors.tokens.number-too-large" +msgstr "Неверное значение токена. Итог слишком велик: %s" + +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +msgid "errors.tokens.opacity-range" +msgstr "Непрозрачность — от 0 до 100% или от 0 до 1 (например, 50% или 0.5)." + +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 +msgid "errors.tokens.self-reference" +msgstr "У токена ссылка на себя" + +#: src/app/main/data/workspace/tokens/errors.cljs:140 +msgid "errors.tokens.shadow-blur-range" +msgstr "Размытие тени — не меньше 0." + +#: src/app/main/data/workspace/tokens/errors.cljs:144 +msgid "errors.tokens.shadow-spread-range" +msgstr "Распространение тени — не меньше 0." + +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 +msgid "errors.tokens.stroke-width-range" +msgstr "Толщина обводки — не меньше 0." + +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 +msgid "errors.tokens.value-with-percent" +msgstr "Неверное значение: символ % запрещён." + +#: src/app/main/data/workspace/tokens/errors.cljs:96 +msgid "errors.tokens.value-with-units" +msgstr "Неверное значение: единицы запрещены." + #: src/app/main/data/media.cljs:77 msgid "errors.unexpected-error" msgstr "Произошла ошибка." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Неизвестный токен" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Эта версия уже заблокирована" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Эта версия заблокирована и не может быть удалена другими пользователями" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Файл имеет несовместимый номер версии" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/auth/recovery.cljs:44, src/app/main/ui/auth/register.cljs:125, src/app/main/ui/settings/password.cljs:42 +msgid "errors.weak-password" +msgstr "Пароль не соответствует требованиям" + +#: src/app/main/ui/auth/recovery.cljs:37, src/app/main/ui/auth/register.cljs:119, src/app/main/ui/settings/password.cljs:35 +#, backend +msgid "errors.weak-password.insufficient-digits" +msgstr "Минимум 1 цифра" + +#: src/app/main/ui/auth/recovery.cljs:38, src/app/main/ui/auth/register.cljs:120, src/app/main/ui/settings/password.cljs:36 +#, backend +msgid "errors.weak-password.insufficient-lowercase" +msgstr "Минимум 1 строчная буква" + +#: src/app/main/ui/auth/recovery.cljs:40, src/app/main/ui/auth/register.cljs:122, src/app/main/ui/settings/password.cljs:38 +#, backend +msgid "errors.weak-password.insufficient-special" +msgstr "Минимум 1 спецсимвол" + +#: src/app/main/ui/auth/recovery.cljs:39, src/app/main/ui/auth/register.cljs:121, src/app/main/ui/settings/password.cljs:37 +#, backend +msgid "errors.weak-password.insufficient-uppercase" +msgstr "Минимум 1 заглавная буква" + +#: src/app/main/ui/auth/recovery.cljs:36, src/app/main/ui/auth/register.cljs:118, src/app/main/ui/settings/password.cljs:34 +#, backend +msgid "errors.weak-password.too-short" +msgstr "Минимум 8 символов" + +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "" "WebGL прекратил работу. Пожалуйста, перезагрузите страницу чтобы сбросить " "его" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Упс! Контекст холста был потерян" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Ошибка подключения, URL недоступен" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL-адрес не проходит проверку." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Последняя доставка не удалась." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Ошибка при проверке SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Время вышло" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Неожиданная ошибка при проверке" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Неожиданный статус %s" @@ -1463,17 +2017,17 @@ msgstr "Описание" msgid "feedback.description-placeholder" msgstr "Пожалуйста, опишите причину вашего обращения" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Мы рады вас здесь видеть. Если вам нужна помощь, пожалуйста, поищите ответ, " "возможно он уже есть." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Сообщество Penpot" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Другие способы связаться с нами" @@ -1497,11 +2051,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Связаться с нами" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Здесь, чтобы помочь с вашими техническими запросами." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Аккаунт поддержки в X" @@ -1521,14 +2075,60 @@ msgstr "Идея" msgid "feedback.type.issue" msgstr "Вопрос" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Скачать файлы" +#: src/app/main/ui/exports/files.cljs:431 +msgid "files-export-modal.options.detach-libraries.message" +msgstr "Ресурсы связанных библиотек не попадут в файл." + +#: src/app/main/ui/exports/files.cljs:425 +msgid "files-export-modal.options.detach-libraries.title" +msgstr "Считать ресурсы простыми объектами" + +#: src/app/main/ui/exports/files.cljs:429 +msgid "files-export-modal.options.include-libraries.message" +msgstr "Файлы со связанными библиотеками попадут в экспорт и сохранят связи." + +#: src/app/main/ui/exports/files.cljs:423 +msgid "files-export-modal.options.include-libraries.title" +msgstr "Экспорт файла и библиотек" + +#: src/app/main/ui/exports/files.cljs:432 +msgid "files-export-modal.options.link-later.message" +msgstr "" +"При импорте можно будет связать существующие библиотеки с совпадающими " +"именами." + +#: src/app/main/ui/exports/files.cljs:426 +msgid "files-export-modal.options.link-later.title" +msgstr "Связать подходящие библиотеки при импорте" + +#: src/app/main/ui/exports/files.cljs:430 +msgid "files-export-modal.options.merge-libraries.message" +msgstr "Файл экспортируется, а ресурсы библиотек вольются в локальную библиотеку." + +#: src/app/main/ui/exports/files.cljs:424 +msgid "files-export-modal.options.merge-libraries.title" +msgstr "Встроить ресурсы библиотек в файл" #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "Произошла ошибка" +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 +msgid "import-shortcuts.apply" +msgstr "Применить" + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:102 +msgid "import-shortcuts.diff-modal-text" +msgstr "Импорт изменит эти сочетания." + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:98 +msgid "import-shortcuts.diff-modal-title" +msgstr "Импорт сочетаний" + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:148 +msgid "import-shortcuts.no-changes" +msgstr "Различий между текущими и импортированными сочетаниями нет." + #: src/app/main/ui/components/color_input.cljs:31 msgid "inspect.attributes.color" msgstr "Цвет" @@ -1561,7 +2161,7 @@ msgstr "Предварительный просмотр заливки фигу msgid "inspect.attributes.shadow" msgstr "Тень" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Размер и положение" @@ -1569,55 +2169,51 @@ msgstr "Размер и положение" msgid "inspect.attributes.stroke" msgstr "Обводка" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Центр" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Внутрь" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Наружу" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Текст" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Гарнитура" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Размер" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Шрифт" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Трекинг" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Интерлиньяж" +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 +msgid "inspect.attributes.typography.mixed-font-family" +msgstr "Разные семейства" + #: src/app/main/ui/inspect/attributes/text.cljs:114 msgid "inspect.attributes.typography.text-decoration.none" msgstr "Нет" +#: src/app/main/ui/inspect/attributes/text.cljs:117 +msgid "inspect.attributes.typography.text-decoration.strikethrough" +msgstr "Зачёркнутый" + #: src/app/main/ui/inspect/attributes/text.cljs:115 msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Подчёркнутый" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Регистр Заголовка" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Нижний регистр" @@ -1629,7 +2225,7 @@ msgstr "Какой есть" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Не установлено" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Верхний регистр" @@ -1675,6 +2271,10 @@ msgstr "Копировать" msgid "inspect.subtitle.main" msgstr "Основной компонент" +#: src/app/main/ui/inspect/right_sidebar.cljs:176 +msgid "inspect.tabs-switcher-label" +msgstr "Переключить вкладку просмотра" + #: src/app/main/ui/inspect/right_sidebar.cljs:142 msgid "inspect.tabs.code.selected.circle" msgstr "Круг" @@ -1689,7 +2289,7 @@ msgstr "Кривая" #: src/app/main/ui/inspect/right_sidebar.cljs:145 msgid "inspect.tabs.code.selected.frame" -msgstr "Кадр" +msgstr "Доска" #: src/app/main/ui/inspect/right_sidebar.cljs:146 msgid "inspect.tabs.code.selected.group" @@ -1751,47 +2351,259 @@ msgstr "Полученное значение:" msgid "inspect.tabs.styles.variants-panel" msgstr "Вариант свойств" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/settings/integrations.cljs:612 +msgid "integrations.access-tokens.create" +msgstr "Создать токен доступа" + +#: src/app/main/ui/settings/integrations.cljs:628 +msgid "integrations.access-tokens.empty.add-one" +msgstr "Нажмите «Создать токен доступа»." + +#: src/app/main/ui/settings/integrations.cljs:627 +msgid "integrations.access-tokens.empty.no-access-tokens" +msgstr "Токенов пока нет." + +#: src/app/main/ui/settings/integrations.cljs:602 +msgid "integrations.access-tokens.personal" +msgstr "Личные токены доступа" + +#: src/app/main/ui/settings/integrations.cljs:607 +msgid "integrations.access-tokens.personal.description" +msgstr "" +"Личные токены — замена логину с паролем: открывают приложениям внутреннее " +"API Penpot" + +#: src/app/main/ui/settings/integrations.cljs:68 +msgid "integrations.copy-to-clipboard" +msgstr "Скопировать в буфер обмена" + +#: src/app/main/ui/settings/integrations.cljs:253 +msgid "integrations.create-access-token.title" +msgstr "Создать токен доступа" + +#: src/app/main/ui/settings/integrations.cljs:252 +msgid "integrations.create-access-token.title.created" +msgstr "Токен доступа создан" + +#: src/app/main/ui/settings/integrations.cljs:371 +msgid "integrations.delete-token.accept" +msgstr "Удалить токен" + +#: src/app/main/ui/settings/integrations.cljs:370 +msgid "integrations.delete-token.message" +msgstr "Вы уверены, что хотите удалить токен?" + +#: src/app/main/ui/settings/integrations.cljs:369 +msgid "integrations.delete-token.title" +msgstr "Удалить токен" + +#: src/app/main/ui/settings/integrations.cljs:213 +msgid "integrations.expiration-180-days" +msgstr "180 дней" + +#: src/app/main/ui/settings/integrations.cljs:210 +msgid "integrations.expiration-30-days" +msgstr "30 дней" + +#: src/app/main/ui/settings/integrations.cljs:211 +msgid "integrations.expiration-60-days" +msgstr "60 дней" + +#: src/app/main/ui/settings/integrations.cljs:212 +msgid "integrations.expiration-90-days" +msgstr "90 дней" + +#: src/app/main/ui/settings/integrations.cljs:208 +msgid "integrations.expiration-date.label" +msgstr "Срок действия" + +#: src/app/main/ui/settings/integrations.cljs:209 +msgid "integrations.expiration-never" +msgstr "Никогда" + +#: src/app/main/ui/settings/integrations.cljs:393 +msgid "integrations.expired-on" +msgstr "Истёк %s" + +#: src/app/main/ui/settings/integrations.cljs:394 +msgid "integrations.expires-on" +msgstr "Истекает %s" + +#: src/app/main/ui/settings/integrations.cljs:294 +msgid "integrations.generate-mcp-key.title" +msgstr "Создать MCP-ключ" + +#: src/app/main/ui/settings/integrations.cljs:292 +msgid "integrations.generate-mcp-key.title.created" +msgstr "MCP-ключ создан" + +#: src/app/main/ui/settings/integrations.cljs:193 +msgid "integrations.info.mcp-server" +msgstr "MCP-сервер Penpot даёт MCP-клиентам прямой доступ к файлам дизайна." + +#: src/app/main/ui/settings/integrations.cljs:119 +msgid "integrations.mcp-key.will-expire" +msgstr "MCP-ключ истекает %s" + +#: src/app/main/ui/settings/integrations.cljs:122 +msgid "integrations.mcp-key.will-not-expire" +msgstr "MCP-ключ бессрочный" + +#: src/app/main/ui/settings/integrations.cljs:494 +msgid "integrations.mcp-server.description" +msgstr "MCP-сервер Penpot даёт MCP-клиентам прямой доступ к файлам дизайна." + +#: src/app/main/ui/settings/integrations.cljs:577 +msgid "integrations.mcp-server.mcp-keys.help" +msgstr "Как настроить MCP-клиенты" + +#: src/app/main/ui/settings/integrations.cljs:564 +msgid "integrations.mcp-server.mcp-keys.info" +msgstr "Этот URL сервера нужен MCP-клиенту для связи с MCP-сервером Penpot." + +#: src/app/main/ui/settings/integrations.cljs:544 +msgid "integrations.mcp-server.mcp-keys.regenerate" +msgstr "Перевыпустить MCP-ключ" + +#: src/app/main/ui/settings/integrations.cljs:537 +msgid "integrations.mcp-server.mcp-keys.title" +msgstr "MCP-ключ" + +#: src/app/main/ui/settings/integrations.cljs:545 +msgid "integrations.mcp-server.mcp-keys.tootip" +msgstr "MCP-ключ нужен для настройки клиента" + +#: src/app/main/ui/settings/integrations.cljs:500 +msgid "integrations.mcp-server.status" +msgstr "Статус" + +#: src/app/main/ui/settings/integrations.cljs:520 +msgid "integrations.mcp-server.status.disabled" +msgstr "Отключён" + +#: src/app/main/ui/settings/integrations.cljs:519 +msgid "integrations.mcp-server.status.enabled" +msgstr "Включён" + +#: src/app/main/ui/settings/integrations.cljs:510 +msgid "integrations.mcp-server.status.expired.0" +msgstr "Ключ для связи с MCP-сервером истёк. Соединение не установить." + +#: src/app/main/ui/settings/integrations.cljs:515 +msgid "integrations.mcp-server.status.expired.1" +msgstr "Перевыпустите ключ и обновите настройки клиента." + +#: src/app/main/ui/settings/integrations.cljs:489 +msgid "integrations.mcp-server.title" +msgstr "MCP-сервер" + +#: src/app/main/ui/settings/integrations.cljs:200 +msgid "integrations.name.label" +msgstr "Имя" + +#: src/app/main/ui/settings/integrations.cljs:201 +msgid "integrations.name.placeholder" +msgstr "Имя подскажет, для чего токен" + +#: src/app/main/ui/settings/integrations.cljs:392 +msgid "integrations.no-expiration" +msgstr "Без срока" + +#: src/app/main/ui/settings/integrations.cljs:478 +msgid "integrations.notification.success.copied-link" +msgstr "Ссылка скопирована в буфер обмена" + +#: src/app/main/ui/settings/integrations.cljs:241, src/app/main/ui/settings/integrations.cljs:274, src/app/main/ui/settings/integrations.cljs:319 +msgid "integrations.notification.success.created" +msgstr "Токен успешно создан" + +#: src/app/main/ui/settings/integrations.cljs:87 +msgid "integrations.notification.success.mcp-key-copied" +msgstr "MCP-ключ скопирован" + +#: src/app/main/ui/settings/integrations.cljs:444 +msgid "integrations.notification.success.mcp-server-disabled" +msgstr "MCP-сервер отключён" + +#: src/app/main/ui/settings/integrations.cljs:443 +msgid "integrations.notification.success.mcp-server-enabled" +msgstr "MCP-сервер включён" + +#: src/app/main/ui/settings/integrations.cljs:88 +msgid "integrations.notification.success.token-copied" +msgstr "Токен скопирован" + +#: src/app/main/ui/settings/integrations.cljs:337 +msgid "integrations.regenerate-mcp-key.info" +msgstr "" +"Перевыпуск сразу отзовёт текущий ключ. Приложения на нём перестанут " +"работать." + +#: src/app/main/ui/settings/integrations.cljs:336 +msgid "integrations.regenerate-mcp-key.title" +msgstr "Перевыпустить MCP-ключ" + +#: src/app/main/ui/settings/integrations.cljs:334 +msgid "integrations.regenerate-mcp-key.title.created" +msgstr "MCP-ключ перевыпущен" + +#: src/app/main/ui/settings/integrations.cljs:651 +msgid "integrations.title" +msgstr "Интеграции" + +#: src/app/main/ui/settings/integrations.cljs:108 +msgid "integrations.token.info.non-recuperable" +msgstr "Ключ уникален и невосстановим. Потеряете — создайте новый." + +#: src/app/main/ui/settings/integrations.cljs:120 +msgid "integrations.token.will-expire" +msgstr "Токен истекает %s" + +#: src/app/main/ui/settings/integrations.cljs:123 +msgid "integrations.token.will-not-expire" +msgstr "Токен бессрочный" + +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Отметить все как прочитанное" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Сочетания клавиш" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "О Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Принять" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Действующий" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Добавить" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Добавить произвольный шрифт" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Добавление..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Администратор" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Все" @@ -1799,15 +2611,15 @@ msgstr "Все" msgid "labels.and" msgstr "и" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Назад" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "Возможны технические работы. Пожалуйста, зайдите чуть позже." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Bad Gateway" @@ -1815,15 +2627,27 @@ msgstr "Bad Gateway" msgid "labels.blur" msgstr "Размытие" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/ui/inspect/attributes/blur.cljs:31, src/app/main/ui/inspect/styles/style_box.cljs:32, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:262, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:274 +msgid "labels.blur-effects" +msgstr "Эффекты размытия" + +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Отмена" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/ui/dashboard/sidebar.cljs:546 +msgid "labels.check-for-updates" +msgstr "Проверить обновления" + +#: src/app/main/ui/dashboard/sidebar.cljs:545 +msgid "labels.checking-for-updates" +msgstr "Проверка обновлений…" + +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Закрыть" @@ -1831,7 +2655,7 @@ msgstr "Закрыть" msgid "labels.code" msgstr "Код" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Свернуть" @@ -1843,15 +2667,15 @@ msgstr "Цвет" msgid "labels.comment" msgstr "Комментарий" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Комментарии" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Сообщество" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Сообщество и содействие" @@ -1863,19 +2687,19 @@ msgstr "Вычисленные" msgid "labels.confirm-password" msgstr "Подтвердите пароль" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Связаться с поддержкой" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Связаться с нами" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Продолжить" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Вы можете продолжить с аккаунтом Penpot" @@ -1887,31 +2711,39 @@ msgstr "Копировать" msgid "labels.copy-color" msgstr "Копировать цвет" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Скопировать ссылку" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019–по настоящее время" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Создать" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Создать новую команду" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Введите название новой команды" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:111, src/app/main/ui/settings/restore_shortcuts_modal.cljs:102 +msgid "labels.current" +msgstr "Текущее" + +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Панель управления" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:104 +msgid "labels.default" +msgstr "По умолчанию" + +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Удалить" @@ -1923,7 +2755,7 @@ msgstr "Удалить комментарий" msgid "labels.delete-comment-thread" msgstr "Удалить обсуждение" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Удалить приглашение" @@ -1931,23 +2763,35 @@ msgstr "Удалить приглашение" msgid "labels.delete-multi-files" msgstr "Удалить файлы (%s)" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/settings/profile.cljs:122 +msgid "labels.delete-profile-photo.message" +msgstr "Вы уверены, что хотите удалить фото профиля?" + +#: src/app/main/ui/settings/profile.cljs:121 +msgid "labels.delete-profile-photo.title" +msgstr "Удалить фото профиля" + +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Удаленные" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Разработка" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Отказаться" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "Скачать %s" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:300 +msgid "labels.download-simple" +msgstr "Скачать" + +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Черновики" @@ -1955,27 +2799,31 @@ msgstr "Черновики" msgid "labels.duplicate" msgstr "Дубликат" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Редактировать" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Редактор" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Пусто" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Ошибка" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/data/workspace/versions.cljs:314 +msgid "labels.exit" +msgstr "Выйти" + +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Истекло" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Экспорт" @@ -1987,7 +2835,7 @@ msgstr "Обратная связь отключена" msgid "labels.feedback-sent" msgstr "Отзыв отправлен" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -1995,55 +2843,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Заливка" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +msgid "labels.flex-layout" +msgstr "Flex-макет" + +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Семейство шрифтов" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Начертания" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Шрифты" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Репозиторий на Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Оставить отзыв" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Назад" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Графический дизайн" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 +msgid "labels.grid-layout" +msgstr "Grid-макет" + +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Помощь" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Помощь и обучение" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Скрыть" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Скрыть решённые комментарии" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Импорт" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Недействующий" @@ -2051,49 +2907,53 @@ msgstr "Недействующий" msgid "labels.info" msgstr "Информация" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Установленные шрифты" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/settings/sidebar.cljs:130 +msgid "labels.integrations" +msgstr "Интеграции" + +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Что-то пошло не так." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "" "Повторите попытку или свяжитесь с технической поддержкой, чтобы сообщить об " "ошибке." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Внутренняя ошибка" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Приглашения" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Язык" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" -msgstr "Слой" +msgstr "Макет" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Центр обучения" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Библиотеки и шаблоны" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Загрузка…" @@ -2101,7 +2961,7 @@ msgstr "Загрузка…" msgid "labels.lock" msgstr "Заблокировать" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Войти или зарегистрироваться" @@ -2109,19 +2969,19 @@ msgstr "Войти или зарегистрироваться" msgid "labels.login" msgstr "Логин" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Выйти" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Маркетинг" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Участник" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Участники" @@ -2133,45 +2993,69 @@ msgstr "Отметить" msgid "labels.new-password" msgstr "Новый пароль" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Далее" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/static.cljs:367 +msgid "labels.nitrate-not-configured.desc-message" +msgstr "Этому экземпляру нужен шаг настройки из последней версии." + +#: src/app/main/ui/static.cljs:370 +msgid "labels.nitrate-not-configured.learn-more" +msgstr "Загляните в наше" + +#: src/app/main/ui/static.cljs:365 +msgid "labels.nitrate-not-configured.main-message" +msgstr "Penpot пока не может запуститься." + +#: src/app/main/ui/static.cljs:375 +msgid "labels.nitrate-not-configured.technical-guide" +msgstr "техническое руководство" + +#: src/app/main/ui/static.cljs:355 +msgid "labels.nitrate-unavailable.main-message" +msgstr "Penpot временно недоступен из-за системной ошибки. Попробуйте чуть позже." + +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "" "Вы ничего не пропустили! Здесь будут появляться уведомления о новых " "комментариях." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Нет ожидающих приглашений." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Объединяйтесь и творите вместе." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/components/select.cljs:256 +msgid "labels.no-matches" +msgstr "Совпадений нет" + +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Ой!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Уведомления" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "Файл" msgstr[1] "Файлы (%s)" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" -msgstr[0] "Кадр" -msgstr[1] "Кадры (%s)" +msgstr[0] "1 доска" +msgstr[1] "%s досок" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "Проект" @@ -2181,39 +3065,43 @@ msgstr[1] "Проекты (%s)" msgid "labels.old-password" msgstr "Старый пароль" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 +msgid "labels.open" +msgstr "Открыть" + +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Параметры" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Другое (укажите)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Другое" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Владелец" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Пароль" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Ожидание" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Журнал изменений Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Хаб Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Закреплённые Проекты" @@ -2221,31 +3109,31 @@ msgstr "Закреплённые Проекты" msgid "labels.post" msgstr "Опубликовать" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Предыдущий" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Дизайн продуктов или UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Управление продуктом" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Профиль" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Проекты" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Недавние" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Референс" @@ -2253,27 +3141,27 @@ msgstr "Референс" msgid "labels.refresh" msgstr "Перезагрузить" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Примечания к выпуску" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Обновить страницу" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Удалить" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Удалить участника" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Переименовать" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Переименовать команду" @@ -2297,11 +3185,11 @@ msgstr "новый ответ" msgid "labels.reply.thread" msgstr "Ответить" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Отправить повторно" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Снова отправить приглашение" @@ -2309,43 +3197,43 @@ msgstr "Снова отправить приглашение" msgid "labels.restore" msgstr "Восстановить" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Повторить" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Роль" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Сохранить" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Поиск" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Искать шрифт" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Выбрать вариант" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Отправить" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Отправка…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Мы проводим диагностику наших систем." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Сервис недоступен" @@ -2353,55 +3241,73 @@ msgstr "Сервис недоступен" msgid "labels.sets" msgstr "Наборы" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Настройки" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Тень" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Поделиться" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Библиотеки" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Показать" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Все комментарии" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Показать список комментариев" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Показать только упоминания" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Только мои" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/import.cljs:808 +msgid "labels.skip" +msgstr "Пропустить" + +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Источники" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/static.cljs:562 +msgid "labels.sso-error.desc-message" +msgstr "" +"Вход через поставщика удостоверений Вашей организации не завершился. " +"Поставщик может быть недоступен, или Вашего аккаунта ещё нет в его каталоге." + +#: src/app/main/ui/static.cljs:563 +msgid "labels.sso-error.retry" +msgstr "Попробовать снова" + +#: src/app/main/ui/static.cljs:561 +msgid "labels.sso-error.title" +msgstr "Не удалось войти в %s" + +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Начать" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Состояние" @@ -2409,7 +3315,7 @@ msgstr "Состояние" msgid "labels.stroke" msgstr "Обводка" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Студент или преподаватель" @@ -2421,27 +3327,31 @@ msgstr "Стили" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Переключить" +#: src/app/main/ui/dashboard/organization_team_switch.cljs:657 +msgid "labels.team-management" +msgstr "Управление командой" + #: src/app/main/ui/inspect/styles/style_box.cljs:30 msgid "labels.text" msgstr "Текст" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Темы" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Руководства" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Типографика" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Неизвестная ошибка" @@ -2453,31 +3363,31 @@ msgstr "Разблокировать" msgid "labels.unpublish-multi-files" msgstr "Отменить публикацию %s файлов" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Обновить" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Обновить команду" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Загрузить" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Загрузить произвольные шрифты" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Загрузка…" -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Вариант" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Примечания к версии %s" @@ -2485,7 +3395,7 @@ msgstr "Примечания к версии %s" msgid "labels.view-only" msgstr "Только просмотр" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Наблюдатель" @@ -2493,7 +3403,13 @@ msgstr "Наблюдатель" msgid "labels.visibility" msgstr "Видимость" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/data/workspace/tokens/import_export.cljs:62 +msgid "labels.warning-count" +msgid_plural "labels.warning-count" +msgstr[0] "%s предупреждение" +msgstr[1] "%s предупреждения" + +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Вебхуки" @@ -2501,14 +3417,18 @@ msgstr "Вебхуки" msgid "labels.write-new-comment" msgstr "Написать комментарий" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(вы)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Ваш аккаунт" +#: src/app/main/ui/ds/product/loader.cljs:21 +msgid "loader.tips.01.message" +msgstr "Держите дизайны цельными: так их легко обновлять во всех проектах." + #: src/app/main/ui/ds/product/loader.cljs:20 msgid "loader.tips.01.title" msgstr "Переиспользуемые компоненты" @@ -2521,6 +3441,14 @@ msgstr "Работайте с командой в реальном времен msgid "loader.tips.02.title" msgstr "Совместная работа в реальном времени" +#: src/app/main/ui/ds/product/loader.cljs:25 +msgid "loader.tips.03.message" +msgstr "Проектируйте гибко с привычными настройками раскладки в духе CSS." + +#: src/app/main/ui/ds/product/loader.cljs:24 +msgid "loader.tips.03.title" +msgstr "Макеты как в CSS" + #: src/app/main/ui/ds/product/loader.cljs:27 msgid "loader.tips.04.message" msgstr "Получите CSS и SVG код напрямую из ваших макетов." @@ -2529,6 +3457,14 @@ msgstr "Получите CSS и SVG код напрямую из ваших ма msgid "loader.tips.04.title" msgstr "Экспорт в код" +#: src/app/main/ui/ds/product/loader.cljs:29 +msgid "loader.tips.05.message" +msgstr "Делитесь ресурсами и стилями, чтобы держать единство." + +#: src/app/main/ui/ds/product/loader.cljs:28 +msgid "loader.tips.05.title" +msgstr "Библиотеки дизайна" + #: src/app/main/ui/ds/product/loader.cljs:31 msgid "loader.tips.06.message" msgstr "Вдохните жизнь в ваши идеи с помощью анимаций и переходов." @@ -2537,6 +3473,20 @@ msgstr "Вдохните жизнь в ваши идеи с помощью ан msgid "loader.tips.06.title" msgstr "Интерактивные прототипы" +#: src/app/main/ui/ds/product/loader.cljs:33 +msgid "loader.tips.07.message" +msgstr "Penpot строит на SVG и CSS, и разработка идёт без швов." + +#: src/app/main/ui/ds/product/loader.cljs:32 +msgid "loader.tips.07.title" +msgstr "Веб-стандарты" + +#: src/app/main/ui/ds/product/loader.cljs:35 +msgid "loader.tips.08.message" +msgstr "" +"Ускорьте работу удобными сочетаниями клавиш, например Shift + A для " +"автомакета." + #: src/app/main/ui/ds/product/loader.cljs:34 msgid "loader.tips.08.title" msgstr "Горячие клавиши" @@ -2553,6 +3503,10 @@ msgstr "Темный & светлый режим" msgid "loader.tips.10.message" msgstr "Дополните Penpot плагинами от сообщества для расширения функционала." +#: src/app/main/ui/ds/product/loader.cljs:38 +msgid "loader.tips.10.title" +msgstr "Поддержка плагинов" + #: src/app/main/ui/workspace/colorpicker.cljs:494 msgid "media.choose-image" msgstr "Выбрать изображение" @@ -2602,64 +3556,106 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Добавить \"%s\" как общую библиотеку" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/data/nitrate.cljs:320 +msgid "modals.before-leave-organization.message" +msgstr "" +"В некоторых командах Вы единственный участник. Эти команды удалятся вместе " +"с проектами и файлами." + +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 +msgid "modals.before-leave-organization.title" +msgstr "ПЕРЕД УХОДОМ ИЗ ОРГАНИЗАЦИИ" + +#: src/app/main/data/nitrate.cljs:323 +msgid "modals.before-leave-organization.warning" +msgstr "Все команды, где Вы единственный участник, будут удалены." + +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Большой сдвиг" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Подтвердить новую эл. почту" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Мы отправим эл. письмо для подтверждения личности на текущую эл. почту " "\"%s\"." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Новая эл. почта" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Изменить эл. почту" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Изменить эл. почту" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Создать вебхук" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Создать вебхук" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Ссылка полезной нагрузки" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://пример.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Отменить и сохранить мой аккаунт" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Да, удалить мой аккаунт" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Удалив аккаунт, вы потеряете все свои текущие проекты и архивы." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:77 +msgid "modals.delete-account.info.with-organizations" +msgstr "" +"Действие необратимо. Ваш аккаунт, подписка и организации удалятся сразу. Вы " +"также потеряете доступ к текущим проектам и файлам." + +#: src/app/main/ui/settings/delete_account.cljs:87 +msgid "modals.delete-account.owned-organizations.list-title" +msgstr "Организации, которые будут удалены" + +#: src/app/main/ui/settings/delete_account.cljs:102 +msgid "modals.delete-account.owned-organizations.members-count" +msgstr "Участников: %s" + +#: src/app/main/ui/settings/delete_account.cljs:100 +msgid "modals.delete-account.owned-organizations.teams-count" +msgstr "Команд: %s" + +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Вы уверены, что хотите удалить аккаунт?" +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:229 +msgid "modals.delete-asset-group.message" +msgid_plural "modals.delete-asset-group.message" +msgstr[0] "Вы уверены, что хотите удалить этот ресурс?" +msgstr[1] "Вы уверены, что хотите удалить %s ресурсов?" + +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:228 +msgid "modals.delete-asset-group.title" +msgstr "Удалить группу" + #: src/app/main/ui/comments.cljs:941 msgid "modals.delete-comment-thread.accept" msgstr "Удалить переписку" @@ -2706,34 +3702,46 @@ msgstr "Точно хотите удалить файлы (%s)?" msgid "modals.delete-file-multi-confirm.title" msgstr "Удаление файлов (%s)" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Вы действительно хотите удалить это начертание шрифта? Оно не будет " "загружаться, если используется в файле." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Удаление начертания шрифта" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Вы действительно хотите удалить этот шрифт? Он не будет загружаться, если " "используется в файле." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Удаление шрифта" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/data/team.cljs:159 +msgid "modals.delete-organization-team-confirm.message" +msgstr "Вы уверены, что хотите удалить команду из организации %s?" + +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Вы уверены, что хотите удалить эту страницу?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Удаление страницы" +#: src/app/main/ui/workspace/context_menu.cljs:822 +msgid "modals.delete-pages.body" +msgstr "Вы уверены, что хотите удалить выбранные страницы?" + +#: src/app/main/ui/workspace/context_menu.cljs:821 +msgid "modals.delete-pages.title" +msgstr "Удалить страницы" + #: src/app/main/ui/dashboard/project_menu.cljs:84 msgid "modals.delete-project-confirm.accept" msgstr "Удалить проект" @@ -2746,31 +3754,31 @@ msgstr "Вы уверены, что хотите удалить этот про msgid "modals.delete-project-confirm.title" msgstr "Удаление проекта" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Удалить файл" msgstr[1] "Удалить файлы" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Он не активирован ни в одном файле." msgstr[1] "Они не активированы ни в одном файле." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Эта библиотека активируется здесь: " msgstr[1] "Эти библиотеки активируются здесь: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Вы уверены, что хотите удалить этот файл?" msgstr[1] "Вы уверены, что хотите удалить эти файлы?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Удаление файла" @@ -2790,19 +3798,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Удаление команды" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Удалить участника" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Вы уверены, что хотите удалить этого участника из команды?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Удалить участника команды" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -2812,167 +3820,268 @@ msgstr[1] "" "Ресурсы, которые уже использовались в этих файлах, останутся там (дизайн не " "будет нарушен)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Удалить вебхук" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Вы уверены, что хотите удалить этот вебхук?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Удаление вебхука" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Изменить вебхук" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Изменить вебхук" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/workspace/tokens/import_from_library.cljs:75 +msgid "modals.import-library-tokens.description" +msgstr "" +"В библиотеке есть токены и темы, которые, вероятно, используют её " +"компоненты." + +#: src/app/main/ui/workspace/tokens/import_from_library.cljs:92 +msgid "modals.import-library-tokens.import" +msgstr "Импорт токенов" + +#: src/app/main/ui/workspace/tokens/import_from_library.cljs:72 +msgid "modals.import-library-tokens.title" +msgstr "Импортировать токены из библиотеки?" + +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Отправить приглашение" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Эл. почты, разделённые запятой" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Некоторые электронные письма отправлены от текущих членов команды. Их " "приглашения не будут отправлены." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/data/team.cljs:649 +msgid "modals.invite-restricted-members.all-blocked" +msgstr "" +"Приглашать можно только действующих участников организации. Никто из Вашего " +"списка не входит в неё, поэтому приглашения не отправлены." + +#: src/app/main/data/team.cljs:648 +msgid "modals.invite-restricted-members.all-blocked-title" +msgstr "Приглашения не отправлены" + +#: src/app/main/data/team.cljs:192 +msgid "modals.invite-restricted-members.all-organization-members-in-team" +msgstr "" +"В эту команду можно приглашать только участников организации «%s», а они " +"уже все в команде. Если нужна информация, свяжитесь с владельцем " +"организации." + +#: src/app/main/ui/dashboard/team.cljs:327 +msgid "modals.invite-restricted-members.blocked-addresses" +msgstr "Адреса, которым приглашение не придёт" + +#: src/app/main/ui/dashboard/team.cljs:344 +msgid "modals.invite-restricted-members.cancel" +msgstr "Отмена" + +#: src/app/main/ui/dashboard/team.cljs:317 +msgid "modals.invite-restricted-members.description" +msgstr "Приглашать можно только действующих участников организации." + +#: src/app/main/ui/dashboard/team.cljs:352 +msgid "modals.invite-restricted-members.send" +msgstr "Отправить приглашения" + +#: src/app/main/ui/dashboard/team.cljs:309 +msgid "modals.invite-restricted-members.title" +msgstr "Часть приглашений нельзя отправить" + +#: src/app/main/ui/dashboard/team.cljs:318 +msgid "modals.invite-restricted-members.warning" +msgstr "Часть людей из Вашего списка приглашений не получит." + +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Вы можете пригласить участников в команду, чтобы они получили доступ к " "этому и другим файлам команды." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Пригласить участников в команду" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Так как вы единственный участник этой команды, она будет удалена вместе с " "проектами и файлами." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Вы уверены, что хотите покинуть команду %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Вы не можете покинуть команду, если нет другого участника, которого можно " "было бы назначить владельцем. Возможно, вы захотите удалить команду." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Вы владелец этой команды. Прежде чем покинуть её, выберите другого " "участника, чтобы сделать его владельцем." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Назначить и покинуть" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Выберите участника для назначения" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Прежде чем вы уйдёте" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Покинуть команду" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Вы уверены, что хотите покинуть эту команду?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Выход из команды" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/dashboard/change_owner.cljs:163 +msgid "modals.leave-organization-and-reassign.hint" +msgstr "Вы владеете командами организации. Назначьте владельцем другого участника." + +#: src/app/main/ui/dashboard/change_owner.cljs:166 +msgid "modals.leave-organization-and-reassign.hint-delete" +msgstr "" +"В некоторых командах Вы единственный участник. Эти команды удалятся вместе " +"с проектами и файлами." + +#: src/app/main/ui/dashboard/change_owner.cljs:168 +msgid "modals.leave-organization-and-reassign.hint-promote" +msgstr "" +"Кроме того, Вы владеете командами организации. Назначьте владельцем другого " +"участника." + +#: src/app/main/data/nitrate.cljs:321, src/app/main/data/nitrate.cljs:330 +msgid "modals.leave-organization-confirm.accept" +msgstr "Покинуть организацию" + +#: src/app/main/data/nitrate.cljs:329 +msgid "modals.leave-organization-confirm.message" +msgstr "Вы навсегда потеряете доступ ко всем командам, проектам и файлам внутри." + +#: src/app/main/data/nitrate.cljs:328 +msgid "modals.leave-organization-confirm.title" +msgstr "Покинуть организацию %s?" + +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Переместить" msgstr[1] "Перемещение" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Вы уверены, что хотите переместить эту библиотеку?" msgstr[1] "Вы уверены, что хотите переместить эти библиотеки?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Переместить библиотеку" msgstr[1] "Переместить библиотеки" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Величина сдвига" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Передача прав собственности" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Если вы передадите права собственности, вы измените свою роль на " "администратора, потеряв некоторые права доступа в этой команде. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Вы текущий владелец этой команды. Вы уверены, что хотите сделать %s новым " "владельцем команды?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Новый владелец команды" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Опубликовать" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Ваша библиотека пуста. Вы уверены, что хотите опубликовать её?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Опубликовать пустую библиотеку" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/data/nitrate.cljs:377 +msgid "modals.remove-team-organization.accept" +msgstr "Убрать из организации" + +#: src/app/main/data/nitrate.cljs:375 +msgid "modals.remove-team-organization.info" +msgstr "" +"Проекты и файлы останутся доступны участникам команды, но настройки " +"организации больше не применятся." + +#: src/app/main/data/nitrate.cljs:374 +msgid "modals.remove-team-organization.text" +msgstr "Вы уверены, что хотите убрать команду «%s» из организации «%s»?" + +#: src/app/main/data/nitrate.cljs:373 +msgid "modals.remove-team-organization.title" +msgstr "УБРАТЬ КОМАНДУ ИЗ ОРГАНИЗАЦИИ" + +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Небольшое подталкивание" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Отменить публикацию" msgstr[1] "Отменить публикации" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Вы уверены, что хотите снять с публикации эту библиотеку?" msgstr[1] "Вы уверены, что хотите снять с публикации эти библиотеки?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Снять библиотеку с публикации" @@ -2996,11 +4105,199 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Обновить компонент в общей библиотеке" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:58 +msgid "nitrate.activation-code.expired-error" +msgstr "Срок кода истёк." + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:60 +msgid "nitrate.activation-code.invalid-error" +msgstr "Неверный код." + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:59 +msgid "nitrate.activation-code.used-error" +msgstr "Код уже использован." + +#: src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:58 +msgid "nitrate.activation-success.active-until" +msgstr "Тариф активен до %s." + +#: src/app/main/ui/dashboard/subscription.cljs:211, src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:69 +msgid "nitrate.activation-success.create-organization" +msgstr "Создать организацию" + +#: src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:64 +msgid "nitrate.activation-success.enjoy" +msgstr "Наслаждайтесь тарифом!" + +#: src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:61 +msgid "nitrate.activation-success.manage-info" +msgstr "Подпиской управляйте когда угодно — страница «Подписка» в данных аккаунта." + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:121 +msgid "nitrate.code-activation.footer-after" +msgstr "Отправьте файл на" + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:126 +msgid "nitrate.code-activation.footer-before" +msgstr "— и вышлем Вам код." + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:119 +msgid "nitrate.code-activation.footer-download" +msgstr "Скачать заявку" + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:114 +msgid "nitrate.code-activation.footer-title" +msgstr "Нужен код?" + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:96 +msgid "nitrate.code-activation.input-label" +msgstr "Введите код активации" + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:100 +msgid "nitrate.code-activation.placeholder" +msgstr "Вставьте код активации сюда" + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:89 +msgid "nitrate.code-activation.renew-title" +msgstr "ПРОДЛИТЬ КОРПОРАТИВНЫЙ" + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:111 +msgid "nitrate.code-activation.submit" +msgstr "Активировать" + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:90 +msgid "nitrate.code-activation.title" +msgstr "Подключить Корпоративный" + +#: src/app/main/ui/settings/subscription.cljs:933 +msgid "nitrate.contact-sales.button" +msgstr "Связаться с продажами" + +#: src/app/main/ui/settings/subscription.cljs:926 +msgid "nitrate.contact-sales.downgrade-contact-info" +msgstr "" +"Чтобы перейти на тариф, свяжитесь с отделом продаж. Поможем обновить " +"подписку и всё настроить." + +#: src/app/main/ui/settings/subscription.cljs:912 +msgid "nitrate.contact-sales.downgrade-organization-deleted" +msgstr "Ваша организация удалится." + +#: src/app/main/ui/settings/subscription.cljs:914 +msgid "nitrate.contact-sales.downgrade-storage-limited" +msgstr "Общее хранилище, автоверсии и срок восстановления файлов урежутся." + +#: src/app/main/ui/settings/subscription.cljs:913 +msgid "nitrate.contact-sales.downgrade-teams-available" +msgstr "Команды, проекты и файлы выйдут из организации, но останутся доступны." + +#: src/app/main/ui/settings/subscription.cljs:910 +msgid "nitrate.contact-sales.downgrade-title" +msgstr "При понижении тарифа:" + +#: src/app/main/ui/settings/subscription.cljs:907 +msgid "nitrate.contact-sales.title" +msgstr "Перейти на тариф %s?" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:103 +msgid "nitrate.form.cancel-anytime" +msgstr "Оплата — в конце пробного периода. Отмена в любой момент." + +#: src/app/main/ui/nitrate/nitrate_form.cljs:120 +msgid "nitrate.form.contact-trial" +msgstr "Напишите нам, чтобы попробовать Корпоративный 14 дней:" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:119 +msgid "nitrate.form.contact-upgrade" +msgstr "Напишите нам, чтобы перейти на Корпоративный:" + +#: src/app/main/ui/settings/subscription.cljs:879 +msgid "nitrate.form.contact-us-free-trial" +msgstr "Свяжитесь с нами, чтобы попробовать Корпоративный 14 дней:" + +#: src/app/main/ui/settings/subscription.cljs:879 +msgid "nitrate.form.contact-us-upgrade" +msgstr "Свяжитесь с нами, чтобы перейти на Корпоративный:" + +#: src/app/main/ui/settings/subscription.cljs:862 +msgid "nitrate.form.enterprise-description" +msgstr "" +"Сейчас платить не нужно. Оплата — в конце пробного периода. Отмена в любой " +"момент." + +#: src/app/main/ui/nitrate/nitrate_form.cljs:83 +msgid "nitrate.form.enterprise-feature-1" +msgstr "Права от общих до точечных — для команд и проектов" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:85 +msgid "nitrate.form.enterprise-feature-2" +msgstr "Единое управление: кто, что и где — из Admin Console" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:87 +msgid "nitrate.form.enterprise-feature-3" +msgstr "Вся свобода опенсорса и порядок для Вашей организации" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 +msgid "nitrate.form.enterprise-intro" +msgstr "Всё для безопасной работы команд в масштабе%s" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:92 +msgid "nitrate.form.enterprise.price" +msgstr "с участника в месяц" + +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 +msgid "nitrate.form.free-trial-button" +msgstr "14 дней бесплатно" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:114 +msgid "nitrate.form.see-plan" +msgstr "Мой текущий тариф" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:100 +msgid "nitrate.form.start-enterprise" +msgstr "Начать с Корпоративным" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:108, src/app/main/ui/nitrate/nitrate_form.cljs:128 +msgid "nitrate.form.subscribe-with-code" +msgstr "Подписка по коду активации" + +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 +msgid "nitrate.form.title" +msgstr "Откройте возможности Корпоративного" + +#: src/app/main/ui/dashboard/subscription.cljs:225, src/app/main/ui/nitrate/nitrate_form.cljs:101 +msgid "nitrate.form.try-free" +msgstr "14 дней бесплатно" + +#: src/app/main/ui/nitrate/nitrate_activation_success_modal.cljs:54 +msgid "nitrate.modal-success.title" +msgstr "Добро пожаловать на Корпоративный тариф!" + +#: src/app/main/ui/settings/subscription.cljs:634 +msgid "nitrate.subscription.active-until" +msgstr "Активна до %s" + +#: src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:51 +msgid "nitrate.subscription.renew-success" +msgstr "Корпоративная подписка продлена." + +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 +msgid "nitrate.subscription.settings.manual-cancel" +msgstr "Отменить подписку" + +#: src/app/main/ui/settings/subscription.cljs:966 +msgid "nitrate.subscription.settings.manual-contact-us" +msgstr "Чтобы отменить корпоративную подписку, напишите нам:" + +#: src/app/main/ui/settings/subscription.cljs:113 +msgid "nitrate.subscription.settings.renew-with-code" +msgstr "Продлить по коду активации" + +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Эта страница не существует" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 ошибка" @@ -3018,11 +4315,11 @@ msgstr "Вы направили письмо для восстановления msgid "not-found.login.sent-recovery-check" msgstr "Проверьте вашу почту и перейдите по ссылке, чтобы задать новый пароль." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Зарегистрироваться бесплатно" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "И начните использовать Penpot мгновенно!" @@ -3030,33 +4327,105 @@ msgstr "И начните использовать Penpot мгновенно!" msgid "not-found.made-with-love" msgstr "Сделано с любовью и открытым исходным кодом" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Вы уже запросили доступ к этому файлу." +#: src/app/main/ui/static.cljs:288 +msgid "not-found.no-permission.already-requested.or-others.file" +msgstr "" +"Вы уже запросили доступ к этому файлу либо к другим файлам и проектам " +"команды." + +#: src/app/main/ui/static.cljs:294 +msgid "not-found.no-permission.already-requested.or-others.project" +msgstr "" +"Вы уже запросили доступ к этому проекту либо к другим проектам и файлам " +"команды." + +#: src/app/main/ui/static.cljs:293 +msgid "not-found.no-permission.already-requested.project" +msgstr "Вы уже запросили доступ к этому проекту." + +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 +msgid "not-found.no-permission.ask" +msgstr "ЗАПРОСИТЬ ДОСТУП" + +#: src/app/main/ui/static.cljs:300 +msgid "not-found.no-permission.done.remember" +msgstr "Помните: если владелец разрешит, Вас пригласят в команду." + +#: src/app/main/ui/static.cljs:299 +msgid "not-found.no-permission.done.success" +msgstr "Ваш запрос успешно отправлен!" + +#: src/app/main/ui/static.cljs:305 +msgid "not-found.no-permission.file" +msgstr "У Вас нет доступа к этому файлу." + +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 +msgid "not-found.no-permission.go-dashboard" +msgstr "Перейти к личным проектам" + +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 +msgid "not-found.no-permission.if-approves" +msgstr "Если владелец разрешит, Вас пригласят в команду." + +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 +msgid "not-found.no-permission.penpot-file" +msgstr "Файл Penpot" + +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 +msgid "not-found.no-permission.project" +msgstr "У Вас нет доступа к этому проекту." + +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 +msgid "not-found.no-permission.project-name" +msgstr "ПРОЕКТ" + +#: src/app/main/ui/static.cljs:306 +msgid "not-found.no-permission.you-can-ask.file" +msgstr "Чтобы получить файл, попросите владельца команды." + +#: src/app/main/ui/static.cljs:315 +msgid "not-found.no-permission.you-can-ask.project" +msgstr "Чтобы получить проект, попросите владельца команды." + #: src/app/main/data/common.cljs:91 msgid "notifications.by-code.maintenance" msgstr "" "Технический перерыв: сервис будет недоступен короткое время в течение 5 " "минут." -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/data/common.cljs:84 +msgid "notifications.by-code.upgrade-version" +msgstr "Доступна новая версия." + +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 +msgid "notifications.invitation-canceled" +msgstr "Это приглашение больше недоступно." + +#: src/app/main/ui/dashboard/team.cljs:1097 +msgid "notifications.invitation-deleted" +msgstr "Приглашение успешно удалено" + +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Приглашение успешно отправлено" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Ссылка на приглашение скопирована" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "Вы не можете удалить свой профиль. Сначала переназначьте ваши команды." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Профиль успешно сохранён!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Письмо с подтверждением отправлено на %s. Проверьте свою эл. почту!" @@ -3082,82 +4451,120 @@ msgstr "" "После добавления названия команды, вы сможете пригласить людей " "присоединиться." +#: src/app/main/ui/onboarding/team_choice.cljs:189 +msgid "onboarding.choice.team-up.create-team-enter" +msgstr "Введите название команды" + +#: src/app/main/ui/onboarding/team_choice.cljs:196 +msgid "onboarding.choice.team-up.create-team-name" +msgstr "Название команды" + #: src/app/main/ui/onboarding/team_choice.cljs:237 msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Создать команду" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Пригласить участников" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/onboarding/team_choice.cljs:226 +msgid "onboarding.choice.team-up.invite-members-desc" +msgstr "" +"Разработчики, дизайнеры, менеджеры… приглашаются все. Позже сможете позвать " +"ещё." + +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Пригласить с ролью:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Давайте начнём!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Генерация реального кода дизайнов" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Создание систем дизайна" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Создание прототипов" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Проектирование UI/UX приложения" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Вайрфрейминг" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "В каких целях вы планируете использовать Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Расскажите нам немного о себе, чтобы мы могли заставить Penpot работать на " "вас. Ваши ответы помогут нам определить приоритетность новых функций и " "укажут вам правильное направление для начала работы." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Помогите нам лучше узнать вас" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Каким из этих инструментов вы пользуетесь чаще всего?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Какую работу вы выполняете?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Расскажите нам о своей работе" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "С чего бы вы хотели начать?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:228 +msgid "onboarding.questions.team-size.1001-5000" +msgstr "1001 - 5000" + +#: src/app/main/ui/onboarding/questions.cljs:224 +msgid "onboarding.questions.team-size.101-500" +msgstr "101 - 500" + +#: src/app/main/ui/onboarding/questions.cljs:222 +msgid "onboarding.questions.team-size.2-100" +msgstr "2 - 100" + +#: src/app/main/ui/onboarding/questions.cljs:226 +msgid "onboarding.questions.team-size.501-1000" +msgstr "501 - 1000" + +#: src/app/main/ui/onboarding/questions.cljs:220 +msgid "onboarding.questions.team-size.just-me" +msgstr "Только я" + +#: src/app/main/ui/onboarding/questions.cljs:230 +msgid "onboarding.questions.team-size.more-than-5001" +msgstr "5001+" + +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Образование" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "В личных целях" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Для работы" @@ -3203,15 +4610,43 @@ msgstr "Вот несколько шаблонов." msgid "onboarding.templates.title" msgstr "Заняться дизайном" +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:52, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:57, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:121, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:137 +msgid "options.deleted-token-with-name" +msgstr "Токена {%s} нет — удалён." + +#: src/app/plugins/utils.cljs:555 +msgid "plugins.validation.invalid-value" +msgstr "ждали %s, пришло %s (%s)" + +#: src/app/plugins/utils.cljs:569 +msgid "plugins.validation.message" +msgstr "Поле %s неверно: %s" + +#: src/app/plugins/utils.cljs:558 +msgid "plugins.validation.received-value" +msgstr "пришло %s (%s)" + #: src/app/main/ui/auth/recovery.cljs:103 msgid "profile.recovery.go-to-login" msgstr "Перейти к входу" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:94 +msgid "restore-shortcuts.modal-text" +msgstr "Эти сочетания вернутся к исходным значениям." + +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:90 +msgid "restore-shortcuts.modal-title" +msgstr "Вернуть настройки по умолчанию" + +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:170 +msgid "restore-shortcuts.restore" +msgstr "Восстановить" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Отсоединить" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Смешаный" @@ -3223,35 +4658,35 @@ msgstr "Удалить цвет" msgid "settings.select-this-color" msgstr "Выбрать элементы, использующие этот стиль" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Комментарии" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Выровнять по нижнему краю" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Выровнять по центру по горизонтали" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Выровнять по левому краю" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Выровнять по правому краю" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Выровнять по верхнему краю" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Выровнять по центру по вертикали" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Создать доску из выбора" @@ -3259,123 +4694,183 @@ msgstr "Создать доску из выбора" msgid "shortcuts.bold" msgstr "Переключить толщину" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Логическая разница" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Логическое исключение" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Логическое пересечение" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Логическое объединение" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Переместить на задний план" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Переместить назад" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Перенести вперёд" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Вынести на передний план" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Очистить отмену" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Скопировать" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Скопировать ссылку" +#: src/app/main/data/workspace/shortcuts.cljs:134 +msgid "shortcuts.copy-props" +msgstr "Скопировать свойства" + +#: src/app/main/data/workspace/shortcuts.cljs:233 +msgid "shortcuts.create-component-variant" +msgstr "Создать компонент / вариант" + #: src/app/main/data/dashboard/shortcuts.cljs:52 msgid "shortcuts.create-new-project" msgstr "Создать новый" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Вырезать" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Отдалить" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Удалить" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Удалить узел" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 +msgid "shortcuts.delete-node-and-segments" +msgstr "Удалить узел и сегменты" + +#: src/app/main/data/workspace/path/shortcuts.cljs:151 +msgid "shortcuts.deselect-all" +msgstr "Снять выделение" + +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Отсоединить компонент" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/ui/shortcuts.cljs:795 +msgid "shortcuts.disable" +msgstr "Отключить сочетание" + +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 +msgid "shortcuts.disabled" +msgstr "Отключено" + +#: src/app/main/ui/shortcuts.cljs:868 +msgid "shortcuts.disabled-shortcut" +msgstr "Отключённое сочетание" + +#: src/app/main/data/workspace/shortcuts.cljs:420 +msgid "shortcuts.draw-arrow" +msgstr "Стрелка" + +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Кривая" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Эллипс" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Доска" +#: src/app/main/data/workspace/shortcuts.cljs:413 +msgid "shortcuts.draw-line" +msgstr "Линия" + #: src/app/main/data/workspace/path/shortcuts.cljs:53 msgid "shortcuts.draw-nodes" msgstr "Нарисовать контур" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Контур" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Прямоугольник" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Текст" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Дублировать" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/ui/shortcuts.cljs:775 +msgid "shortcuts.edit-modal.conflict" +msgstr "Сочетание занято «%s». Сохранение уберёт его оттуда." + +#: src/app/main/ui/shortcuts.cljs:761 +msgid "shortcuts.edit-modal.reset-pending" +msgstr "Сохраните — сочетание вернётся к исходному:" + +#: src/app/main/ui/shortcuts.cljs:779 +msgid "shortcuts.edit-modal.success" +msgstr "Сочетание свободно, конфликтов нет." + +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 +msgid "shortcuts.edit-on-settings" +msgstr "Меняется в настройках" + +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Отмена" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Экспорт фигур" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/shortcuts.cljs:187 +msgid "shortcuts.find" +msgstr "Найти" + +#: src/app/main/data/workspace/shortcuts.cljs:191 +msgid "shortcuts.find-and-replace" +msgstr "Найти и заменить" + +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Увеличить, чтобы вместить все" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Отразить по горизонтали" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Отразить по вертикали" @@ -3399,23 +4894,27 @@ msgstr "Перейти к общим библиотекам" msgid "shortcuts.go-to-search" msgstr "Поиск" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Группа" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Распределить горизонтально" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Показать/скрыть интерфейс" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/ui/settings/shortcuts.cljs:587 +msgid "shortcuts.import-export" +msgstr "Импорт/экспорт" + +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Приблизить" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Вставить изображение" @@ -3423,47 +4922,51 @@ msgstr "Вставить изображение" msgid "shortcuts.italic" msgstr "Переключить курсив" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Соединение узлов" +#: src/app/main/ui/shortcuts.cljs:749 +msgid "shortcuts.key-combo" +msgstr "Нажмите сочетание клавиш" + #: src/app/main/data/workspace/text/shortcuts.cljs:249 msgid "shortcuts.line-through" msgstr "Переключить междустрочность" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Сделать угол" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Сделать изгиб" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Маска" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Объединить узлы" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Переместить" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Быстрое перемещение вниз" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Быстрое перемещение влево" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Быстрое перемещение вправо" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Быстрое перемещение вверх" @@ -3471,325 +4974,377 @@ msgstr "Быстрое перемещение вверх" msgid "shortcuts.move-nodes" msgstr "Переместить узел" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Переместить вниз" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Переместить влево" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Переместить вправо" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Переместить вверх" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Следующая доска" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 -msgid "shortcuts.not-found" -msgstr "Ярлыки не найдены" +#: src/app/main/ui/settings/shortcuts.cljs:561 +msgid "shortcuts.no-disabled" +msgstr "Отключённых сочетаний нет" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/ui/settings/shortcuts.cljs:551 +msgid "shortcuts.no-personalized" +msgstr "Откройте вкладку «Все» и настройте сочетания." + +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 +msgid "shortcuts.no-shortcuts" +msgstr "Под поиск сочетаний ничего нет" + +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 +msgid "shortcuts.not-found" +msgstr "Сочетания клавиш не найдены" + +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Установить непрозрачность на 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Установить непрозрачность на 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Установить непрозрачность на 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Установить непрозрачность на 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Установить непрозрачность на 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Установить непрозрачность на 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Установить непрозрачность на 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Установить непрозрачность на 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Установить непрозрачность на 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Установить непрозрачность на 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Выбор цвета" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Перейти в раздел комментариев" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Перейти в панель управления" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Перейти в раздел осмотра" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Перейти в раздел взаимодействия" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Перейти в раздел взаимодействия" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Перейти к рабочей области" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " или " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 +msgid "shortcuts.overwritten-by" +msgstr "Сочетание переназначено на %s" + +#: src/app/main/ui/settings/shortcuts.cljs:519 +msgid "shortcuts.page" +msgstr "Страница сочетаний" + +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Вставить" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/workspace/shortcuts.cljs:141 +msgid "shortcuts.paste-props" +msgstr "Вставить свойства" + +#: src/app/main/data/workspace/shortcuts.cljs:127 +msgid "shortcuts.paste-replace" +msgstr "Вставить и заменить" + +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 +msgid "shortcuts.personalized" +msgstr "Свои" + +#: src/app/main/data/workspace/shortcuts.cljs:834 +msgid "shortcuts.plugins" +msgstr "Управление плагинами" + +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Предыдущая доска" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Вернуть" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/ui/settings/shortcuts.cljs:530 +msgid "shortcuts.reload-hint" +msgstr "После правки сочетаний обновите другие вкладки Penpot." + +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Переименовать" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/ui/shortcuts.cljs:803 +msgid "shortcuts.reset" +msgstr "Сбросить" + +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Сбросить масштаб" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Масштаб" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Основное" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Панель управления" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Просмотрщик" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Рабочая область" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Выбрать все" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Выбрать следующий слой" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Выбрать родительский слой" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Выбрать предыдущий слой" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Отдельные узлы" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:169 +msgid "shortcuts.show-measure" +msgstr "Показать размеры" + +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Показать/скрыть пиксельную сетку" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" -msgstr "Показать/скрыть горячие клавиши" +msgstr "Показать/скрыть сочетания клавиш" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Привязать к узлам" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Привязать к пиксельной сетке" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Начать редактирование" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Выравнивание" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Редактировать" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Общее" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Главное меню" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Изменить слои" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Навигация" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Навигация" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Навигация" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Панели" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Пути" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Фигуры" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Тексты" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Инструменты" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Увеличение" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Увеличение" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Установить миниатюры" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" -msgstr "Горячие клавиши" +msgstr "Сочетания клавиш" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Переключить динамическое выравнивание" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Переключить ресурсы" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Переключить палитру цветов" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:442 +msgid "shortcuts.toggle-comments-visibility" +msgstr "Показать/скрыть комментарии" + +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Переключить режим фокуса" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Переключить полноэкранный режим" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Показать/скрыть направляющие" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Переключить историю" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Переключить слои" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Добавить/удалить макет гибкости" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Добавить/удалить макет сетки" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Запереть/Отпереть" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Запереть пропорции" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Показать/скрыть линейки" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Привязать к направляющим" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Привязать к направляющим линейки" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Переключить текстовую палитру" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Сменить тему" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Показать/скрыть" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Переключить стиль масштабирования" @@ -3797,34 +5352,102 @@ msgstr "Переключить стиль масштабирования" msgid "shortcuts.underline" msgstr "Переключить подчёркивание" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Отменить" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Разгруппировать" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Убрать маску" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Распределить вертикально" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Уменьшить зум-объектив" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Увеличить зум-объектив" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Приблизить выбранное" +#: src/app/main/ui/dashboard/subscription.cljs:207 +msgid "subscription.banner.create-organization-info" +msgstr "" +"Создайте организацию и посмотрите Корпоративный в деле. Задавайте правила, " +"управляйте командами — новый уровень контроля над дизайном." + +#: src/app/main/ui/dashboard/subscription.cljs:205 +msgid "subscription.banner.see-enterprise" +msgstr "Взгляните на Корпоративный в деле" + +#: src/app/main/ui/dashboard/subscription.cljs:269 +msgid "subscription.current-plan.enterprise" +msgstr "Корпоративный" + +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 +msgid "subscription.current-plan.nitrate" +msgstr "Корпоративный" + +#: src/app/main/ui/dashboard/subscription.cljs:267 +msgid "subscription.current-plan.nitrate-trial" +msgstr "Корпоративный (пробный)" + +#: src/app/main/ui/dashboard/subscription.cljs:262 +msgid "subscription.current-plan.professional" +msgstr "Профессиональный" + +#: src/app/main/ui/dashboard/subscription.cljs:259 +msgid "subscription.current-plan.title" +msgstr "Ваша подписка" + +#: src/app/main/ui/dashboard/subscription.cljs:265 +msgid "subscription.current-plan.unlimited" +msgstr "Безлимитный" + +#: src/app/main/ui/dashboard/subscription.cljs:264 +msgid "subscription.current-plan.unlimited-trial" +msgstr "Безлимитный (пробный)" + +#: src/app/main/ui/dashboard/subscription.cljs:245 +msgid "subscription.dashboard.banner.renew" +msgstr "Продлить" + +#: src/app/main/ui/dashboard/subscription.cljs:232 +msgid "subscription.dashboard.banner.renew-subscription" +msgstr "Продлите подписку" + +#: src/app/main/ui/dashboard/subscription.cljs:238 +msgid "subscription.dashboard.banner.subscription-expire-days" +msgstr "Корпоративная подписка истекает через %s дней (%s)" + +#: src/app/main/ui/dashboard/subscription.cljs:236 +msgid "subscription.dashboard.banner.subscription-expired" +msgstr "Корпоративная подписка истекла. (%s)" + +#: src/app/main/ui/dashboard/subscription.cljs:217 +msgid "subscription.dashboard.banner.unlock-features" +msgstr "Откройте возможности Корпоративного" + +#: src/app/main/ui/dashboard/subscription.cljs:219 +msgid "subscription.dashboard.banner.unlock-features-description-text" +msgstr "" +"Точечные права и полный контроль — без потери свободы опенсорса, которую " +"любит команда." + +#: src/app/main/ui/dashboard/subscription.cljs:224 +msgid "subscription.dashboard.banner.upgrade-nitrate" +msgstr "Начать с Корпоративным" + #: src/app/main/ui/dashboard/subscription.cljs:117, src/app/main/ui/dashboard/subscription.cljs:316 msgid "subscription.dashboard.power-up.enterprise-plan" msgstr "Корпоративный тариф" @@ -3845,6 +5468,13 @@ msgstr "Дополнительное хранилище, восстановле msgid "subscription.dashboard.power-up.professional.top-title" msgstr "Профессиональный тариф" +#: src/app/main/ui/dashboard/subscription.cljs:97 +#, markdown +msgid "subscription.dashboard.power-up.trial.bottom-description" +msgstr "" +"Нравится пробный период? Откройте полный доступ " +"навсегда.[Подписаться|target:self](%s)" + #: src/app/main/ui/dashboard/subscription.cljs:96 msgid "subscription.dashboard.power-up.trial.top-title" msgstr "Безлимитный тариф (пробный)" @@ -3853,15 +5483,163 @@ msgstr "Безлимитный тариф (пробный)" msgid "subscription.dashboard.power-up.unlimited-plan" msgstr "Безлимитный тариф" +#: src/app/main/ui/dashboard/subscription.cljs:104 +#, markdown +msgid "subscription.dashboard.power-up.unlimited.bottom-text" +msgstr "" +"Безлимитное хранилище, долгое восстановление файлов и сколько угодно " +"редакторов для команд — по фиксу. [Взгляните на " +"Корпоративный.|target:self](%s)" + #: src/app/main/ui/dashboard/subscription.cljs:84, src/app/main/ui/dashboard/subscription.cljs:95, src/app/main/ui/dashboard/subscription.cljs:102, src/app/main/ui/dashboard/subscription.cljs:111, src/app/main/ui/dashboard/subscription.cljs:116 msgid "subscription.dashboard.power-up.your-subscription" msgstr "Ваша подписка:" +#: src/app/main/ui/dashboard/subscription.cljs:350 +msgid "subscription.dashboard.professional-dashboard-cta-title" +msgstr "У Вас %s редакторов в командах, а Профессиональный покрывает до 8." + +#: src/app/main/ui/dashboard/subscription.cljs:358 +#, markdown +msgid "subscription.dashboard.professional-dashboard-cta-upgrade-owner" +msgstr "" +"Перейдите на Безлимитный или Корпоративный — больше редакторов, хранилища и " +"восстановлений. [Подписаться.|target:self](%s)" + #: src/app/main/ui/dashboard/subscription.cljs:291 msgid "subscription.dashboard.team-plan" msgstr "Командный тариф" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/dashboard/subscription.cljs:353 +msgid "subscription.dashboard.unlimited-dashboard-cta-title" +msgstr "Команда растёт! Безлимитный покрывает до %s редакторов, а у Вас уже %s." + +#: src/app/main/ui/dashboard/subscription.cljs:361 +#, markdown +msgid "subscription.dashboard.unlimited-dashboard-cta-upgrade-owner" +msgstr "Поднимите тариф под число редакторов. [Подписаться.|target:self](%s)" + +#: src/app/main/ui/dashboard/subscription.cljs:338 +msgid "subscription.dashboard.unlimited-members-extra-editors-cta-text" +msgstr "" +"В будущие счета попадут только новые редакторы Ваших команд. От 25 " +"редакторов — фикс 175 $/мес." + +#: src/app/main/ui/dashboard/subscription.cljs:334 +msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" +msgstr "Приглашения на Безлимитном" + +#: src/app/main/ui/settings/subscription.cljs:439 +msgid "subscription.error.nitrate.checkout-cancelled" +msgstr "Оплата не прошла. Попробуйте, когда будете готовы." + +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 +msgid "subscription.error.nitrate.checkout-failed" +msgstr "" +"Оплата не прошла. Попробуйте снова. Если не выйдет — напишите нам: " +"support@penpot.app." + +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 +msgid "subscription.labels" +msgstr "Подписка" + +#: src/app/main/ui/settings/subscription.cljs:107 +msgid "subscription.settings.activate-by-code" +msgstr "Введите код активации" + +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 +msgid "subscription.settings.add-payment-to-continue" +msgstr "Добавьте способ оплаты, чтобы продолжить после пробного периода" + +#: src/app/main/ui/settings/subscription.cljs:642 +msgid "subscription.settings.admin-console" +msgstr "Admin Console" + +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 +msgid "subscription.settings.benefits.all-professional-benefits" +msgstr "Всё из Профессионального плюс:" + +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 +msgid "subscription.settings.benefits.all-unlimited-benefits" +msgstr "Всё из Безлимитного плюс:" + +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 +msgid "subscription.settings.benefits.nitrate-unlimited-benefits" +msgstr "Всё из Безлимитного и ещё:" + +#: src/app/main/ui/settings/subscription.cljs:65 +msgid "subscription.settings.editors" +msgstr "(x %s редакторов)" + +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 +msgid "subscription.settings.enterprise" +msgstr "Корпоративный" + +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 +msgid "subscription.settings.enterprise-trial" +msgstr "Корпоративный (пробный)" + +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 +msgid "subscription.settings.enterprise.autosave" +msgstr "Автоверсии за 90 дней и восстановление файлов" + +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 +msgid "subscription.settings.enterprise.capped-bill" +msgstr "Фикс в месяц" + +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 +msgid "subscription.settings.enterprise.nitrate.advanced-control" +msgstr "Продвинутое управление:" + +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 +msgid "subscription.settings.enterprise.nitrate.enterprise-security" +msgstr "Корпоративная безопасность:" + +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 +msgid "subscription.settings.enterprise.nitrate.multi-organization-management" +msgstr "Управление организациями:" + +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 +msgid "subscription.settings.enterprise.nitrate.native-sso" +msgstr "SAML/SSO из коробки и полный журнал аудита для отчётности." + +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 +msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" +msgstr "" +"Точечный белый список плагинов, включая наш закрытый MCP-сервер для связи с " +"ИИ." + +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 +msgid "subscription.settings.enterprise.nitrate.support-team" +msgstr "Сложные иерархии команд и отдельные подорганизации." + +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 +msgid "subscription.settings.enterprise.unlimited-storage-benefit" +msgstr "Безлимитное хранилище" + +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 +msgid "subscription.settings.manage-your-subscription" +msgstr "Управляйте подпиской" + +#: src/app/main/ui/settings/subscription.cljs:356 +msgid "subscription.settings.management-dialog.step-2-add-payment-button" +msgstr "Добавить платёжные данные" + +#: src/app/main/ui/settings/subscription.cljs:343 +msgid "subscription.settings.management-dialog.step-2-description" +msgstr "" +"Добавьте платёжные данные сейчас — подписка продолжится без сбоев, а " +"опенсорс получит поддержку. Пока без списаний." + +#: src/app/main/ui/settings/subscription.cljs:351 +msgid "subscription.settings.management-dialog.step-2-skip-button" +msgstr "Пока пропустить и начать пробный период" + +#: src/app/main/ui/settings/subscription.cljs:274 +msgid "subscription.settings.management-dialog.step-2-title" +msgstr "Помогите нам расти — и пробный период станет проще" + +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "" @@ -3871,30 +5649,159 @@ msgstr[1] "" "В настоящее время в ваших командах есть %s человек(а), которые могут " "редактировать." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:288 +msgid "subscription.settings.management.dialog.downgrade" +msgstr "Учтите: низкий тариф — меньше хранилища, короче бэкапы и история." + +#: src/app/main/ui/settings/subscription.cljs:140 +msgid "subscription.settings.management.dialog.editors" +msgstr "Редакторы" + +#: src/app/main/ui/settings/subscription.cljs:147 +msgid "subscription.settings.management.dialog.editors-explanation" +msgstr "(Владельцы, администраторы и редакторы. Наблюдатели не в счёт.)" + +#: src/app/main/ui/settings/subscription.cljs:321 +msgid "subscription.settings.management.dialog.input-error" +msgstr "" +"Редакторов не может быть меньше, чем сейчас. Смените роль (с редактора и " +"администратора на наблюдателя) тем, кто не правит файлы." + +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "Оплата после пробного периода. Кредитная карта сейчас не требуется." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 +#, markdown +msgid "subscription.settings.management.dialog.price-month" +msgstr "**$%s**/мес." + +#: src/app/main/ui/settings/subscription.cljs:275 +msgid "subscription.settings.management.dialog.title" +msgstr "Примените %s к командам" + +#: src/app/main/ui/settings/subscription.cljs:324 +msgid "subscription.settings.management.dialog.unlimited-capped-warning" +msgstr "" +"Совет: поднимите число мест заранее, до приглашений. От 25 редакторов — " +"фикс 175 $/мес." + +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Участник Penpot с: %s" -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 +msgid "subscription.settings.more-information" +msgstr "Подробнее" + +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 +msgid "subscription.settings.organization-member-month" +msgstr "участник организации в месяц" + +#: src/app/main/ui/settings/subscription.cljs:740 +msgid "subscription.settings.other-plans" +msgstr "Другие тарифы Penpot" + +#: src/app/main/ui/settings/subscription.cljs:783 +msgid "subscription.settings.price-organization-month" +msgstr "организация в месяц" + +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 +msgid "subscription.settings.price-user-month" +msgstr "пользователь в месяц" + +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 +msgid "subscription.settings.professional" +msgstr "Профессиональный" + +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 +msgid "subscription.settings.professional.autosave-benefit" +msgstr "Автоверсии за 7 дней и восстановление файлов" + +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 +msgid "subscription.settings.professional.selfhost.community-support" +msgstr "Все возможности дизайна и прототипирования. Поддержка сообщества." + +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 +msgid "subscription.settings.professional.selfhost.control-over-data" +msgstr "Полный контроль над данными и инфраструктурой." + +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 +msgid "subscription.settings.professional.selfhost.unlimited-users" +msgstr "Сколько угодно пользователей, команд и файлов." + +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 +msgid "subscription.settings.professional.storage-benefit" +msgstr "10 ГБ хранилища" + +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 +msgid "subscription.settings.professional.teams-editors-benefit" +msgstr "Сколько угодно команд. До 8 редакторов в Ваших командах." + +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Рекомендуется" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/settings/subscription.cljs:628 +msgid "subscription.settings.section-plan" +msgstr "Ваша подписка" + +#: src/app/main/ui/settings/subscription.cljs:371 +msgid "subscription.settings.start-trial" +msgstr "Начать пробный период" + +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 +msgid "subscription.settings.subscribe" +msgstr "Подписаться" + +#: src/app/main/ui/settings/subscription.cljs:730 +msgid "subscription.settings.subscribed-since" +msgstr "Тариф %s у Вас с %s." + +#: src/app/main/ui/settings/subscription.cljs:406 +msgid "subscription.settings.success.dialog.description" +msgstr "Подпиской управляйте когда угодно — страница «Подписка» в данных аккаунта." + +#: src/app/main/ui/settings/subscription.cljs:408 +msgid "subscription.settings.success.dialog.footer" +msgstr "Наслаждайтесь тарифом!" + +#: src/app/main/ui/settings/subscription.cljs:404 +msgid "subscription.settings.success.dialog.thanks" +msgstr "Спасибо, что выбрали тариф Penpot %s!" + +#: src/app/main/ui/settings/subscription.cljs:401 +msgid "subscription.settings.success.dialog.title" +msgstr "Вы — %s!" + +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 +msgid "subscription.settings.try-it-free" +msgstr "14 дней бесплатно" + +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Безлимитный" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Безлимитный (пробный)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30-дневное автосохранение версий и восстановление файлов" +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 +msgid "subscription.settings.unlimited.bill" +msgstr "Не более 175 $/мес." + +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 +msgid "subscription.settings.unlimited.storage-benefit" +msgstr "25 ГБ хранилища" + +#: src/app/main/ui/dashboard/subscription.cljs:329 +msgid "subscription.workspace.header.menu.option.power-up" +msgstr "Прокачайте тариф" + #: src/app/main/ui/workspace/sidebar/versions.cljs:62 #, markdown msgid "subscription.workspace.versions.warning.enterprise.subtext-owner" @@ -3912,49 +5819,53 @@ msgstr "" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "Если вы хотите увеличить этот лимит, [обновите тариф|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 приглашение выбрано" msgstr[1] "%s приглашений выбрано" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Поставщики шрифтов - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Шрифты - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Проекты - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Поиск - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Общие библиотеки - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot — свобода в дизайне для команд" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Оставить отзыв - Penpot" +#: src/app/main/ui/settings/integrations.cljs:642 +msgid "title.settings.integrations" +msgstr "Интеграции - Penpot" + #: src/app/main/ui/settings/notifications.cljs:45 msgid "title.settings.notifications" msgstr "Уведомления - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Настройки - Penpot" @@ -3962,27 +5873,27 @@ msgstr "Настройки - Penpot" msgid "title.settings.password" msgstr "Пароль - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Профиль - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Приглашения - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Участники - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Настройки - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Вебхуки - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Режим просмотра - Penpot" @@ -3994,121 +5905,191 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Отсоединить токен" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "На странице не найдено ни одного кадра." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" -msgstr "Кадр не найден." +msgstr "Доска не найдена." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Комментарии (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Не показывать взаимодействия" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Полный экран" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Осмотр (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Интерактив" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Интерактив (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Скопировать ссылку" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Показывать взаимодействия" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Показывать взаимодействия по клику" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "План сайта" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:76 +msgid "webgl.modals.webgl-unavailable.cta-dashboard" +msgstr "В панель управления" + +#: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:78 +msgid "webgl.modals.webgl-unavailable.cta-troubleshooting" +msgstr "Руководство по неполадкам" + +#: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:62 +msgid "webgl.modals.webgl-unavailable.message" +msgstr "" +"WebGL недоступен в браузере, а без него Penpot не работает. Проверьте " +"настройки браузера и закройте тяжёлые вкладки." + +#: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:58 +msgid "webgl.modals.webgl-unavailable.title" +msgstr "Упс! WebGL недоступен" + +#: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:71 +msgid "webgl.modals.webgl-unavailable.troubleshooting.after" +msgstr " — там про настройки браузера, ускорение GPU, драйверы и частые сбои." + +#: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:65 +msgid "webgl.modals.webgl-unavailable.troubleshooting.before" +msgstr "Загляните в наше " + +#: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:70 +msgid "webgl.modals.webgl-unavailable.troubleshooting.link" +msgstr "руководство по WebGL" + +#: src/app/main/ui/settings/options.cljs:111 +msgid "webgl.toast.webgl-render-disabled" +msgstr "Рендеринг WebGL выключен" + +#: src/app/main/ui/settings/options.cljs:110 +msgid "webgl.toast.webgl-render-enabled" +msgstr "Рендеринг WebGL включён" + +#: src/app/render_wasm/api.cljs:2402 +msgid "webgl.webgl-context-lost.toast" +msgstr "Контекст WebGL потерян" + +#: src/app/render_wasm/api.cljs:2425 +msgid "webgl.webgl-context-recovered.toast" +msgstr "Контекст WebGL восстановлен" + +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Последняя доставка прошла успешно." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Выровнять по горизонтали (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Распределить горизонтальное пространство (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Выровнять по левому краю (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Выровнять по правому краю (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Выровнять по нижнему краю (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Выровнять по вертикали (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Распределить вертикальное пространство (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Выровнять по верхнему краю (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Добавить библиотеку" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Все ресурсы" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Цвета" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:520 +msgid "workspace.assets.colors.add-color" +msgstr "Добавить цвет" + +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:91 +msgid "workspace.assets.component-group-options" +msgstr "Настройки группы компонентов" + +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Компоненты" +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:609 +msgid "workspace.assets.components.add-component" +msgstr "Добавить компонент" + +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 +msgid "workspace.assets.components.num-variants" +msgstr "Вариантов: %s" + #: src/app/main/ui/workspace/sidebar/assets/groups.cljs:160 msgid "workspace.assets.create-group" msgstr "Создать группу" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "" "Вашим элементам будет автоматически присвоено название «название " "группы/название элемента»" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Удалить" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:74 +msgid "workspace.assets.delete-group" +msgstr "Удалить группу" + +#: src/app/main/ui/workspace/context_menu.cljs:832 +msgid "workspace.assets.delete-pages" +msgstr "Удалить страницы" + +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Дублировать" @@ -4116,27 +6097,39 @@ msgstr "Дублировать" msgid "workspace.assets.duplicate-main" msgstr "Дублировать главное" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Редактировать" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Фильтр" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +msgid "workspace.assets.grid-view" +msgstr "Вид сетки" + +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Группа" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Имя группы" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +msgid "workspace.assets.list-view" +msgstr "Вид списка" + +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "локальная библиотека" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 +msgid "workspace.assets.manage-library" +msgstr "Управлять библиотекой" + +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Ресурсы не найдены" @@ -4144,7 +6137,7 @@ msgstr "Ресурсы не найдены" msgid "workspace.assets.open-library" msgstr "Открыть файл библиотеки" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Переименовать" @@ -4152,11 +6145,11 @@ msgstr "Переименовать" msgid "workspace.assets.rename-group" msgstr "Переименовать группу" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Поиск ресурсов" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Общая библиотека" @@ -4166,39 +6159,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 компонент" msgstr[1] "%s компонентов" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Сортировка" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Типографика" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 +msgid "workspace.assets.typography.add-typography" +msgstr "Добавить типографику" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Размер" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Шрифт" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Перейти к файлу библиотеки стилей для редактирования" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Кернинг" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Интерлиньяж" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Регистр" @@ -4206,67 +6203,119 @@ msgstr "Регистр" msgid "workspace.assets.ungroup" msgstr "Разгруппировать" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/data/workspace/clipboard.cljs:1242 +msgid "workspace.clipboard.copying" +msgstr "Копирование картинки…" + +#: src/app/main/data/workspace/clipboard.cljs:1264 +msgid "workspace.clipboard.image-copied" +msgstr "Картинка скопирована в буфер обмена" + +#: src/app/main/data/workspace/clipboard.cljs:1270 +msgid "workspace.clipboard.image-copy-failed" +msgstr "Не удалось скопировать картинку" + +#: src/app/main/ui/workspace/colorpicker.cljs:447 +msgid "workspace.colorpicker.color-picker" +msgstr "Взять цвет с экрана" + +#: src/app/main/ui/workspace/colorpicker.cljs:439, src/app/main/ui/workspace/colorpicker.cljs:454 +msgid "workspace.colorpicker.color-tokens" +msgstr "Токены цвета" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 +msgid "workspace.component.swap.loop-error" +msgstr "Компоненты нельзя вкладывать в себя." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 +msgid "workspace.component.switch.loop-error-multi" +msgstr "Часть копий не переключить: компоненты нельзя вкладывать в себя." + +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Создать область" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:999 +msgid "workspace.context-menu.grid-cells.copy-columns" +msgstr "Скопировать столбцы" + +#: src/app/main/ui/workspace/context_menu.cljs:995 +msgid "workspace.context-menu.grid-cells.copy-rows" +msgstr "Скопировать строки" + +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Создать доску" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Объединить ячейки" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:1003 +msgid "workspace.context-menu.grid-cells.paste-tracks" +msgstr "Вставить" + +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Добавить 1 столбец справа" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Добавить 1 столбец слева" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Удалить столбец" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Удалить столбец и фигуры" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Дублировать столбец" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Добавить 1 строку ниже" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Добавить 1 строку выше" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Удалить строку" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Удалить строку и фигуры" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Дублировать строку" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/context_menu.cljs:1037 +msgid "workspace.context-menu.guides.change-color" +msgstr "Цвет направляющей" + +#: src/app/main/ui/workspace/context_menu.cljs:1049 +msgid "workspace.context-menu.guides.remove" +msgstr "Убрать направляющую" + +#: src/app/main/ui/workspace/sidebar/debug.cljs:52 +msgid "workspace.debug.title" +msgstr "Отладка" + +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Режим фокусировки" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Фокусировка выключена" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Фокусировка включена" @@ -4278,190 +6327,278 @@ msgstr "Линейный градиент" msgid "workspace.gradients.radial" msgstr "Радиальный градиент" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Отключить активное выравнивание" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Отключить пропорциональное масштабирование" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Отключить привязку к направляющим" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Отключить привязку к пикселю" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Отключить привязку к направляющим линейки" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:342 +msgid "workspace.header.menu.disable-webgl" +msgstr "Выключить рендеринг WebGL (бета)" + +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Включить активное выравнивание" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Включить пропорциональный масштаб" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Привязка к направляющим" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Включить привязку к пикселю" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Привязка к направляющим линейки" -#: src/app/main/ui/workspace/main_menu.cljs:465 -msgid "workspace.header.menu.hide-artboard-names" -msgstr "Скрыть имена кадров" +#: src/app/main/ui/workspace/main_menu.cljs:343 +msgid "workspace.header.menu.enable-webgl" +msgstr "Включить рендеринг WebGL (бета)" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:536 +msgid "workspace.header.menu.find" +msgstr "Найти" + +#: src/app/main/ui/workspace/main_menu.cljs:543 +msgid "workspace.header.menu.find-and-replace" +msgstr "Найти и заменить" + +#: src/app/main/ui/workspace/main_menu.cljs:466 +msgid "workspace.header.menu.hide-artboard-names" +msgstr "Скрыть имена досок" + +#: src/app/main/ui/workspace/main_menu.cljs:427 +msgid "workspace.header.menu.hide-comments" +msgstr "Скрыть комментарии" + +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Скрыть направляющие" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Скрыть палитру цветов" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Скрыть сетку пикселей" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Скрыть линейки" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Скрыть палитру шрифтов" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:416 +msgid "workspace.header.menu.lock-guides" +msgstr "Закрепить направляющие" + +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 +msgid "workspace.header.menu.mcp.plugin.status.connect" +msgstr "Подключить" + +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 +msgid "workspace.header.menu.mcp.plugin.status.disconnect" +msgstr "Отключить" + +#: src/app/main/ui/workspace/main_menu.cljs:884 +msgid "workspace.header.menu.mcp.server.status.disabled" +msgstr "Управлять (статус: отключён)" + +#: src/app/main/ui/workspace/main_menu.cljs:883 +msgid "workspace.header.menu.mcp.server.status.enabled" +msgstr "Управлять (статус: включён)" + +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Изменить" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Файл" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Помощь и информация" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1105 +msgid "workspace.header.menu.option.mcp" +msgstr "MCP-сервер" + +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Предпочтения" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Вид" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Вернуть" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Выбрать все" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" -msgstr "Показать имена кадров" +msgstr "Показать имена досок" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:428 +msgid "workspace.header.menu.show-comments" +msgstr "Показать комментарии" + +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Показать направляющие" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Показать палитру цветов" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Показать пиксельную сетку" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Показать линейки" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Показать палитру шрифтов" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Переключить на тёмную тему" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Переключить на светлую тему" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:330 +msgid "workspace.header.menu.toggle-system-theme" +msgstr "На системную тему" + +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Отменить" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/workspace/main_menu.cljs:415 +msgid "workspace.header.menu.unlock-guides" +msgstr "Открепить направляющие" + +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Сброс" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:133 +msgid "workspace.header.retrying" +msgstr "Пробуем сохранить" + +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Ошибка при сохранении" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Сохранено" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Сохранение" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:243 +msgid "workspace.header.share" +msgstr "Поделиться" + +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Открыть предпросмотр (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Масштаб" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Заполнение - Масштаб заполнения" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Подогнать - масштабировать до необходимого размера" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Масштабировать, чтобы всё вместилось" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "На весь экран" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Масштабировать до выбранного" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Изменить сетку" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Выход" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:592, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:598 +msgid "workspace.layout-grid.editor.padding.bottom" +msgstr "Отступ снизу" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Настроить внутренний отступ по отдельности" +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:419, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:430, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:436 +msgid "workspace.layout-grid.editor.padding.horizontal" +msgstr "Отступ по горизонтали" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:617, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:626, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:632 +msgid "workspace.layout-grid.editor.padding.left" +msgstr "Отступ слева" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:550, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:559, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:565 +msgid "workspace.layout-grid.editor.padding.right" +msgstr "Отступ справа" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:516, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:531 +msgid "workspace.layout-grid.editor.padding.top" +msgstr "Отступ сверху" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:383, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:394, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:400 +msgid "workspace.layout-grid.editor.padding.vertical" +msgstr "Отступ по вертикали" + #: src/app/main/ui/workspace/viewport/grid_layout_editor.cljs:61 msgid "workspace.layout-grid.editor.title" msgstr "Редактирование сетки" @@ -4474,29 +6611,37 @@ msgstr "Готово" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Разместить" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Размещение макета сетки" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 цвет" msgstr[1] "%s цветов" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 +msgid "workspace.libraries.colors.add-library-color" +msgstr "Добавить цвет в библиотеку" + +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "В вашей библиотеке ещё нет цветовых стилей" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 +msgid "workspace.libraries.colors.empty-recent-colors" +msgstr "Недавних цветов пока нет" + +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "В вашей библиотеке ещё нет стилей типографики" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Библиотека файлов" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Недавние цвета" @@ -4508,72 +6653,204 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Сохранить стиль цвета" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 +msgid "workspace.libraries.colors.toggle-color-palette" +msgstr "Показать/скрыть палитру" + +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 компонент" msgstr[1] "%s компонентов" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:864 +msgid "workspace.libraries.connected-libraries" +msgstr "Подключённая библиотека" + +#: src/app/main/ui/workspace/libraries.cljs:235 +msgid "workspace.libraries.connected-through" +msgstr "Подключена через" + +#: src/app/main/ui/workspace/libraries.cljs:507 +msgid "workspace.libraries.connected-to" +msgstr "Подключена к" + +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 +msgid "workspace.libraries.empty.add-some" +msgstr "Или добавьте что-нибудь для пробы:" + +#: src/app/main/ui/workspace/libraries.cljs:834 +msgid "workspace.libraries.empty.no-assets" +msgstr "Ресурсов пока нет" + +#: src/app/main/ui/workspace/libraries.cljs:918 +msgid "workspace.libraries.empty.no-connected-libraries" +msgstr "Подключённых библиотек пока нет." + +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 +msgid "workspace.libraries.empty.no-libraries" +msgstr "В команде нет общих библиотек, поищите" + +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 +msgid "workspace.libraries.empty.some-templates" +msgstr "что-нибудь из шаблонов" + +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Библиотека файлов" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 +msgid "workspace.libraries.graphics" +msgid_plural "workspace.libraries.graphics" +msgstr[0] "1 изображение" +msgstr[1] "%s изображений" + +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "БИБЛИОТЕКИ В ЭТОМ ФАЙЛЕ" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "БИБЛИОТЕКИ" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "ОБНОВЛЕНИЯ БИБЛИОТЕКИ" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Загрузка…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Нет общих библиотек, требующих обновления" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Совпадений для \"%s\" не найдено" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Искать общие библиотеки" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:849, src/app/main/ui/workspace/libraries.cljs:897 +msgid "workspace.libraries.set-as-tokens-source" +msgstr "Сделать источником токенов" + +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "ОБЩИЕ БИБЛИОТЕКИ" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:674 +msgid "workspace.libraries.shared-library-added" +msgstr "Добавлена" + +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Подключить библиотеку" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:552 +msgid "workspace.libraries.text.mixed-tokens" +msgstr "Разные токены" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:547 +msgid "workspace.libraries.text.mixed-tokens-and-assets" +msgstr "Токены и ресурсы" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:567 +msgid "workspace.libraries.text.mixed-typography" +msgstr "Разные ресурсы" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:569 +msgid "workspace.libraries.text.multiple-assets-tooltip" +msgstr "Отвязать все ресурсы" + +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:554 +msgid "workspace.libraries.text.multiple-token-tooltip" +msgstr "Отвязать все токены" + +#: src/app/main/ui/workspace/libraries.cljs:219, src/app/main/ui/workspace/libraries.cljs:263 +msgid "workspace.libraries.token-sets" +msgid_plural "workspace.libraries.token-sets" +msgstr[0] "1 набор" +msgstr[1] "%s наборов" + +#: src/app/main/ui/workspace/libraries.cljs:222, src/app/main/ui/workspace/libraries.cljs:267 +msgid "workspace.libraries.token-themes" +msgid_plural "workspace.libraries.token-themes" +msgstr[0] "1 тема" +msgstr[1] "%s тем" + +#: src/app/main/ui/workspace/libraries.cljs:216, src/app/main/ui/workspace/libraries.cljs:259 +msgid "workspace.libraries.tokens" +msgid_plural "workspace.libraries.tokens" +msgstr[0] "1 токен" +msgstr[1] "%s токенов" + +#: src/app/main/ui/workspace/libraries.cljs:842, src/app/main/ui/workspace/libraries.cljs:889 +msgid "workspace.libraries.tokens-source" +msgstr "Источник токенов" + +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 +msgid "workspace.libraries.typography" +msgid_plural "workspace.libraries.typography" +msgstr[0] "1 типографика" +msgstr[1] "%s типографик" + +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Отключить библиотеку" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Обновить" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "смотреть все изменения" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "ОБНОВЛЕНИЯ" +#: src/app/main/ui/ds/notifications/shared/notification_pill.cljs:61 +msgid "workspace.notification-pill.detail" +msgstr "Подробности" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:784 msgid "workspace.options.add-interaction" msgstr "Нажмите кнопку \"+\" для добавления интерактива." +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:273 +msgid "workspace.options.blur-effects-options.title.group" +msgstr "Размытие группы" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:272 +msgid "workspace.options.blur-effects-options.title.multiple" +msgstr "Размытие выбранного" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:287 +msgid "workspace.options.blur-options.add-blur" +msgstr "Добавить размытие" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:99, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:109 +msgid "workspace.options.blur-options.background-blur" +msgstr "Фоновое размытие" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:144 +msgid "workspace.options.blur-options.blur-type-select" +msgstr "Тип размытия" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:153 +msgid "workspace.options.blur-options.disabled-blur-label" +msgstr "" +"Фоновое размытие — только в новом рендере. Включите его в меню " +"«Предпочтения»." + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:98, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:112 +msgid "workspace.options.blur-options.layer-blur" +msgstr "Размытие слоя" + #: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:169, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:301 msgid "workspace.options.blur-options.remove-blur" msgstr "Удалить размытие" @@ -4586,46 +6863,174 @@ msgstr "Групповое размытие" msgid "workspace.options.blur-options.title.multiple" msgstr "Выборка размытия" +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:160 +msgid "workspace.options.blur-options.toggle-blur" +msgstr "Показать/скрыть размытие" + +#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:138 +msgid "workspace.options.blur-options.toggle-more-options" +msgstr "Показать/скрыть ещё" + +#: src/app/main/ui/exports/assets.cljs:327 +msgid "workspace.options.cancel-export" +msgstr "Отмена" + #: src/app/main/ui/workspace/sidebar/options/page.cljs:78, src/app/main/ui/workspace/sidebar/options/page.cljs:86 msgid "workspace.options.canvas-background" msgstr "Фон холста" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:75 +msgid "workspace.options.canvas-background-section" +msgstr "Фон холста" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Зажать содержимое" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Компонент" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Аннотация" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Копировать" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Создать аннотацию" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Изменить аннотацию" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Главный" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Заменить компонент" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "В этой библиотеке ещё нет ресурсов" +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 +msgid "workspace.options.component.unlinked" +msgstr "Отвязан" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 +msgid "workspace.options.component.variant.duplicated.copy.locate" +msgstr "Найти конфликтующие варианты" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 +msgid "workspace.options.component.variant.duplicated.copy.title" +msgstr "" +"У компонента конфликтующие варианты. У каждого сочетания свойств должны " +"быть свои значения." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 +msgid "workspace.options.component.variant.duplicated.group.locate" +msgstr "Найти дублирующиеся варианты" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 +msgid "workspace.options.component.variant.duplicated.group.title" +msgstr "У части вариантов совпадают свойства и значения" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 +msgid "workspace.options.component.variant.duplicated.single.all" +msgstr "" +"У этих вариантов совпадают свойства и значения. Подправьте значения, чтобы " +"их можно было найти." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 +msgid "workspace.options.component.variant.duplicated.single.one" +msgstr "" +"У варианта такие же свойства и значения, как у другого. Подправьте " +"значения, чтобы его можно было найти." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 +msgid "workspace.options.component.variant.duplicated.single.some" +msgstr "" +"У части вариантов совпадают свойства и значения. Подправьте значения, чтобы " +"их можно было найти." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 +msgid "workspace.options.component.variant.malformed.copy" +msgstr "" +"У компонента варианты с неверными именами. Каждый вариант должен следовать " +"структуре." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 +msgid "workspace.options.component.variant.malformed.group.locate" +msgstr "Найти неверные варианты" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 +msgid "workspace.options.component.variant.malformed.group.title" +msgstr "У части вариантов неверные имена" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +msgid "workspace.options.component.variant.malformed.locate" +msgstr "Найти неверные варианты" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 +msgid "workspace.options.component.variant.malformed.single.all" +msgstr "У этих вариантов неверные имена." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 +msgid "workspace.options.component.variant.malformed.single.one" +msgstr "У варианта неверное имя." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 +msgid "workspace.options.component.variant.malformed.single.some" +msgstr "У части вариантов неверные имена." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 +msgid "workspace.options.component.variant.malformed.structure.example" +msgstr "[свойство]=[значение], [свойство]=[значение]" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 +msgid "workspace.options.component.variant.malformed.structure.title" +msgstr "Попробуйте такую структуру:" + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:54 +msgid "workspace.options.component.variants-help-modal.intro" +msgstr "Чтобы не терять правки при смене вариантов, Penpot связывает слои, которые:" + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:91 +msgid "workspace.options.component.variants-help-modal.outro" +msgstr "" +"Смена любого пункта (например, переименование или группировка слоя) рвёт " +"связь, но отмена вернёт её." + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:67 +msgid "workspace.options.component.variants-help-modal.rule1" +msgstr "Носят одно имя." + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:76 +msgid "workspace.options.component.variants-help-modal.rule2" +msgstr "Одного типа." + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:77 +msgid "workspace.options.component.variants-help-modal.rule2.detail" +msgstr "Прямоугольник, эллипс, контуры и булевы операции — один тип." + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:87 +msgid "workspace.options.component.variants-help-modal.rule3" +msgstr "На одном уровне вложенности." + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:88 +msgid "workspace.options.component.variants-help-modal.rule3.detail" +msgstr "Группы, доски и макеты считаются равными." + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +msgid "workspace.options.component.variants-help-modal.title" +msgstr "Как варианты держат связь" + #: src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:189 msgid "workspace.options.constraints" msgstr "Ограничения" @@ -4666,11 +7071,15 @@ msgstr "Верх" msgid "workspace.options.constraints.topbottom" msgstr "Верх и низ" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:527 +msgid "workspace.options.convert-to-typography" +msgstr "Создать стиль типографики" + +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Дизайн" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Экспорт" @@ -4678,44 +7087,76 @@ msgstr "Экспорт" msgid "workspace.options.export-multiple" msgstr "Экспорт выбранного" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Экспорт 1 элемента" msgstr[1] "Экспорт %s элементов" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +msgid "workspace.options.export.add-export" +msgstr "Добавить экспорт" + +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Удалить экспорт" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Суффикс" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:260 +msgid "workspace.options.exporting-busy" +msgstr "Служба экспорта занята, попробуйте позже" + +#: src/app/main/ui/exports/assets.cljs:263 +msgid "workspace.options.exporting-cancelled" +msgstr "Экспорт отменён" + +#: src/app/main/ui/exports/assets.cljs:262 +msgid "workspace.options.exporting-cancelling" +msgstr "Отмена…" + +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Экспорт завершён" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Экспортирование…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Экспорт не удался" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Неожиданно медленный экспорт" +#: src/app/main/ui/exports/assets.cljs:264 +msgid "workspace.options.exporting-queued" +msgstr "Ожидание…" + #: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:108, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:238 msgid "workspace.options.fill" msgstr "Заливка" +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:210 +msgid "workspace.options.fill.add-fill" +msgstr "Добавить заливку" + #: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:225 msgid "workspace.options.fill.remove-fill" msgstr "Удалить заливку" +#: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:200 +msgid "workspace.options.fill.section" +msgstr "Заливка" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 +msgid "workspace.options.fit-content" +msgstr "Подогнать доску под содержимое" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:700 msgid "workspace.options.flows.add-flow-start" msgstr "Добавить начало потока" @@ -4732,71 +7173,75 @@ msgstr "Начало потока" msgid "workspace.options.flows.flow-starts" msgstr "Начала потоков" +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:300, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 +msgid "workspace.options.font-not-available" +msgstr "Шрифта (%s) больше нет. Загрузите его снова, чтобы создать стиль типографики" + #: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:34 msgid "workspace.options.grid.auto" msgstr "Авто" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Колонки" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Цвет" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Желоб" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Высота" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Поле" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Установить по умолчанию" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Низ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Центр" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Левый" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Правый" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Растягивать" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Верх" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Использовать значение по умолчанию" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Строки" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Квадрат" @@ -4808,15 +7253,27 @@ msgstr "Заливка для группы" msgid "workspace.options.group-stroke" msgstr "Обводка для группы" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 +msgid "workspace.options.guides.add-guide" +msgstr "Добавить направляющую" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:235 +msgid "workspace.options.guides.remove-guide" +msgstr "Убрать направляющую" + +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Направляющие" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 +msgid "workspace.options.guides.toggle-guide" +msgstr "Показать/скрыть направляющую" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Высота" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Осмотр" @@ -5028,99 +7485,123 @@ msgstr "При нажатии" msgid "workspace.options.interactions" msgstr "Интерактив" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:746 +msgid "workspace.options.interactions.add-interaction" +msgstr "Добавить взаимодействие" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Цвет" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Затемнение основы" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Осветление основы" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Затемнение" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Разница" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Исключение" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Жёсткий свет" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Цветовой тон" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Замена светлым" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Яркость" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Умножение" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Нормально" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Наложение" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Насыщенность" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Экран" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Мягкий свет" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 +msgid "workspace.options.layer-options.layer-section" +msgstr "Слой" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 +msgid "workspace.options.layer-options.toggle-layer" +msgstr "Показать/скрыть слой" + +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Макс. высота" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Макс. ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Мин. высота" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Мин. ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Больше цветов" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Больше цветов библиотеки" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 +msgid "workspace.options.more-token-colors" +msgstr "Ещё токены цвета" + +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 +msgid "workspace.options.no-size-preset-results" +msgstr "Нет подходящего пресета размера" + +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Непрозрачность" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:99, src/app/main/ui/workspace/sidebar/options/page.cljs:106 +msgid "workspace.options.pixel-grid-color" +msgstr "Цвет пиксельной сетки" + +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Прототип" @@ -5144,30 +7625,50 @@ msgstr "Сверху слева" msgid "workspace.options.radius-top-right" msgstr "Сверху справа" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:346 +msgid "workspace.options.radius.hide-all-corners" +msgstr "Свернуть радиусы по отдельности" + +#: src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:169, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:287 +msgid "workspace.options.radius.radius-section" +msgstr "Скругление" + +#: src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:206, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:347 +msgid "workspace.options.radius.show-single-corners" +msgstr "Показать радиусы по отдельности" + +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Последние" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Повторить" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Вращение" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Искать шрифт" +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 +msgid "workspace.options.search-size-preset" +msgstr "Найти пресет размера" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:786 msgid "workspace.options.select-a-shape" msgstr "Выберите фигуру, кадр или группу для соединения с другим кадром." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Выбранные цвета" +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 +msgid "workspace.options.selection-color.section" +msgstr "Выбранные цвета" + #: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:106 msgid "workspace.options.selection-fill" msgstr "Заливка выбранного" @@ -5176,6 +7677,10 @@ msgstr "Заливка выбранного" msgid "workspace.options.selection-stroke" msgstr "Обводка выбранного" +#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:154 +msgid "workspace.options.shadow-options.add-shadow" +msgstr "Добавить тень" + #: src/app/main/ui/inspect/attributes/shadow.cljs:47, src/app/main/ui/workspace/sidebar/options/rows/shadow_row.cljs:180, src/app/main/ui/workspace/sidebar/options/rows/shadow_row.cljs:182 msgid "workspace.options.shadow-options.blur" msgstr "Размытие" @@ -5220,51 +7725,63 @@ msgstr "Групповая тень" msgid "workspace.options.shadow-options.title.multiple" msgstr "Выборочные тени" +#: src/app/main/ui/workspace/sidebar/options/rows/shadow_row.cljs:156 +msgid "workspace.options.shadow-options.toggle-shadow" +msgstr "Показать/скрыть тень" + #: src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:265 msgid "workspace.options.show-fill-on-export" msgstr "Показать в экспорте" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Показать в режиме просмотра" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Размер" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Предустановки размеров" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 +msgid "workspace.options.size.lock" +msgstr "Запереть пропорции" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 +msgid "workspace.options.size.unlock" +msgstr "Отпереть пропорции" + +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Круг" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Алмаз" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Стрелка" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Ничего" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Круг" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Квадрат" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Прямоугольник" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Треугольник" @@ -5272,42 +7789,82 @@ msgstr "Треугольник" msgid "workspace.options.stroke-color" msgstr "Цвет обводки" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 +msgid "workspace.options.stroke-dash" +msgstr "Штрих" + +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 +msgid "workspace.options.stroke-gap" +msgstr "Пробел" + +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Толщина обводки" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 +msgid "workspace.options.stroke-width-bottom" +msgstr "Низ (конец блока)" + +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 +msgid "workspace.options.stroke-width-left" +msgstr "Лево (начало строки)" + +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 +msgid "workspace.options.stroke-width-right" +msgstr "Право (конец строки)" + +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 +msgid "workspace.options.stroke-width-top" +msgstr "Верх (начало блока)" + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:267 +msgid "workspace.options.stroke.add-stroke" +msgstr "Добавить цвет обводки" + +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Центр" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Пунктирный" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Точечный" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Внутрь" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Смешаный" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Наружу" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 +msgid "workspace.options.stroke.per-side" +msgstr "Обводка по сторонам" + +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 +msgid "workspace.options.stroke.per-side-disabled" +msgstr "Обводка по сторонам — только в новом рендере. Переключите рендер." + +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Удалить обводку" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Сплошной" +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 +msgid "workspace.options.stroke.toggle-stroke" +msgstr "Показать/скрыть обводку" + #: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:135 msgid "workspace.options.text-options.align-bottom" msgstr "Выровнять низ" @@ -5340,11 +7897,11 @@ msgstr "Автоширина" msgid "workspace.options.text-options.grow-fixed" msgstr "Фиксированно" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Межсимвольный интервал" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Высота строки" @@ -5368,6 +7925,10 @@ msgstr "Выравнивание по левому краю" msgid "workspace.options.text-options.text-align-right" msgstr "Выравнивание по правому краю" +#: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:509 +msgid "workspace.options.text-options.text-section" +msgstr "Текст" + #: src/app/main/ui/workspace/sidebar/options/menus/text.cljs:380 msgid "workspace.options.text-options.title" msgstr "Текст" @@ -5388,111 +7949,227 @@ msgstr "Подчеркнутый (%s)" msgid "workspace.options.use-play-button" msgstr "Нажмите кнопку воспроизведения вверху для показа прототипа." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Ось X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Ось Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Показать узлы (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 +msgid "workspace.path.actions.handler-aligned" +msgstr "Выровненный" + +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 +msgid "workspace.path.actions.handler-independent" +msgstr "Независимый" + +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 +msgid "workspace.path.actions.handler-mirror" +msgstr "Равный" + +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 +msgid "workspace.path.actions.handler-type" +msgstr "Поведение маркера" + +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Соединить узлы (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "В угол (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "В кривую (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Объединить узлы (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Сместить узлы (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Разделить узлы (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Прилипать узлами (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Открыть" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Откройте для себя [больше плагинов](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Плагины ещё не установлены" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Манифест плагина неверен." -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 +msgid "workspace.plugins.error.need-editor" +msgstr "Чтобы пользоваться плагином, нужна роль редактора" + +#: src/app/main/data/plugins.cljs:160 +msgid "workspace.plugins.error.unreachable" +msgstr "До адреса плагина не достучаться." + +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Плагин не существует или его URL-адрес неверен." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Установить" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Установленные плагины" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Менеджер плагинов" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Плагины" -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:491 +msgid "workspace.plugins.permissions-update.title" +msgstr "ОБНОВИТЕ ЭТОТ ПЛАГИН" + +#: src/app/main/ui/workspace/plugins.cljs:495 +msgid "workspace.plugins.permissions-update.warning" +msgstr "Плагин изменился с прошлого открытия. Теперь он просит доступ к:" + +#: src/app/main/ui/workspace/plugins.cljs:330 +msgid "workspace.plugins.permissions.allow-download" +msgstr "Начинать скачивание файлов." + +#: src/app/main/ui/workspace/plugins.cljs:337 +msgid "workspace.plugins.permissions.allow-localstorage" +msgstr "Хранить данные в браузере." + +#: src/app/main/ui/workspace/plugins.cljs:378 +msgid "workspace.plugins.permissions.clipboard-read" +msgstr "Читать содержимое буфера обмена." + +#: src/app/main/ui/workspace/plugins.cljs:372 +msgid "workspace.plugins.permissions.clipboard-write" +msgstr "Читать буфер обмена и писать в него." + +#: src/app/main/ui/workspace/plugins.cljs:323 +msgid "workspace.plugins.permissions.comment-read" +msgstr "Читать Ваши комментарии и ответы." + +#: src/app/main/ui/workspace/plugins.cljs:317 +msgid "workspace.plugins.permissions.comment-write" +msgstr "Читать и менять Ваши комментарии и отвечать от Вашего имени." + +#: src/app/main/ui/workspace/plugins.cljs:290 +msgid "workspace.plugins.permissions.content-read" +msgstr "Читать содержимое доступных Вам файлов." + +#: src/app/main/ui/workspace/plugins.cljs:284 +msgid "workspace.plugins.permissions.content-write" +msgstr "Читать и менять содержимое доступных Вам файлов." + +#: src/app/main/ui/workspace/plugins.cljs:431 +msgid "workspace.plugins.permissions.disclaimer" +msgstr "" +"Учтите: плагин сделал посторонний, убедитесь, что ему доверяете. Нам важны " +"Ваши данные и безопасность. Сомневаетесь — напишите в поддержку." + +#: src/app/main/ui/workspace/plugins.cljs:310 +msgid "workspace.plugins.permissions.library-read" +msgstr "Читать Ваши библиотеки и ресурсы." + +#: src/app/main/ui/workspace/plugins.cljs:304 +msgid "workspace.plugins.permissions.library-write" +msgstr "Читать и менять Ваши библиотеки и ресурсы." + +#: src/app/main/ui/workspace/plugins.cljs:424 +msgid "workspace.plugins.permissions.title" +msgstr "ПЛАГИН «%s» ПРОСИТ ДОСТУП К:" + +#: src/app/main/ui/workspace/plugins.cljs:297 +msgid "workspace.plugins.permissions.user-read" +msgstr "Читать профиль текущего пользователя." + +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Список плагинов" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Удалить плагин" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Введите URL-адрес плагина" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Плагины" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/plugins.cljs:563 +msgid "workspace.plugins.try-out.cancel" +msgstr "НЕ СЕЙЧАС" + +#: src/app/main/ui/workspace/plugins.cljs:556 +msgid "workspace.plugins.try-out.message" +msgstr "" +"Заглянуть? Откроется в новом черновике Вашей команды. (Если нет — " +"установленные плагины лежат в любом файле.)" + +#: src/app/main/ui/workspace/plugins.cljs:552 +msgid "workspace.plugins.try-out.title" +msgstr "ПЛАГИН «%s» УСТАНОВЛЕН!" + +#: src/app/main/ui/workspace/plugins.cljs:568 +msgid "workspace.plugins.try-out.try" +msgstr "ПОПРОБОВАТЬ ПЛАГИН" + +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Добавить макет гибкости" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Добавить макет сетки" +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 +msgid "workspace.shape.menu.add-layout" +msgstr "Добавить макет" + +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 +msgid "workspace.shape.menu.add-variant" +msgstr "Создать вариант" + +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 +msgid "workspace.shape.menu.add-variant-property" +msgstr "Добавить свойство" + #: src/app/main/ui/workspace/context_menu.cljs:327 msgid "workspace.shape.menu.back" msgstr "На задний план" @@ -5501,14 +8178,38 @@ msgstr "На задний план" msgid "workspace.shape.menu.backward" msgstr "Перенести назад" +#: src/app/main/ui/workspace/context_menu.cljs:754 +msgid "workspace.shape.menu.clear-guides" +msgstr "Убрать направляющие доски" + +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 +msgid "workspace.shape.menu.combine-as-variants" +msgstr "Объединить как варианты" + #: src/app/main/ui/workspace/sidebar/assets/components.cljs:665 msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Компоненты должны находиться на одной странице" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Скопировать" +#: src/app/main/ui/workspace/context_menu.cljs:259 +msgid "workspace.shape.menu.copy-as-image" +msgstr "Скопировать как картинку" + +#: src/app/main/ui/workspace/context_menu.cljs:251 +msgid "workspace.shape.menu.copy-css" +msgstr "Скопировать как CSS" + +#: src/app/main/ui/workspace/context_menu.cljs:253 +msgid "workspace.shape.menu.copy-css-nested" +msgstr "Скопировать как CSS (вложенные слои)" + +#: src/app/main/ui/workspace/context_menu.cljs:226 +msgid "workspace.shape.menu.copy-id" +msgstr "Скопировать id" + #: src/app/main/ui/workspace/context_menu.cljs:232 msgid "workspace.shape.menu.copy-link" msgstr "Скопировать ссылку" @@ -5517,31 +8218,43 @@ msgstr "Скопировать ссылку" msgid "workspace.shape.menu.copy-paste-as" msgstr "Скопировать/Вставить как..." +#: src/app/main/ui/workspace/context_menu.cljs:268 +msgid "workspace.shape.menu.copy-props" +msgstr "Скопировать свойства" + +#: src/app/main/ui/workspace/context_menu.cljs:255 +msgid "workspace.shape.menu.copy-svg" +msgstr "Скопировать как SVG" + +#: src/app/main/ui/workspace/context_menu.cljs:265 +msgid "workspace.shape.menu.copy-text" +msgstr "Скопировать как текст" + #: src/app/main/ui/workspace/sidebar/assets/common.cljs:561 msgid "workspace.shape.menu.create-annotation" msgstr "Создать аннотацию" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" -msgstr "Выделенное в кадр" +msgstr "Выделенное в доску" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Создать компонент" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Создать несколько компонентов" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Вырезать" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Удалить" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Удалить начало потока" @@ -5553,7 +8266,7 @@ msgstr "Отсоединить экземпляр" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Отсоединить экземпляры" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Разница" @@ -5561,15 +8274,15 @@ msgstr "Разница" msgid "workspace.shape.menu.duplicate" msgstr "Дублировать" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Редактировать" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Исключить" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Flatten" @@ -5581,7 +8294,7 @@ msgstr "Отразить по горизонтали" msgid "workspace.shape.menu.flip-vertical" msgstr "Отразить по вертикали" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Начало потока" @@ -5593,46 +8306,62 @@ msgstr "Перенести вперёд" msgid "workspace.shape.menu.front" msgstr "На передний план" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Группа" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Скрыть" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Показать/скрыть UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Пересечь" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Заблокировать" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Маска" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Вставить" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:273 +msgid "workspace.shape.menu.paste-props" +msgstr "Вставить свойства" + +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Контур" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Удалить макет гибкости" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Удалить макет сетки" +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +msgid "workspace.shape.menu.remove-layout" +msgstr "Убрать макет" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 +msgid "workspace.shape.menu.remove-variant-property" +msgstr "Убрать свойство" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 +msgid "workspace.shape.menu.remove-variant-property.last-property" +msgstr "У варианта должно быть хотя бы одно свойство" + #: src/app/main/ui/workspace/context_menu.cljs:384 msgid "workspace.shape.menu.rename" msgstr "Переименовать" @@ -5645,15 +8374,19 @@ msgstr "Сбросить переопределения" msgid "workspace.shape.menu.restore-main" msgstr "Восстановить основной компонент" +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:575 +msgid "workspace.shape.menu.restore-variant" +msgstr "Восстановить вариант" + #: src/app/main/ui/workspace/context_menu.cljs:305 msgid "workspace.shape.menu.select-layer" msgstr "Выбрать слой" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Показать" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Показать на панели ресурсов" @@ -5661,6 +8394,10 @@ msgstr "Показать на панели ресурсов" msgid "workspace.shape.menu.show-main" msgstr "Показать основной компонент" +#: src/app/main/ui/workspace/context_menu.cljs:526 +msgid "workspace.shape.menu.stroke-to-path" +msgstr "Обводку в контур" + #: src/app/main/ui/workspace/context_menu.cljs:366 msgid "workspace.shape.menu.thumbnail-remove" msgstr "Удалить миниатюру" @@ -5669,19 +8406,19 @@ msgstr "Удалить миниатюру" msgid "workspace.shape.menu.thumbnail-set" msgstr "Сделать миниатюрой" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Разгруппировать" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Суммировать" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Paзблокировать" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Убрать маску" @@ -5697,47 +8434,79 @@ msgstr "Свернуть боковую панель" msgid "workspace.sidebar.expand" msgstr "Расширить боковую панель" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "История" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Слои" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Компоненты" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +msgid "workspace.sidebar.layers.filter" +msgstr "Фильтр" + +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Доски" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Группы" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Изображения" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Маски" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 +msgid "workspace.sidebar.layers.no-matches" +msgstr "Совпадений нет" + +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 +msgid "workspace.sidebar.layers.replace" +msgstr "Заменить" + +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 +msgid "workspace.sidebar.layers.replace-all" +msgstr "Заменить всё" + +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +msgid "workspace.sidebar.layers.replace-placeholder" +msgstr "Заменить на…" + +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Искать слои" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 +msgid "workspace.sidebar.layers.search-and-replace" +msgstr "Найти и заменить" + +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 +msgid "workspace.sidebar.layers.search-scope-canvas" +msgstr "Текст" + +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 +msgid "workspace.sidebar.layers.search-scope-layers" +msgstr "Имена слоёв" + +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Формы" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Тексты" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Импортированные атрибуты SVG" @@ -5749,83 +8518,623 @@ msgstr "Страницы" msgid "workspace.sidebar.sitemap.add-page" msgstr "Добавить страницу" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Карта сайта" +#: src/app/main/data/workspace/comments.cljs:49 +msgid "workspace.toast.comments-hidden" +msgstr "Комментарии скрыты" + +#: src/app/main/data/workspace/comments.cljs:50 +msgid "workspace.toast.comments-visible" +msgstr "Комментарии видны" + #: src/app/main/ui/workspace/tokens/themes/theme_selector.cljs:112 msgid "workspace.tokens.active-themes" msgstr "Активных тем: %s" +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:149, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:360 +msgid "workspace.tokens.add-new-theme" +msgstr "Добавить тему" + +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:106 +msgid "workspace.tokens.add-set" +msgstr "Добавить набор" + +#: src/app/main/ui/workspace/tokens/sets/context_menu.cljs:62 +msgid "workspace.tokens.add-set-to-group" +msgstr "Добавить набор в группу" + +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:201, src/app/main/ui/workspace/tokens/management/group.cljs:186 +msgid "workspace.tokens.add-token" +msgstr "Добавить токен: %s" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 +msgid "workspace.tokens.applied-to" +msgstr "Применено к" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:400 +msgid "workspace.tokens.axis" +msgstr "Ось" + #: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:369 msgid "workspace.tokens.back-to-themes" msgstr "К списку тем" +#: src/app/main/ui/workspace/tokens/settings/menu.cljs:88 +msgid "workspace.tokens.base-font-size" +msgstr "Базовый размер шрифта" + +#: src/app/main/ui/workspace/tokens/settings/menu.cljs:42 +msgid "workspace.tokens.base-font-size.error" +msgstr "Базовый размер — в пикселях или без единиц." + +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:98 +msgid "workspace.tokens.change-token-source" +msgstr "Сменить источник токенов" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 +msgid "workspace.tokens.color" +msgstr "Цвет" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:433 +msgid "workspace.tokens.copy-name" +msgstr "Скопировать путь токена" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:57 +msgid "workspace.tokens.create-new-theme" +msgstr "Создайте первую тему прямо сейчас." + +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 +msgid "workspace.tokens.create-one" +msgstr "Создать набор." + +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:293 +msgid "workspace.tokens.create-token" +msgstr "Создать токен %s" + +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:101 +msgid "workspace.tokens.current-tokens-source-tooltip" +msgstr "Это текущий источник токенов" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:437 +msgid "workspace.tokens.delete" +msgstr "Удалить токен" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:138 +msgid "workspace.tokens.delete-theme-title" +msgstr "Удалить тему" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:430 +msgid "workspace.tokens.duplicate" +msgstr "Дублировать токен" + +#: src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:60 +msgid "workspace.tokens.duplicate-group" +msgstr "Дублировать группу токенов" + +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 +msgid "workspace.tokens.duplicate-suffix" +msgstr "копия" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:416 +msgid "workspace.tokens.edit" +msgstr "Править токен" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:359 +msgid "workspace.tokens.edit-theme-title" +msgstr "Править тему" + #: src/app/main/ui/workspace/tokens/themes/theme_selector.cljs:87 msgid "workspace.tokens.edit-themes" msgstr "Изменить темы" +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:292 +msgid "workspace.tokens.edit-token" +msgstr "Править токен %s" + +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:299 +msgid "workspace.tokens.enter-token-name" +msgstr "Введите имя токена %s" + +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 +msgid "workspace.tokens.error-text-edition" +msgstr "Токены нельзя применить во время правки текста. Выберите текстовый слой." + +#: src/app/main/ui/workspace/tokens/export/modal.cljs:48 +msgid "workspace.tokens.export" +msgstr "Экспорт" + +#: src/app/main/ui/workspace/tokens/export/modal.cljs:124 +msgid "workspace.tokens.export-tokens" +msgstr "Экспорт токенов" + +#: src/app/main/ui/workspace/tokens/export/modal.cljs:117 +msgid "workspace.tokens.export.multiple-files" +msgstr "Несколько файлов" + +#: src/app/main/ui/workspace/tokens/export/modal.cljs:37 +msgid "workspace.tokens.export.no-tokens-themes-sets" +msgstr "Экспортировать нечего: нет токенов, тем и наборов." + +#: src/app/main/ui/workspace/tokens/export/modal.cljs:34 +msgid "workspace.tokens.export.preview" +msgstr "Предпросмотр:" + +#: src/app/main/ui/workspace/tokens/export/modal.cljs:115 +msgid "workspace.tokens.export.single-file" +msgstr "Один файл" + +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 +msgid "workspace.tokens.font-size-value-enter" +msgstr "Размер шрифта или {alias}" + +#: src/app/main/data/workspace/tokens/application.cljs:364 +msgid "workspace.tokens.font-variant-not-found" +msgstr "Не удалось задать насыщенность и стиль: этого стиля нет в текущем шрифте" + +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 +msgid "workspace.tokens.font-weight-value-enter" +msgstr "Насыщенность (300, Bold Italic...) или {alias}" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:253 +msgid "workspace.tokens.gaps" +msgstr "Промежутки" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:93 +msgid "workspace.tokens.group-name" +msgstr "Имя группы" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:232 +msgid "workspace.tokens.import-button-prefix" +msgstr "Импорт %s" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:272 +msgid "workspace.tokens.import-menu-folder-option" +msgstr "Папка" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:270 +msgid "workspace.tokens.import-menu-json-option" +msgstr "Один JSON-файл" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:271 +msgid "workspace.tokens.import-menu-zip-option" +msgstr "ZIP-файл" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:240 +msgid "workspace.tokens.import-multiple-files" +msgstr "В нескольких файлах именами наборов служат имя и путь файла." + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:239 +msgid "workspace.tokens.import-single-file" +msgstr "В одном JSON-файле ключи верхнего уровня — имена наборов токенов." + +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +msgid "workspace.tokens.import-tokens" +msgstr "Импорт токенов" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:246, src/app/main/ui/workspace/tokens/import_from_library.cljs:81 +msgid "workspace.tokens.import-warning" +msgstr "Импорт токенов заменит текущие токены, наборы и темы." + +#: src/app/main/ui/workspace/tokens/management.cljs:88 +msgid "workspace.tokens.inactive-set" +msgstr "Неактивен" + +#: src/app/main/ui/workspace/tokens/management.cljs:80 +msgid "workspace.tokens.inactive-set-description" +msgstr "" +"Набор неактивен. Смените тему или активируйте набор, чтобы увидеть " +"изменения во вьюпорте" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 +msgid "workspace.tokens.individual-tokens" +msgstr "Использовать отдельные токены" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:203 +msgid "workspace.tokens.label.group" +msgstr "Группа" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:205 +msgid "workspace.tokens.label.group-placeholder" +msgstr "Добавьте группу (например, Mode)" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:212 +msgid "workspace.tokens.label.theme" +msgstr "Тема" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:213 +msgid "workspace.tokens.label.theme-placeholder" +msgstr "Добавьте тему (например, Light)" + +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 +msgid "workspace.tokens.letter-spacing-value-enter-composite" +msgstr "Межбуквенный интервал или {alias}" + +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 +msgid "workspace.tokens.line-height-value-enter" +msgstr "Высота строки (множитель, px, %) или {alias}" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:245 +msgid "workspace.tokens.margins" +msgstr "Внешние отступы" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:282 +msgid "workspace.tokens.max-size" +msgstr "Макс. размер" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:276 +msgid "workspace.tokens.min-size" +msgstr "Мин. размер" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 +msgid "workspace.tokens.missing-reference" +msgstr "Нет ссылки" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 +msgid "workspace.tokens.more-options" +msgstr "Щёлкните правой кнопкой — там настройки" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:133 +msgid "workspace.tokens.no-active-sets" +msgstr "Нет активных наборов" + +#: src/app/main/ui/workspace/tokens/themes/theme_selector.cljs:115 +msgid "workspace.tokens.no-active-theme" +msgstr "Нет активной темы" + +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:72 +msgid "workspace.tokens.no-permisions-set" +msgstr "Чтобы включать и отключать наборы, нужна роль редактора" + +#: src/app/main/ui/workspace/tokens/themes.cljs:57 +msgid "workspace.tokens.no-permission-themes" +msgstr "Чтобы пользоваться темами, нужна роль редактора" + +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:492 +msgid "workspace.tokens.no-sets-create" +msgstr "Наборы пока не заданы. Создайте первый." + +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:93, src/app/main/ui/workspace/tokens/sets/lists.cljs:99 +msgid "workspace.tokens.no-sets-yet" +msgstr "Наборов пока нет." + +#: src/app/main/ui/workspace/tokens/themes.cljs:43 +msgid "workspace.tokens.no-themes" +msgstr "Тем пока нет." + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:53 +msgid "workspace.tokens.no-themes-currently" +msgstr "У Вас пока нет тем." + +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:113 +msgid "workspace.tokens.not-remap" +msgstr "Не переназначать" + +#: src/app/main/data/workspace/libraries.cljs:848 +msgid "workspace.tokens.notifications.source-sets-or-themes-updated" +msgstr "Наборы и темы источника токенов изменены" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:132 +msgid "workspace.tokens.num-active-sets" +msgstr "Активных наборов: %s" + +#: src/app/main/ui/workspace/colorpicker.cljs:438, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:112 +msgid "workspace.tokens.only-first-fill-or-stroke" +msgstr "Токены дизайна применяются только к первой заливке или обводке в списке." + +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:104 +msgid "workspace.tokens.open-source-new-tab" +msgstr "Открыть источник токенов в новой вкладке" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 +msgid "workspace.tokens.original-value" +msgstr "Исходное значение: %s" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:229 +msgid "workspace.tokens.paddings" +msgstr "Внутренние отступы" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:362 +msgid "workspace.tokens.radius" +msgstr "Радиус" + +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 +msgid "workspace.tokens.ref-not-valid" +msgstr "Ссылка в {%s} неверна или отсутствует в активных наборах." + +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 +msgid "workspace.tokens.reference-composite" +msgstr "Введите алиас типографики токена" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 +msgid "workspace.tokens.reference-composite-shadow" +msgstr "Введите алиас тени токена" + +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:117 +msgid "workspace.tokens.remap" +msgstr "Переназначить токены" + +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:102 +msgid "workspace.tokens.remap-node-references-title" +msgstr "Переименовать «%s» в «%s» и переназначить токены группы?" + +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:106 +msgid "workspace.tokens.remap-node-warning-effects" +msgstr "Обновятся все токены и ссылки со старым именем." + +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:101 +msgid "workspace.tokens.remap-token-references-title" +msgstr "Переназначить токены с «%s» на «%s»?" + +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:105 +msgid "workspace.tokens.remap-token-warning-effects" +msgstr "Изменятся все слои и ссылки со старым именем токена." + +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:107 +msgid "workspace.tokens.remap-warning-time" +msgstr "Действие может занять время." + +#: src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:59 +msgid "workspace.tokens.rename-group" +msgstr "Переименовать группу токенов" + +#: src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:68 +msgid "workspace.tokens.rename-group-name-hint" +msgstr "Токены переименуются сами: %s.(суффикс).(токен)" + +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 +msgid "workspace.tokens.resolved-value" +msgstr "Итоговое значение: %s" + #: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:249 msgid "workspace.tokens.save-theme" msgstr "Сохранить тему" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:307, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:538 +msgid "workspace.tokens.search-by-token" +msgstr "Поиск по имени токена" + +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:207, src/app/main/ui/workspace/tokens/sets/lists.cljs:316 +msgid "workspace.tokens.select-set" +msgstr "Выберите набор." + +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:60 +msgid "workspace.tokens.set-edit-placeholder" +msgstr "Введите имя (группы — через «/»)" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:374 +msgid "workspace.tokens.set-selection-theme" +msgstr "Укажите, какие наборы токенов входят в эту тему:" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:127 +msgid "workspace.tokens.sets-hint" +msgstr "Правка темы и наборов" + +#: src/app/main/ui/workspace/tokens/settings/menu.cljs:90 +msgid "workspace.tokens.setting-description" +msgstr "Здесь задаётся базовый размер шрифта — значение 1rem:" + +#: src/app/main/ui/workspace/tokens/settings/menu.cljs:83 +msgid "workspace.tokens.settings" +msgstr "Настройки токенов" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 +msgid "workspace.tokens.shadow-add-shadow" +msgstr "Добавить тень" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 +msgid "workspace.tokens.shadow-blur" +msgstr "Размытие" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 +msgid "workspace.tokens.shadow-inset" +msgstr "Внутренняя" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 +msgid "workspace.tokens.shadow-remove-shadow" +msgstr "Убрать тень" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 +msgid "workspace.tokens.shadow-spread" +msgstr "Распространение" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 +msgid "workspace.tokens.shadow-token-blur-value-error" +msgstr "Размытие не может быть отрицательным" + +#: ../common/src/app/common/files/tokens.cljc:119 +msgid "workspace.tokens.shadow-token-spread-value-error" +msgstr "Распространение не может быть отрицательным" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 +msgid "workspace.tokens.shadow-x" +msgstr "X" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 +msgid "workspace.tokens.shadow-y" +msgstr "Y" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:270 +msgid "workspace.tokens.size" +msgstr "Размер" + +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:75 +msgid "workspace.tokens.source" +msgstr "Источник:" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:311, src/app/main/ui/workspace/tokens/management/context_menu.cljs:335 +msgid "workspace.tokens.stroke-width" +msgstr "Толщина обводки" + +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 +msgid "workspace.tokens.text-case-value-enter" +msgstr "none | uppercase | lowercase | capitalize или {alias}" + +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 +msgid "workspace.tokens.text-decoration-value-enter" +msgstr "none | underline | strike-through или {alias}" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:85 +msgid "workspace.tokens.themes-description" +msgstr "" +"Здесь управляйте темами: включайте и отключайте их и задавайте активные " +"наборы." + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:49, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:83 +msgid "workspace.tokens.themes-list" +msgstr "Список тем" + +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:80 +msgid "workspace.tokens.this-file" +msgstr "Этот файл" + +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:334, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:335 +msgid "workspace.tokens.token-description" +msgstr "Описание" + +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 +msgid "workspace.tokens.token-font-family-select" +msgstr "Выберите семейство шрифтов" + +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 +msgid "workspace.tokens.token-font-family-value" +msgstr "Семейство шрифтов" + +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 +msgid "workspace.tokens.token-font-family-value-enter" +msgstr "Семейство шрифтов или шрифты через запятую" + +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 +msgid "workspace.tokens.token-name" +msgstr "Имя" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +msgid "workspace.tokens.token-name-duplication-validation-error" +msgstr "По пути %s токен уже есть (или в его начале)." + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +msgid "workspace.tokens.token-name-length-validation-error" +msgstr "Имя — минимум 1 символ" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +msgid "workspace.tokens.token-name-validation-error" +msgstr "" +" — неверное имя токена.\n" +"Имена токенов — только буквы и цифры через «.» и не с «$» в начале." + +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:325 +msgid "workspace.tokens.token-value" +msgstr "Значение" + +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 +msgid "workspace.tokens.token-value-enter" +msgstr "Введите значение или алиас через {alias}" + +#: src/app/main/ui/workspace/tokens/management.cljs:77 +msgid "workspace.tokens.tokens-section-title" +msgstr "ТОКЕНЫ - %s" + +#: src/app/main/ui/workspace/tokens/sidebar.cljs:133 +msgid "workspace.tokens.tools" +msgstr "Инструменты" + +#: src/app/main/data/workspace/tokens/import_export.cljs:57 +msgid "workspace.tokens.unknown-token-type-message" +msgstr "" +"Импорт удался, но часть токенов пропущена: неподдерживаемые значения $type. " +"Раскройте подробности — там список." + +#: src/app/main/data/workspace/tokens/import_export.cljs:60 +msgid "workspace.tokens.unknown-token-type-section" +msgstr "Тип «%s» не поддерживается (%s):" + +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 +msgid "workspace.tokens.use-reference" +msgstr "Взять ссылку" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Значение недействительное" +#: src/app/main/ui/workspace/top_toolbar.cljs:68 +msgid "workspace.toolbar.arrow" +msgstr "Стрелка (%s)" + #: src/app/main/ui/workspace/sidebar.cljs:159, src/app/main/ui/workspace/sidebar.cljs:166 msgid "workspace.toolbar.assets" msgstr "Ресурсы" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Палитра цветов (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Комментарии (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Кривая (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Эллипс (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" -msgstr "Кадр (%s)" +msgstr "Доска (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Изображение (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:362 +msgid "workspace.toolbar.label" +msgstr "Рисование" + +#: src/app/main/ui/workspace/top_toolbar.cljs:67 +msgid "workspace.toolbar.line" +msgstr "Линия (%s)" + +#: src/app/main/ui/components/mcp_menu.cljs:120 +msgid "workspace.toolbar.mcp" +msgstr "MCP" + +#: src/app/main/ui/components/mcp_menu.cljs:130 +msgid "workspace.toolbar.mcp-connected" +msgstr "MCP подключён" + +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Перемещение (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/palette.cljs:175 +msgid "workspace.toolbar.palette-bar" +msgstr "Палитра" + +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Контур (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Плагины (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Прямоугольник (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Текст (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Типографики (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Переключить панель инструментов" @@ -5838,10 +9147,22 @@ msgstr "Готово" msgid "workspace.top-bar.view-only" msgstr "**Проверка кода** (Только просмотр)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/viewport/top_bar.cljs:39 +msgid "workspace.top-bar.webgl-context-lost" +msgstr "Рендеринг недоступен. Обновите страницу." + +#: src/app/main/ui/workspace/viewport/top_bar.cljs:43 +msgid "workspace.top-bar.webgl-context-lost.reload" +msgstr "Обновить" + +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Изменений в истории ещё нет" +#: src/app/main/ui/workspace/sidebar/history.cljs:365 +msgid "workspace.undo.entry.by" +msgstr "от %s" + #: src/app/main/ui/workspace/sidebar/history.cljs:154 msgid "workspace.undo.entry.delete" msgstr "Удалено %s" @@ -5872,7 +9193,7 @@ msgstr "кривые" #: src/app/main/ui/workspace/sidebar/history.cljs:119 msgid "workspace.undo.entry.multiple.frame" -msgstr "кадры" +msgstr "доски" #: src/app/main/ui/workspace/sidebar/history.cljs:120 msgid "workspace.undo.entry.multiple.group" @@ -5932,7 +9253,7 @@ msgstr "кривая" #: src/app/main/ui/workspace/sidebar/history.cljs:136 msgid "workspace.undo.entry.single.frame" -msgstr "кадр" +msgstr "доска" #: src/app/main/ui/workspace/sidebar/history.cljs:137 msgid "workspace.undo.entry.single.group" @@ -5978,26 +9299,38 @@ msgstr "ресурс типографики" msgid "workspace.undo.entry.unknown" msgstr "Работа над %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Отклонить" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Дополнительная информация" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Обнаружены обновления общих библиотек" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Обновить" +#: src/app/main/ui/ds/product/milestone_group.cljs:78 +msgid "workspace.versions.autosaved.entry" +msgstr "Автосохранений: %s" + +#: src/app/main/data/workspace/versions.cljs:301, src/app/main/ui/workspace/sidebar/versions.cljs:300 +msgid "workspace.versions.autosaved.version" +msgstr "Автосохранение %s" + #: src/app/main/ui/workspace/sidebar/versions.cljs:323 msgid "workspace.versions.button.pin" msgstr "Закрепить версию" +#: src/app/main/ui/workspace/sidebar/versions.cljs:233, src/app/main/ui/workspace/sidebar/versions.cljs:313 +msgid "workspace.versions.button.preview" +msgstr "Посмотреть версию" + #: src/app/main/ui/workspace/sidebar/versions.cljs:318 msgid "workspace.versions.button.restore" msgstr "Восстановить версию" @@ -6010,6 +9343,10 @@ msgstr "Сохранить версию" msgid "workspace.versions.empty" msgstr "Версий пока нет" +#: src/app/main/ui/ds/product/milestone_group.cljs:72 +msgid "workspace.versions.expand-snapshot" +msgstr "Развернуть снимки" + #: src/app/main/ui/workspace/sidebar/versions.cljs:398 msgid "workspace.versions.filter.all" msgstr "Все версии" @@ -6022,10 +9359,22 @@ msgstr "Фильтр версий" msgid "workspace.versions.filter.mine" msgstr "Мои версии" +#: src/app/main/ui/workspace/sidebar/versions.cljs:403 +msgid "workspace.versions.filter.user" +msgstr "Версии: %s" + #: src/app/main/ui/workspace/sidebar/versions.cljs:420 msgid "workspace.versions.loading" msgstr "Загрузка..." +#: src/app/main/data/workspace/versions.cljs:312 +msgid "workspace.versions.preview-banner-title" +msgstr "Просмотр версии: %s" + +#: src/app/main/data/workspace/versions.cljs:306 +msgid "workspace.versions.preview.unnamed" +msgstr "Без названия" + #: src/app/main/data/workspace/versions.cljs:239 msgid "workspace.versions.restore-warning" msgstr "Вы хотите восстановить эту версию?" @@ -6038,6 +9387,10 @@ msgstr "Действия" msgid "workspace.versions.tab.history" msgstr "История" +#: src/app/main/ui/ds/product/milestone.cljs:84, src/app/main/ui/ds/product/milestone_group.cljs:91 +msgid "workspace.versions.version-menu" +msgstr "Открыть версии" + #: src/app/main/ui/workspace/sidebar/versions.cljs:65 #, markdown msgid "workspace.versions.warning.subtext" diff --git a/frontend/translations/sr.po b/frontend/translations/sr.po index 75ebabcdde..35224e7dba 100644 --- a/frontend/translations/sr.po +++ b/frontend/translations/sr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:19+0000\n" -"Last-Translator: Црнобог <68vuletic@gmail.com>\n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Serbian " "\n" "Language: sr\n" @@ -10,9 +10,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Већ имате налог?" @@ -30,7 +30,7 @@ msgstr "" "Ово је ДЕМО услуга, НЕ КОРИСТИТЕ за прави рад, пројекти ће се повремено " "брисати." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Заборављена лозинка?" @@ -38,35 +38,35 @@ msgstr "Заборављена лозинка?" msgid "auth.fullname" msgstr "Пуно име" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Пријавите се на свој налог" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Пријавите се овде." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot је бесплатан алат отвореног кода за сарадњу у дизајну и програмирању" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -86,7 +86,7 @@ msgstr "Профил није верификован, проверите про msgid "auth.notifications.recovery-token-sent" msgstr "Линк за опоравак лозинке је послат у Ваше пријемно сандуче." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Успешно придружење тиму" @@ -118,7 +118,7 @@ msgstr "Заборавили сте лозинку?" msgid "auth.recovery-submit" msgstr "Промените своју лозинку" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Још немате налог?" @@ -130,7 +130,7 @@ msgstr "Како желите да Вас ословљавамо на управ msgid "auth.register-account-title" msgstr "Ваше име" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Креирајте налог" @@ -143,7 +143,7 @@ msgstr "Креирајте налог" msgid "auth.terms-and-privacy-agreement" msgstr "Слажем се са [условима коришћења](%s) и [политиком приватности](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Услови коришћења" @@ -151,110 +151,110 @@ msgstr "Услови коришћења" msgid "auth.work-email" msgstr "Радна е-пошта" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Објави" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Сви Penpot корисници" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Да ли сте сигурни да желите да уклоните ову везу? Ако то урадите више неће " "бити доступно никоме" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(тренутни)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Уништи везу" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Добиј везу" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Веза је успешно копирана" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Управљање дозволама" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "страница подељена" msgstr[1] "%s странице подељено" msgstr[2] "%s страница подељено" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Може оставити коментар" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Може да провери кôд" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Свако са везом ће имати приступ" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Страница подељено" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Овде ће се појавити веза за дељење" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Само чланови тима" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Дели прототипе" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Изабери све" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Опозови објављивање" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Управљање тимом" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot је намењен тимовима. Позовите чланове да заједно раде на пројектима " "и датотекама" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Уједините снаге!" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Додај као дељену библиотеку" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Промените е-пошту" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(копија)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Направите нови тим" @@ -262,11 +262,11 @@ msgstr "Направите нови тим" msgid "dashboard.default-team-name" msgstr "Ваш Penpot" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Избришите тим" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Преузмите Penpot датотеку (.penpot)" @@ -282,60 +282,60 @@ msgstr "Дуплирај %s датотека" msgid "dashboard.export-binary-multi" msgstr "Преузмите % Penpot датотека (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Извезите табле као PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Извези као PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s од %s елемената изабрано" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Извези" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Можете додати подешавања извоза елементима из својстава дизајна (на дну " "десне бочне траке)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Информације како да подесите извоз у Penpot-у." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Нема елемената са подешавањима извоза." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Избор извоза" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Недостаје фонт" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Одбаци све" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Овде ће се појавити прилагођени фонтови које отпремите." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "фонт додат" msgstr[1] "%s фонта додато" msgstr[2] "%s фонтова додато" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -344,7 +344,7 @@ msgstr "" "фонта биће груписани као **једна породица фонта**. Можете отпремити фонтове " "у следећим форматима: **TTF, OTF и WOFF** (довољан је само један)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -353,11 +353,11 @@ msgstr "" "у [Условима коришћења Penpot-а](%s). Такође, можете прочитати о " "[лиценцирању фонтовa](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Отпреми све" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -368,74 +368,46 @@ msgstr "" "коришћење [Transfonter](https://transfonter.org/) за генерисање веб фонтовa " "и исправљање грешака. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Увези Penpot датотеке" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Упс! Нисмо успели да увеземо ову датотеку" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "%s датотека је успешно увезена." msgstr[1] "%s датотеке су успешно увезене." msgstr[2] "%s датотека успешно увезено." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Неке датотеке су садржале неважеће објекте који су уклоњени." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Обрада боја" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Обрада компоненти" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Обрада медија" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Обрада стране: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Обрада типографија" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Отпремање података на сервер (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Отпремање датотеке: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Позовите људе" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Напустите тим" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Библиотеке и шаблони" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Истражите више њих и знајте како да допринесете" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Дошло је до проблема при увозу шаблона. Шаблон није увезен." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Библиотеке" @@ -443,7 +415,7 @@ msgstr "Библиотеке" msgid "dashboard.loading-files" msgstr "учитавање ваших датотека…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "учитавање ваших фонтова…" @@ -459,35 +431,35 @@ msgstr "Премести %s датотека у" msgid "dashboard.move-to-other-team" msgstr "Премести у други тим" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Нова датотека" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Нова датотека" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Нови пројекат" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Нови пројекат" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Нема подударања за \"%s\"" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Закачени пројекти ће се појавити овде" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Ваша адреса е-поште је успешно ажурирана" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Ваша адреса е-поште је успешно верификована" @@ -495,7 +467,7 @@ msgstr "Ваша адреса е-поште је успешно верифико msgid "dashboard.notifications.password-saved" msgstr "Лозинка је успешно сачувана!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s чланова" @@ -503,7 +475,7 @@ msgstr "%s чланова" msgid "dashboard.open-in-new-tab" msgstr "Отворите датотеку у новој картици" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Опције" @@ -511,39 +483,39 @@ msgstr "Опције" msgid "dashboard.password-change" msgstr "Промените лозинку" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Прикачи/Откачи" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Пројекти" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Желите да уклоните свој налог?" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Сачувај подешавања" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Претражи…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Претражи за \"%s\"…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Изаберите језик корисничког интерфејса" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Изаберите тему" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Прикажи све датотеке" @@ -569,7 +541,7 @@ msgstr[2] "Ваше датотеке су успешно дуплиране" msgid "dashboard.success-duplicate-project" msgstr "Ваш пројекат је успешно дуплиран" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Ваша датотека је успешно премештена" @@ -581,82 +553,82 @@ msgstr "Ваше датотеке су успешно премештене" msgid "dashboard.success-move-project" msgstr "Ваш пројекат је успешно премештен" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Информације о тиму" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Чланови тима" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Тимски пројекти" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Тема корисничког интерфејса" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Резултати претраге" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Унесите за резултате претраге" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Опозови објављивање библиотеке" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Ажурирајте подешавања" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Је активно" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Када се ова кука покрене биће испоручени детаљи догађаја" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Тип садржаја" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Креирајте веб-куку" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Веб-кука је успешно креирана." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Веб-куке су једноставан начин да омогућите другим веб-сајтовима и " "апликацијама да буду обавештени када се одређени догађаји догоде у " "Penpot-у. Послаћемо POST захтев на сваку од URL адреса које наведете." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Притисните дугме „Додај веб-куку“ да бисте је додали." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "До сада није направљена ниједана веб-кука." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Ваш налог" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Е-пошта" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Ваше име" @@ -672,7 +644,7 @@ msgstr "Пажња" msgid "ds.component-subtitle" msgstr "Компоненте за ажурирање:" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Откажите" @@ -692,15 +664,15 @@ msgstr "Провајдер ауторизације није дозвољен з msgid "errors.auth-provider-not-configured" msgstr "Провајдер аутентификације није конфигурисан." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Изгледа да нисте аутентификовани или је сесија истекла." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Фонт %s није могао бити учитан" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Фонтови %s нису могли бити учитани" @@ -708,15 +680,15 @@ msgstr "Фонтови %s нису могли бити учитани" msgid "errors.cannot-upload" msgstr "Није могуће отпремити медијску датотеку." -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Ваш претраживач не може да одради ову операцију" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Е-пошта је већ коришћена" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Е-пошта је већ потврђена." @@ -724,36 +696,36 @@ msgstr "Е-пошта је већ потврђена." msgid "errors.email-as-password" msgstr "Не можете да користите своју е-пошту као лозинку" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Домен није дозвољен" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Е-пошта «%s» има много сталних извештаја о одбијању." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "Е-пошта «%s» је пријављеа као непожељана или трајно одбијена." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Изгледа да отварате датотеку која има омогућену функцију '%s', али је " "тренутна верзија penpot-а не подржава или је онемогућена." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Функција '%s' није подржана." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Чини се да постоји неусклађеност између омогућених функција и функција " "датотеке коју покушавате да отворите. Миграције за \"%s\" морају бити " "примењене пре него што се датотека може отворити." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Нешто погрешно се десило." @@ -773,7 +745,7 @@ msgstr "Ова позивница је можда отказана или ист msgid "errors.ldap-disabled" msgstr "LDAP аутентификација је онемогућена." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Достигли сте квоту од '%s'. Контактирајте подршку." @@ -789,7 +761,7 @@ msgstr "Чини се да садржај слике не одговара ек msgid "errors.media-type-not-allowed" msgstr "Чини се да ово није исправна слика." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Профил који сте позвали има искључене поруке е-поште (извештаји о нежељеној " @@ -803,7 +775,7 @@ msgstr "Лозинка за потврду мора да се подудара" msgid "errors.password-too-short" msgstr "Лозинка треба да има најмање 8 знакова" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Неважећи подаци у међуспремнику" @@ -811,7 +783,7 @@ msgstr "Неважећи подаци у међуспремнику" msgid "errors.profile-blocked" msgstr "Налог је блокиран" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Ваш профил има искључене поруке е-поште (извештаји о нежељеној пошти или " @@ -821,19 +793,19 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Регистрација је тренутно онемогућена." -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Откривена је некомпатибилна функција \"%s\"" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "Нема довољно чланова за напуштање тима, вероватно желите да га избришете." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Члан којег покушавате да доделите не постоји." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Власник не може да напусти тим, морате да пренесете улогу власника." @@ -841,39 +813,39 @@ msgstr "Власник не може да напусти тим, морате д msgid "errors.unexpected-error" msgstr "Дошло је до неочекиване грешке." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Непознати токен" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Датотека има некомпатибилан број верзије" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Грешка у вези, URL није доступан" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL не пролази валидацију." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Последња испорука није успела." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Грешка при SSL валидацији." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Време је истекло" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Неочекивана грешка при валидацији" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Неочекивани статус %s" @@ -889,13 +861,13 @@ msgstr "Стара лозинка је нетачна" msgid "feedback.description" msgstr "Опис" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Срећни смо што сте овде. Ако вам је потребна помоћ, претражите пре него што " "објавите." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot заједница" @@ -909,11 +881,11 @@ msgstr "" "Молимо опишите разлог ваше е-поште, наводећи да ли постоји проблем, идеја " "или сумња. Члан нашег тима ће одговорити у најкраћем могућем року." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Овде смо да помогнемо са вашим техничким питањима." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X налог за подршку" @@ -945,7 +917,7 @@ msgstr "Преузмите изворну слику" msgid "inspect.attributes.shadow" msgstr "Сенка" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Величина и положај" @@ -953,39 +925,27 @@ msgstr "Величина и положај" msgid "inspect.attributes.stroke" msgstr "Контура" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Средина" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Унутра" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Споља" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Типографија" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Породица фонта" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Величина фонта" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Стил фонта" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Размак између слова" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Висина линије" @@ -997,11 +957,11 @@ msgstr "Ништа" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Подвачење" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Насловно слово" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Мала слова" @@ -1013,7 +973,7 @@ msgstr "Ништа" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Без вредности" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Велико слово" @@ -1075,31 +1035,31 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Текст" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Пречице" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Прихвати" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Активно" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Додај прилагођени фонт" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Админ" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Све" @@ -1107,29 +1067,29 @@ msgstr "Све" msgid "labels.and" msgstr "и" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Назад" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Изгледа да морате да сачекате неколико тренутака и покушате поново; " "изводимо мање одржавање наших сервера." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Лош Gateway" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Откажите" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Затвори" @@ -1137,11 +1097,11 @@ msgstr "Затвори" msgid "labels.code" msgstr "Код" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Коментари" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Заједница" @@ -1149,35 +1109,35 @@ msgstr "Заједница" msgid "labels.confirm-password" msgstr "Потврди лозинку" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Настави" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Можете наставити са Penpot налогом" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Копирај везу" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Створи" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Направи нови тим" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Унесите ново име тима" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Управљачка табла" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Избришите" @@ -1189,7 +1149,7 @@ msgstr "Избришите коментар" msgid "labels.delete-comment-thread" msgstr "Избришите дискусију" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Избришите позивницу" @@ -1197,31 +1157,31 @@ msgstr "Избришите позивницу" msgid "labels.delete-multi-files" msgstr "Избришите %s датотека" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Развој" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Одбаци" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Нацрти" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Уреди" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Уредник" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Истекло" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Извези" @@ -1233,47 +1193,47 @@ msgstr "Повратне информације су онемогућене" msgid "labels.feedback-sent" msgstr "Повратне информације су послате" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Породица фонта" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Стилови" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Фонтови" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Github репозиторијум" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Оставите повратне информације" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Иди назад" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Графички дизајн" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Центар за помоћ" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Сакриј решене коментаре" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Неактиван" @@ -1281,47 +1241,47 @@ msgstr "Неактиван" msgid "labels.info" msgstr "Инфо" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Инсталирани фонтови" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Унутрашња гершка" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Позивнице" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Језик" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Библиотеке и шаблони" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Пријава или регистрација" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Одјава" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Маркетинг" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Члан" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Чланови" @@ -1329,37 +1289,37 @@ msgstr "Чланови" msgid "labels.new-password" msgstr "Нова лозинка" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Даље" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Све сте прегледали! Нова обавештења о коментарима ће се појавити овде." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Нема позивница на чекању." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Упс!" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "датотека" msgstr[1] "%s датотеке" msgstr[2] "%s датотека" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "табла" msgstr[1] "%s табле" msgstr[2] "%s табли" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "пројекат" @@ -1370,151 +1330,151 @@ msgstr[2] "%s пројеката" msgid "labels.old-password" msgstr "Стара лозинка" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Друго (навести)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Друго" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Власник" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Лозинка" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "На чекању" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Предходно" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Дизајн производа или дизајн корисничког искуства" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Управљање производима" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Налог" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Пројекти" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Напомене о издању" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Уколни" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Уклони члана" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Преименуј" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Преименуј тим" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Поново пошаљите позивницу" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Покушај поново" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Улога" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Сачувај" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Претражи" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Претражи фонт" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Изаберите опцију" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Пошаљи" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Шаље се…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Тренутно вршимо програмско одржавање наших система." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Услуга недоступна" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Подешавања" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Дели" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Библиотеке" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Прикажи све коментаре" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Прикажи листу кометара" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Покажите само своје коментаре" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Почните" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Стање" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Ученик или наставник" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Туторијали" @@ -1522,23 +1482,23 @@ msgstr "Туторијали" msgid "labels.unpublish-multi-files" msgstr "Опозови објављивање %s датотека" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Ажурирајте" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Ажурирајте тим" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Отпреми" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Отпреми прилагођене фонтове" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Отпремање…" @@ -1546,11 +1506,11 @@ msgstr "Отпремање…" msgid "labels.view-only" msgstr "Само за преглед" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Прегледник" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Веб-куке" @@ -1558,11 +1518,11 @@ msgstr "Веб-куке" msgid "labels.write-new-comment" msgstr "Напиши нови коментар" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(ти)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Ваш налог" @@ -1615,61 +1575,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Додај \"%s\" као дељену библиотеку" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Велики помак" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Верификујте нову е-пошту" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Послаћемо вам е-пошту на вашу тренутну е-пошту \"%s\" да потврдимо ваш " "идентитет." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Нова е-пошта" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Промените е-пошту" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Промените своју е-пошту" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Направи веб-куку" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Направи веб-куку" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "URL оптерећења" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Откажи и задржи мој налог" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Да, избришите мој налог" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Уклањањем налога изгубићете све своје тренутне пројекте и архиве." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Да ли сте сигурни да желите да избришете свој налог?" @@ -1719,31 +1679,31 @@ msgstr "Да ли сте сигурни да желите да обришете msgid "modals.delete-file-multi-confirm.title" msgstr "Брисање %s датотека" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Да ли сте сигурни да желите да избришете овај стил фонта? Неће се учитати " "ако се користи у датотеци." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Брисање стила фонта" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Да ли сте сигурни да желите да избришете овај фонт? Неће се учитати ако се " "користи у датотеци." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Брисање фонта" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Да ли сте сигурни да желите да обришете ову страницу?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Избришите страницу" @@ -1759,35 +1719,35 @@ msgstr "Да ли сте сигурни да желите да обришете msgid "modals.delete-project-confirm.title" msgstr "Избришите пројекат" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Избришите датотеку" msgstr[1] "Избришите датотеке" msgstr[2] "Избришите датотеке" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Није активиран ни у једној датотеци." msgstr[1] "Нису активиране ни у једној датотеци." msgstr[2] "Нису активиране ни у једној датотеци." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Овда библиотека је активиране овде: " msgstr[1] "Ове датотеке су активиране овде: " msgstr[2] "Ове библиотеке су активиране овде: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Да си сте сигурни да желите да обришете овај документ?" msgstr[1] "Да ли сте сигурни да жели да бришете ове документе?" msgstr[2] "Да ли сте сигурни да жели да бришете ове документе?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Брисање датотеке" @@ -1808,19 +1768,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Брисање тима" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Избришите члана" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Да ли сте сигурни да желите да избришете овог члана из тима?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Избришите члана тима" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -1833,166 +1793,166 @@ msgstr[2] "" "Средства која су већ коришћена у овој датотеци ће остати тамо (ниједан " "дизајн неће бити покварен)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Избришите веб-куку" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Да ли сте сигурни да желите да избришете ову веб-куку?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Брисање веб-куке" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Уреди веб-куку" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Уреди веб-куку" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Пошаљи позивницу" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Е-поште, раздвојене зарезима" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Неки е-поште су од тренутних чланова тима. Њихове позивнице неће бити " "послате." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Позовите чланове у тим" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Пошто сте једини члан овог тима, тим ће бити избрисан заједно са пројектима " "и датотекама." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Да ли сте сигурни да желите да напустите тим %s?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Не можете напустити тим ако нема другог члана за унапређење у власника. " "Можда бисте желели да избришете тим." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Ви сте власник овог тима. Изаберите другог члана за унапређење у власника " "пре него што напустите тим." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Унапреди и напусти" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Изаберите члана за унапређње" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Пре него што напустите" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Напустите тим" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Да ли сте сигурни да желите да напустите овај тим?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Напуштање тима" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Преместите" msgstr[1] "Преместите" msgstr[2] "Преместите" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Да ли сте сигурни да желите да преместите ову библиотеку?" msgstr[1] "Да ли сте сигурни да желите да преместите ове библиотеке?" msgstr[2] "Да ли сте сигурни да желите да преместите ове библиотеке?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Премести библиотеку" msgstr[1] "Преместите библиотеке" msgstr[2] "Преместите библиотеке" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Гурните износ" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Пренесите власништво" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Ако пренесете власништво, променићете своју улогу у администратора, чиме " "ћете изгубити неке дозволе над овим тимом. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Ви сте тренутни власник овог тима. Да ли сте сигурни да желите да %s буде " "нови власник тима?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Нови власник тима" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Објавите" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Ваша библиотека је празна. Да ли сте сигурни да желите да је објавите?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Објавите празну библиотеку" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Мало гуркање" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Опозови објављивање" msgstr[1] "Опозови објављивање" msgstr[2] "Опозови објављивање" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Да ли сте сигурни да желите да опозовете објављивање ове библиотеке?" msgstr[1] "Да ли сте сигурни да желите да опозовете објављивање ових библиотека?" msgstr[2] "Да ли сте сигурни да желите да опозовете објављивање ових библиотека?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Опозови објављивање библиотеке" @@ -2017,25 +1977,25 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Ажурирајте компоненту у дељеној библиотеци" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Позивница је успешно послата" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Веза за позивницу је копирана" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Не можете избрисати свој профил. Поново доделитте улоге својих тимова пре " "него што наставите." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Налог је успешно сачуван!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Е-пошта за верификацију је послата на %с. Проверите своју е-пошту!" @@ -2063,78 +2023,78 @@ msgstr "Након што дате име свом тиму, моћи ћете msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Направите тим" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Позовите чланове" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Позовите са улогом:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Хајде да почнемо!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Генерисање правог дизајна кода" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Креирање система дизајна" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Израда прототипа" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Дизајнирање интерфејса корисника и корисничког искуства апликације" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Вајрфрејминг" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "За шта ћете користити Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Реците нам нешто о вама како бисте нам помогли да Penpot ради за вас. Ваши " "одговори ће нам помоћи да дамо приоритет новим функцијама и упутити вас у " "правом смеру да почнете." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Помозите нам да вас упознамо" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Који од ових алата највише користите?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Којим послом се бавите?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Реци нам нешто о свом послу" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Где бисте желели да почнете?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Образовање" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Лично" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Посао" @@ -2184,11 +2144,11 @@ msgstr "Почните да дизајнирате" msgid "profile.recovery.go-to-login" msgstr "Идите на пријаву" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Одвоји" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Мешано" @@ -2196,35 +2156,35 @@ msgstr "Мешано" msgid "settings.select-this-color" msgstr "Изаберите ставке користећи овај стил" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Коментари" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Поравнај на дно" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Поравнај по центру водоравно" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Поравнај на лево" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Поравнај на десно" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Поравнај по врху" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Поравнај по центру усправно" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Направите таблу из избора" @@ -2232,43 +2192,43 @@ msgstr "Направите таблу из избора" msgid "shortcuts.bold" msgstr "Укључите подебљано" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Boolean разлика" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Boolean изузми" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Boolean пресек" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Boolean унија" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Пошаљи позади" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Пошаљи назад" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Изнесите напред" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Изнесите на предњи део" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Очисти отказивање" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Копирај" @@ -2276,35 +2236,35 @@ msgstr "Копирај" msgid "shortcuts.create-new-project" msgstr "Направи ново" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Исеци" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Умањи" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Избриши" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Избриши чвор" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Одвојите компоненту" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Кривина" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Елипса" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Табла" @@ -2312,39 +2272,39 @@ msgstr "Табла" msgid "shortcuts.draw-nodes" msgstr "Нацртај путању" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Путања" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Правоугаоник" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Текст" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Дуплирај" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Откажи" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Извезите облике" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Увећај да све стане" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Окрени водоравно" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Окрени усправно" @@ -2368,23 +2328,23 @@ msgstr "Иди на дељене библиотеке" msgid "shortcuts.go-to-search" msgstr "Претражи" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Групирај" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Распореди водоравно" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Прикажи / Сакриј интерфејс" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Увећај" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Уметни слику" @@ -2392,7 +2352,7 @@ msgstr "Уметни слику" msgid "shortcuts.italic" msgstr "Укључи курзив" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Придружи чворове" @@ -2400,39 +2360,39 @@ msgstr "Придружи чворове" msgid "shortcuts.line-through" msgstr "Укључи прелазну линију" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Направи угао" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Направи кривину" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Маска" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Спаји чворове" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Премести" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Премести доле брзо" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Премести лево брзо" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Премести десно брзо" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Премести горе брзо" @@ -2440,321 +2400,321 @@ msgstr "Премести горе брзо" msgid "shortcuts.move-nodes" msgstr "Премести чвор" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Премести доле" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Премести лево" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Премести десно" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Премести горе" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Следећа табла" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Нису пронађене пречице" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Подеси непрозирност на 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Подеси непрозирност на 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Подеси непрозирност на 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Подеси непрозирност на 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Подеси непрозирност на 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Подеси непрозирност на 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Подеси непрозирност на 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Подеси непрозирност на 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Подеси непрозирност на 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Подеси непрозирност на 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Бирач боја" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Иди на одељак за коментаре гледалаца" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Иди на управљачку таблу" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Иди на одељак за преглед гледалаца" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Иди на одељак интеракције са гледаоцима" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Иди на одељак интеракције са гледаоцима" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Иди на радни простор" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " или " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Налепи" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Претходна табла" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Поврати" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Врати увећање на почетне вредности" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Размера" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Основе" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Управљачка табла" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Прегледач" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Радни простор" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Изабери све" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Изабери следећи слој" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Изабери родитељски слој" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Изабери претходни слој" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Раздвоји чворове" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Прикажи / Сакриј мрежу пиксела" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Прикажи / Сакриј пречице" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Залепи на чворове" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Залепи на мрежу пиксела" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Започни уређивање" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Поравнање" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Уреди" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Опште" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Главни мени" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Измените слојеве" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Навигација" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Навигација" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Навигација" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Панели" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Путање" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Облици" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Текстови" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Алатке" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Увећање" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Увећање" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Подеси сличице" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Пречице на тастатури" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Укључи динамичко поравнање" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Укључи средства" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Укључи палету боја" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Укључи режим фокуса" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Режим целог екрана" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Прикажи / Сакриј вођице" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Укључи историју" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Укључи слојеве" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Додај / уклони флексибилни распоред" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Додај/уклони мрежни распоред" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Закључај / Откључај" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Закључај пропорције" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Прикажи / Сакриј лењире" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Залепи за вођице" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Залепи на линије за поравнања" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Укљичи палету текста" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Промени тему" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Прикажи / Сакриј" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Укљичи стил увећања" @@ -2762,67 +2722,67 @@ msgstr "Укљичи стил увећања" msgid "shortcuts.underline" msgstr "Укљичи подвачење" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Опозови" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Разгрупиши" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Демаскирај" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Распореди усправно" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Смањи увећање" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Увећај увећање" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Увећај на изабрано" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Добављачи фонтова - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Фонтови - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Пројекти - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Претражи - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Дељене библиотеке - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Слобода дизајнирања за тимове" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Оставите повратне информације - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Подешавања - Penpot" @@ -2830,27 +2790,27 @@ msgstr "Подешавања - Penpot" msgid "title.settings.password" msgstr "Лозинка - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Налог - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Позивнице - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Чланови - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Подешавања - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Веб-куке - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Режим прегледа - Penpot" @@ -2858,99 +2818,99 @@ msgstr "%s - Режим прегледа - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Ниједна табла није пронађена на овој страници." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Табла није пронађена." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Коментари (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Не приказуј интеракције" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Цели екран" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Преглед (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Интеракције" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Интеракције (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Копирај везу" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Прикажи интеракције" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Прикажи интеракције на клик" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Мапа сајта" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Последња испорука је била успешна." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Поравнај на водоравну средину (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Распореди водоравни размак (%с)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Поравнај на лево (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Поравнај на десно (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Поравнај по дну (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Поравнај на усправну средину (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Распореди усправни размак (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Поравнај по врху (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Сва средства" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Боје" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Компоненте" @@ -2958,15 +2918,15 @@ msgstr "Компоненте" msgid "workspace.assets.create-group" msgstr "Направите групу" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Ваше ставке ће бити аутоматски именоване као \"име групе / име ставке“" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Избирши" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Дуплирај" @@ -2974,27 +2934,27 @@ msgstr "Дуплирај" msgid "workspace.assets.duplicate-main" msgstr "Дуплирај главно" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Уреди" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Филтер" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Група" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Име групе" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "локална библиотека" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Нема пронађених средстава" @@ -3002,7 +2962,7 @@ msgstr "Нема пронађених средстава" msgid "workspace.assets.open-library" msgstr "Отворите датотеку библиотеке" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Преименуј" @@ -3010,11 +2970,11 @@ msgstr "Преименуј" msgid "workspace.assets.rename-group" msgstr "Преименуј гурпу" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Претражите средства" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Дељена библиотека" @@ -3025,39 +2985,39 @@ msgstr[0] "компонента" msgstr[1] "%s компоненте" msgstr[2] "%s компоненти" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Сортирај" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Тиупографије" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Величина" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Стил фонта" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Идите на датотеку библиотеке стилова да бисте је уредили" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Размак између слова" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Висина линије" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Трансформација текста" @@ -3065,67 +3025,67 @@ msgstr "Трансформација текста" msgid "workspace.assets.ungroup" msgstr "Разгрупиши" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Направи област" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Направи таблу" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Споји ћелије" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Додај 1 колону десно" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Додај 1 колону лево" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Избирши колону" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Избриши колоне и облике" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Дуплирај колону" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Додај 1 ред испод" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Додај 1 ред изнад" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Избриши ред" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Избриши редове и облике" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Дуплирај ред" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Режим фокуса" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Искључи фокус" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Укљичи фокус" @@ -3137,187 +3097,187 @@ msgstr "Линеарни градијент" msgid "workspace.gradients.radial" msgstr "Радијални градијент" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Онемогућите динамичко поравнање" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Онемогући пропорционалну размеру" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Искључи лепљење на вођице" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Искључи лепљење на пиксел" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Искључи лепљење на линије за поравнања" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Омогућите динамичко поравнање" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Омогући пропорционалну скалу" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Заклепи на вођице" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Омогући лепљење на пиксел" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Залепи на линије за поравнања" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Сакриј имена табли" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Сакриј вођице" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Сакриј палету боја" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Сакриј мрежу пиксела" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Сакриј лењире" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Сакриј палету фонтова" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Уреди" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Датотека" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Помоћ и информације" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Поставке" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Поглед" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Поврати" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Изабери све" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Прикажи имена табли" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Прикажи вођице" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Прикажи палету боја" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Прикажи мрежу пиксела" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Прикажи лењире" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Прикажи палету фонтова" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Пребаците се на тамну тему" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Пребаците се на светлу тему" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Опозови" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Почни поново" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Грешка при чувању" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Сачувано" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Чување" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Режим прегледа (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Увећај" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Попуњавање - Размера за попуњавање" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Прилагоди - Смањи да се уклопи" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Увећај да се све уклопи" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Цели екран" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Увећај на изабрано" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Уреди мрежу" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Изађи" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Прикажи опције за 4-страни padding" @@ -3333,23 +3293,23 @@ msgstr "Готово" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Пронађи" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Пронађи распоред мреже" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "У вашој библиотеци још увек нема стилова боја" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "У вашој библиотеци још нема стилова типографије" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Библиотека датотека" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Недавне боје" @@ -3361,59 +3321,59 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Сачувајте стил боја" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Библиотека датотека" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "БИБЛИОТЕКЕ У ОВОЈ ДАТОТЕЦИ" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "БИБЛИОТЕКЕ" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "АЖУРИРАЊА БИБЛИОТЕКЕ" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Учитавање…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Нема дељених библиотека које треба ажурирати" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Нема резултата за %s" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Претражите заједничке библиотеке" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "ДЕЉЕНЕ БИБЛИОТЕКЕ" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Повежите библиотеку" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Прекините везу са библиотеком" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Ажурирај" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "види све промене" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "АЖУРИРАЊА" @@ -3433,39 +3393,39 @@ msgstr "Замућење избора" msgid "workspace.options.canvas-background" msgstr "Позадина платна" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Ограничите садржај" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Компонента" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Напомена" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Копирај" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Направи напомену" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Уреди напомену" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Главно" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Замени компоненту" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "У овој библиотеци још увек нема средстава" @@ -3509,11 +3469,11 @@ msgstr "Врх" msgid "workspace.options.constraints.topbottom" msgstr "Врх и Дно" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Дизајн" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Извоз" @@ -3521,30 +3481,30 @@ msgstr "Извоз" msgid "workspace.options.export-multiple" msgstr "Избор извоза" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Извоз 1 елемента" msgstr[1] "Извоз %s елемента" msgstr[2] "Извоз %s елементата" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Суфикс" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Извоз је завршен" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Извоз…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Извоз није успео" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Извоз је неочекивано спор" @@ -3572,67 +3532,67 @@ msgstr "Проток почиње" msgid "workspace.options.grid.auto" msgstr "Ауто" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "колоне" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Боја" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Гутер (Gutter)" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Висина" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Маргина" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Постави као подразумевано" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Дно" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Средина" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Лево" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Десно" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Прошири" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Врх" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Користи подразумевао" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Редови" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Квадрат" @@ -3644,15 +3604,15 @@ msgstr "Попуни за групу" msgid "workspace.options.group-stroke" msgstr "Контура групе" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Вођице" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Висина" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Преглед" @@ -3864,99 +3824,99 @@ msgstr "Током притиска" msgid "workspace.options.interactions" msgstr "Интеракције" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Боја" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Изгоревање боје" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Избегавање боја" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Потамни" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Разлика" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Искључење" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Тешко светло" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Нијанса" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Посветли" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Осветљење" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Помножи" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Нормално" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Оквир" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Засићеност" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Екран" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Меко светло" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Макс.Висина" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Макс.Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Мин.Висина" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Мин.Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Више боја" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Више боја библиотеке" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Непрозирност" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Прототип" @@ -3980,19 +3940,19 @@ msgstr "Горе лево" msgid "workspace.options.radius-top-right" msgstr "Горе десно" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Недавно" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Покушајте поново" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Ротација" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Претражите фонт" @@ -4000,7 +3960,7 @@ msgstr "Претражите фонт" msgid "workspace.options.select-a-shape" msgstr "Изаберите облик, таблу или групу да бисте превукли везу на другу таблу." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Изабране боје" @@ -4056,47 +4016,47 @@ msgstr "Избор сенки" msgid "workspace.options.show-fill-on-export" msgstr "Прикажи у извозу" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Прикажи у режиму приказа" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Величина" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Унапред подешене величине" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Круг" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Дијамант" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Стрелица" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Ништа" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Забољено" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Квадратно" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Правоугаоник" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Троугао" @@ -4104,35 +4064,35 @@ msgstr "Троугао" msgid "workspace.options.stroke-color" msgstr "Боја контуре" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Ширина контуре" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Средина" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Испрекидана" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Тачкаста" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Унутрашња" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Мешана" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Спољашња" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Чврста" @@ -4168,11 +4128,11 @@ msgstr "Ауто ширина" msgid "workspace.options.text-options.grow-fixed" msgstr "Фиксно" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Размак између слова" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Висина линије" @@ -4218,95 +4178,95 @@ msgstr "" "Користите дугме за репродукцију у заглављу да бисте покренули приказ " "прототипа." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X оса" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y оса" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Цртај чворове (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Придружи чворове (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "До угла (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "До кривине (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Споји чворове (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Помери чворове (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Раздвоји чворове (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Залепи чворове (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Отвори" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Још увек нема инсталираних додатака" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Додатак не постоји или URL није исправан." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Инсталирај" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Инсталирани додаци" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Управљач додатака" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Додаци" -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Листа додатака" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Напишите URL додатка" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Додаци" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Додајте флексибилни распоред" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Додајте распоред мреже" @@ -4318,7 +4278,7 @@ msgstr "Пошаљи позади" msgid "workspace.shape.menu.backward" msgstr "Пошаљи назад" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Копирај" @@ -4326,27 +4286,27 @@ msgstr "Копирај" msgid "workspace.shape.menu.create-annotation" msgstr "Направи напомену" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Избор на табли" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Направи компоненту" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Направи више компоненти" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Исеци" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Избриши" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Избриши почетак протока" @@ -4358,7 +4318,7 @@ msgstr "Раздвоји инстанцу" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Раздвоји инстанце" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Разлика" @@ -4366,15 +4326,15 @@ msgstr "Разлика" msgid "workspace.shape.menu.duplicate" msgstr "Дуплицирај" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Уреди" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Изузми" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Изравнај" @@ -4386,7 +4346,7 @@ msgstr "Окрени водоравно" msgid "workspace.shape.menu.flip-vertical" msgstr "Окрени усправно" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Почетак протока" @@ -4398,43 +4358,43 @@ msgstr "Изнесите напред" msgid "workspace.shape.menu.front" msgstr "Изнесите напред" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Групиши" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Сакриј" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Прикажи / Сакриј интерфејс" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Раскрсница" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Закључај" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Маскирај" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Залепи" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Путања" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Уклоните флексибилни распоред" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Уклоните распоред мреже" @@ -4450,11 +4410,11 @@ msgstr "Врати главну компоненту" msgid "workspace.shape.menu.select-layer" msgstr "Изабери слој" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Прикажи" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Прикажи на панелу са средствима" @@ -4470,19 +4430,19 @@ msgstr "Уклони сличицу" msgid "workspace.shape.menu.thumbnail-set" msgstr "Постави као сличицу" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Разгрупиши" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Унија" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Откључај" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Демаскирај" @@ -4498,47 +4458,47 @@ msgstr "Скупи бочну траку" msgid "workspace.sidebar.expand" msgstr "Прошири бочну траку" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Историја" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Слојеви" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Компоненте" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Табле" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Групе" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Слике" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Маске" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Претражите слојеве" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Облици" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Текстови" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Увезена SVG својства" @@ -4546,7 +4506,7 @@ msgstr "Увезена SVG својства" msgid "workspace.sidebar.sitemap" msgstr "Странице" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Мапа сајта" @@ -4554,51 +4514,51 @@ msgstr "Мапа сајта" msgid "workspace.toolbar.assets" msgstr "Средства" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Палета боја (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Коментари (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Контура (%s0" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Елипса (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Табла (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Слика (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Премести (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Путања (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Правоугаоник (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Текст (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Типографије (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Укључите траку са алаткама" @@ -4611,7 +4571,7 @@ msgstr "Готово" msgid "workspace.top-bar.view-only" msgstr "**Прегледање кода** (Само за преглед)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "За сада нема промена у историји" @@ -4751,18 +4711,18 @@ msgstr "типографско средство" msgid "workspace.undo.entry.unknown" msgstr "Операција над %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Одбаци" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Више информација" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Постоје ажурирања у дељеним библиотекама" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Ажурирај" diff --git a/frontend/translations/sv.po b/frontend/translations/sv.po index c749f67233..494713c3e2 100644 --- a/frontend/translations/sv.po +++ b/frontend/translations/sv.po @@ -1,17 +1,17 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:14+0000\n" +"PO-Revision-Date: 2026-11-09 10:06+0000\n" "Last-Translator: AntonPalmqvist \n" -"Language-Team: Swedish " -"\n" +"Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Har du redan ett konto?" @@ -33,7 +33,7 @@ msgstr "" "Detta är en DEMO-tjänst, ANVÄND INTE för produktion, projekten i miljön " "raderas regelbundet." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Glömt lösenordet?" @@ -41,35 +41,35 @@ msgstr "Glömt lösenordet?" msgid "auth.fullname" msgstr "Fullständigt namn" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Logga in på mitt konto" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Logga in här." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot är det fria open-source designverktyget för design and kod samarbete" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -77,7 +77,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Skriv ett nytt lösenord" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "Du är nu med i %s" @@ -95,7 +95,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "En återställningslänk har skickats till din inkorg." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Tillagd i teamet" @@ -127,7 +127,7 @@ msgstr "Glömt lösenordet?" msgid "auth.recovery-submit" msgstr "Ändra lösenordet" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Inget konto?" @@ -139,7 +139,7 @@ msgstr "Vilket visningsnamn ska vara på översiktsvyn och i mail från oss." msgid "auth.register-account-title" msgstr "Ditt namn" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Skapa ett konto" @@ -152,7 +152,7 @@ msgstr "Skapa ett konto" msgid "auth.terms-and-privacy-agreement" msgstr "Jag godkänner [terms of service](%s) och [privacy policy](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Användarvillkor" @@ -174,93 +174,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Inga personer hittades för @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Publicera" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Alla Penpot-användare" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Är du säker på att du vill ta bort den här länken? Om du gör det, kommer " "den inte längre att vara tillgänglig för någon" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(nuvarande)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Ta bort länken" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Hämta länk" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Länken kopierad" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Hantera behörigheter" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 sida delad" msgstr[1] "%s sidor delade" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Kan kommentera" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Kan inspektera kod" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Alla med länk kommer att ha tillgång" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Sidor delade" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Delningslänk kommer visas här" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Endast teammedlemmar" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Dela prototyper" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Markera allt" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Avpublicera" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Teamhantering" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot är avsett för team. Bjud in medlemmar att arbeta tillsammans på " "projekt och filer" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Samarbete!" @@ -276,33 +276,33 @@ msgstr "Återställ fil" msgid "dashboard.add-file" msgstr "Lägg till fil" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Lägg till som delat bibliotek" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Ändra e-post" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "FLYTTA TEAM" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Flytta till:" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "" "Projekt och filer kommer att förbli tillgängliga för teammedlemmarna. " "Teamet kommer att hämta konfigurationen från den nya organisationen." -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Välj en organisation" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "Ändra teamets organisation" @@ -360,23 +360,23 @@ msgstr "Visa fullständig ändringslogg" msgid "dashboard.check-updates.view-release-notes" msgstr "Visa versionsinformation" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Töm papperskorgen" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopia)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Skapa org" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Skapa nytt team" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Nåla fast denna version" @@ -384,7 +384,7 @@ msgstr "Nåla fast denna version" msgid "dashboard.default-team-name" msgstr "Ditt Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Är du säker på att du vill radera alla dina borttagna projekt och filer " @@ -396,42 +396,42 @@ msgstr "" "Är du säker på att du vill ta bort %s permanent? Detta är en åtgärd som " "inte kan ångras." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s filer har raderats." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Radera permanent" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Radera projekt" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Är du säker på att du vill radera projekt %s permanent? Du kommer att " "radera det permanent och alla filer i det. Detta är en åtgärd som inte kan " "ångras." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s har raderats." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Radera team" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "Din papperskorg är tom. Borttagna filer och projekt visas här." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "%s kommer att raderas" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Ladda ner Penpot-fil (.penpot)" @@ -443,19 +443,19 @@ msgstr "Duplicera" msgid "dashboard.duplicate-multi" msgstr "Duplicera %s filer" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "När en projektmedlem skapar ett utkast kommer det att visas här." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Inga utkast än." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "När en projektmedlem skapar en fil kommer den att visas här." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Inga filer än." @@ -503,23 +503,23 @@ msgstr "Importera en .penpot-fil" msgid "dashboard.empty-project.start" msgstr "Börja bygga fantastiska saker" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "Det uppstod ett fel när filerna skulle raderas." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "Det uppstod ett fel när projekt %s skulle raderas." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "Det uppstod ett fel när filen %s skulle återställas." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "Det uppstod ett fel när filerna skulle återställas." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "Det uppstod ett fel när projekt %s och dess filer skulle återställas." @@ -527,37 +527,37 @@ msgstr "Det uppstod ett fel när projekt %s och dess filer skulle återställas. msgid "dashboard.export-binary-multi" msgstr "Ladda ner % Penpot-filer (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Exportera tavla som PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Exportera som PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s av %s objekt markerade" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Exportera" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Du kan lägga till exportinställningar till objekt från designegenskaperna " "(vid botten av höger sidnavigation)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Info hur man ställer in export i Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Det finns inga objekt med exportinställningar." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Exportera markerade" @@ -581,33 +581,33 @@ msgstr "Rutnätsvy" msgid "dashboard.files-layout.list" msgstr "Listvy" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Typsnittet saknas" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Avfärda alla" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Egna typsnitt som du laddar upp visas här." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Inga anpassade typsnitt än." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "När en projektmedlem laddar upp ett anpassat typsnitt visas det här." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 typsnitt adderas" msgstr[1] "%s typsnitt adderas" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -617,7 +617,7 @@ msgstr "" "en **enkel typsnittsfamilj**. Du kan ladda upp typsnitt med följande " "format: **TTF, OTF och WOFF** (endast ett format behövs)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -626,11 +626,11 @@ msgstr "" "användarvillkor](%s). Du kanske också vill läsa om [licensiering av " "typsnitt](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Ladda upp allt" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -641,23 +641,23 @@ msgstr "" "använder [Transfonter](https://transfonter.org/) för att generera " "webbtypsnitt och åtgärda fel. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Gå till administratörskonsolen" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "Senast ändrad %s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Importera Penpot-filer" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Oops! Vi kunde inte importera denna fil" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "1 bibliotek länkades automatiskt via namn." @@ -667,121 +667,125 @@ msgstr[1] "%s bibliotek länkades automatiskt via namn." msgid "dashboard.import.bad-url" msgstr "Importen misslyckades. Mall URL:en är inkorrekt" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "Bekräfta bibliotekslänkar" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:814 +msgid "dashboard.import.connect-selected-libraries" +msgstr "Anslut valda bibliotek" + +#: src/app/main/ui/dashboard/import.cljs:288 +msgid "dashboard.import.file-name-label" +msgstr "Filnamn" + +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "Vi kunde inte verifiera den här filen." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Den här filen verkar vara skadad." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Alla filer har inte importerats" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Följande filer har fel:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Filer med fel laddas inte upp." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "Något gick fel när den här filen bearbetades." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 fil importerades." msgstr[1] "%s filer importerades." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Vissa filer innehöll ogiltiga objekt som har tagits bort." +#: src/app/main/ui/dashboard/import.cljs:815 +msgid "dashboard.import.next-file" +msgstr "Nästa fil" + #: src/app/main/ui/dashboard.cljs:265 msgid "dashboard.import.no-perms" msgstr "Du har inte behörighet att importera till det här teamet" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Bearbetningsfärger" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Bearbetning av komponenter" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Bearbetning av media" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Bearbetar sida: %" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Bearbetning av typografier" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Laddar upp data till server (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Laddar upp fil: %" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "" "Vissa bibliotek kunde inte länkas automatiskt. Välj rätt bibliotek för " "varje:" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "Granska bibliotekslänkarna innan du bekräftar:" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:447 +msgid "dashboard.import.resolve-libraries.connect-to" +msgstr "anslut till" + +#: src/app/main/ui/dashboard/import.cljs:441 +msgid "dashboard.import.resolve-libraries.original-library" +msgstr "originalbibliotek" + +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "Länkad" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:517 +msgid "dashboard.import.summary.manually-linked" +msgstr "manuellt länkad" + +#: src/app/main/ui/dashboard/import.cljs:525 +msgid "dashboard.import.summary.new" +msgstr "Ny" + +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "Inget bibliotek valt" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/import.cljs:522 +msgid "dashboard.import.summary.original" +msgstr "Original" + +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Du kommer att radera inbjudningarna till:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Du kommer att skicka om inbjudningarna till:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Ta bort inbjudningar" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Skicka inbjudningar igen" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "Du har inte behörighet att bjuda in personer i det här teamet eller att " "redigera eller ta bort inbjudningar." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Bjud in personer" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "Du har inte behörighet att bjuda in personer till det här teamet" @@ -793,27 +797,27 @@ msgstr "Lämna org" msgid "dashboard.leave-organization.toast" msgstr "Du är inte längre med i organisationen %s." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Lämna teamet" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Bibliotek & mallar" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "Här har du några bibliotek och mallar som du kan lägga till i ditt projekt" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Ta reda på mer om dem och hur du kan bidra" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Problem när mallen importerades. Mallen importerades inte." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Bibliotek" @@ -821,7 +825,7 @@ msgstr "Bibliotek" msgid "dashboard.loading-files" msgstr "laddar dina filer …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "laddar dina typsnitt …" @@ -841,61 +845,65 @@ msgstr "Flytta %s filer till" msgid "dashboard.move-to-other-team" msgstr "Flytta till andra team" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Ny fil" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Ny fil" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Nytt projekt" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Nytt projekt" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Inga träffar hittades för ”%s”" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "Du har inte behörighet att lägga till team i någon av dina organisationer." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 +msgid "dashboard.no-organizations-yet" +msgstr "Inga organisationer än." + +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "Du har inte tillåtelse att skapa team inom organisationen %s. Om du behöver " "mer information, kontakta organisationens ägare." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "Du har inte tillåtelse att ta bort team som ingår i organisation %s. Om du " "behöver mer information, kontakta organisationens ägare." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "Du har inte tillåtelse att flytta team som ingår i organisation %s. Om du " "behöver mer information, kontakta organisationens ägare." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Fastklistrade projekt visas här" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Notifieringar" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Din e-postadress har uppdaterats" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Din e-postadress har verifierats" @@ -907,11 +915,11 @@ msgstr "Notifieringsinställningar uppdaterade" msgid "dashboard.notifications.password-saved" msgstr "Lösenordet sparat!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Visa notifieringar" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s medlemmar" @@ -919,23 +927,23 @@ msgstr "%s medlemmar" msgid "dashboard.open-in-new-tab" msgstr "Öppna filen i en ny flik" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Alternativ" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Sortera efter roll" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Sortera efter status" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "Organisationen %s har tagits bort." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "Andra team" @@ -959,11 +967,11 @@ msgstr "Du är nu ägare till detta team." msgid "dashboard.permissions-change.viewer" msgstr "Du är nu en läsare i det här teamet." -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "Egna projekt" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Klistra fast/Lossa" @@ -979,27 +987,27 @@ msgstr "Kan inte tolka plugin-manifestet" msgid "dashboard.plugins.try-plugin" msgstr "Testa plugin: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "Raderar filer…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "Återställer filer…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "Raderingen är oväntat långsam" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "Återställningen är oväntat långsam" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projekt" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Vill du ta bort ditt konto?" @@ -1007,114 +1015,114 @@ msgstr "Vill du ta bort ditt konto?" msgid "dashboard.removed-from-team" msgstr "Du är inte längre en del av teamet ”%s”." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "Du kommer att återställa alla dina projekt och filer. Det kan ta ett tag." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Återställ alla projekt och filer" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Återställ allt" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s filer har återställts." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Återställ projekt" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Du kommer att återställa projekt %s och alla filer som finns i det." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Återställ projekt" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s har återställts." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Spara inställningar" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Sök…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Söker efter \"%s\"…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Organisationer" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "Team" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "LÄGG TILL I ORGANISATION" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "Välj en organisation:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\" tillåter endast organisationsmedlemmar att gå med i dess team. Alla " "väntande inbjudningar till personer utanför organisationen kommer att " "återkallas." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Väntande inbjudningar till externa användare kommer att avbrytas." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Här hittar du alla dina organisationer där du har rätt att skapa eller " "lägga till team." -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "" "Här hittar du de organisationer du ingår där du har behörighet att skapa " "eller lägga till team." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "Välj en organisation" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "Lägg till team i en organisation" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Välj språk för användargränssnittet" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Välj tema" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Mörk (standard)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Ljus" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Systemtema" @@ -1178,11 +1186,11 @@ msgstr "Uppdatera inställningar" msgid "dashboard.settings.notifications.title" msgstr "Notifieringar" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Visa alla filer" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Versionshistorik" @@ -1206,7 +1214,7 @@ msgstr[1] "Dina filer har duplicerats" msgid "dashboard.success-duplicate-project" msgstr "Ditt projekt har duplicerats" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Din fil har flyttats" @@ -1218,184 +1226,184 @@ msgstr "Dina filer har flyttats" msgid "dashboard.success-move-project" msgstr "Ditt projekt har flyttats" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s tillhör nu %s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Team info" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Teammedlemmar" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s tillhör inte längre %s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Teamorganisation" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Lägg till i en organisation" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "" "Du har inte behörighet att lägga till det här teamet i någon av dina " "organisationer." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Ändra teamorganisation" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Endast personer inom din organisation kan bjudas in." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "Detta team är inte en del av någon organisation" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Ta bort teamet från organisationen" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Teamprojekt" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Lägg till i ditt projekt" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Tema för användargränssnitt" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Sökresultat" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Raderade filer kommer att finnas kvar i papperskorgen i" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s dagar. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "Efter det kommer de att raderas permanent." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Om du ångrar dig kan du återställa dem eller ta bort dem permanent från " "varje fils meny." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Skriv för att söka i resultatet" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Avpublicera biblioteket" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Uppdatera inställningarna" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "Du är inte längre medlem i organisation %s" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Beta" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "WebGL-rendering kan förbättra prestandan, men den är i beta och kan vara " "mindre stabil. Visuella skillnader kan förekomma mellan arbetsytan, " "exporterna (SVG/PDF) och visningsläget." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Inaktiverad" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Aktiverad" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Ge feedback" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Status" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "WebGL-rendering" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Är aktiv" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "När denna hook triggas kommer händelsedetaljer att sändas" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Du kan bara ta bort eller ändra webhooks som du har skapat." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Innehållstyp" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Skapa webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook skapades." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Webhooks är ett enkelt sätt att låta andra webbplatser och appar bli " "meddelade när vissa händelser inträffar i Penpot. Vi skickar en " "POST-begäran till var och en av de webbadresser du anger." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Tryck på knappen \"Lägg till webhook\" för att lägga till en." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Det finns inga webhooks." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Ditt konto" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-post" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Ditt namn" @@ -1411,11 +1419,11 @@ msgstr "Observera" msgid "ds.component-subtitle" msgstr "Komponenter att uppdatera:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Tillåt" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Avbryt" @@ -1435,7 +1443,7 @@ msgstr "Inga tillämpliga tokens i aktiva uppsättningar eller teman." msgid "ds.inputs.numeric-input.no-matches" msgstr "Inga träffar hittades." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Öppna token-lista" @@ -1445,6 +1453,12 @@ msgstr "" "Token {%s} finns inte i någon aktiv uppsättning eller har ett ogiltigt " "värde." +#: src/app/main/ui/auth/login.cljs:113 +msgid "errors.account-locked" +msgstr "" +"För många misslyckade försök. Ditt konto har låsts i %s minuter. Vänligen " +"försök igen senare." + #: src/app/main/data/auth.cljs:344 msgid "errors.auth-provider-not-allowed" msgstr "Autentiseringsleverantör inte tillåten för denna profil" @@ -1453,15 +1467,15 @@ msgstr "Autentiseringsleverantör inte tillåten för denna profil" msgid "errors.auth-provider-not-configured" msgstr "Autentiseringsleverantör inte konfigurerad." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Du är inte autentiserad eller har sessionen löpt ut." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Typsnittet % kunde inte laddas" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Typsnittet %s kunde inte laddas" @@ -1473,57 +1487,61 @@ msgstr "Kan inte ladda upp mediafilen." msgid "errors.character-limit-exceeded" msgstr "Teckengränsen har överskridits" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "Kan inte lägga till bibliotek: detta skulle skapa ett cirkulärt beroende" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "Urklipps-API:et är inte tillgängligt. Hosta Penpot via HTTPS för att " "aktivera åtkomst till urklipp" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Din webbläsare kan inte utföra denna åtgärd" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "Åtkomst till urklipp nekad. Vänligen ge urklippsbehörighet i din webbläsare " "för att klistra in innehåll" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "Det uppstod ett fel med kommentaren" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:586 +msgid "errors.connection-error" +msgstr "Kan inte nå servern. Kontrollera din anslutning och försök igen." + +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Tyvärr! Det här är en gammal fil som använder en föråldrad typ av " "Penpot-tillgångar och du kan inte öppna den." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "så att vi kan hjälpa dig." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Även om Penpot inte längre stödjer denna typ av tillgångar, kan du" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "kontakta oss" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Kan inte släppa en överordnad uppsättning på en egen underordnad sökväg." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E-postadressen används redan" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-postadressen är redan validerad." @@ -1535,31 +1553,31 @@ msgstr "Du kan inte använda e-postadressen som lösenord" msgid "errors.email-does-not-match-invitation" msgstr "E-post matchar inte inbjudan." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Domänen är inte tillåten" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "E-postmeddelandet «%s» har genererat många returmeddelanden." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "" "E-postmeddelandet ”%s” har rapporterats som skräppost eller permanenta " "returmeddelanden." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Det verkar som om du öppnar en fil som har funktionen ”%s” aktiverad, men " "den aktuella Penpot-versionen stöder den inte eller har den inaktiverad." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Funktionen \"%s\" stöds inte." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Får innehålla högst %s tecken." @@ -1567,7 +1585,7 @@ msgstr "Får innehålla högst %s tecken." msgid "errors.field-min-length" msgstr "Måste innehålla minst 1 tecken." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Tomt fält" @@ -1575,24 +1593,24 @@ msgstr "Tomt fält" msgid "errors.field-not-all-whitespace" msgstr "Namnet måste innehålla något annat tecken än mellanslag." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Det verkar som om det inte finns någon överensstämmelse mellan de " "aktiverade funktionerna och funktionerna i den fil du försöker öppna. " "Migreringar för ”%s” måste tillämpas innan filen kan öppnas." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "Teckenfamiljens namn får bara innehålla bokstäver, siffror, mellanslag, " "bindestreck, understreck och punkter." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Något fel har inträffat." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Något fel har hänt med webbarbetaren." @@ -1608,7 +1626,7 @@ msgstr "Ogiltig data" msgid "errors.invalid-email" msgstr "Ange en giltig e-postadress" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Bekräftelsemail måste matcha" @@ -1642,11 +1660,11 @@ msgstr "Denna inbjudan kan vara avbruten eller har upphört att gälla." msgid "errors.ldap-disabled" msgstr "LDAP-autentisering är inaktiverad." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Du har nått \"%s\" kvoten. Kontakta supporten." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Det maximala (%s) antalet e-postmeddelanden som kan bjudas in i en enda " @@ -1664,21 +1682,21 @@ msgstr "Det verkar som om innehållet i bilden inte stämmer överens med filtil msgid "errors.media-type-not-allowed" msgstr "Det här är inte en giltig bild." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Den profil du bjuder in har e-postmeddelanden som är avstängda " "(skräppostrapporter eller höga studsar)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Migrering pågår" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Endast skaparen av versionen kan låsa den" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Endast skaparen av versionen kan låsa upp den" @@ -1690,7 +1708,7 @@ msgstr "Det uppstod ett fel när du skulle lämna organisationen. Försök igen. msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "Organisationsägaren kan inte lämna organisationen." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "Den organisationen finns inte" @@ -1702,7 +1720,7 @@ msgstr "Lösenordet för bekräftelse måste matcha" msgid "errors.password-too-short" msgstr "Lösenordet måste innehålla minst 8 tecken" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Ogiltiga data i urklipp" @@ -1710,7 +1728,7 @@ msgstr "Ogiltiga data i urklipp" msgid "errors.profile-blocked" msgstr "Profilen är blockerad" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "Din profil har e-post avstängd (spamrapporter eller många returmeddelanden)." @@ -1718,28 +1736,32 @@ msgstr "Din profil har e-post avstängd (spamrapporter eller många returmeddela msgid "errors.registration-disabled" msgstr "Registreringen är för närvarande inaktiverad." -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:700 msgid "errors.save-failed" msgstr "" "Dina senaste ändringar har inte bekräftats som sparade. Sparandet kanske " "inte återupptas automatiskt. Stäng inte och uppdatera inte fliken; du kan " "förlora ändringarna. Kontakta supporten för hjälp." -#: src/app/main/errors.cljs:764 +#: src/app/main/data/persistence.cljs:447 +msgid "errors.save-retrying" +msgstr "Anslutningen bröts. Försöker spara dina ändringar igen." + +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG är ogiltig eller missformad" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Upptäckt av inkompatibel funktion '%s'" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Otillräckligt med medlemmar för att lämna teamet, du vill förmodligen ta " "bort det." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Medlemmen du försöker tilldela finns inte." @@ -1747,7 +1769,7 @@ msgstr "Medlemmen du försöker tilldela finns inte." msgid "errors.team-leave.only-owner-can-delete" msgstr "Endast ägaren till ett team kan ta bort det." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Ägare kan inte lämna teamet, du måste tilldela ägarrollen till någon annan." @@ -1759,13 +1781,13 @@ msgstr "Teamnamnet får inte innehålla något av följande tecken: '.', ':' ell msgid "errors.token-set-already-exists" msgstr "En uppsättning med samma namn finns redan" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "" "Kan inte slutföra släppet, en uppsättning med samma namn finns redan vid " "sökvägen." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Tema-alternativ med samma namn finns" @@ -1777,13 +1799,13 @@ msgstr "Temat refererar till några icke-existerande uppsättningar: %s" msgid "errors.tokens.circular-reference" msgstr "Token har en cirkulär referens" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "Radavstånd beror på Teckenstorlek. Lägg till en Teckenstorlek för att få " "det uträknade värdet." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "Detta fält får inte vara tomt" @@ -1803,11 +1825,11 @@ msgstr "Importeringsfel:" msgid "errors.tokens.invalid-color" msgstr "Ogiltigt färgvärde: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "Ogiltigt token-värde: du kan bara referera till en typsnitts-token" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Ogiltigt värde för typsnittsvikt: använd numeriska värden (100–950) eller " @@ -1828,37 +1850,37 @@ msgstr "" "Token-namn ska endast innehålla bokstäver och siffror separerade med . " "tecken och får inte börja med ett $-tecken." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "Ogiltig skuggningstyp: endast 'innerShadow' eller 'dropShadow' accepteras" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Ogiltigt token-värde: endast ingen, versaler, gemener eller versalisera " "accepteras" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "Ogiltigt token-värde: endast ingen, understruken och genomstruken accepteras" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "Ogiltigt värde: måste referera till en sammansatt skuggnings-token." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "Ogiltigt värde: måste referera till en sammansatt typografi-token." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Ogiltigt token-värde: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Saknade token-referenser: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "" "Gruppnamnet %s står i konflikt med en token med samma namn i en annan aktiv " @@ -1872,33 +1894,33 @@ msgstr "Inga tokens, uppsättningar eller teman hittades i den här filen." msgid "errors.tokens.number-too-large" msgstr "Ogiltigt token-värde. Det uträknade värdet är för stort: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "" "Opaciteten måste vara mellan 0 och 100 % eller 0 och 1 (t.ex. 50 % eller " "0,5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Token har självreferens" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "Skuggningsoskärpan måste vara större än eller lika med 0." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "Skuggningsspridning måste vara större än eller lika med 0." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "Streckbredden måste vara större än eller lika med 0." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Ogiltigt värde: % är inte tillåtet." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Ogiltigt värde: Enheter är ej tillåtna." @@ -1906,19 +1928,19 @@ msgstr "Ogiltigt värde: Enheter är ej tillåtna." msgid "errors.unexpected-error" msgstr "Ett oväntat fel inträffade." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Okänd token" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Denna version är redan låst" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Denna version är låst och kan inte raderas av andra" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Filen har ett inkompatibelt versionsnummer" @@ -1951,39 +1973,39 @@ msgstr "Minst en versal" msgid "errors.weak-password.too-short" msgstr "Minst 8 tecken" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL har slutat fungera. Vänligen ladda om sidan för att återställa den" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Hoppsan! Kontexten för arbetsytan försvann" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Anslutningsfel, URL:en är inte tillgänglig" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "Valideringsfel av URL:en." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Den sista leveransen lyckades inte." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Fel på SSL validering." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Timeout" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Oväntat fel vid validering" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Oväntad status %" @@ -2003,17 +2025,17 @@ msgstr "Beskrivning" msgid "feedback.description-placeholder" msgstr "Vänligen beskriv orsaken till din feedback" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Vi är glada över att ha dig här. Om du behöver hjälp, vänligen sök innan du " "postar." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot community" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Andra sätt att kontakta oss" @@ -2038,11 +2060,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Kontakta oss" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Här kan du få hjälp med dina tekniska frågor." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X supportkonto" @@ -2062,45 +2084,41 @@ msgstr "Idé" msgid "feedback.type.issue" msgstr "Problem" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Ladda ner filer" - -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "Länkade bibliotekstillgångar kommer inte att inkluderas i filen." -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "Behandla tillgångar som grundläggande objekt" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "" "Filer med länkade bibliotek inkluderas i exporten, och deras kopplingar " "bevaras. " -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "Exportera fil + bibliotek" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "" "Vid import kommer du att kunna koppla samman befintliga bibliotek med " "matchande namn." -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "Länka matchande bibliotek vid import" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "" "Din fil kommer att exporteras med bibliotekstillgången sammanfogad med det " "lokala biblioteket." -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "Bädda in bibliotekstillgångar i filen" @@ -2108,7 +2126,7 @@ msgstr "Bädda in bibliotekstillgångar i filen" msgid "generic.error" msgstr "Ett fel har inträffat" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "Tillämpa" @@ -2160,7 +2178,7 @@ msgstr "Förhandsgranskning av formens ifyllnadsbild" msgid "inspect.attributes.shadow" msgstr "Skugga" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Storlek och position" @@ -2168,43 +2186,31 @@ msgstr "Storlek och position" msgid "inspect.attributes.stroke" msgstr "Fet" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Center" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Inuti" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Utanför" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Typografi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Typsnittsfamilj" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Teckenstorlek" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Typsnittsstil" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Teckenavstånd" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Radavstånd" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "Blandade teckenfamiljer" @@ -2212,15 +2218,19 @@ msgstr "Blandade teckenfamiljer" msgid "inspect.attributes.typography.text-decoration.none" msgstr "Ingen" +#: src/app/main/ui/inspect/attributes/text.cljs:117 +msgid "inspect.attributes.typography.text-decoration.strikethrough" +msgstr "Genomstrykning" + #: src/app/main/ui/inspect/attributes/text.cljs:115 msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Understrykning" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Versalbörjan" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Gemener" @@ -2232,7 +2242,7 @@ msgstr "Ingen" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Oinställt" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Versaler" @@ -2278,6 +2288,10 @@ msgstr "Kopiera" msgid "inspect.subtitle.main" msgstr "Huvudkomponent" +#: src/app/main/ui/inspect/right_sidebar.cljs:176 +msgid "inspect.tabs-switcher-label" +msgstr "Byt inspekteringstabb" + #: src/app/main/ui/inspect/right_sidebar.cljs:142 msgid "inspect.tabs.code.selected.circle" msgstr "Cirkel" @@ -2354,23 +2368,23 @@ msgstr "Uträknat värde:" msgid "inspect.tabs.styles.variants-panel" msgstr "Variantegenskaper" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "Skapa ny åtkomsttoken" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "Tryck på knappen \"Skapa ny åtkomsttoken\" för att generera en." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "Du har inga tokens än." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "Personliga åtkomsttokens" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "Personliga åtkomsttokens fungerar som ett alternativ till vårt system för " @@ -2455,53 +2469,53 @@ msgstr "MCP-nyckeln går ut den %s" msgid "integrations.mcp-key.will-not-expire" msgstr "MCP-nyckeln har inget utgångsdatum" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "Penpots MCP-server gör det möjligt för MCP-klienter att interagera direkt " "med Penpot-designfiler." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "Så här konfigurerar du MCP-klienter" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "Detta är server-URL:en du behöver för att konfigurera din MCP-klient för " "att ansluta den till Penpot MCP-servern." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "Återskapa MCP-nyckel" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "MCP-nyckel" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "MCP-nyckeln behövs för MCP-klientkonfigurationen" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Status" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Inaktiverad" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Aktiverad" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "MCP-nyckeln som användes för att ansluta till MCP-servern har gått ut. " "Därför kan anslutningen inte upprättas." -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "" "Återskapa MCP-nyckeln och uppdatera din klientkonfiguration med den nya " @@ -2511,10 +2525,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "MCP-server" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "Beta" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "Namn" @@ -2565,7 +2575,7 @@ msgstr "Återskapa MCP-nyckel" msgid "integrations.regenerate-mcp-key.title.created" msgstr "MCP-nyckel återskapad" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "Integrationer" @@ -2583,47 +2593,47 @@ msgstr "Denna token går ut den %s" msgid "integrations.token.will-not-expire" msgstr "Token har inget utgångsdatum" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Markera alla som lästa" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Genvägar" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Om Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Acceptera" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Aktiv" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Lägg till" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Lägg till typsnitt" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Lägger till..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Admin" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Allt" @@ -2631,17 +2641,17 @@ msgstr "Allt" msgid "labels.and" msgstr "och" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Tillbaka" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Det ser ut som om du behöver vänta lite och försöka igen; vi utför " "underhåll av våra servrar." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Bad Gateway" @@ -2653,23 +2663,23 @@ msgstr "Oskärpa" msgid "labels.blur-effects" msgstr "Oskärpaeffekter" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Avbryt" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "Sök efter uppdateringar" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "Söker efter uppdateringar..." -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Stäng" @@ -2677,7 +2687,7 @@ msgstr "Stäng" msgid "labels.code" msgstr "Kod" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Dra ihop" @@ -2689,15 +2699,15 @@ msgstr "Färg" msgid "labels.comment" msgstr "Kommentar" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Kommentarer" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Community" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Community & Bidrag" @@ -2709,19 +2719,19 @@ msgstr "Beräknad" msgid "labels.confirm-password" msgstr "Bekräfta lösenordet" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Kontakta support" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Kontakta oss" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Fortsätt" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Du kan fortsätta med ett Penpot-konto" @@ -2733,23 +2743,23 @@ msgstr "Kopiera" msgid "labels.copy-color" msgstr "Kopiera färg" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Kopiera länk" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-nutid" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Skapa" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Skapa nytt team" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Ange nytt teamnamn" @@ -2757,7 +2767,7 @@ msgstr "Ange nytt teamnamn" msgid "labels.current" msgstr "Aktuell" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Översiktsvy" @@ -2765,7 +2775,7 @@ msgstr "Översiktsvy" msgid "labels.default" msgstr "Standard" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Radera" @@ -2777,7 +2787,7 @@ msgstr "Radera kommentar" msgid "labels.delete-comment-thread" msgstr "Radera tråd" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Ta bort inbjudan" @@ -2785,39 +2795,35 @@ msgstr "Ta bort inbjudan" msgid "labels.delete-multi-files" msgstr "Radera %s filer" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "Är du säker på att du vill ta bort ditt profilfoto?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "Radera profilfoto" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Raderad" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Utveckling" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Kassera" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "Ladda ner %s" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "Ladda ner" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "Laddar ner fil…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Utkast" @@ -2825,19 +2831,19 @@ msgstr "Utkast" msgid "labels.duplicate" msgstr "Duplicera" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Redigera" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Redaktör" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Tom" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Fel" @@ -2845,11 +2851,11 @@ msgstr "Fel" msgid "labels.exit" msgstr "Gå bort" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Löpt ut" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Exportera" @@ -2861,7 +2867,7 @@ msgstr "Feedback inaktiverad" msgid "labels.feedback-sent" msgstr "Feedback skickad" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2869,63 +2875,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Fyll" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "Flex-layout" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Typsnittsfamilj" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Stilar" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Typsnitt" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Github repo" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Ge feedback" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Gå tillbaka" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Grafisk design" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "Rutnätslayout" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Hjälpcenter" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Hjälp och utbildning" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Dölj" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Dölj åtgärdade kommentarer" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Importera" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Inaktiv" @@ -2933,51 +2939,51 @@ msgstr "Inaktiv" msgid "labels.info" msgstr "Info" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Installerade typsnitt" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "Integrationer" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Något dåligt hände." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "Du kan försöka igen eller kontakta supporten för att anmäla felet." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Internt fel" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Inbjudningar" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Språk" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Layout" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Utbildningscenter" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Bibliotek & mallar" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Laddar…" @@ -2985,7 +2991,7 @@ msgstr "Laddar…" msgid "labels.lock" msgstr "Lås" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Logga in eller registrera dig" @@ -2993,19 +2999,19 @@ msgstr "Logga in eller registrera dig" msgid "labels.login" msgstr "Logga in" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Logga ut" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Marknadsföring" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Medlem" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Medlemmar" @@ -3017,43 +3023,43 @@ msgstr "Omnämnande" msgid "labels.new-password" msgstr "Nytt lösenord" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Nästa" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "" "Denna instans kräver ett konfigurationssteg som kommer med den senaste " "versionen." -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "Kolla in vår" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "Penpot kan inte starta än." -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "teknisk guide" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "" "Penpot är tillfälligt otillgänglig på grund av ett systemfel. Försök igen " "om en liten stund." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Du har kommit ikapp! Nya meddelanden om kommentarer visas här." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Inga pågående inbjudningar." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Samla ditt folk och bygg fantastiska saker tillsammans." @@ -3061,27 +3067,27 @@ msgstr "Samla ditt folk och bygg fantastiska saker tillsammans." msgid "labels.no-matches" msgstr "Inga träffar" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Oops!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Notifieringar" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 fil" msgstr[1] "%s filer" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 tavla" msgstr[1] "%s tavlor" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 projekt" @@ -3091,43 +3097,43 @@ msgstr[1] "% projekt" msgid "labels.old-password" msgstr "Gammalt lösenord" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "Öppna" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Alternativ" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Annat (ange)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Andra" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Ägare" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Lösenord" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Pågående" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Penpots ändringslogg" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot hubb" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Fästa projekt" @@ -3135,31 +3141,31 @@ msgstr "Fästa projekt" msgid "labels.post" msgstr "Inlägg" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Föregående" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Produkt eller UX design" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Produktledning" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projekt" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Senaste" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Hänvisning" @@ -3167,27 +3173,27 @@ msgstr "Hänvisning" msgid "labels.refresh" msgstr "Uppdatera" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Releaseinformation" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Ladda om sidan" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Ta bort" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Ta bort medlem" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Byt namn" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Byt teamnamn" @@ -3211,11 +3217,11 @@ msgstr "nytt svar" msgid "labels.reply.thread" msgstr "Svara" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Skicka igen" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Skicka inbjudan igen" @@ -3223,43 +3229,43 @@ msgstr "Skicka inbjudan igen" msgid "labels.restore" msgstr "Återställ" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Försök igen" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Roll" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Spara" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Sök" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Sök typsnitt" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Välj alternativ" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Skicka" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Skickar…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Vi utför schemalagt underhåll av våra system." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Tjänsten är inte tillgänglig" @@ -3267,70 +3273,74 @@ msgstr "Tjänsten är inte tillgänglig" msgid "labels.sets" msgstr "Uppsättningar" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Inställningar" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Skugga" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Dela" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Bibliotek" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Visa" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Visa alla kommentarer" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Visa kommentarslista" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Visa bara dina omnämnanden" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Visa bara dina kommentarer" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Koncept" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/import.cljs:808 +msgid "labels.skip" +msgstr "Hoppa över" + +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Källor" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "" "Inloggningen via din organisations identitetsleverantör slutfördes inte. " "Leverantören kan vara otillgänglig, eller så finns ditt konto kanske ännu " "inte i dess katalog." -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "Försök igen" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "Vi kunde inte logga in dig på %s" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Start" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Status" @@ -3338,7 +3348,7 @@ msgstr "Status" msgid "labels.stroke" msgstr "Streck" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Student eller lärare" @@ -3350,27 +3360,31 @@ msgstr "Stilar" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Växla" +#: src/app/main/ui/dashboard/organization_team_switch.cljs:657 +msgid "labels.team-management" +msgstr "Teamhantering" + #: src/app/main/ui/inspect/styles/style_box.cljs:30 msgid "labels.text" msgstr "Text" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Teman" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Självstudier" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Typografi" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Okänt fel" @@ -3382,35 +3396,31 @@ msgstr "Lås upp" msgid "labels.unpublish-multi-files" msgstr "Avpublicera %s filerna" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Uppdatera" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Uppdatera teamet" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Ladda upp" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Ladda upp typsnitt" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Laddar upp…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "Laddar upp fil…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Variant" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Version anteckningar" @@ -3418,7 +3428,7 @@ msgstr "Version anteckningar" msgid "labels.view-only" msgstr "Enbart visning" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Visare" @@ -3432,7 +3442,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s varning" msgstr[1] "%s varningar" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhooks" @@ -3440,11 +3450,11 @@ msgstr "Webhooks" msgid "labels.write-new-comment" msgstr "Skriv ny kommentar" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(du)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Ditt konto" @@ -3586,7 +3596,7 @@ msgstr "" "Du är den enda medlemmen i vissa av teamen. Dessa team kommer att raderas " "tillsammans med deras projekt och filer." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "INNAN DU LÄMNAR ORGANISATIONEN" @@ -3594,80 +3604,80 @@ msgstr "INNAN DU LÄMNAR ORGANISATIONEN" msgid "modals.before-leave-organization.warning" msgstr "Alla team där du är den enda medlemmen kommer att raderas." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Stor knuff" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Verifiera ny e-post" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Vi skickar ett e-postmeddelande till din nuvarande e-post \"%s\" för att " "verifiera din identitet." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Ny e-post" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Ändra e-post" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Ändra din e-post" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Skapa webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Skapa webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Payload URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Avbryt och behåll mitt konto" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Ja, radera mitt konto" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Genom att ta bort ditt konto förlorar du alla dina projekt och arkiv." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "Denna åtgärd kan inte ångras. Ditt konto, din prenumeration och dina " "organisationer kommer att raderas omedelbart. Du kommer också att förlora " "åtkomsten till dina nuvarande projekt och filer." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "Organisationer som kommer att raderas" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s medlemmar" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s team" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Är du säker på att du vill radera ditt konto?" @@ -3727,23 +3737,23 @@ msgstr "Är du säker på att du vill radera %s filerna?" msgid "modals.delete-file-multi-confirm.title" msgstr "Raderar %s filer" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Är du säker på att du vill radera denna typsnittsstil? Den laddas inte om " "den används i en fil." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Tar bort typsnittsstil" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Är du säker på att du vill ta bort detta typsnitt? Den kommer inte ladda om " "den används i en fil." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Tar bort typsnitt" @@ -3753,19 +3763,19 @@ msgstr "" "Är du säker på att du vill ta bort det här teamet som är en del av " "organisation %s?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Är du säker på att du vill ta bort den här sidan?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Radera sida" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "Är du säker på att du vill ta bort de markerade sidorna?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "Ta bort sidor" @@ -3781,31 +3791,31 @@ msgstr "Är du säker på att du vill ta bort detta projekt?" msgid "modals.delete-project-confirm.title" msgstr "Radera projekt" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Radera fil" msgstr[1] "Radera filer" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Det är inte aktiverat i någon fil." msgstr[1] "De är inte aktiverade i några filer." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Biblioteket aktiveras här: " msgstr[1] "Biblioteken aktiveras här: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Är du säker på att du vill radera filen?" msgstr[1] "Är du säker på att du vill radera filerna?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Raderar fil" @@ -3825,19 +3835,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Ta bort team" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Ta bort medlemmar" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Är du säker på att du vill ta bort den här medlemmen från teamet?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Ta bort teammedlem" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3847,23 +3857,23 @@ msgstr[1] "" "Tillgångar som redan har använts i dessa filer kommer att finnas kvar där " "(ingen design kommer att förstöras)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Ta bort webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Är du säker på att du vill ta bort denna webhook?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Raderar webhook" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Redigera webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Redigera webhook" @@ -3879,15 +3889,15 @@ msgstr "Importera tokens" msgid "modals.import-library-tokens.title" msgstr "Importera tokens från biblioteket?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Skicka inbjudan" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-post, kommaseparerad" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Vissa e-postmeddelanden är från nuvarande teammedlemmar. Deras inbjudningar " @@ -3910,99 +3920,99 @@ msgstr "" "och de är alla redan teammedlemmar. Om du behöver mer information, kontakta " "organisationens ägare." -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "Adresser som inte kommer att få en inbjudan" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "Avbryt" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "Endast befintliga medlemmar i organisationen kan bjudas in." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "Skicka inbjudningar" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "Vissa inbjudningar kan inte skickas" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "Vissa personer på din lista kommer inte att få inbjudningarna." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Du kan bjuda in medlemmar till teamet så att de får tillgång till den här " "filen och alla teamets filer." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Bjud in teammedlemmar till teamet" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Eftersom du är den enda medlemmen i det här teamet raderas teamet " "tillsammans med sina projekt och filer." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Är du säker på att du vill lämna %s teamet?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Du kan inte lämna teamet om det inte finns någon annan medlem att tilldela " "som ägare. Du kanske vill ta bort teamet." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Du är ägare till detta team. Välj en annan medlem att tilldela rollen ägare " "innan du lämnar teamet." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Befodra och lämna" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Välj en medlem att befordra" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Innan du lämnar" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Lämna teamet" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Är du säker på att du vill lämna teamet?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Lämnar teamet" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Du är ägare till teamen i vissa organisationer. Vänligen befordra en annan " "medlem till att bli ägare." -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Du är den enda medlemmen i vissa av teamen. Dessa team kommer att raderas " "tillsammans med deras projekt och filer." -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "Du är också ägare till vissa organisationers team. Vänligen befordra en " @@ -4022,57 +4032,57 @@ msgstr "" msgid "modals.leave-organization-confirm.title" msgstr "Lämnar du organisation %s?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Flytta" msgstr[1] "Flytta" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Är du säker på att du vill flytta det här biblioteket?" msgstr[1] "Är du säker på att du vill flytta dessa bibliotek?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Flytta biblioteket" msgstr[1] "Flytta biblioteken" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Knuffmängd" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Överför ägandet" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Om du överför ägandet kommer du att ändra din roll till Admin och förlora " "vissa behörigheter över det här teamet. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Du är den nuvarande ägaren av detta team. Är du säker på att du vill göra " "%s till den nya ägaren av teamet?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Ny teamägare" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Publicera" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Ditt bibliotek är tomt. Är du säker på att du vill publicera den?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Publicera tomt bibliotek" @@ -4094,23 +4104,23 @@ msgstr "Är du säker på att du vill ta bort teamet '%s' från organisationen ' msgid "modals.remove-team-organization.title" msgstr "TA BORT TEAMET FRÅN ORGANISATIONEN" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Liten knuff" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Avpublicera" msgstr[1] "Avpublicera" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Är du säker på att du vill avpublicera detta bibliotek?" msgstr[1] "Är du säker på att du vill avpublicera dessa bibliotek?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Avpublicera biblioteket" @@ -4200,38 +4210,38 @@ msgstr "Aktivera" msgid "nitrate.code-activation.title" msgstr "Aktivera Nitrate" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "Kontakta säljare" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "" "För att byta till det här abonnemanget, vänligen kontakta vårt säljteam. Vi " "hjälper dig att uppdatera din prenumeration och ser till att allt är " "korrekt konfigurerat." -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Din organisation kommer att raderas." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "" "Ditt totala lagringsutrymme, din automatiska versionshistorik och din " "filåterställningsperiod kommer att bli begränsad." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "" "Teamen, projekten och filerna kommer inte längre att ingå i någon " "organisation men de kommer att förbli tillgängliga." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "När du nedgraderar:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "Byt till abonnemang %s?" @@ -4249,15 +4259,15 @@ msgstr "Kontakta oss för att prova Enterprise i 14 dagar:" msgid "nitrate.form.contact-upgrade" msgstr "Kontakta oss för att uppgradera till Enterprise:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "Kontakta oss för att prova Enterprise-planen i 14 dagar:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "Kontakta oss för att uppgradera till Enterprise-planen:" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "" "Du debiteras inte just nu. Betalningen genomförs när provperioden är slut. " @@ -4277,7 +4287,7 @@ msgstr "" msgid "nitrate.form.enterprise-feature-3" msgstr "Full frihet med öppen källkod, med den styrning din organisation behöver" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "Allt dina team behöver för att arbeta säkert i stor skala%s" @@ -4285,7 +4295,7 @@ msgstr "Allt dina team behöver för att arbeta säkert i stor skala%s" msgid "nitrate.form.enterprise.price" msgstr "per medlem per månad" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "Prova 14 dagar gratis" @@ -4301,7 +4311,7 @@ msgstr "Kom igång med Enterprise" msgid "nitrate.form.subscribe-with-code" msgstr "Prenumerera med en aktiveringskod" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "Lås upp Enterprise-funktioner" @@ -4313,7 +4323,7 @@ msgstr "Prova det gratis i 14 dagar" msgid "nitrate.modal-success.title" msgstr "Välkommen till Enterprise!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "Aktiv till %s" @@ -4321,23 +4331,23 @@ msgstr "Aktiv till %s" msgid "nitrate.subscription.renew-success" msgstr "Din Nitrate-prenumeration har förnyats." -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "Avsluta prenumeration" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "För att säga upp din Enterprise-prenumeration, vänligen kontakta oss på:" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "Förnya med aktiveringskod" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Denna sida existerar inte" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 fel" @@ -4355,11 +4365,11 @@ msgstr "Vi har skickat ett återställningsmail till" msgid "not-found.login.sent-recovery-check" msgstr "Kolla din e-post och klicka på länken för att skapa ett nytt lösenord." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Registrera dig kostnadsfritt" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "Börja använda Penpot på några sekunder!" @@ -4367,67 +4377,67 @@ msgstr "Börja använda Penpot på några sekunder!" msgid "not-found.made-with-love" msgstr "Tillverkad med KÄRLEK och Open Source" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Du har redan begärt tillgång till denna fil." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Du har redan begärt tillgång till denna fil eller andra filer eller projekt " "i detta team." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Du har redan begärt tillgång till detta projekt eller andra projekt eller " "filer i det här teamet." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Du har redan begärt tillgång till detta projekt." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "BEGÄR TILLGÅNG" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Kom ihåg att om ägaren tillåter det kommer du att bli inbjuden till teamet." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Din förfrågan har skickats!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Du har inte tillgång till den här filen." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Gå till din Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Om ägaren tillåter det kommer du att bli inbjuden till teamet." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot-fil" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Du har inte tillgång till detta projekt." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJEKT" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "För att komma åt den här filen kan du fråga ägaren av teamet." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "För att komma åt detta projekt kan du fråga ägaren av teamet." @@ -4441,31 +4451,31 @@ msgstr "" msgid "notifications.by-code.upgrade-version" msgstr "En ny version är tillgänglig." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "Denna inbjudan är inte längre tillgänglig." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Inbjudan har raderats" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Inbjudan skickades" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Inbjudningslänken kopierades" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "Du kan inte ta bort din profil. Omfördela dina team innan du fortsätter." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profilen sparades!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Verifieringsmejl skickas till %s. Kolla din e-post!" @@ -4503,7 +4513,7 @@ msgstr "Team-namn" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Skapa team" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Bjud in medlemmar" @@ -4513,98 +4523,98 @@ msgstr "" "Utvecklare, designers, chefer ... alla är välkomna. Du kan bjuda in fler " "senare." -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Bjud in med rollen:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Låt oss komma igång!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Generera riktig kod från design" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Skapa designsystem" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototyping" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Designa UI/UX av en app" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Wireframing" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Vad ska du använda Penpot till?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Låt oss veta lite om dig för att hjälpa oss att få Penpot att fungera för " "dig. Dina svar hjälper oss att prioritera nya funktioner och peka dig i " "rätt riktning för att komma igång." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Hjälp oss att lära känna dig" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Vilken av dessa verktyg använder du mest?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Vilken typ av arbete gör du?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Hur stort är ditt företag?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Var vill du komma igång?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1001 - 5000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 - 500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 - 100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 - 1000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "Bara jag" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5001+" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Utbildning" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Personlig" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Arbete" @@ -4686,11 +4696,11 @@ msgstr "Återställ till standardkonfiguration" msgid "restore-shortcuts.restore" msgstr "Återställ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Separera" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Mixad" @@ -4702,35 +4712,35 @@ msgstr "Ta bort färg" msgid "settings.select-this-color" msgstr "Välj objekt med denna stil" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Kommentarer" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Justera botten" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Justera centrum horisontellt" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Justera vänster" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Justera höger" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Justera toppen" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Justera centrum vertikalt" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Skapa tavla från markering" @@ -4738,55 +4748,55 @@ msgstr "Skapa tavla från markering" msgid "shortcuts.bold" msgstr "Växla mellan fetstil" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Boolesk skillnad" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Boolean exkluderar" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Boolean korsning" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Booleansk union" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Flytta längst bak" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Flytta bakåt" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Flytta framåt" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Flytta längst fram" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Ångra rensa" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Kopiera" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Kopiera länk till urklipp" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Kopiera egenskaper" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Skapa komponent / variant" @@ -4794,63 +4804,63 @@ msgstr "Skapa komponent / variant" msgid "shortcuts.create-new-project" msgstr "Skapa ny" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Klipp ut" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Zooma ut" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Radera" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Radera nod" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "Ta bort nod och segment" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "Avmarkera alla" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Separera komponent" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "Inaktivera kortkommando" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "Inaktiverad" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "Inaktiverat kortkommando" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "Pil" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Kurva" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Ellips" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Tavla" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "Linje" @@ -4858,65 +4868,65 @@ msgstr "Linje" msgid "shortcuts.draw-nodes" msgstr "Rita bana" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Bana" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Rektangel" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Text" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Duplicera" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "" "Kombinationen är tilldelad \"%s\". Om du sparar kommer den att tas bort " "därifrån." -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "Om du sparar återgår det här kortkommandot till det ursprungliga värdet:" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "Denna tangentkombination är tillgänglig och kan tilldelas utan konflikter." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "Anpassa i inställningarna" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Avbryt" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Exportera former" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "Hitta" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "Hitta och ersätt" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Zooma för att få plats med alla" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Vänd horisontellt" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Vänd vertikalt" @@ -4940,27 +4950,27 @@ msgstr "Gå till delade bibliotek" msgid "shortcuts.go-to-search" msgstr "Sök" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Grupp" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Distribuera horisontellt" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Visa / dölja UI" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "Import/export" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Zooma in" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Infoga bild" @@ -4968,11 +4978,11 @@ msgstr "Infoga bild" msgid "shortcuts.italic" msgstr "Växla mellan kursiv stil" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Sammanfoga noder" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "Tryck tangentkombinationen" @@ -4980,39 +4990,39 @@ msgstr "Tryck tangentkombinationen" msgid "shortcuts.line-through" msgstr "Växla mellan linje igenom" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Gör ett hörn" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Gör en kurva" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Mask" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Sammanfoga noder" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Flytta" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Flytta ner snabbt" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Flytta vänster snabbt" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Flytta höger snabbt" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Flytta upp snabbt" @@ -5020,379 +5030,379 @@ msgstr "Flytta upp snabbt" msgid "shortcuts.move-nodes" msgstr "Flytta nod" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Flytta ner" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Flytta vänster" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Flytta höger" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Flytta upp" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Nästa tavla" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "Det finns inga inaktiverade kortkommandon" -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "Gå till Alla för att börja anpassa dina kortkommandon." -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "Det finns inga genvägar som matchar din sökning" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Inga genvägar hittades" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Ställ in opacitet på 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Ställ in opacitet på 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Ställ in opacitet på 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Ställ in opacitet på 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Ställ in opacitet på 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Ställ in opacitet på 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Ställ in opacitet på 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Ställ in opacitet på 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Ställ in opacitet på 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Ställ in opacitet på 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Färgväljare" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Gå till visarens kommentarsektion" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Gå till översiktsvyn" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Gå till visarens inspektionssektion" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Gå till visarens interaktionssektion" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Gå till visarens interaktionssektion" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Gå till arbetsytan" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " eller " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "Den här genvägen har tilldelats om till %s" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "Sida för kortkommandon" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Klistra in" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Klista in egenskaper" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "Klistra in och ersätt" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "Personanpassad" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Pluginhanterare" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Föregående tavla" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Gör om" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "" "När du har redigerat kortkommandon, ladda om alla andra öppna Penpot-flikar " "för att tillämpa ändringarna." -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Byt namn" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "Återställ till standard" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Nollställ zoomen" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Skala" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Grundläggande" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Översiktsvy" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Visare" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Arbetsyta" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Markera allt" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Välj nästa lager" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Välj överordnat lager" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Välj föregående lager" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Separera noder" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "Visa mått" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Visa / dölj pixelrutnät" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Visa / dölj genvägar" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Fäst vid noder" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Fäst till pixelrutnät" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Börja redigera" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Justering" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Redigera" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Allmän" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Huvudmeny" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Redigera lager" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Navigation" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Paneler" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Banor" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Former" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Texter" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Verktyg" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Zoom" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Zoom" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Ställ in miniatyrbilder" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Snabbkommandon" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Växla dynamisk justering" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Växla tillgångar" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Växla färgpalett" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "Växla synlighet för kommentarer" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Växla fokusläge" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Växla till helskärm" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Visa / dölj guidelinjer" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Växla historia" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Växla lager" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Lägg till/ta bort flex layout" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Lägg till/ta bort grid-layout" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Lås / lås upp" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Lås proportioner" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Visa / dölja linjaler" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Fäst till guidelinjer" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Fäst till linjaler" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Växla textpalett" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Ändra tema" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Visa / dölj" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Växla zoomstil" @@ -5400,31 +5410,31 @@ msgstr "Växla zoomstil" msgid "shortcuts.underline" msgstr "Växla understrykning" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Ångra" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Avgruppera" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Avmaska" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Distribuera vertikalt" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Minskning av zoom" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Ökning av zoom" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Zooma in markerad" @@ -5443,7 +5453,7 @@ msgstr "Se hur Enterprise fungerar" msgid "subscription.current-plan.enterprise" msgstr "Enterprise" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Enterprise" @@ -5455,7 +5465,7 @@ msgstr "Enterprise-testversion" msgid "subscription.current-plan.professional" msgstr "Professional" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "Din prenumeration" @@ -5585,276 +5595,276 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Bjuda in personer medan du har Unlimited-abonnemanget" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "Betalningen slutfördes inte. Försök igen när du är redo." -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "" "Vi kunde inte starta utcheckningen. Försök igen. Om problemet kvarstår, " "kontakta oss: support@penpot.app." -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Prenumeration" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "Ange aktiveringskod" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Lägg till en betalningsmetod för att fortsätta efter din provperiod" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "Adminkonsol" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Alla förmåner för Professional-abonnemanget och:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Alla förmåner för Unlimited-abonnemanget och:" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "Alla förmåner från Unlimited-planen, plus:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s redaktörer)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise (provperiod)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90-dagar av autosparade versioner och filåterställning" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Fast månadsfaktura" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "Avancerad kontroll:" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "Enterprise-säkerhet:" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Multi-orghantering:" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "" "Inbyggd SAML/SSO och fullständig revisionsloggning för " "compliance-rapportering." -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "" "Detaljerad vitlistning av plugins, inklusive vår säkra och isolerade " "MCP-server för AI-anslutning." -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "Stöd för komplexa teamhierarkier och separata underorganisationer." -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Obegränsad lagring" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Hantera din prenumeration" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Lägg till betalningsuppgifter" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Lägg till betalningsuppgifter så att din prenumeration ska fortsätta " "fungera efter provperioden och för att stödja vårt projekt med öppen " "källkod. Du kommer inte att debiteras ännu." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Hoppa över för tillfället och starta provperioden" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Hjälp oss att växa och gör din provperiod enklare" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "För närvarande har du %s person i dina team som kan redigera." msgstr[1] "För närvarande har du %s personer i dina team som kan redigera." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Obs: att byta till ett lägre abonnemang innebär mindre lagringsutrymme och " "kortare säkerhetskopior och versionshistorik." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Redaktörer" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(Ägare, administratör och redaktörer. Läsare räknas inte som redaktörer)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "Du kan inte ange färre redaktörer än du har nu. Ändra rollen " "(redaktör/administratör till läsare) för personer som inte faktiskt " "redigerar filer i teaminställningarna." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "Debiterad efter provperioden. Inget kreditkort krävs just nu." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/månad" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Applicera %s till dina team" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Tips: Du kan öka antalet platser nu för att ligga steget före inbjudningar. " "Med 25+ redaktörer över teamen åtnjuter du en fast avgift på $175/månad." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Penpot-medlem sedan: %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Mer information" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "org-medlem per månad" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Övriga Penpot-abonnemang" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "organisation per månad" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "användare / månad" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professional" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7-dagars autosparversioner och filåterställning" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "Alla design- och prototypfunktioner har Community-support." -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "Fullständig kontroll över data och infrastruktur." -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "Obegränsat antal användare, team och filer." -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10 GB lagringsutrymme" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Obegränsat antal team. Upp till 8 redaktörer över alla dina egna team." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Rekommenderad" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Din prenumeration" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Starta gratis provperiod" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Prenumerera" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "Du har haft abonnemanget %s sedan %s." -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Du kan ändra din prenumeration när som helst från sidan \"Prenumeration\" i " "dina kontouppgifter." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Lycka till med ditt abonnemang!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Tack för att du valt Penpot %s-abonnemanget!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Du är %!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Prova det gratis i 14 dagar" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Unlimited" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unlimited (provperiod)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30-dagars autosparversioner och filåterställning" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Månadsfaktura med maxbelopp på $175" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25 GB lagringsutrymme" @@ -5877,45 +5887,45 @@ msgstr "Om du vill öka denna gräns, kontakta teamägaren: [%s](mailto)" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "Om du vill öka denna gräns, [uppgradera ditt abonnemang|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 inbjudan vald" msgstr[1] "%s inbjudningar valda" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Typsnittsleverantörer - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Typsnitt - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projekt - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Sök -%s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Delade bibliotek -%s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Designfrihet för team" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Ge feedback - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "Integrationer - Penpot" @@ -5923,7 +5933,7 @@ msgstr "Integrationer - Penpot" msgid "title.settings.notifications" msgstr "Notifieringar - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Inställningar - Penpot" @@ -5931,27 +5941,27 @@ msgstr "Inställningar - Penpot" msgid "title.settings.password" msgstr "Lösenord - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Inbjudningar - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Medlemmar - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Inställningar - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Visningsläge - Penpot" @@ -5963,51 +5973,51 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Lösgör token" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Inga tavlor hittade på sidan." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Tavlan hittades inte." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Kommentarer (%)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Visa inte interaktioner" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Helskärm" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Inspektera (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Interaktioner" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Interaktioner (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Kopiera länk" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Visa interaktioner" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Visa interaktioner på klick" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Översiktsplan" @@ -6044,67 +6054,67 @@ msgstr "Följ vår " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "WebGL-felsökningsguide" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "WebGL-rendering inaktiverad" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "WebGL-rendering aktiverad" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "WebGL-kontexten tappades" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "WebGL-kontexten återställdes" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Senaste leveransen lyckades." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Justera horisontellt centrum (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Distribuera horisontellt avstånd (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Justera vänster (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Justera höger (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Justera botten (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Justera vertikalt centrum (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Distribuera vertikalt avstånd (%)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Justera toppen (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Lägg till bibliotek" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Alla tillgångar" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Färger" @@ -6116,7 +6126,7 @@ msgstr "Lägg till färg" msgid "workspace.assets.component-group-options" msgstr "Komponentgruppsalternativ" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Komponenter" @@ -6124,7 +6134,7 @@ msgstr "Komponenter" msgid "workspace.assets.components.add-component" msgstr "Lägg till komponent" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "% Varianter" @@ -6132,11 +6142,11 @@ msgstr "% Varianter" msgid "workspace.assets.create-group" msgstr "Skapa en grupp" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Dina objekt kommer att namnges automatiskt som \"gruppnamn / objektnamn\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Radera" @@ -6144,11 +6154,11 @@ msgstr "Radera" msgid "workspace.assets.delete-group" msgstr "Ta bort grupp" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "Ta bort sidor" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Duplicera" @@ -6156,39 +6166,39 @@ msgstr "Duplicera" msgid "workspace.assets.duplicate-main" msgstr "Duplicera huvudarbetsytan" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Redigera" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filter" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Rutnätsvy" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Grupp" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Gruppnamn" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Listvy" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "lokala bibliotek" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Hantera bibliotek" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Inga tillgångar hittades" @@ -6196,7 +6206,7 @@ msgstr "Inga tillgångar hittades" msgid "workspace.assets.open-library" msgstr "Öppna bibliotek fil" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Byt namn" @@ -6204,11 +6214,11 @@ msgstr "Byt namn" msgid "workspace.assets.rename-group" msgstr "Byt namn på gruppen" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Sök tillgångar" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Delat bibliotek" @@ -6218,43 +6228,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 komponent" msgstr[1] "%s komponenter" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Sortera" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Typografi" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Lägg till teckenssnitt" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Storlek" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Typsnitt" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Gå till stilbibliotek fil för att redigera" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Teckenavstånd" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Radavstånd" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Texttransformation" @@ -6262,101 +6272,105 @@ msgstr "Texttransformation" msgid "workspace.assets.ungroup" msgstr "Avgruppera" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "Kopierar bild…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "Bild kopierad till urklipp" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "Fel vid kopiering av bild" +#: src/app/main/ui/workspace/colorpicker.cljs:447 +msgid "workspace.colorpicker.color-picker" +msgstr "Välj färg från skärmen" + #: src/app/main/ui/workspace/colorpicker.cljs:439, src/app/main/ui/workspace/colorpicker.cljs:454 msgid "workspace.colorpicker.color-tokens" msgstr "Färg-tokens" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Komponenter kan inte kapslas inuti sig själva." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "" "Vissa kopior kunde inte bytas. Komponenter kan inte kapslas inuti sig " "själva." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Skapa område" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "Kopiera kolumner" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "Kopiera rader" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Skapa tavla" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Sammanfoga celler" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "Klistra in" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Lägg till 1 kolumn till höger" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Lägg till 1 kolumn till vänster" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Radera kolumn" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Ta bort kolumn och former" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Duplicera kolumn" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Lägg till 1 rad nedanför" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Lägg till 1 rad ovanför" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Ta bort rad" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Ta bort rad och former" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Duplicera rad" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "Guidefärg" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "Ta bort guide" @@ -6364,15 +6378,15 @@ msgstr "Ta bort guide" msgid "workspace.debug.title" msgstr "Felsökningsverktyg" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Fokusläge" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Fokusera av" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Fokusera på" @@ -6384,243 +6398,247 @@ msgstr "Linjär gradient" msgid "workspace.gradients.radial" msgstr "Radiell gradient" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Inaktivera dynamisk justering" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Inaktivera proportionell skala" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Inaktivera fäst till guidelinjer" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Inaktivera fäst till pixelrutnät" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Inaktivera fäst till linjaler" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "Inaktivera WebGL-rendering (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Aktivera dynamisk justering" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Aktivera proportionell skalning" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Fäst till guidelinjer" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Aktivera fäste till pixelrutnät" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Fäst till linjaler" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "Aktivera WebGL-rendering (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "Hitta" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "Hitta och ersätt" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Dölj tavelnamn" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "Dölj kommentarer" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Dölj guidelinjer" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Dölj färgpalett" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Dölj pixelrutnät" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Dölj linjaler" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Dölj typsnittspaletten" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "Lås guider" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "Anslut" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "Koppla från" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "Hantera (Status: inaktiverad)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "Hantera (Status: aktiverad)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Redigera" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Fil" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Hjälp & info" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "MCP-server" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Inställningar" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Visa" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Gör om" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Markera allt" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Visa kartnamn" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "Visa kommentarer" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Visa guidelinjer" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Visa färgpalett" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Visa pixelrutnät" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Visa linjaler" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Visa typsnittspalett" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Växla till mörkt tema" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Växla till ljust tema" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Växla till systemtema" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Ångra" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "Lås upp guider" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Återställ" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:133 +msgid "workspace.header.retrying" +msgstr "Försöker spara igen" + +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Fel vid sparandet" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Sparad" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Sparar" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Dela" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Visningsläge (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Zoom" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Fylla - Skala för att fylla" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Anpassa - Skala ner för att passa" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Zooma för att få plats med alla" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Helskärm" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Zooma in markerad" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Redigera grid" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Avsluta" @@ -6628,7 +6646,7 @@ msgstr "Avsluta" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Bottenutfyllnad" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Visa 4-sidiga utfyllnadsalternativ" @@ -6664,37 +6682,37 @@ msgstr "Klart" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Lokalisera" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Lokalisera rutnätslayout" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 färg" msgstr[1] "%s färger" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "Lägg till färg i biblioteket" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Du har inga färgstilar i ditt bibliotek" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "Det finns inga nya färger ännu" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Du har inga typsnittsstilar i ditt bibliotek än" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Filbibliotek" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Senast använda färger" @@ -6706,75 +6724,99 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Spara färgstil" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "Växla färgpalett" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 komponent" msgstr[1] "%s komponenter" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:864 +msgid "workspace.libraries.connected-libraries" +msgstr "Anslutet bibliotek" + +#: src/app/main/ui/workspace/libraries.cljs:235 +msgid "workspace.libraries.connected-through" +msgstr "Ansluten genom" + +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Ansluten till" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Eller lägg till några av dessa för att prova:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:834 +msgid "workspace.libraries.empty.no-assets" +msgstr "Inga objekt än" + +#: src/app/main/ui/workspace/libraries.cljs:918 +msgid "workspace.libraries.empty.no-connected-libraries" +msgstr "Inga anslutna bibliotek än." + +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "Det finns inga delade bibliotek i ditt team, du kan leta efter" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "några mallar här" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Filbibliotek" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 grafik" msgstr[1] "%s grafiker" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BIBLIOTEK I DENNA FIL" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "BIBLIOTEK" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "BIBLIOTEKUPDATERINGAR" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Laddar…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Det finns inga delade bibliotek som behöver uppdateras" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Inga träffar hittades för ”%s”" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Sök delade bibliotek" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:849, src/app/main/ui/workspace/libraries.cljs:897 +msgid "workspace.libraries.set-as-tokens-source" +msgstr "Ange som källa för tokens" + +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "DELADE BIBLIOTEK" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:674 +msgid "workspace.libraries.shared-library-added" +msgstr "Lade till" + +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Anslut bibliotek" @@ -6798,25 +6840,47 @@ msgstr "Koppla bort alla objekt" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "Lossa alla tokens" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:219, src/app/main/ui/workspace/libraries.cljs:263 +msgid "workspace.libraries.token-sets" +msgid_plural "workspace.libraries.token-sets" +msgstr[0] "1 uppsättning" +msgstr[1] "%s uppsättningar" + +#: src/app/main/ui/workspace/libraries.cljs:222, src/app/main/ui/workspace/libraries.cljs:267 +msgid "workspace.libraries.token-themes" +msgid_plural "workspace.libraries.token-themes" +msgstr[0] "1 tema" +msgstr[1] "%s teman" + +#: src/app/main/ui/workspace/libraries.cljs:216, src/app/main/ui/workspace/libraries.cljs:259 +msgid "workspace.libraries.tokens" +msgid_plural "workspace.libraries.tokens" +msgstr[0] "1 token" +msgstr[1] "%s tokens" + +#: src/app/main/ui/workspace/libraries.cljs:842, src/app/main/ui/workspace/libraries.cljs:889 +msgid "workspace.libraries.tokens-source" +msgstr "Källa för tokens" + +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 typografi" msgstr[1] "%s typografier" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Koppla från biblioteket" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Uppdatera" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "se alla ändringar" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "UPDATERINGAR" @@ -6879,7 +6943,7 @@ msgstr "Växla oskärpa" msgid "workspace.options.blur-options.toggle-more-options" msgstr "Visa/dölj fler alternativ" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "Avbryt" @@ -6887,117 +6951,121 @@ msgstr "Avbryt" msgid "workspace.options.canvas-background" msgstr "Canvas bakgrund" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:75 +msgid "workspace.options.canvas-background-section" +msgstr "Sektion för canvasbakgrund" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Innehåll i urklipp" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Komponent" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Anteckning" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Kopiera" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Skapa en anteckning" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Redigera en anteckning" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Huvuddel" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Växla komponent" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Det finns inga tillgångar i detta bibliotek" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Olänkad" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Hitta motstridiga varianter" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Denna komponent har motstridiga varianter. Se till att varje variation har " "en unik uppsättning egenskapsvärden." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Hitta duplicerade varianter" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Vissa varianter har identiska egenskaper och värden" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Dessa varianter har identiska egenskaper och värden. Justera värdena så att " "de kan hämtas." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Denna variant har identiska egenskaper och värden som en annan variant. " "Justera värdena så att de kan hämtas." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Några av dessa varianter har identiska egenskaper och värden. Justera " "värdena så att de kan hämtas." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Denna komponent har varianter med ogiltiga namn. Se till att varje variant " "följer rätt struktur." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Hitta ogiltiga varianter" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Vissa varianter har ogiltiga namn" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Hitta ogiltiga varianter" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Dessa varianter har ogiltiga namn." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Denna variant har ett ogiltigt namn." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Några av dessa varianter har ogiltiga namn." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[egenskap]=[värde], [egenskap]=[värde]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Försök att använda följande struktur:" @@ -7033,7 +7101,7 @@ msgstr "Har samma hierarkinivå." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Grupper, tavlor och layouter anses vara likvärdiga." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Hur varianter förblir sammankopplade" @@ -7081,11 +7149,11 @@ msgstr "Topp & Botten" msgid "workspace.options.convert-to-typography" msgstr "Skapa typografistil" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Design" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Exportera" @@ -7093,53 +7161,53 @@ msgstr "Exportera" msgid "workspace.options.export-multiple" msgstr "Exportera markerade" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Exportera 1 element" msgstr[1] "Exportera %s element" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Lägg till export" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Ta bort export" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Suffix" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "Exporttjänsten är upptagen, försök igen senare" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "Export avbruten" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "Avbryter..." -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Exporten lyckades" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Exporterar…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Exporten misslyckades" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Exporten oväntat långsam" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "Väntar..." @@ -7159,7 +7227,7 @@ msgstr "Ta bort fyllningsfärg" msgid "workspace.options.fill.section" msgstr "Fyll sektion" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Ändra storlek på tavlan för att passa innehållet" @@ -7179,71 +7247,77 @@ msgstr "Flödesstart" msgid "workspace.options.flows.flow-starts" msgstr "Flödet startar" +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:300, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 +msgid "workspace.options.font-not-available" +msgstr "" +"Typsnittet som används i denna text (%s) är inte längre tillgänglig. Ladda " +"upp typsnittet på nytt för att skapa en typografistil" + #: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:34 msgid "workspace.options.grid.auto" msgstr "Auto" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Kolumner" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Färg" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Mellanrum" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Höjd" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Marginal" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Sätt som standard" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Botten" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Center" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Vänster" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Höger" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Stretcha" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Topp" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Använd standard" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Bredd" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Rader" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Kvadrat" @@ -7255,7 +7329,7 @@ msgstr "Gruppfyllning" msgid "workspace.options.group-stroke" msgstr "Gruppera fet" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Lägg till guide" @@ -7263,19 +7337,19 @@ msgstr "Lägg till guide" msgid "workspace.options.guides.remove-guide" msgstr "Ta bort guide" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Guidelinjer" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Växla guide" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Höjd" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Inspektion" @@ -7491,115 +7565,119 @@ msgstr "Interaktioner" msgid "workspace.options.interactions.add-interaction" msgstr "Lägg till interaktion" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Färg" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Efterbelys färg" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Färgskugga" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Mörkare" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Differens" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Uteslutning" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Skarpt ljus" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Nyans" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Ljusare" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Luminiscens" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Multiplicera" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Täcka över" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Mättnad" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Raster" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Mjukt ljus" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "Lagermenysektion" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Växla lagersynlighet" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Max.höjd" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Max.bredd" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Min.höjd" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Min.bredd" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Fler färger" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Fler biblioteksfärger" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "Fler färg-tokens" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "Ingen matchande förinställd storlek" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opacitet" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:99, src/app/main/ui/workspace/sidebar/options/page.cljs:106 +msgid "workspace.options.pixel-grid-color" +msgstr "Färg för pixelrutnät" + +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototyp" @@ -7635,23 +7713,23 @@ msgstr "Ramradiesektion" msgid "workspace.options.radius.show-single-corners" msgstr "Visa oberoende radie" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Senaste" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Försök igen" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Rotering" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Sök typsnitt" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "Sök förinställda storlekar" @@ -7659,11 +7737,11 @@ msgstr "Sök förinställda storlekar" msgid "workspace.options.select-a-shape" msgstr "Välj en form, tavla eller grupp för att dra en anslutning till andra kartor." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Markerade färger" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "Färgvalssektion" @@ -7731,55 +7809,55 @@ msgstr "Växla skugga" msgid "workspace.options.show-fill-on-export" msgstr "Visa i exporter" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Visa i visningsläge" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Storlek" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Storleksinställningar" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Lås förhållande" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Lås upp förhållande" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Cirkel" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Diamant" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Pil" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Ingen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Runda" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Kvadrat" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Rektangel" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Triangel" @@ -7787,31 +7865,31 @@ msgstr "Triangel" msgid "workspace.options.stroke-color" msgstr "Konturfärg" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "Streck" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "Mellanrum" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Konturbredd" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "Botten (blockslut)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "Vänster (radstart)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "Höger (radslut)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "Topp (blockstart)" @@ -7819,49 +7897,49 @@ msgstr "Topp (blockstart)" msgid "workspace.options.stroke.add-stroke" msgstr "Lägg till streckfärg" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Center" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Streckad" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Prickad" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Inuti" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Mixad" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Utanför" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "Streck per sida" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "" "Funktionen för antal streck per sida är endast tillgänglig i den nya " "renderingen. Byt renderingsläge för att använda den." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Ta bort streckfärg" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Fylld" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "Växla streckning" @@ -7897,11 +7975,11 @@ msgstr "Autobredd" msgid "workspace.options.text-options.grow-fixed" msgstr "Fast" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Teckenavstånd" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Radavstånd" @@ -7949,224 +8027,228 @@ msgstr "Understruken (%s)" msgid "workspace.options.use-play-button" msgstr "Använd spelknappen i sidhuvudet för att köra prototypvisaren." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Bredd" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X axel" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y axel" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Rita noder (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "Justerad" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "Fristående" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "Jämn" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "Hanterarbeteende" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Sätt ihop noder (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Till hörnet (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Till kurva (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Sammanfoga noder (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Flytta noder (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Separera noder (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Fästnoder (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Öppna" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Upptäck [fler plugins](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Inga plugins installerade" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Pluginmanifestet är felaktigt." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Du måste vara redaktör för att kunna använda detta plugin" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/data/plugins.cljs:160 +msgid "workspace.plugins.error.unreachable" +msgstr "Det gick inte att nå plugin-URL:en." + +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Pluginen finns inte eller är URL:en fel." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Installera" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Installerade plugins" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Pluginhanterare" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Plugins" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "UPPDATERA DETTA PLUGIN" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Detta plugin har ändrats sedan du senast öppnade det. Det vill nu också få " "tillgång till:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Starta nedladdning av filer." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Lagra data i webbläsaren." -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "Läs innehållet i ditt urklipp." -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "Läs och skriv till urklipp." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Läs dina kommentarer och svar." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Läs och modifiera dina kommentarer och svara i ditt namn." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Läs innehållet i filer som användarna har tillgång till." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Läs och ändra innehållet i filer som användarna har tillgång till." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Observera att detta plugin har skapats av en extern part, så se till att du " "litar på den innan du ger åtkomst. Din datasekretess och säkerhet är " "viktiga för oss. Om du har några frågor, vänligen kontakta support." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Läs dina bibliotek och tillgångar." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Läs och modifiera dina bibliotek och tillgångar." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "'%s' PLUGIN ÖNSKAR TILLGÅNG TILL:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Läs den aktuella användarens profilinformation." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Lista på plugins" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Ta bort plugin" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Skriv in plugin URL" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Plugins" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "INTE NU" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Vill du ta en titt? Det kommer att öppnas i ett nytt utkast för ditt " "nuvarande team. (Om inte, kan du alltid hitta det i de installerade plugins " "för alla filer.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "'%s' PLUGIN ÄR INSTALLERAD FÖR DIN ANVÄNDARE!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "TESTA PLUGIN" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Lägg till flex layout" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Lägg till grid layout" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Lägg till layout" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Skapa variant" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Lägg till ny egenskap" @@ -8178,11 +8260,11 @@ msgstr "Flytta längst bak" msgid "workspace.shape.menu.backward" msgstr "Flytta bakåt" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "Rensa guidelinjer" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Kombinera som varianter" @@ -8190,7 +8272,7 @@ msgstr "Kombinera som varianter" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Komponenter måste vara på samma sida" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Kopiera" @@ -8234,27 +8316,27 @@ msgstr "Kopiera som text" msgid "workspace.shape.menu.create-annotation" msgstr "Skapa anteckning" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Markering till tavla" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Skapa komponent" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Skapa flera komponenter" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Klipp ut" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Radera" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Ta bort flödesstart" @@ -8266,7 +8348,7 @@ msgstr "Koppla från instans" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Koppla från instanser" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Skillnad" @@ -8274,15 +8356,15 @@ msgstr "Skillnad" msgid "workspace.shape.menu.duplicate" msgstr "Duplicera" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Redigera" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Exkludera" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Platta ut" @@ -8294,7 +8376,7 @@ msgstr "Vänd horisontellt" msgid "workspace.shape.menu.flip-vertical" msgstr "Vänd vertikalt" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Flödesstart" @@ -8306,31 +8388,31 @@ msgstr "Flytta framåt" msgid "workspace.shape.menu.front" msgstr "Flytta längst fram" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Grupp" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Dölj" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Via / Dölj UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Intersektion" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Lås" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Mask" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Klistra in" @@ -8338,27 +8420,27 @@ msgstr "Klistra in" msgid "workspace.shape.menu.paste-props" msgstr "Klista in egenskaper" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Bana" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Ta bort flex layout" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Ta bort grid layout" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Ta bort layout" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Ta bort egenskap" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "Varianten bör ha minst en egenskap" @@ -8382,11 +8464,11 @@ msgstr "Återställ variant" msgid "workspace.shape.menu.select-layer" msgstr "Välj lager" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Visa" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Visa i tillgångspanelen" @@ -8394,7 +8476,7 @@ msgstr "Visa i tillgångspanelen" msgid "workspace.shape.menu.show-main" msgstr "Visa huvudkomponent" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "Linje till bana" @@ -8406,19 +8488,19 @@ msgstr "Ta bort miniatyrbild" msgid "workspace.shape.menu.thumbnail-set" msgstr "Ställ in som miniatyrbild" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Avgruppera" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Sammanfoga" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Lås upp" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Avmaska" @@ -8434,79 +8516,79 @@ msgstr "Minimera sidonavigation" msgid "workspace.sidebar.expand" msgstr "Expandera sidonavigation" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Historia" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Lager" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Komponenter" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Filter" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Tavlor" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Grupper" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Bilder" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Masker" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "Inga träffar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "Ersätt" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "Ersätt alla" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "Ersätt med..." -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Sök…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "Hitta och ersätt" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "Textinnehåll" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "Lagernamn" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Former" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Texter" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Importerade SVG attribut" @@ -8518,7 +8600,7 @@ msgstr "Sidor" msgid "workspace.sidebar.sitemap.add-page" msgstr "Lägg till sida" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Översiktsplan" @@ -8538,6 +8620,10 @@ msgstr "%s aktiva teman" msgid "workspace.tokens.add-new-theme" msgstr "Lägg till nytt tema" +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:106 +msgid "workspace.tokens.add-set" +msgstr "Lägg till uppsättning" + #: src/app/main/ui/workspace/tokens/sets/context_menu.cljs:62 msgid "workspace.tokens.add-set-to-group" msgstr "Lägg till uppsättning i denna grupp" @@ -8546,7 +8632,7 @@ msgstr "Lägg till uppsättning i denna grupp" msgid "workspace.tokens.add-token" msgstr "Lägg till token: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Applicerad på" @@ -8566,7 +8652,11 @@ msgstr "Basteckenstorlek" msgid "workspace.tokens.base-font-size.error" msgstr "Basteckenstorlek måste anges i pixlar eller vara enhetslöst." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:98 +msgid "workspace.tokens.change-token-source" +msgstr "Byt källa för tokens" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Färg" @@ -8578,7 +8668,7 @@ msgstr "Kopiera token-sökväg" msgid "workspace.tokens.create-new-theme" msgstr "Skapa ditt första tema nu." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Skapa ett." @@ -8586,6 +8676,10 @@ msgstr "Skapa ett." msgid "workspace.tokens.create-token" msgstr "Skapa en ny %s token" +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:101 +msgid "workspace.tokens.current-tokens-source-tooltip" +msgstr "Detta är den nuvarande källan för tokens" + #: src/app/main/ui/workspace/tokens/management/context_menu.cljs:437 msgid "workspace.tokens.delete" msgstr "Ta bort token" @@ -8602,7 +8696,7 @@ msgstr "Duplicera token" msgid "workspace.tokens.duplicate-group" msgstr "Duplicera token-grupp" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "kopiera" @@ -8626,7 +8720,7 @@ msgstr "Redigera %s token" msgid "workspace.tokens.enter-token-name" msgstr "Ange %s tokennamn" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "Tokens kan inte tillämpas när du redigerar text. Välj textlagret istället." @@ -8654,7 +8748,7 @@ msgstr "Förhandsgranskning:" msgid "workspace.tokens.export.single-file" msgstr "Enskild fil" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Teckenstorlek eller {alias}" @@ -8664,7 +8758,7 @@ msgstr "" "Fel vid inställning av typsnittsvikt/stil. Den här typsnittsstilen finns " "inte i det aktuella typsnittet" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Ange ett värde (300, fet, normal kursiv ...) eller en {alias}" @@ -8702,7 +8796,7 @@ msgstr "" "I en enskild JSON-fil bör nycklarna på första nivån vara namnen på " "token-uppsättningarna." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Importera tokens" @@ -8712,17 +8806,17 @@ msgstr "" "Import av tokens kommer att åsidosätta alla dina nuvarande tokens, " "uppsättningar och teman." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Inaktiv" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Denna uppsättning är inte aktiv. Ändra temat eller aktivera denna " "uppsättning för att se ändringar i visningsfönstret" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Använd individuella tokens" @@ -8742,11 +8836,11 @@ msgstr "Tema" msgid "workspace.tokens.label.theme-placeholder" msgstr "Lägg till ett tema (dvs. ljust)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Lägg till bokstavsavstånd eller {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Radavstånd (multiplikator, px, %) eller {alias}" @@ -8762,11 +8856,11 @@ msgstr "Max. storlek" msgid "workspace.tokens.min-size" msgstr "Min. storlek" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Referens saknas" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Högerklicka för att se alternativ" @@ -8782,7 +8876,7 @@ msgstr "Inget tema aktiverat" msgid "workspace.tokens.no-permisions-set" msgstr "Du måste vara en redaktör för att aktivera / inaktivera uppsättningar" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Du måste vara redaktör för att använda teman" @@ -8794,7 +8888,7 @@ msgstr "Det finns inga uppsättningar definierade. Skapa ett först." msgid "workspace.tokens.no-sets-yet" msgstr "Det finns inga uppsättningar." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Det finns inga teman." @@ -8806,11 +8900,25 @@ msgstr "Du har för närvarande inga teman." msgid "workspace.tokens.not-remap" msgstr "Ommappa ej" +#: src/app/main/data/workspace/libraries.cljs:848 +msgid "workspace.tokens.notifications.source-sets-or-themes-updated" +msgstr "Källuppsättningar eller teman för tokens har ändrats" + #: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:132 msgid "workspace.tokens.num-active-sets" msgstr "%s aktiva uppsättningar" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/colorpicker.cljs:438, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:112 +msgid "workspace.tokens.only-first-fill-or-stroke" +msgstr "" +"Design-tokens kan endast tillämpas på den första fyllningen eller linjen i " +"listan." + +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:104 +msgid "workspace.tokens.open-source-new-tab" +msgstr "Öppna tokens källa i en ny flik" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Ursprungligt värde: %s" @@ -8822,15 +8930,15 @@ msgstr "Utfyllnader" msgid "workspace.tokens.radius" msgstr "Radie" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "Referensen i {%s} är ogiltig eller ingår inte i någon aktiv uppsättning." -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Ange ett alias för token-typografi" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Ange ett token-skuggalias" @@ -8870,7 +8978,7 @@ msgstr "Byt namn på token-grupp" msgid "workspace.tokens.rename-group-name-hint" msgstr "Dina tokens kommer automatiskt att byta namn till %s.(suffix).(tokenName)" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Uträknat värde: %s" @@ -8908,27 +9016,27 @@ msgstr "Här kan du ställa in basteckenstorlek, vilket definierar värdet för msgid "workspace.tokens.settings" msgstr "Token-inställningar" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Lägg till skugga" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Oskärpa" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Infälld" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Ta bort skugga" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Spridning" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "Oskärpa kan inte vara negativt" @@ -8936,11 +9044,11 @@ msgstr "Oskärpa kan inte vara negativt" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "Spridningsvärdet kan inte vara negativt" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -8948,11 +9056,19 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "Storlek" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:75 +msgid "workspace.tokens.source" +msgstr "Källa:" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:311, src/app/main/ui/workspace/tokens/management/context_menu.cljs:335 +msgid "workspace.tokens.stroke-width" +msgstr "Linjetjocklek" + +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "Ange: ingen | versaler | gemener | versalisera eller {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "Ange textdekoration: ingen | understrykning | genomstrykning" @@ -8966,35 +9082,39 @@ msgstr "" msgid "workspace.tokens.themes-list" msgstr "Temalista" +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:80 +msgid "workspace.tokens.this-file" +msgstr "Denna fil" + #: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:334, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:335 msgid "workspace.tokens.token-description" msgstr "Beskrivning" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Välj typsnittsfamilj" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Typsnittsfamilj" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Typsnittsfamilj eller lista över typsnitt separerade med kommatecken (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Namn" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "En token finns redan på sökvägen: %s eller vid ett prefix därav." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "Namnet måste innehålla minst 1 tecken" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " är inte ett giltigt token-namn.\n" @@ -9005,11 +9125,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Värde" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Ange ett värde eller alias med {alias}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKEN - %s" @@ -9028,15 +9148,15 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "Typen '%s' stöds ej (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Använd en referens" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Värdet är inte giltigt" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "Pil (%s)" @@ -9044,75 +9164,75 @@ msgstr "Pil (%s)" msgid "workspace.toolbar.assets" msgstr "Tillgångar" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Färgpalett (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Kommentarer (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Kurva (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Ellips (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Tavla (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Bild (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:362 +msgid "workspace.toolbar.label" +msgstr "Ritverktyg" + +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "Linje (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "Anslut här" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP ansluten" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Flytta (%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "Palett" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Bana (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Plugins (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Rektangel (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Text (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Typografier (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Växla verktygsfält" @@ -9133,11 +9253,11 @@ msgstr "Rendering är inte tillgänglig. Uppdatera för att återställa rediger msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "Ladda om" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Det finns inga tidigare ändringar" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "av %s" @@ -9277,19 +9397,19 @@ msgstr "typografitillgång" msgid "workspace.undo.entry.unknown" msgstr "Körning över %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Avfärda" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Mer info" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Det finns uppdateringar i delade bibliotek" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Uppdatera" @@ -9379,3 +9499,36 @@ msgstr "" #: src/app/main/ui/workspace/sidebar/versions.cljs:462 msgid "workspace.versions.warning.text" msgstr "Autosparade versioner sparas i %s dagar." + +#: src/app/main/ui/dashboard/sidebar.cljs:228 +msgid "dashboard.clear-search" +msgstr "Rensa sökning" + +#: src/app/main/ui/dashboard/fonts.cljs:442, src/app/main/ui/dashboard/fonts.cljs:443 +msgid "dashboard.fonts.delete-variant" +msgstr "Radera typsnittsstil" + +#: src/app/main/ui/dashboard/import.cljs:625 +msgid "dashboard.import.check.message" +msgstr "" +"Säkerställer att varje fil kan importeras. Detta tar bara ett ögonblick." + +#: src/app/main/ui/dashboard/import.cljs:624 +msgid "dashboard.import.check.title" +msgstr "Kontrollerar dina filer..." + +#: src/app/main/ui/dashboard/import.cljs:600 +msgid "dashboard.import.importing.done" +msgid_plural "dashboard.import.importing.done" +msgstr[0] "1 klar" +msgstr[1] "%s klara" + +#: src/app/main/ui/dashboard/import.cljs:637 +msgid "dashboard.import.importing.message" +msgstr "Bygger om sidor, komponenter och objekt i ditt projekt." + +#: src/app/main/ui/dashboard/import.cljs:598 +msgid "dashboard.import.importing.title" +msgid_plural "dashboard.import.importing.title" +msgstr[0] "Importerar 1 fil" +msgstr[1] "Importerar %s filer" diff --git a/frontend/translations/ta.po b/frontend/translations/ta.po index 99dbe76696..5af90355d0 100644 --- a/frontend/translations/ta.po +++ b/frontend/translations/ta.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2026.6.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "ஏற்கனவே ஒரு கணக்கு உள்ளதா?" @@ -29,7 +29,7 @@ msgstr "" "இது ஒரு டெமோ சேவை, உண்மையான வேலைக்கு பயன்படுத்த வேண்டாம், திட்டங்கள் " "அவ்வப்போது அழிக்கப்படும்." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "கடவுச்சொல்லை மறந்துவிட்டீர்களா?" @@ -37,27 +37,27 @@ msgstr "கடவுச்சொல்லை மறந்துவிட் msgid "auth.fullname" msgstr "முழு பெயர்" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "இங்கே உள்நுழைக" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "கிட்லேப்" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "கூகுள்" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "ஓப்பன் ஐடி" @@ -79,7 +79,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "கடவுச்சொல் மீட்பு இணைப்பு உங்கள் இன்பாக்ஸிற்கு அனுப்பப்பட்டது." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "அணியில் வெற்றிகரமாக இணைந்தார்" @@ -111,11 +111,11 @@ msgstr "கடவுச்சொல்லை மறந்துவிட் msgid "auth.recovery-submit" msgstr "உங்கள் கடவுச்சொல்லை மாற்றுக" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "இன்னும் கணக்கு இல்லையா?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "ஒரு கணக்கை உருவாக்கவும்" @@ -123,63 +123,63 @@ msgstr "ஒரு கணக்கை உருவாக்கவும்" msgid "auth.register-title" msgstr "ஒரு கணக்கை உருவாக்கவும்" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "சேவை விதிமுறைகள்" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "வெளியிடுங்கள்" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "அனைத்து Penpot பயனர்களும்" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "இந்த இணைப்பை நிச்சயமாக அகற்ற விரும்புகிறீர்களா? நீங்கள் அதைச் செய்தால், அது " "இனி யாருக்கும் கிடைக்காது" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(தற்போதைய)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "இணைப்பை அழிக்கவும்" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "இணைப்பைப் பெறுங்கள்" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "இணைப்பு வெற்றிகரமாக நகலெடுக்கப்பட்டது" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "அனுமதிகளை நிர்வகிக்கவும்" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 பக்கம் பகிரப்பட்டது" msgstr[1] "%s பக்கங்கள் பகிரப்பட்டன" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "கருத்து தெரிவிக்கலாம்" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "குறியீட்டை ஆய்வு செய்யலாம்" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "இணைப்பு உள்ள எவருக்கும் அணுகல் இருக்கும்" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "பக்கங்கள் பகிரப்பட்டன" diff --git a/frontend/translations/th.po b/frontend/translations/th.po index 8addf5215a..b1412c17a2 100644 --- a/frontend/translations/th.po +++ b/frontend/translations/th.po @@ -11,7 +11,7 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 2026.10.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "มีบัญชีแล้ว?" @@ -29,7 +29,7 @@ msgstr "" "บริการนี้มีไว้สำหรับสาธิตการใช้งาน ห้ามใช้สำหรับชิ้นงานจริง " "เนื่องจากข้อมูลจะถูกลบเป็นระยะ ๆ" -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "ลืมรหัสผ่าน?" @@ -37,35 +37,35 @@ msgstr "ลืมรหัสผ่าน?" msgid "auth.fullname" msgstr "ชื่อเต็ม" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "ลงชื่อเข้าใช้บัญชีของฉัน" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "ลงชื่อเข้าใช้ที่นี่" -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot เป็นเครื่องมือออกแบบที่ฟรีและโอเพนซอร์สสำหรับการดีไซน์และโค้ดร่วมกัน" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -85,7 +85,7 @@ msgstr "โปรไฟล์นี้ยังไม่ได้รับกา msgid "auth.notifications.recovery-token-sent" msgstr "ลิงก์กู้คืนรหัสผ่านถูกส่งไปยังกล่องข้อความของคุณแล้ว" -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "เข้าร่วมทีมสำเร็จ" @@ -113,7 +113,7 @@ msgstr "ลืมรหัสผ่าน?" msgid "auth.recovery-submit" msgstr "เปลี่ยนรหัสผ่านของคุณ" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "ยังไม่มีบัญชี?" @@ -125,7 +125,7 @@ msgstr "เราควรเรียกคุณในแดชบอร์ด msgid "auth.register-account-title" msgstr "ชื่อของคุณ" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "สร้างบัญชี" @@ -138,7 +138,7 @@ msgstr "สร้างบัญชี" msgid "auth.terms-and-privacy-agreement" msgstr "ฉันยอมรับ [ข้อกำหนดการให้บริการ](%s)และ[นโยบายความเป็นส่วนตัว](%s)" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "เงื่อนไขการให้บริการ" @@ -146,94 +146,94 @@ msgstr "เงื่อนไขการให้บริการ" msgid "auth.work-email" msgstr "อีเมลทำงาน" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "ผู้ใช้ Penpot ทั้งหมด" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "คุณแน่ใจหรือไม่ว่าต้องการลบลิงก์นี้? หากคุณลบ จะไม่มีใครสามารถเปิดได้" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(ปัจจุบัน)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "ทำลายลิงก์" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "สร้างลิงก์" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "คัดลอกลิงก์แล้ว" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "จัดการสิทธิ์" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "แชร์แล้ว %s หน้า" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "สามารถคอมเมนต์" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "สามารถตรวจดูโคด" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "ทุกคนที่มีลิงก์สามารถเข้าถึงได้" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "หน้าที่ต้องการแชร์" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "ลิงก์ที่สามารถแชร์ได้จะปรากฏที่นี่" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "สมาชิกในทีมเท่านั้น" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "แชร์โปรโตไทป์" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "เลือกทั้งหมด" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "การจัดการทีม" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot เป็นเครื่องมือสำหรับการทำงานเป็นทีม " "เชิญชวนสมาชิกมาทำงานด้วยกันผ่านโปรเจกต์และไฟล์ต่าง ๆ เลย" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "รวบรวมทีมเลย!" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "เปลี่ยนอีเมล" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(คัดลอก)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "สร้างทีมใหม่" @@ -241,27 +241,27 @@ msgstr "สร้างทีมใหม่" msgid "dashboard.default-team-name" msgstr "Penpot ของคุณ" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "ลบทีม" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "ดาวน์โหลดไฟล์ Penpot (.penpot)" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "เมื่อสมาชิกโปรเจกต์สร้างแบบร่าง จะปรากฏที่นี่" -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "ยังไม่มีแบบร่าง" -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "เมื่อสมาชิกของโปรเจกต์สร้างไฟล์ จะแสดงผลที่นี่" -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "ยังไม่มีไฟล์" @@ -285,44 +285,44 @@ msgstr "ยังไม่มีไลบรารี" msgid "dashboard.export-binary-multi" msgstr "ดาวน์โหลด %s ไฟล์ Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "ส่งออกบอร์ดเป็น PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "ส่งออกเป็น PDF" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "ส่งออก" -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "ส่งออกส่วนที่เลือก" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "ฟอนต์ที่หายไป" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "ฟอนต์ที่กำหนดเองที่คุณอัปโหลดจะปรากฏที่นี่" -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "ยังไม่มีฟอนต์ที่กำหนดเอง" -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "เมื่อสมาชิกโปรเจกต์เพิ่มฟอนต์ที่กำหนดเอง จะปรากฏที่นี่" -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "เพิ่มแล้ว %s ฟอนต์" -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -331,64 +331,40 @@ msgstr "" "นอกจากนี้คุณอาจต้องการอ่านเพิ่มเติมเกี่ยวกับ[ลิขสิทธิ์ฟอนต์](https://www." "typography.com/faq)" -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "อัปโหลดทั้งหมด" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "นำเข้าไฟล์ Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "ไม่สามารถนำเข้าไฟล์นี้ได้" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "นำเข้าสำเร็จแล้ว %s ไฟล์" -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "บางไฟล์มีวัตถุที่ไม่ถูกต้อง ซึ่งถูกนำออกให้แล้ว" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "กำลังประมวลผลสี" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "กำลังประมวลผลคอมโพเนนต์" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "กำลังประมวลผลสื่อ" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "กำลังประมวลผลหน้า: %s" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "กำลังอัปโหลดข้อมูลไปยังเซิร์ฟเวอร์ (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "กำลังอัปโหลดไฟล์: %s" - -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "ออกจากทีม" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "ไลบรารีและเทมเพลต" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "พบปัญหาในการนำเข้าเทมเพลต เทมเพลตนี้จึงไม่ถูกนำเข้า" -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "ไลบรารี" @@ -396,7 +372,7 @@ msgstr "ไลบรารี" msgid "dashboard.loading-files" msgstr "กำลังโหลดไฟล์ของคุณ…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "กำลังโหลดฟอนต์ของคุณ…" @@ -412,23 +388,23 @@ msgstr "ย้าน %s ไฟล์ไปยัง" msgid "dashboard.move-to-other-team" msgstr "ย้ายไปยังทีมอื่น" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ สร้างไฟล์ใหม่" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "สร้างไฟล์ใหม่" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ สร้างโปรเจกต์ใหม่" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "สร้างโปรเจกต์ใหม่" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "อัปเดตอีเมลสำเร็จแล้ว" @@ -440,7 +416,7 @@ msgstr "บันทึกรหัสผ่านแล้ว" msgid "dashboard.open-in-new-tab" msgstr "เปิดไฟล์ในแท็บใหม่" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "ตัวเลือก" @@ -460,87 +436,87 @@ msgstr "คุณเป็นเอดิเตอร์ในทีมนี้ msgid "dashboard.permissions-change.owner" msgstr "คุณเป็นเจ้าของในทีมนี้แล้ว" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "บันทึกการตั้งค่า" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "ค้นหา…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "เลือกภาษาของ UI" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "เลือกธีม" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "แสดงไฟล์ทั้งหมด" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "ข้อมูลของทีม" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "สมาชิกของทีม" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "โปรเจกต์ของทีม" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "ธีมของ UI" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "ผลการค้นหา" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "อัปเดตการตั้งค่า" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "ประเภทเนื้อหา" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "สร้าง Webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "สร้าง Webhook แล้ว" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "บัญชีของคุณ" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "อีเมล" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "ชื่อของคุณ" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "อนุญาต" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "ยกเลิก" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "อีเมลถูกใช้ไปแล้ว" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "โดเมนนี้ไม่ได้รับอนุญาต" @@ -568,7 +544,7 @@ msgstr "อีเมลหรือรหัสผ่านไม่ถูกต msgid "errors.wrong-old-password" msgstr "รหัสผ่านเดิมไม่ถูกต้อง" -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "ชุมชน Penpot" @@ -576,11 +552,11 @@ msgstr "ชุมชน Penpot" msgid "inspect.attributes.shadow" msgstr "เงา" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "ขนาดฟอนต์" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "ระยะห่างระหว่างตัวอักษร" @@ -596,7 +572,7 @@ msgstr "SVG" msgid "labels.code" msgstr "โคด" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "ชุมชน" @@ -604,63 +580,63 @@ msgstr "ชุมชน" msgid "labels.confirm-password" msgstr "ยืนยันรหัสผ่าน" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "คัดลอกลิงก์" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "สร้างทีมใหม่" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "แดชบอร์ด" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "แก้ไข" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "เอดิเตอร์" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "ส่งออก" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "ฟอนต์" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "กราฟิกดีไซน์" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "ภาษา" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "ไลบรารีและเทมเพลต" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "กำลังโหลด…" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "ลงชื่อออก" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "สมาชิก" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "สมาชิก" @@ -672,18 +648,18 @@ msgstr "รหัสผ่านใหม่" msgid "labels.old-password" msgstr "รหัสผ่านเดิม" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "เจ้าของ" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "รหัสผ่าน" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "โปรไฟล์" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "โปรเจกต์" diff --git a/frontend/translations/tr.po b/frontend/translations/tr.po index 853e76ab7a..65bcff3456 100644 --- a/frontend/translations/tr.po +++ b/frontend/translations/tr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:13+0000\n" -"Last-Translator: Oğuz Ersen \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Turkish " "\n" "Language: tr\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Zaten hesabın var mı?" @@ -33,7 +33,7 @@ msgstr "" "Bu bir DEMO hizmettir, gerçek işleriniz için KULLANMAYIN, projeler belirli " "aralıklarla silinecektir." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Parolanı mı unuttun?" @@ -41,37 +41,37 @@ msgstr "Parolanı mı unuttun?" msgid "auth.fullname" msgstr "Tam Adın" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Hesabımda oturum aç" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Buradan oturum açın." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot, tasarım ve kod iş birliği için özgür ve açık kaynaklı tasarım " "aracıdır" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -79,7 +79,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "Yeni bir parola gir" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "Artık %s organizasyonunun bir parçasısınız" @@ -95,7 +95,7 @@ msgstr "Profil onaylanmamış, devam etmeden önce profili onaylayın." msgid "auth.notifications.recovery-token-sent" msgstr "Parola kurtarma bağlantısı e-posta kutuna gönderildi." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Takıma başarıyla katıldın" @@ -127,7 +127,7 @@ msgstr "Parolanı mı unuttun?" msgid "auth.recovery-submit" msgstr "Parolanı değiştir" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Henüz hesabın yok mu?" @@ -139,7 +139,7 @@ msgstr "Denetim panelinde ve e-postalarda size ne dememiz gerektiğini belirtin. msgid "auth.register-account-title" msgstr "Adınız" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Bir hesap oluştur" @@ -152,7 +152,7 @@ msgstr "Bir hesap oluştur" msgid "auth.terms-and-privacy-agreement" msgstr "[Hizmet koşullarını](%s) ve [gizlilik politikasını](%s) kabul ediyorum." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Hizmet şartları" @@ -174,93 +174,93 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "@%s için kişi bulunamadı" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Yayınla" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Tüm Penpot kullanıcıları" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Bu bağlantıyı kaldırmak istediğinizden emin misiniz? Bunu yaparsanız, artık " "kimse tarafından kullanılamayacak" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(geçerli)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Bağlantıyı yok et" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Bağlantıyı al" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Bağlantı başarıyla kopyalandı" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "İzinleri yönet" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 sayfa paylaşıldı" msgstr[1] "%s sayfa paylaşıldı" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Yorum yapabilir" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Kodu inceleyebilir" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Bağlantıya sahip olan herkes erişebilir" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Paylaşılan sayfalar" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Paylaşılabilir bağlantı burada görünecek" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Yalnızca takım üyeleri" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Prototipleri paylaş" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Tümünü Seç" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Yayından kaldır" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Takım yönetimi" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot takımlar içindir. Üyeleri projeler ve dosyalar üzerinde birlikte " "çalışmaya davet edin" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Takım olun!" @@ -276,33 +276,33 @@ msgstr "Dosyayı geri yükle" msgid "dashboard.add-file" msgstr "Dosya ekle" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Paylaşılan Kütüphane olarak ekle" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "E-posta adresini değiştir" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "TAKIMI TAŞI" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "Şuraya taşı:" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "" "Projeler ve dosyalar takım üyeleri tarafından erişilebilir olmaya devam " "edecek. Takım, yeni organizasyondan yapılandırmayı alacak." -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "Organizasyon seç" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "Takımın organizasyonunu değiştir" @@ -360,23 +360,23 @@ msgstr "Tüm değişiklik günlüğünü görüntüle" msgid "dashboard.check-updates.view-release-notes" msgstr "Sürüm notlarını görüntüle" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Çöp kutusunu temizle" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(kopya)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Yeni organizasyon oluştur" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Yeni takım oluştur" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Bu sürümü sabitle" @@ -384,7 +384,7 @@ msgstr "Bu sürümü sabitle" msgid "dashboard.default-team-name" msgstr "Penpot'un" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Silinen tüm projelerinizi ve dosyalarınızı kalıcı olarak silmek " @@ -396,42 +396,42 @@ msgstr "" "%s dosyasını kalıcı olarak silmek istediğinizden emin misiniz? Bu işlem " "geri alınamaz." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s dosya başarıyla silindi." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Kalıcı olarak sil" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Projeyi sil" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "%s projesini kalıcı olarak silmek istediğinizden emin misiniz? Bu projeyi " "ve içindeki tüm dosyaları kalıcı olarak sileceksiniz. Bu işlem geri " "alınamaz." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s başarıyla silindi." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Takımı sil" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "Çöp kutunuz boş. Silinen dosyalar ve projeler burada görünecektir." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "%s'de silinecek" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Penpot dosyasını indir (.penpot)" @@ -443,19 +443,19 @@ msgstr "Kopyasını oluştur" msgid "dashboard.duplicate-multi" msgstr "%s dosyanın kopyasını oluştur" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Bir proje üyesi taslak oluşturduğunda, bu taslak burada gösterilecektir." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Henüz taslak yok." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Bir proje üyesi dosya oluşturduğunda, bu dosya burada gösterilecektir." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Henüz dosya yok." @@ -503,23 +503,23 @@ msgstr ".penpot dosyasını içe aktar" msgid "dashboard.empty-project.start" msgstr "Harika şeyler oluşturmaya başlayın" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "Dosyaları silerken bir hata oluştu." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "%s projesini silerken bir hata oluştu." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "%s dosyasını geri yüklerken bir hata oluştu." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "Dosyaları geri yüklerken bir hata oluştu." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "%s projesini ve dosyalarını geri yüklerken bir hata oluştu." @@ -527,37 +527,37 @@ msgstr "%s projesini ve dosyalarını geri yüklerken bir hata oluştu." msgid "dashboard.export-binary-multi" msgstr "%s Penpot dosyasını indir (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Çalışma yüzeylerini PDF olarak dışa aktar" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "PDF olarak dışa aktar" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "%s / %s öge seçildi" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Dışa aktar" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Tasarım özelliklerinden (sağ kenar çubuğunun alt kısmında) ögelere dışa " "aktarma ayarları ekleyebilirsiniz." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Penpot'ta dışa aktarmaların nasıl ayarlandırılacağı hakkında bilgi." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "Dışa aktarma ayarlarına sahip hiçbir öge yok." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Seçimi dışa aktar" @@ -581,35 +581,35 @@ msgstr "Izgara görünümü" msgid "dashboard.files-layout.list" msgstr "Liste görünümü" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Yazı tipi silindi" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Hepsini kapat" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Yüklediğiniz özel yazı tipleri burada görünecektir." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Henüz özel yazı tipi yok." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Bir proje üyesi özel bir yazı tipi yüklediğinde, bu yazı tipi burada " "gösterilecektir." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "1 yazı tipi eklendi" msgstr[1] "%s yazı tipi eklendi" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -619,7 +619,7 @@ msgstr "" "gruplandırılacak. Yazı tiplerini şu biçimlerde yükleyebilirsiniz: **TTF, " "OTF ve WOFF** (yalnızca bir tane gerekli olacak)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -629,11 +629,11 @@ msgstr "" "[yazı tipi lisanslama](https://www.typography.com/faq) hakkında daha fazla " "bilgi almak isteyebilirsiniz." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Tümünü karşıya yükle" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -644,23 +644,23 @@ msgstr "" "hataları düzeltmek için [Transfonter](https://transfonter.org/) " "kullanmanızı öneririz. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Yönetici Arayüzüne Git" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "Son değişiklik tarihi %s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Penpot dosyalarını içe aktar" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Oops! Bu dosyayı içeri aktaramadık" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "1 kütüphane, adına göre otomatik olarak bağlandı." @@ -670,119 +670,123 @@ msgstr[1] "%s kütüphane, adına göre otomatik olarak bağlandı." msgid "dashboard.import.bad-url" msgstr "İçe aktarma başarısız oldu. Şablon URL'si yanlış" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "Kütüphane bağlantılarını doğrula" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:814 +msgid "dashboard.import.connect-selected-libraries" +msgstr "Seçilen kütüphaneleri bağla" + +#: src/app/main/ui/dashboard/import.cljs:288 +msgid "dashboard.import.file-name-label" +msgstr "Dosya adı" + +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "Bu dosyayı doğrulayamadık." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Bu dosya bozuk gibi görünüyor." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Tüm dosyalar içe aktarılmadı" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Şu dosyalarda hata var:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Hatalı dosyalar karşıya yüklenmeyecektir." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "Bu dosya işlenirken bir hata oluştu." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "1 dosya başarıyla içeri aktarıldı." msgstr[1] "%s dosya başarıyla içeri aktarıldı." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Bazı dosyalar kaldırılmış geçersiz nesneler içeriyordu." +#: src/app/main/ui/dashboard/import.cljs:815 +msgid "dashboard.import.next-file" +msgstr "Sonraki dosya" + #: src/app/main/ui/dashboard.cljs:265 msgid "dashboard.import.no-perms" msgstr "Bu takıma içe aktarma izniniz yok" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Renkler işleniyor" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Bileşenler işleniyor" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Ortam işleniyor" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Sayfa işleniyor: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Tipografiler işleniyor" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Veriler sunucuya yükleniyor (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Dosya yükleniyor: %s" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "" "Bazı kütüphaneler otomatik olarak bağlanamadı. Her biri için doğru " "kütüphaneyi seçin:" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "Onaylamadan önce kütüphane bağlantılarını gözden geçirin:" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:447 +msgid "dashboard.import.resolve-libraries.connect-to" +msgstr "şuna bağla" + +#: src/app/main/ui/dashboard/import.cljs:441 +msgid "dashboard.import.resolve-libraries.original-library" +msgstr "orijinal kütüphane" + +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "Bağlandı" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:517 +msgid "dashboard.import.summary.manually-linked" +msgstr "elle bağlandı" + +#: src/app/main/ui/dashboard/import.cljs:525 +msgid "dashboard.import.summary.new" +msgstr "Yeni" + +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "Kütüphane seçilmedi" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/import.cljs:522 +msgid "dashboard.import.summary.original" +msgstr "Orijinal" + +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Şu davetleri sileceksiniz:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Şu davetleri yeniden göndereceksiniz:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Davetleri sil" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Davetleri yeniden gönder" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "Bu takıma kişileri davet etme, davetleri düzenleme veya silme izniniz yok." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "İnsanları davet et" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "Bu takıma kişi davet etme izniniz yok" @@ -794,29 +798,29 @@ msgstr "Organizasyondan ayrıl" msgid "dashboard.leave-organization.toast" msgstr "Artık %s organizasyonunun bir parçası değilsiniz." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Takımdan ayrıl" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Kütüphaneler ve Şablonlar" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "" "Burada projenize ekleyebileceğiniz bazı kütüphaneler ve şablonlar " "bulunmaktadır" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Daha fazlasını keşfedin ve nasıl katkıda bulunacağınızı öğrenin" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Şablon içe aktarılırken bir sorun oluştu. Şablon içe aktarılmadı." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Paylaşılan Kütüphaneler" @@ -824,7 +828,7 @@ msgstr "Paylaşılan Kütüphaneler" msgid "dashboard.loading-files" msgstr "dosyalarınız yükleniyor …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "yazı tipleriniz yükleniyor…" @@ -844,61 +848,65 @@ msgstr "%s dosyayı şuraya taşı" msgid "dashboard.move-to-other-team" msgstr "Başka takıma taşı" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Yeni Dosya" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Yeni Dosya" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Yeni Proje" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Yeni Proje" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "\"%s\" için sonuç bulunamadı" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "Hiçbir organizasyonunuza takım ekleme izniniz yok." -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:268 +msgid "dashboard.no-organizations-yet" +msgstr "Henüz organizasyon yok." + +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "%s organizasyonu içinde takım oluşturmanıza izin verilmiyor. Daha fazla " "bilgiye ihtiyacınız varsa, organizasyonun sahibiyle iletişime geçin." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "%s organizasyonuna ait takımları silmenize izin verilmiyor. Daha fazla " "bilgiye ihtiyacınız varsa, organizasyonun sahibiyle iletişime geçin." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "%s organizasyonuna ait takımları taşımanıza izin verilmiyor. Daha fazla " "bilgiye ihtiyacınız varsa, organizasyonun sahibiyle iletişime geçin." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Sabitlenmiş projeler burada görünür" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Bildirimler" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "E-posta adresiniz başarıyla güncellendi" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "E-posta adresin başarıyla doğrulandı" @@ -910,11 +918,11 @@ msgstr "Bildirim ayarları güncellendi" msgid "dashboard.notifications.password-saved" msgstr "Parola başarıyla kaydedildi!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Bildirimleri görüntüle" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s üye" @@ -922,23 +930,23 @@ msgstr "%s üye" msgid "dashboard.open-in-new-tab" msgstr "Dosyayı yeni sekmede aç" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Seçenekler" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Role göre sırala" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Duruma göre sırala" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "%s organizasyonu silindi." -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "Diğer takımlar" @@ -962,11 +970,11 @@ msgstr "Artık bu takımın sahibisiniz." msgid "dashboard.permissions-change.viewer" msgstr "Artık bu takımda bir görüntüleyicisiniz." -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "Kişisel Projeler" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Sabitle/Sabitleme" @@ -982,27 +990,27 @@ msgstr "Eklenti bildirim dosyası ayrıştırılamıyor" msgid "dashboard.plugins.try-plugin" msgstr "Eklentiyi deneyin: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "Dosyalar siliniyor…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "Dosyalar geri yükleniyor…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "Silme işlemi beklenmedik şekilde yavaş" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "Geri yükleme işlemi beklenmedik şekilde yavaş" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Projeler" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Hesabınızı kaldırmak mı istiyorsunuz?" @@ -1010,116 +1018,116 @@ msgstr "Hesabınızı kaldırmak mı istiyorsunuz?" msgid "dashboard.removed-from-team" msgstr "Artık “%s” takımının bir parçası değilsiniz." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "" "Tüm projelerinizi ve dosyalarınızı geri yükleyeceksiniz. Bu işlem biraz " "zaman alabilir." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Tüm projeleri ve dosyaları geri yükle" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Tümünü Geri Yükle" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s dosya başarıyla geri yüklendi." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Projeyi geri yükle" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "%s projesini ve içindeki tüm dosyaları geri yükleyeceksiniz." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Projeyi Geri Yükle" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s başarıyla geri yüklendi." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Ayarları kaydet" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Ara…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "“%s“ aranıyor…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Organizasyonlar" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "Takımlar" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "ORGANİZASYONA EKLE" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "Organizasyon seç:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "" "\"%s\", yalnızca organizasyon üyelerinin takımlarına katılmasına izin " "veriyor. Organizasyon dışındaki kişilere gönderilmiş bekleyen davetler " "iptal edilecektir." -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "Dış kullanıcılara gönderilen bekleyen davetler iptal edilecektir." -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "" "Burada, takım oluşturmanıza veya eklemenize izin verilen tüm " "organizasyonlarınızı bulabilirsiniz." -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "" "Burada, parçası olduğunuz ve takım oluşturmanıza veya eklemenize izin " "verilen organizasyonları bulabilirsiniz." -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "Organizasyon seç" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "Takımı bir organizasyona ekle" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Arayüz dilini seç" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Tema seç" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Koyu (öntanımlı)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Açık" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Sistem teması" @@ -1183,11 +1191,11 @@ msgstr "Ayarları güncelle" msgid "dashboard.settings.notifications.title" msgstr "Bildirimler" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Tüm dosyaları göster" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Sürüm geçmişi" @@ -1211,7 +1219,7 @@ msgstr[1] "Dosyalarınız başarıyla kopyalandı" msgid "dashboard.success-duplicate-project" msgstr "Projenin kopyası başarıyla oluşturuldu" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Dosyan başarıyla taşındı" @@ -1223,108 +1231,108 @@ msgstr "Dosyaların başarıyla taşındı" msgid "dashboard.success-move-project" msgstr "Projen başarıyla taşındı" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s artık %s organizasyonuna ait" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Takım bilgisi" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Takım üyeleri" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s artık %s organizasyonuna ait değil" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Takım organizasyonu" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Organizasyona ekle" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "Bu takımı hiçbir organizasyonunuza ekleme izniniz yok." -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Takım organizasyonunu değiştir" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "Yalnızca organizasyonunuzdaki kişiler davet edilebilir." -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "Bu takım herhangi bir organizasyonun parçası değil" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Takımı organizasyondan kaldır" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Takım projeleri" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Projenize ekleyin" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Kullanıcı arayüzü teması" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Arama sonuçları" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Silinen dosyaların çöp kutusunda kalacağı süre" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s gün " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "Bundan sonra, kalıcı olarak silinecektir." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Fikrinizi değiştirirseniz, her dosyanın menüsünden bunları geri " "yükleyebilir veya kalıcı olarak silebilirsiniz." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Aramak için yazın" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Kütüphaneyi Yayından Kaldır" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Ayarları güncelle" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "Artık %s organizasyonunun üyesi değilsiniz" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Beta" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "WebGL görselleştirme performansı iyileştirebilir, ancak bu özellik henüz " @@ -1332,76 +1340,76 @@ msgstr "" "(SVG/PDF) ve görüntüleme modu arasında bazı görsel farklılıklar ortaya " "çıkabilir." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Devre dışı" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Etkin" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Geri bildirimde bulun" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Durum" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "WebGL görselleştirme" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Etkin" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Bu kanca tetiklendiğinde olay ayrıntıları iletilecektir" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "" "Yalnızca sizin oluşturduğunuz web kancalarını silebilir veya " "değiştirebilirsiniz." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "İçerik türü" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Web kancası oluştur" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Web kancası başarıyla oluşturuldu." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Web kancaları, Penpot'ta belirli olaylar gerçekleştiğinde diğer web " "sitelerinin ve uygulamaların bilgilendirilmesine izin vermenin basit bir " "yoludur. Belirttiğiniz URL'lerin her birine bir POST isteği göndereceğiz." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Bir tane eklemek için \"Web kancası ekle\" düğmesine basın." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "Şimdiye kadar web kancası oluşturulmadı." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Hesabınız" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "E-posta" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Adın" @@ -1417,11 +1425,11 @@ msgstr "Dikkat" msgid "ds.component-subtitle" msgstr "Güncellenecek bileşenler:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "İzin ver" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "İptal" @@ -1441,7 +1449,7 @@ msgstr "Etkin kümelerde veya temalarda geçerli token yok." msgid "ds.inputs.numeric-input.no-matches" msgstr "Eşleşme bulunamadı." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Token listesini aç" @@ -1449,6 +1457,12 @@ msgstr "Token listesini aç" msgid "ds.inputs.token-field.no-active-token-option" msgstr "{%s} herhangi bir etkin kümede bulunmuyor veya geçersiz bir değere sahip." +#: src/app/main/ui/auth/login.cljs:113 +msgid "errors.account-locked" +msgstr "" +"Çok fazla başarısız deneme yapıldı. Hesabınız %s dakika kilitlendi. Lütfen " +"daha sonra tekrar deneyin." + #: src/app/main/data/auth.cljs:344 msgid "errors.auth-provider-not-allowed" msgstr "Kimlik doğrulama sağlayıcısına bu profil için izin verilmiyor" @@ -1457,15 +1471,15 @@ msgstr "Kimlik doğrulama sağlayıcısına bu profil için izin verilmiyor" msgid "errors.auth-provider-not-configured" msgstr "Kimlik doğrulama sağlayıcısı yapılandırılmadı." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Kimliğiniz doğrulanmamış veya oturumun süresi dolmuş gibi görünüyor." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "%s yazı tipi yüklenemedi" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "%s yazı tipleri yüklenemedi" @@ -1477,57 +1491,61 @@ msgstr "Medya dosyası yüklenemedi." msgid "errors.character-limit-exceeded" msgstr "Karakter sınırı aşıldı" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "Kütüphane eklenemiyor: bu, döngüsel bir bağımlılık oluşturur" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "Pano API'si kullanılabilir değil. Pano erişimini etkinleştirmek için " "Penpot'u HTTPS üzerinden sunun" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Tarayıcın bu işlemi gerçekleştiremiyor" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "Pano erişimi reddedildi. İçeriği yapıştırmak için lütfen tarayıcınızda pano " "izinlerini etkinleştirin" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "Yorumla ilgili bir hata oluştu" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:586 +msgid "errors.connection-error" +msgstr "Sunucuya erişilemiyor. Bağlantınızı gözden geçirin ve tekrar deneyin." + +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Üzgünüz! Bu, artık kullanılmayan bir Penpot varlık türü kullanan eski bir " "dosyadır ve açamazsınız." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "böylece size yardımcı olabiliriz." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Penpot artık bu tür varlıkları desteklemese de, bizimle" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "iletişime geçebilirsiniz" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Bir üst kümeyi kendi alt kümesine bırakamazsınız." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "E-posta zaten kullanımda" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "E-posta zaten doğrulandı." @@ -1539,29 +1557,29 @@ msgstr "E-postanızı parola olarak kullanamazsınız" msgid "errors.email-does-not-match-invitation" msgstr "E-posta adresi davetiyeyle eşleşmiyor." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Etki alanına izin verilmiyor" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "«%s» adresi için çok fazla geri dönme raporu var." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "«%s» e-postasının spam veya kalıcı olarak geri döndüğü bildirildi." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Görünüşe göre '%s' özelliğinin etkin olduğu bir dosyayı açıyorsunuz, ancak " "şu anki penpot sürümü bunu desteklemiyor veya devre dışı bırakıldı." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "'%s' özelliği desteklenmiyor." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "En fazla %s karakter içermelidir." @@ -1569,7 +1587,7 @@ msgstr "En fazla %s karakter içermelidir." msgid "errors.field-min-length" msgstr "En az 1 karakter içermelidir." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Boş alan" @@ -1577,24 +1595,24 @@ msgstr "Boş alan" msgid "errors.field-not-all-whitespace" msgstr "Ad boşluk dışında bir karakter içermelidir." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Etkinleştirilen özellikler ile açmaya çalıştığınız dosyanın özellikleri " "arasında bir uyumsuzluk var gibi görünüyor. Dosyanın açılabilmesi için önce " "'%s' için geçişlerin uygulanması gerekiyor." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "Yazı tipi ailesi adı yalnızca harfler, rakamlar, boşluklar, tireler, alt " "çizgiler ve noktalardan oluşabilir." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Bir şeyler ters gitti." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Web çalıştırıcısında bir sorun oluştu." @@ -1610,7 +1628,7 @@ msgstr "Geçersiz veri" msgid "errors.invalid-email" msgstr "Lütfen geçerli bir e-posta adresi girin" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Doğrulama e-postası eşleşmiyor" @@ -1646,11 +1664,11 @@ msgstr "Bu davet iptal edilmiş veya süresi dolmuş olabilir." msgid "errors.ldap-disabled" msgstr "LDAP ile oturum açma devre dışı bırakıldı." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "'%s' sınırına ulaştınız. Destek ile iletişime geçin." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "Tek bir istekte davet edilebilecek en fazla (%s) e-posta sayısına ulaşıldı" @@ -1666,21 +1684,21 @@ msgstr "Görselin içeriği, dosya uzantısı ile eşleşmiyor gibi görünüyor msgid "errors.media-type-not-allowed" msgstr "Geçerli bir görsel gibi görünmüyor." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Davet ettiğiniz profilin e-posta adresine ait çok fazla geri dönme raporu " "var veya spam olarak bildirilmiş." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Geçiş devam ediyor" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Yalnızca sürüm oluşturucu onu kilitleyebilir" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Yalnızca sürüm oluşturucu onun kilidini açabilir" @@ -1692,7 +1710,7 @@ msgstr "Organizasyondan ayrılma sırasında bir hata oluştu. Lütfen tekrar de msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "Organizasyon sahibi organizasyondan ayrılamaz." -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "Bu organizasyon yok" @@ -1704,7 +1722,7 @@ msgstr "Parolalar eşleşmedi" msgid "errors.password-too-short" msgstr "Parola en az 8 karakterden oluşmalı" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Panoda geçersiz veri" @@ -1712,7 +1730,7 @@ msgstr "Panoda geçersiz veri" msgid "errors.profile-blocked" msgstr "Profil engellendi" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Profilinizde sessize alınmış e-postalar var (spam raporları veya yüksek " @@ -1722,7 +1740,7 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Kayıt olma şu anda devre dışı." -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:700 msgid "errors.save-failed" msgstr "" "En son yaptığınız değişiklikler kaydedildi olarak onaylanmadı. Kaydetme " @@ -1730,19 +1748,23 @@ msgstr "" "yüklemeyin: değişiklikleriniz kaybolabilir. Yardım için destek ile " "iletişime geçin." -#: src/app/main/errors.cljs:764 +#: src/app/main/data/persistence.cljs:447 +msgid "errors.save-retrying" +msgstr "Bağlantı kesildi. Değişikliklerinizi kaydetmek için yeniden deneniyor." + +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG geçersiz veya biçimi yanlış" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Uyumsuz '%s' özelliği algılandı" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "Takımdan ayrılmak için yeterli üye yok, onu silmek isteyebilirsiniz." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Atamaya çalıştığınız üye mevcut değil." @@ -1750,7 +1772,7 @@ msgstr "Atamaya çalıştığınız üye mevcut değil." msgid "errors.team-leave.only-owner-can-delete" msgstr "Bir takımı yalnızca sahibi silebilir." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Sahip takımdan ayrılamaz, sahip rolünü yeniden atamanız gerekir." @@ -1762,11 +1784,11 @@ msgstr "Takım adı şu karakterleri içeremez:'.', ':' veya '/'" msgid "errors.token-set-already-exists" msgstr "Aynı ada sahip bir küme zaten var" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "Bırakma işlemi tamamlanamıyor, aynı ada sahip bir küme zaten bu yolda var." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Aynı ada sahip tema seçeneği var" @@ -1778,13 +1800,13 @@ msgstr "Tema, var olmayan bazı kümelere referans veriyor: %s" msgid "errors.tokens.circular-reference" msgstr "Tokende döngüsel referans var" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "" "Satır yüksekliği yazı tipi boyutuna bağlıdır. Çözülen değeri elde etmek " "için bir yazı tipi boyutu ekleyin." -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "Bu alan boş olamaz" @@ -1804,11 +1826,11 @@ msgstr "İçe Aktarma Hatası:" msgid "errors.tokens.invalid-color" msgstr "Geçersiz renk değeri: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "Geçersiz token değeri: yalnızca font-family tokenine referans verebilirsiniz" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "" "Geçersiz yazı tipi kalınlığı değeri: sayısal değerler (100-950) veya " @@ -1830,39 +1852,39 @@ msgstr "" "Token adları yalnızca . karakterleriyle ayrılan harfler ve rakamlar " "içermeli ve $ işaretiyle başlamamalıdır." -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "Geçersiz gölge türü: yalnızca 'innerShadow' veya 'dropShadow' kabul edilir" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "" "Geçersiz token değeri: yalnızca none, Uppercase, Lowercase veya Capitalize " "kabul edilir" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "" "Geçersiz token değeri: yalnızca none, underline ve strike-through kabul " "edilir" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "Geçersiz değer: bileşik gölge tokenine referans vermelidir." -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "Geçersiz değer: bileşik tipografi tokenine referans vermelidir." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Geçersiz token değeri: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Eksik token referansları: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "%s grup adı, başka bir etkin kümedeki aynı adlı bir tokenle çakışıyor." @@ -1874,31 +1896,31 @@ msgstr "Bu dosyada token, küme veya tema bulunamadı." msgid "errors.tokens.number-too-large" msgstr "Geçersiz token değeri. Çözülen değer çok büyük: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "Opaklık 0 ile %100 veya 0 ile 1 arasında olmalıdır (örneğin %50 veya 0.5)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Tokenin kendine referansı var" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "Gölge bulanıklığı 0 veya 0'dan büyük olmalıdır." -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "Gölge yayılımı 0 veya 0'dan büyük olmalıdır." -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "Çerçeve genişliği 0'dan büyük veya 0 olmalıdır." -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "Geçersiz değer: % izin verilmiyor." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Geçersiz değer: Birimlere izin verilmiyor." @@ -1906,19 +1928,19 @@ msgstr "Geçersiz değer: Birimlere izin verilmiyor." msgid "errors.unexpected-error" msgstr "Beklenmedik bir hata oluştu." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Bilinmeyen jeton" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Bu sürüm zaten kilitli" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Bu sürüm kilitlidir ve başkaları tarafından silinemez" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Dosyanın uyumsuz bir sürüm numarası var" @@ -1951,39 +1973,39 @@ msgstr "En az 1 büyük harf" msgid "errors.weak-password.too-short" msgstr "En az 8 karakter" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL çalışmayı durdurdu. Sıfırlamak için lütfen sayfayı yeniden yükleyin" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "Oops! Tuval içeriği kayboldu" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Bağlantı hatası, URL'ye erişilemiyor" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL doğrulamayı geçemiyor." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Son iletim başarılı olmadı." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "SSL doğrulamasında hata." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Zaman aşımı" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Doğrulamada beklenmeyen hata" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Beklenmeyen durum %s" @@ -2003,17 +2025,17 @@ msgstr "Açıklama" msgid "feedback.description-placeholder" msgstr "Lütfen geri bildiriminizin nedenini açıklayın" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Burada olmanızdan mutluluk duyuyoruz. Yardıma ihtiyacınız varsa, lütfen " "göndermeden önce arama yapın." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot topluluğu" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Bizimle iletişime geçmenin diğer yolları" @@ -2037,11 +2059,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Bizimle iletişime geçin" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Teknik sorularınıza yardımcı olmak için buradayız." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X destek hesabı" @@ -2061,45 +2083,41 @@ msgstr "Fikir" msgid "feedback.type.issue" msgstr "Sorun" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Dosyaları indir" - -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "Bağlantılı kütüphane varlıkları dosyaya dahil edilmeyecek." -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "Varlıkları temel nesneler olarak değerlendir" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "" "Bağlantılı kütüphaneler içeren dosyalar, bağlantıları korunacak şekilde " "dışa aktarıma dahil edilecek. " -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "Dosyayı ve kütüphaneleri dışa aktar" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "" "İçe aktarıldığında, aynı ada sahip var olan kütüphaneleri birbirine " "bağlayabileceksiniz." -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "İçe aktarımda eşleşen kütüphaneleri bağla" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "" "Dosyanız, kütüphane varlığı yerel kütüphaneye birleştirilmiş olarak dışa " "aktarılacak." -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "Kütüphane varlıklarını dosyaya göm" @@ -2107,7 +2125,7 @@ msgstr "Kütüphane varlıklarını dosyaya göm" msgid "generic.error" msgstr "Bir hata oluştu" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "Uygula" @@ -2155,7 +2173,7 @@ msgstr "Şeklin dolgu görselinin ön izlemesi" msgid "inspect.attributes.shadow" msgstr "Gölge" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Boyut ve konum" @@ -2163,43 +2181,31 @@ msgstr "Boyut ve konum" msgid "inspect.attributes.stroke" msgstr "Çerçeve" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Merkezi" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "İçinde" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Dışarıda" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Tipografi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Yazı Tipi Ailesi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Yazı Tipi Boyutu" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Yazı Tipi Biçimi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Harf Aralığı" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Satır Yüksekliği" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "Karışık Yazı Tipi Aileleri" @@ -2207,15 +2213,19 @@ msgstr "Karışık Yazı Tipi Aileleri" msgid "inspect.attributes.typography.text-decoration.none" msgstr "Hiçbiri" +#: src/app/main/ui/inspect/attributes/text.cljs:117 +msgid "inspect.attributes.typography.text-decoration.strikethrough" +msgstr "Üstü çizili" + #: src/app/main/ui/inspect/attributes/text.cljs:115 msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Altı Çizili" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "İlk Harfleri Büyük" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Küçük Harf" @@ -2227,7 +2237,7 @@ msgstr "Hiçbiri" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Ayarlanmadı" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Büyük Harf" @@ -2275,6 +2285,10 @@ msgstr "Kopyala" msgid "inspect.subtitle.main" msgstr "Ana bileşen" +#: src/app/main/ui/inspect/right_sidebar.cljs:176 +msgid "inspect.tabs-switcher-label" +msgstr "Sekme incelemeye geç" + #: src/app/main/ui/inspect/right_sidebar.cljs:142 msgid "inspect.tabs.code.selected.circle" msgstr "Daire" @@ -2351,23 +2365,23 @@ msgstr "Çözülen değer:" msgid "inspect.tabs.styles.variants-panel" msgstr "Çeşit özellikleri" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "Yeni erişim tokeni oluştur" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "Bir tane oluşturmak için \"Yeni erişim tokeni oluştur\" düğmesine basın." -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "Şu ana kadar hiç tokeniniz yok." -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "Kişisel erişim tokenleri" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "" "Kişisel erişim tokenleri, kullanıcı adı/parola kimlik doğrulama sistemimize " @@ -2452,53 +2466,53 @@ msgstr "MCP anahtarının geçerlilik süresi %s tarihinde sona erecek" msgid "integrations.mcp-key.will-not-expire" msgstr "MCP anahtarının geçerlilik süresinin sonu yok" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "" "Penpot MCP Sunucusu, MCP istemcilerinin Penpot tasarım dosyalarıyla " "doğrudan etkileşime girmesini sağlar." -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "MCP istemcileri nasıl yapılandırılır" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "Bu, MCP istemcinizi Penpot MCP sunucusuna bağlamak için yapılandırmanız " "gereken sunucu adresidir." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "MCP anahtarını yeniden oluştur" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "MCP anahtarı" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "MCP istemcisinin kurulumu için MCP anahtarı gereklidir" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Durum" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Devre dışı" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Etkin" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "" "MCP sunucusuna bağlanmak için kullanılan MCP anahtarının geçerlilik süresi " "doldu. Bu nedenle bağlantı kurulamıyor." -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "" "Lütfen MCP anahtarını yeniden oluşturun ve istemci yapılandırmanızı yeni " @@ -2508,10 +2522,6 @@ msgstr "" msgid "integrations.mcp-server.title" msgstr "MCP Sunucusu" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "Beta" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "Ad" @@ -2562,7 +2572,7 @@ msgstr "MCP anahtarını yeniden oluştur" msgid "integrations.regenerate-mcp-key.title.created" msgstr "MCP anahtarı yeniden oluşturuldu" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "Bütünleşmeler" @@ -2580,47 +2590,47 @@ msgstr "Tokenin geçerlilik süresi %s tarihinde sona erecek" msgid "integrations.token.will-not-expire" msgstr "Tokenin geçerlilik süresinin sonu yok" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Tümünü okundu olarak işaretle" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Kısayollar" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Penpot hakkında" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Kabul et" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Etkin" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Ekle" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Özel yazı tipi ekle" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Ekleniyor..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Yönetici" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Hepsi" @@ -2628,17 +2638,17 @@ msgstr "Hepsi" msgid "labels.and" msgstr "ve" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Geri" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Görünüşe göre biraz beklemen ve yeniden denemen gerekiyor; sunucularımızda " "küçük bir bakım yapıyoruz." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Hatalı Ağ Geçidi" @@ -2650,23 +2660,23 @@ msgstr "Bulanıklık" msgid "labels.blur-effects" msgstr "Bulanıklık efektleri" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "İptal" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "Güncellemeleri denetle" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "Güncellemeler denetleniyor..." -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Kapat" @@ -2674,7 +2684,7 @@ msgstr "Kapat" msgid "labels.code" msgstr "Kod" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Daralt" @@ -2686,15 +2696,15 @@ msgstr "Renk" msgid "labels.comment" msgstr "Yorum" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Yorumlar" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Topluluk" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Topluluk ve Katkılar" @@ -2706,19 +2716,19 @@ msgstr "Hesaplanan" msgid "labels.confirm-password" msgstr "Parolayı onayla" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Destek için iletişime geçin" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Bizimle iletişime geçin" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Devam et" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Penpot hesabıyla devam edebilirsiniz" @@ -2730,23 +2740,23 @@ msgstr "Kopyala" msgid "labels.copy-color" msgstr "Rengi kopyala" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Bağlantıyı kopyala" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-şimdiye kadar" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Oluştur" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Yeni takım oluştur" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Yeni takım adı gir" @@ -2754,7 +2764,7 @@ msgstr "Yeni takım adı gir" msgid "labels.current" msgstr "Geçerli" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Denetim paneli" @@ -2762,7 +2772,7 @@ msgstr "Denetim paneli" msgid "labels.default" msgstr "Öntanımlı" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Sil" @@ -2774,7 +2784,7 @@ msgstr "Yorumu sil" msgid "labels.delete-comment-thread" msgstr "Mesaj dizisini sil" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Daveti sil" @@ -2782,39 +2792,35 @@ msgstr "Daveti sil" msgid "labels.delete-multi-files" msgstr "%s dosyayı sil" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "Profil fotoğrafınızı silmek istediğinizden emin misiniz?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "Profil fotoğrafını sil" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Silindi" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Geliştirici" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "At" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "%s indir" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "İndir" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "Dosya indiriliyor…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Taslak" @@ -2822,19 +2828,19 @@ msgstr "Taslak" msgid "labels.duplicate" msgstr "Çoğalt" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Düzenle" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Düzenleyici" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Boş" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Hata" @@ -2842,11 +2848,11 @@ msgstr "Hata" msgid "labels.exit" msgstr "Çıkış" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Süresi doldu" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Dışa aktar" @@ -2858,7 +2864,7 @@ msgstr "Geri bildirim devre dışı bırakıldı" msgid "labels.feedback-sent" msgstr "Geri bildirim gönderildi" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2866,63 +2872,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "Doldur" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "Esnek düzen" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Yazı Tipi Ailesi" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Biçimler" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Yazı tipleri" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Github deposu" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Geri bildirimde bulun" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Geri dön" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Grafik tasarımı" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "Izgara düzen" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Yardım Merkezi" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Yardım ve Öğrenme" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Gizle" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Çözülen yorumları gizle" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "İçe aktar" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Etkin değil" @@ -2930,53 +2936,53 @@ msgstr "Etkin değil" msgid "labels.info" msgstr "Bilgi" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Kurulu yazı tipleri" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "Bütünleşmeler" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Kötü bir şey oldu." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "" "İşlemi yeniden deneyebilir veya hatayı bildirmek için destek ekibiyle " "iletişime geçebilirsiniz." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "İç Hata" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Davetler" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Dil" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Yerleşim düzeni" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Öğrenme Merkezi" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Kütüphaneler ve Şablonlar" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Yükleniyor…" @@ -2984,7 +2990,7 @@ msgstr "Yükleniyor…" msgid "labels.lock" msgstr "Kilitle" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Oturum açın veya kaydolun" @@ -2992,19 +2998,19 @@ msgstr "Oturum açın veya kaydolun" msgid "labels.login" msgstr "Oturum aç" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Oturumu kapat" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Pazarlama" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Üye" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Üyeler" @@ -3016,41 +3022,41 @@ msgstr "Değinme" msgid "labels.new-password" msgstr "Yeni parola" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Sonraki" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "Bu örnek, en son sürümle birlikte gelen bir kurulum adımına ihtiyaç duyuyor." -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "Göz atın" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "Penpot henüz başlatılamıyor." -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "teknik kılavuz" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "" "Penpot, bir sistem sorunu nedeniyle geçici olarak kullanılamıyor. Lütfen " "biraz sonra tekrar deneyin." -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Hepsini bitirdiniz! Yeni yorum bildirimleri burada görünecektir." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "Bekleyen davetiye yok." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Arkadaşlarınızı toplayın ve birlikte harika şeyler oluşturun." @@ -3058,27 +3064,27 @@ msgstr "Arkadaşlarınızı toplayın ve birlikte harika şeyler oluşturun." msgid "labels.no-matches" msgstr "Eşleşme yok" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Oops!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Bildirimler" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 dosya" msgstr[1] "%s dosya" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 çalışma yüzeyi" msgstr[1] "%s çalışma yüzeyi" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 proje" @@ -3088,43 +3094,43 @@ msgstr[1] "%s proje" msgid "labels.old-password" msgstr "Eski parola" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "Aç" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Seçenekler" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Diğer (lütfen belirtiniz)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Diğer" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Sahip" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Parola" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Bekliyor" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Penpot Değişiklik Günlüğü" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot merkezi" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Sabitlenen Projeler" @@ -3132,31 +3138,31 @@ msgstr "Sabitlenen Projeler" msgid "labels.post" msgstr "Gönder" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Önceki" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Ürün veya kullanıcı deneyimi tasarımı" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Ürün veya Proje yöneticisi" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Profil" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Projeler" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Son kullanılanlar" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Referans" @@ -3164,27 +3170,27 @@ msgstr "Referans" msgid "labels.refresh" msgstr "Yenile" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Sürüm notları" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Sayfayı yeniden yükle" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Kaldır" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Üyeyi kaldır" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Yeniden adlandır" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Takımı yeniden adlandır" @@ -3208,11 +3214,11 @@ msgstr "yeni yanıt" msgid "labels.reply.thread" msgstr "Yanıtla" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Yeniden gönder" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Daveti yeniden gönder" @@ -3220,43 +3226,43 @@ msgstr "Daveti yeniden gönder" msgid "labels.restore" msgstr "Geri yükle" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Yeniden dene" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Rol" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Kaydet" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Ara" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Yazı tipi ara" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Bir seçenek belirleyin" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Gönder" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Gönderiliyor…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Sistemlerimizin programlı bakımını yapıyoruz." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Hizmet Kullanılamıyor" @@ -3264,70 +3270,74 @@ msgstr "Hizmet Kullanılamıyor" msgid "labels.sets" msgstr "Kümeler" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Ayarlar" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Gölge" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Paylaş" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Paylaşılan Kütüphaneler" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Göster" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Tüm yorumları göster" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Yorum listesini göster" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Yalnızca değinmelerinizi göster" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Yalnızca kendi yorumlarımı göster" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/import.cljs:808 +msgid "labels.skip" +msgstr "Atla" + +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Kaynaklar" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "" "Organizasyonunuzun kimlik sağlayıcısıyla oturum açma işlemi tamamlanmadı. " "Sağlayıcı şu anda kullanılamıyor olabilir veya hesabınız henüz sağlayıcının " "dizininde yer almıyor olabilir." -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "Tekrar deneyin" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "%s için oturum açamadık" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Başla" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Durum" @@ -3335,7 +3345,7 @@ msgstr "Durum" msgid "labels.stroke" msgstr "Çerçeve" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Öğrenci veya öğretmen" @@ -3347,27 +3357,31 @@ msgstr "Biçimler" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Değiştir" +#: src/app/main/ui/dashboard/organization_team_switch.cljs:657 +msgid "labels.team-management" +msgstr "Takım yönetimi" + #: src/app/main/ui/inspect/styles/style_box.cljs:30 msgid "labels.text" msgstr "Metin" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Temalar" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Öğreticiler" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Tipografi" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Bilinmeyen hata" @@ -3379,35 +3393,31 @@ msgstr "Kilidi aç" msgid "labels.unpublish-multi-files" msgstr "%s dosyayı yayından kaldır" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Güncelle" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Takımı güncelle" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Yükle" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Özel yazı tipi yükle" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Yükleniyor…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "Dosya karşıya yükleniyor…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Çeşit" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Sürüm %s notları" @@ -3415,7 +3425,7 @@ msgstr "Sürüm %s notları" msgid "labels.view-only" msgstr "Yalnızca görüntüle" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Görüntüleyici" @@ -3429,7 +3439,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s uyarı" msgstr[1] "%s uyarı" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Web kancaları" @@ -3437,11 +3447,11 @@ msgstr "Web kancaları" msgid "labels.write-new-comment" msgstr "Yeni yorum yaz" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(siz)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Hesabınız" @@ -3584,7 +3594,7 @@ msgstr "" "Bazı takımların tek üyesi sizsiniz. Bu takımlar, projeleriyle ve " "dosyalarıyla birlikte silinecek." -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "ORGANİZASYONDAN AYRILMADAN ÖNCE" @@ -3592,80 +3602,80 @@ msgstr "ORGANİZASYONDAN AYRILMADAN ÖNCE" msgid "modals.before-leave-organization.warning" msgstr "Tek üyesi olduğunuz tüm takımlar silinecektir." -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Büyük dürtme" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Yeni e-postayı doğrulayın" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "“%s” e-posta adresinize kimliğinizi doğrulamak için bir e-posta " "göndereceğiz." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Yeni e-posta" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "E-postayı değiştir" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "E-postanızı değiştirin" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Web kancası oluştur" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Web kancası oluştur" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Yük URL'si" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "İptal et ve hesabımı koru" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Evet, hesabımı sil" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Hesabını silerek tüm projelerini ve arşivlerini kaybedeceksin." -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "" "Bu işlem geri alınamaz. Hesabınız, aboneliğiniz ve organizasyonlarınız " "hemen silinecektir. Ayrıca var olan projelerinize ve dosyalarınıza " "erişiminizi de kaybedeceksiniz." -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "Silinecek organizasyonlar" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s üye" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s takım" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Hesabını silmek istediğinden emin misin?" @@ -3725,23 +3735,23 @@ msgstr "%s dosyayı silmek istediğinden emin misin?" msgid "modals.delete-file-multi-confirm.title" msgstr "%s dosyayı sil" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Bu yazı tipi biçimini silmek istediğinizden emin misiniz? Bir dosyada " "kullanılıyorsa yüklenmeyecektir." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Yazı tipi biçimini sil" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Bu yazı tipini silmek istediğinize emin misiniz? Bir dosyada kullanılıyorsa " "yüklenmeyecektir." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Yazı tipini sil" @@ -3751,19 +3761,19 @@ msgstr "" "%s organizasyonunun parçası olan bu takımı silmek istediğinizden emin " "misiniz?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Bu sayfayı silmek istediğinden emin misin?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Sayfayı sil" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "Seçilen sayfaları silmek istediğinizden emin misiniz?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "Sayfaları sil" @@ -3779,31 +3789,31 @@ msgstr "Bu projeyi silmek istediğinden emin misin?" msgid "modals.delete-project-confirm.title" msgstr "Projeyi sil" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Dosyayı sil" msgstr[1] "Dosyaları sil" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Hiçbir dosyada etkinleştirilmedi." msgstr[1] "Hiçbir dosyada etkinleştirilmediler." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Bu kütüphane burada etkinleştirildi: " msgstr[1] "Bu kütüphaneler burada etkinleştirildiler: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Bu dosyayı silmek istediğinizden emin misiniz?" msgstr[1] "Bu dosyaları silmek istediğinizden emin misiniz?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Dosya siliniyor" @@ -3823,19 +3833,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Takım siliniyor" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Üyeyi sil" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Bu üyeyi takımdan silmek istediğinden emin misin?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Takım üyesini sil" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3845,23 +3855,23 @@ msgstr[1] "" "Bu dosyalarda daha önce kullanılmış olan varlıklar orada kalmaya devam " "edecek (hiçbir tasarım bozulmayacak)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Web kancasını sil" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Bu web kancasını silmek istediğinizden emin misiniz?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Web kancası siliniyor" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Web kancasını düzenle" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Web kancasını düzenle" @@ -3877,15 +3887,15 @@ msgstr "Tokenleri içe aktar" msgid "modals.import-library-tokens.title" msgstr "Kütüphaneden tokenler içe aktarılsın mı?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Davet gönder" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "E-posta adresleri, virgülle ayrılmış" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Bazı e-posta adresleri mevcut takım üyelerine aittir. Davetleri " @@ -3908,99 +3918,99 @@ msgstr "" "kişiler zaten takımın üyeleridir. Daha fazla bilgiye ihtiyacınız varsa, " "organizasyonun sahibiyle iletişime geçin." -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "Davet almayacak adresler" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "İptal" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "Yalnızca organizasyonun var olan üyeleri davet edilebilir." -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "Davetleri gönder" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "Bazı davetler gönderilemiyor" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "Listenizdeki bazı kişiler davet almayacak." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Üyeleri takıma davet ederek bu dosyaya ve tüm takım dosyalarına " "erişmelerini sağlayabilirsiniz." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Üyeleri takıma davet et" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Tek üyesi siz olduğunuz için takım, projeleri ve dosyaları ile birlikte " "silinecektir." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "%s takımından ayrılmak istediğinizden emin misiniz?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Birisini takımın sahibi yapmadan takımı bırakamazsın. Takımı silmek " "isteyebilirsin." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Bu takımın sahibi sizsiniz. Lütfen ayrılmadan önce sahibi olarak belirlemek " "için başka bir üye seçin." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Terfi ettir ve ayrıl" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Terfi ettirmek için bir üye seçin" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Ayrılmadan önce" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Takımdan ayrıl" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Bu takımdan ayrılmak istediğinden emin misin?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Takımdan ayrıl" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "" "Bazı organizasyon takımlarının sahibi sizsiniz. Lütfen başka bir üyeyi " "sahibi olarak atayın." -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "" "Bazı takımların tek üyesi sizsiniz. Bu takımlar, projeleriyle ve " "dosyalarıyla birlikte silinecek." -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "" "Ayrıca, bazı organizasyon takımlarının sahibi sizsiniz. Lütfen başka bir " @@ -4020,57 +4030,57 @@ msgstr "" msgid "modals.leave-organization-confirm.title" msgstr "%s organizasyonundan ayrılıyor musunuz?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Taşı" msgstr[1] "Taşı" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Bu kütüphaneyi taşımak istediğinizden emin misiniz?" msgstr[1] "Bu kütüphaneleri taşımak istediğinizden emin misiniz?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Kütüphaneyi taşı" msgstr[1] "Kütüphaneleri taşı" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Dürtme miktarı" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Sahipliği aktar" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Sahipliği aktarırsanız, rolünüzü Yönetici olarak değiştireceksiniz ve bu " "takım üzerindeki bazı izinleri kaybedeceksiniz. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Bu takımın şu anki sahibi sizsiniz. %s kişisini takımın yeni sahibi yapmak " "istediğinizden emin misiniz?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Yeni takım sahibi" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Yayınla" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Kütüphaneniz boş. Yine de yayınlamak istediğinizden emin misiniz?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Boş kütüphaneyi yayınla" @@ -4092,23 +4102,23 @@ msgstr "'%s' takımını '%s' organizasyonundan kaldırmak istediğinizden emin msgid "modals.remove-team-organization.title" msgstr "TAKIMI ORGANİZASYONDAN KALDIR" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Küçük dürtme" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Yayından kaldır" msgstr[1] "Yayından kaldır" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Bu kütüphaneyi yayından kaldırmak istediğinizden emin misiniz?" msgstr[1] "Bu kütüphaneleri yayından kaldırmak istediğinizden emin misiniz?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Kütüphaneyi yayından kaldır" @@ -4198,38 +4208,38 @@ msgstr "Etkinleştir" msgid "nitrate.code-activation.title" msgstr "Nitrate'yi etkinleştir" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "Satış birimiyle iletişime geçin" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "" "Bu plana geçmek için lütfen satış birimimizle iletişime geçin. " "Aboneliğinizi güncellemenize yardımcı olacak ve her şeyin doğru şekilde " "ayarlandığından emin olacağız." -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "Organizasyonunuz silinecek." -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "" "Toplam depolama alanınız, otomatik sürüm geçmişiniz ve dosya kurtarma " "süreniz sınırlandırılacak." -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "" "Takımlar, projeler ve dosyalar artık hiçbir organizasyonun parçası " "olmayacak, ancak kullanılabilir olmaya devam edecek." -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "Sürüm düşürdüğünüzde:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "%s planına geçmek ister misiniz?" @@ -4247,15 +4257,15 @@ msgstr "Kurumsal planı 14 gün boyunca denemek için bizimle iletişime geçin: msgid "nitrate.form.contact-upgrade" msgstr "Kurumsal plana yükseltmek için bizimle iletişime geçin:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "Kurumsal planı 14 gün boyunca denemek için bizimle iletişime geçin:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "Kurumsal plana geçmek için bizimle iletişime geçin:" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "" "Şu anda herhangi bir ücret tahsil edilmeyecek. Ödeme, deneme süresinin " @@ -4275,7 +4285,7 @@ msgstr "" msgid "nitrate.form.enterprise-feature-3" msgstr "Organizasyonunuzun ihtiyaç duyduğu yönetim ile tam açık kaynak özgürlüğü" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "Takımlarınızın güvenli ve geniş ölçekte çalışabilmesi için gereken her şey%s" @@ -4283,7 +4293,7 @@ msgstr "Takımlarınızın güvenli ve geniş ölçekte çalışabilmesi için g msgid "nitrate.form.enterprise.price" msgstr "üye başına aylık" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "14 gün boyunca ücretsiz deneyin" @@ -4299,7 +4309,7 @@ msgstr "Kurumsal planı kullanmaya başlayın" msgid "nitrate.form.subscribe-with-code" msgstr "Etkinleştirme koduyla abone olun" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "Kurumsal plan özelliklerinin kilidini açın" @@ -4311,7 +4321,7 @@ msgstr "14 gün boyunca ücretsiz deneyin" msgid "nitrate.modal-success.title" msgstr "Kurumsal plana hoş geldiniz!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "%s tarihine kadar etkin" @@ -4319,23 +4329,23 @@ msgstr "%s tarihine kadar etkin" msgid "nitrate.subscription.renew-success" msgstr "Nitrate aboneliğiniz yenilendi." -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "Aboneliği iptal et" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "Nitrate aboneliğinizi iptal etmek için lütfen bizimle iletişime geçin:" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "Etkinleştirme koduyla yenile" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Bu sayfa yok" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 hatası" @@ -4355,11 +4365,11 @@ msgstr "" "E- posta kutunuza bakın ve yeni bir parola oluşturmak için bağlantıya " "tıklayın." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Ücretsiz kaydolun" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "Ve saniyeler içinde Penpot kullanmaya başlayın!" @@ -4367,67 +4377,67 @@ msgstr "Ve saniyeler içinde Penpot kullanmaya başlayın!" msgid "not-found.made-with-love" msgstr "Aşkla ve Açık Kaynakla Yapıldı" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Bu dosyaya zaten erişim talep ettiniz." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Bu dosyaya veya bu takımın diğer dosyalarına veya projelerine zaten erişim " "talep ettiniz." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Bu projeye veya bu takımın diğer projelerine veya dosyalarına zaten erişim " "talep ettiniz." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Bu projeye zaten erişim talep ettiniz." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "ERİŞİM İSTE" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Unutmayın, sahibi izin verirse takıma davet edileceksiniz." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Talebiniz doğru bir şekilde gönderildi!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Bu dosyaya erişiminiz yok." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Penpot'unuza gidin" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Sahibi izin verirse takıma davet edileceksiniz." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot dosyası" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Bu projeye erişiminiz yok." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "PROJE" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Bu dosyaya erişimi takım sahibinden isteyebilirsiniz." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Bu projeye erişimi takım sahibinden isteyebilirsiniz." @@ -4439,31 +4449,31 @@ msgstr "Bakım arası: 5 dakika içinde kısa bir bakım için kapalı olacağı msgid "notifications.by-code.upgrade-version" msgstr "Yeni bir sürüm mevcut." -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "Bu davet artık geçerli değil." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Davet başarıyla silindi" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Davet başarıyla iletildi" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Davet bağlantısı kopyalandı" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "Profilinizi silemezsiniz. Devam etmeden önce takımlarınızı birine atayın." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Profil başarıyla kaydedildi!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "%s adresine doğrulama e-postası gönderildi. E-posta kutunuza bakın!" @@ -4499,7 +4509,7 @@ msgstr "Takım adı" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Takım oluşturun" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Üyeleri davet edin" @@ -4509,98 +4519,98 @@ msgstr "" "Geliştiriciler, tasarımcılar, yöneticiler... herkese kapımız açık. Daha " "sonra başkalarını da davet edebilirsiniz." -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Rol ile davet et:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Haydi başlayalım!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Tasarımlardan gerçek kod oluşturma" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Tasarım Sistemleri Oluşturma" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Prototip oluşturma" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Bir uygulamanın kullanıcı arayüzünü/deneyimini tasarlama" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Çerçeve oluşturma" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Penpot'u ne için kullanacaksınız?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Penpot'un sizin için çalışmasını sağlamamıza yardımcı olmak için hakkınızda " "biraz bilgi verin. Yanıtlarınız, yeni özelliklere öncelik vermemize ve " "başlamak için sizi doğru yöne yönlendirmemize yardımcı olacaktır." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Sizi tanımamıza yardımcı olun" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Bu araçlardan en çok hangisini kullanıyorsunuz?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Ne tür bir iş yapıyorsunuz?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Şirketinizin büyüklüğü nedir?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Nereden başlamak istersiniz?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1001 - 5000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 - 500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 - 100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 - 1000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "Yalnızca ben" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5001 ve üzeri" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Eğitim" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Kişisel" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "İş" @@ -4682,11 +4692,11 @@ msgstr "Öntanımlı yapılandırmayı geri yükle" msgid "restore-shortcuts.restore" msgstr "Geri yükle" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Çıkar" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Karışık" @@ -4698,35 +4708,35 @@ msgstr "Rengi kaldır" msgid "settings.select-this-color" msgstr "Bu biçimi kullanan ögeleri seç" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Yorumlar" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Alta hizala" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Ortayı yatay olarak hizala" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Sola hizala" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Sağa hizala" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Üste hizala" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Ortayı dikey olarak hizala" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Seçimden çalışma yüzeyi oluştur" @@ -4734,55 +4744,55 @@ msgstr "Seçimden çalışma yüzeyi oluştur" msgid "shortcuts.bold" msgstr "Kalın yazı aç/kapat" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Boole farkı" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Boole hariç tut" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Boole kesişim" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Boole birleşim" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "En arkaya gönder" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Arkaya gönder" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Öne getir" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "En öne getir" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Geri almayı temizle" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Kopyala" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Bağlantıyı kopyala" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Özellikleri kopyala" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Bileşen / çeşit oluştur" @@ -4790,63 +4800,63 @@ msgstr "Bileşen / çeşit oluştur" msgid "shortcuts.create-new-project" msgstr "Yeni oluştur" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Kes" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Uzaklaştır" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Sil" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Düğümü sil" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "Düğümü ve bölümleri sil" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "Tüm seçimi kaldır" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Bileşeni ayır" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "Kısayolu devre dışı bırak" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "Devre dışı" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "Kısayol devre dışı bırakıldı" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "Ok" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Eğri" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Elips" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Çalışma yüzeyi" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "Çizgi" @@ -4854,65 +4864,65 @@ msgstr "Çizgi" msgid "shortcuts.draw-nodes" msgstr "Yol çiz" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Yol" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Dikdörtgen" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Metin" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Çoğalt" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "Birleşim \"%s\" olarak atandı. Kaydedildiğinde buradan kaldırılacak." -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "Kaydederseniz, bu kısayol orijinal değerine geri dönecek:" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "" "Bu tuş birleşimi kullanılabilir durumda ve herhangi bir çakışma olmadan " "atanabilir." -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "Ayarlarda özelleştir" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "İptal" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Şekilleri dışa aktar" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "Bul" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "Bul ve değiştir" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Tümüne uydurmak için yakınlaştır" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Yatay olarak çevir" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Dikey olarak çevir" @@ -4936,27 +4946,27 @@ msgstr "Paylaşılan kütüphanelere git" msgid "shortcuts.go-to-search" msgstr "Ara" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Grup" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Yatay olarak dağıt" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Kullanıcı arayüzünü göster/gizle" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "İçe/Dışa aktar" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Yakınlaştır" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Görsel ekle" @@ -4964,11 +4974,11 @@ msgstr "Görsel ekle" msgid "shortcuts.italic" msgstr "İtalik yazı aç/kapat" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Düğümlere katıl" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "Tuş birleşimine basın" @@ -4976,39 +4986,39 @@ msgstr "Tuş birleşimine basın" msgid "shortcuts.line-through" msgstr "Üstü çizili yazı aç/kapat" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Köşe yap" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Eğri yap" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Maskele" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Düğümleri birleştir" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Taşı" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Hızlı aşağı taşı" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Hızlı sola taşı" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Hızlı sağa taşı" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Hızlı yukarı taşı" @@ -5016,379 +5026,379 @@ msgstr "Hızlı yukarı taşı" msgid "shortcuts.move-nodes" msgstr "Düğümü taşı" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Aşağı taşı" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Sola taşı" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Sağa taşı" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Yukarı taşı" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Sonraki çalışma yüzeyi" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "Devre dışı bırakılan kısayol yok" -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "Kısayollarınızı özelleştirmeye başlamak için Tümü bölümüne gidin." -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "Aramanızla eşleşen kısayol yok" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Kısayol bulunamadı" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Opaklığı %100 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Opaklığı %10 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Opaklığı %20 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Opaklığı %30 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Opaklığı %40 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Opaklığı %50 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Opaklığı %60 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Opaklığı %70 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Opaklığı %80 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Opaklığı %90 olarak ayarla" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Renk seçici" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Görüntüleyici yorum bölümüne git" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Denetim paneline git" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Görüntüleyici inceleme bölümüne git" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Görüntüleyici etkileşimleri bölümüne git" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Görüntüleyici etkileşimleri bölümüne git" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Çalışma alanına git" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " veya " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "Bu kısayol %s olarak yeniden atandı" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "Kısayollar sayfası" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Yapıştır" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Özellikleri yapıştır" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "Yapıştır ve değiştir" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "Kişiselleştirildi" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Eklenti yöneticisi" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Önceki çalışma yüzeyi" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Yeniden yap" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "" "Kısayolları düzenledikten sonra, değişikliklerin geçerli olması için açık " "olan diğer Penpot sekmelerini yeniden yükleyin." -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Yeniden adlandır" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "Öntanımlıya sıfırla" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Yakınlaştırmayı sıfırla" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Ölçeklendir" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Temel bilgiler" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Denetim paneli" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Görüntüleyici" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Çalışma alanı" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Tümünü seç" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Sonraki katmanı seç" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Ana katmanı seç" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Önceki katmanı seç" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Düğümleri ayır" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "Ölçüleri göster" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Piksel ızgarasını göster/gizle" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Kısayolları göster/gizle" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Düğümlere tuttur" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Piksel ızgarasına tuttur" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Düzenlemeye başla" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Hizalama" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Düzenle" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Genel" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Ana menü" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Katmanları değiştir" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Gezinme" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Gezinme" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Gezinme" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Paneller" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Yollar" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Şekiller" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Metinler" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Araçlar" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Yakınlaştır" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Yakınlaştır" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Küçük resimleri ayarla" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Klavye kısayolları" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Dinamik hizalamayı aç/kapat" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Varlıkları değiştir" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Renk paletini değiştir" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "Yorumların görünürlüğünü değiştir" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Odak modunu değiştir" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Tam ekranı değiştir" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Kılavuzları göster/gizle" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Geçmişi değiştir" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Katmanları değiştir" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Düzen esnekliği ekle/kaldır" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Izgara düzeni ekle/kaldır" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Seçileni kilitle" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Oranları kilitle" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Cetvelleri göster/gizle" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Kılavuzlara tuttur" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Cetvel kılavuzlarına tuttur" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Metin paletini değiştir" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Temayı değiştir" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Göster / Gizle" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Yakınlaştırma şeklini değiştir" @@ -5396,31 +5406,31 @@ msgstr "Yakınlaştırma şeklini değiştir" msgid "shortcuts.underline" msgstr "Alt çizgiyi aç/kapat" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Geri al" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Grubu dağıt" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Maskelemeyi kaldır" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Dikey olarak dağıt" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Görüntüyü küçült" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Görüntüyü büyült" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Seçilene yakınlaştır" @@ -5439,7 +5449,7 @@ msgstr "Kurumsal planı çalışırken görün" msgid "subscription.current-plan.enterprise" msgstr "Kurumsal" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Kurumsal" @@ -5451,7 +5461,7 @@ msgstr "Kurumsal Planı Deneyin" msgid "subscription.current-plan.professional" msgstr "Profesyonel" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "Aboneliğiniz" @@ -5584,277 +5594,277 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Sınırsız plandayken kişileri davet etme" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "Ödeme işlemi tamamlanmadı. Hazır olduğunuzda lütfen tekrar deneyin." -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "" "Ödeme işlemi tamamlanmadı. Lütfen tekrar deneyin. Sorun devam ederse " "bizimle iletişime geçin: support@penpot.app." -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Abonelik" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "Etkinleştirme kodunu girin" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Deneme süreniz bittikten sonra devam etmek için bir ödeme yöntemi ekleyin" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "Yönetici Arayüzü" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Tüm Profesyonel plan faydaları ve:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Tüm Sınırsız plan faydaları ve:" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "Tüm Sınırsız plan faydaları ve:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s düzenleyici)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Kurumsal" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Kurumsal (deneme)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90 günlük sürümleri otomatik kaydetme ve dosya kurtarma" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Sabit aylık fatura" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "Gelişmiş Denetim:" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "Kurumsal Güvenlik:" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "Çoklu Organizasyon Yönetimi:" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "Uyumluluk raporlaması için yerel SAML/SSO ve tam denetim izi günlüğü." -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "" "Yapay zeka bağlantısı için güvenli ve yalıtılmış MCP sunucumuz dahil olmak " "üzere, eklentiler için ayrıntılı beyaz liste oluşturma özelliği." -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "Karmaşık takım yapıları ve ayrı alt organizasyonlar için destek." -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Sınırsız depolama alanı" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Aboneliğinizi yönetin" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Ödeme bilgilerini ekleyin" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Deneme süresi sona erdikten sonra aboneliğinizin sorunsuz bir şekilde devam " "etmesi ve açık kaynaklı projemizi desteklemeye devam etmek için şimdi ödeme " "bilgilerinizi ekleyin. Henüz herhangi bir ücret alınmayacaktır." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Şimdilik atlayın ve deneme sürümünü başlatın" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Büyümemize yardım edin ve deneme sürenizi kolaylaştırın" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Şu anda, takımlarınızda düzenleme yapabilen %s kişi bulunuyor." msgstr[1] "Şu anda, takımlarınızda düzenleme yapabilen %s kişi bulunuyor." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Dikkat: Daha düşük bir plana geçmek, daha az depolama alanı, daha kısa " "yedeklemeler ve daha kısa sürüm geçmişi anlamına gelir." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Düzenleyiciler" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "" "(Sahipler, Yöneticiler ve Düzenleyiciler. Görüntüleyiciler, Düzenleyici " "olarak sayılmaz)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "Şu anda sahip olduğunuzdan daha az sayıda düzenleyici ayarlayamazsınız. " "Takım ayarlarında, dosyaları düzenlemeyen kişilerin rolünü " "(düzenleyici/yönetici yerine görüntüleyici) değiştirin." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "Deneme süresinden sonra ücretlendirilir. Şu anda kredi kartı gerekmez." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**%s$**/ay" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Takımlarınıza %s uygulayın" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "İpucu: Davetlerin öncesinde hazırlıklı olmak için şimdi koltuk sayınızı " "artırabilirsiniz. Takımlar genelinde 25'ten fazla düzenleyiciye aylık 175$ " "sabit ücretten sahip olacaksınız." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Penpot üyelik tarihi: %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Daha fazla bilgi" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "organizasyon üyesi / ay" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Diğer penpot planları" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "aylık organizasyon" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "kullanıcı / ay" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Profesyonel" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7 günlük sürümleri otomatik kaydetme ve dosya kurtarma" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "Topluluğun desteklediği tüm tasarım ve prototip oluşturma özellikleri." -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "Veriler ve altyapı üzerinde tam denetim." -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "Sınırsız kullanıcı, takım ve dosya." -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10GB depolama alanı" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Sınırsız sayıda takım. Sahip olduğunuz takımlarda en fazla 8 düzenleyici." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Tavsiye edilen" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Aboneliğiniz" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Ücretsiz denemeyi başlat" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Abone ol" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "%s planındasınız (%s tarihinden bu yana)." -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Aboneliğinizi, hesap ayrıntılarınızdaki 'Abonelik' sayfasından istediğiniz " "zaman düzenleyebilirsiniz." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Planınızın tadını çıkarın!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Penpot %s planını seçtiğiniz için teşekkür ederiz!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "%s oldunuz!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "14 gün boyunca ücretsiz deneyin" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Sınırsız" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Sınırsız (deneme)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30 günlük sürümleri otomatik kaydetme ve dosya kurtarma" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Aylık fatura üst sınırı 175$" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GB depolama alanı" @@ -5879,45 +5889,45 @@ msgstr "" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "Bu sınırı artırmak isterseniz, [planınızı yükseltin|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "1 davet seçildi" msgstr[1] "%s davet seçildi" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Yazı Tipi Sağlayıcıları - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Yazı Tipleri - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Projeler - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Ara - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Paylaşılmış Kütüphaneler - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - Takımlar için Özgür Tasarım" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Geri bildirimde bulun - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "Bütünleşmeler - Penpot" @@ -5925,7 +5935,7 @@ msgstr "Bütünleşmeler - Penpot" msgid "title.settings.notifications" msgstr "Bildirimler - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Ayarlar - Penpot" @@ -5933,27 +5943,27 @@ msgstr "Ayarlar - Penpot" msgid "title.settings.password" msgstr "Parola - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profil - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Davetler - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Üyeler - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Ayarlar - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Web kancaları - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Görünüm modu - Penpot" @@ -5965,51 +5975,51 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "Tokeni ayır" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "Sayfada çalışma yüzeyi bulunamadı." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Çalışma yüzeyi bulunamadı." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Yorumlar (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Etkileşimleri gösterme" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Tam Ekran" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "İncele (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Etkileşimler" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Etkileşimler (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Bağlantıyı kopyala" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Etkileşimleri göster" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Tıklamada etkileşimleri göster" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Site haritası" @@ -6046,67 +6056,67 @@ msgstr "" msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "WebGL sorun giderme kılavuzumuzu" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "WebGL görselleştirme devre dışı" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "WebGL görselleştirme etkin" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "WebGL içeriği kayboldu" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "WebGL içeriği kurtarıldı" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Son iletim başarılı oldu." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Yatay olarak ortaya hizala (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Yatayda dağıt (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Sola hizala (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Sağa hizala (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Alta hizala (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Dikey olarak ortaya hizala (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Dikeyde dağıt (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Üste hizala (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Kütüphane ekle" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Tüm varlıklar" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Renkler" @@ -6118,7 +6128,7 @@ msgstr "Renk ekle" msgid "workspace.assets.component-group-options" msgstr "Bileşen grubu seçenekleri" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Bileşenler" @@ -6126,7 +6136,7 @@ msgstr "Bileşenler" msgid "workspace.assets.components.add-component" msgstr "Bileşen ekle" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s Çeşit" @@ -6134,11 +6144,11 @@ msgstr "%s Çeşit" msgid "workspace.assets.create-group" msgstr "Grup oluştur" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Ögeleriniz otomatik olarak \"grup adı / öge adı\" olarak adlandırılacak" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Sil" @@ -6146,11 +6156,11 @@ msgstr "Sil" msgid "workspace.assets.delete-group" msgstr "Grubu sil" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "Sayfaları sil" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Çoğalt" @@ -6158,39 +6168,39 @@ msgstr "Çoğalt" msgid "workspace.assets.duplicate-main" msgstr "Ana bileşeni çoğalt" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Düzenle" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Filtrele" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Izgara görünümü" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Grup" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Grup adı" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Liste görünümü" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "yerel kütüphane" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Kütüphaneyi yönet" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Varlık bulunmadı" @@ -6198,7 +6208,7 @@ msgstr "Varlık bulunmadı" msgid "workspace.assets.open-library" msgstr "Kütüphane dosyasını aç" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Yeniden adlandır" @@ -6206,11 +6216,11 @@ msgstr "Yeniden adlandır" msgid "workspace.assets.rename-group" msgstr "Grubu yeniden adlandır" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Varlık ara" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Paylaşılan kütüphane" @@ -6220,43 +6230,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 bileşen" msgstr[1] "%s bileşen" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Sırala" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Tipografiler" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Tipografi ekle" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Boyut" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Yazı Tipi Biçimi" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Düzenlemek için biçim kütüphane dosyasına gidin" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Harf Boşluğu" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Satır Yüksekliği" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Metin Dönüşümü" @@ -6264,99 +6274,103 @@ msgstr "Metin Dönüşümü" msgid "workspace.assets.ungroup" msgstr "Grubu dağıt" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "Görsel kopyalanıyor…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "Görsel panoya kopyalandı" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "Görsel kopyalanırken hata oluştu" +#: src/app/main/ui/workspace/colorpicker.cljs:447 +msgid "workspace.colorpicker.color-picker" +msgstr "Ekrandan renk seç" + #: src/app/main/ui/workspace/colorpicker.cljs:439, src/app/main/ui/workspace/colorpicker.cljs:454 msgid "workspace.colorpicker.color-tokens" msgstr "Tokenleri renklendir" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Bileşenler kendi içlerinde iç içe geçemez." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "Bazı kopyalar değiştirilemedi. Bileşenler kendi içlerinde iç içe geçemez." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Alan oluştur" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "Sütunları kopyala" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "Satırları kopyala" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Çalışma yüzeyi oluştur" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Hücreleri birleştir" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "Yapıştır" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Sağa 1 sütun ekle" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Sola 1 sütun ekle" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Sütunu sil" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Sütun ve şekilleri sil" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Yinelenen sütun" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Aşağıya 1 satır ekle" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Yukarıya 1 satır ekle" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Satırı sil" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Satır ve şekilleri sil" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Yinelenen satır" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "Kılavuz rengi" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "Kılavuzu kaldır" @@ -6364,15 +6378,15 @@ msgstr "Kılavuzu kaldır" msgid "workspace.debug.title" msgstr "Hata ayıklama araçları" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Odaklanma modu" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Odaklanma kapalı" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Odaklanma açık" @@ -6384,243 +6398,247 @@ msgstr "Doğrusal degrade" msgid "workspace.gradients.radial" msgstr "Dairesel degrade" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Dinamik hizalamayı devre dışı bırak" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Orantılı ölçeklendirmeyi devre dışı bırak" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Kılavuzlara tutturmayı devre dışı bırak" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Piksele tutturmayı devre dışı bırak" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Cetvel kılavuzlarına tutturmayı devre dışı bırak" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "WebGL görselleştirmeyi devre dışı bırak (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Dinamik hizalamayı etkinleştir" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Orantılı ölçeklendirmeyi etkinleştir" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Kılavuzlara tuttur" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Piksele tutturmayı etkinleştir" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Cetvel kılavuzlarına tuttur" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "WebGL görselleştirmeyi etkinleştir (beta)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "Bul" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "Bul ve Değiştir" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Çalışma yüzeyi adlarını gizle" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "Yorumları gizle" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Kılavuzları gizle" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Renk paletini gizle" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Piksel ızgarasını gizle" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Cetvelleri gizle" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Yazı tipi paletini gizle" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "Kılavuzları kilitle" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "Bağlan" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "Bağlantıyı kes" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "Yönet (Durum: devre dışı)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "Yönet (Durum: etkin)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Düzenle" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Dosya" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Yardım ve bilgi" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "MCP sunucusu" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Tercihler" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Görünüm" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Tekrarla" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Tümünü seç" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Çalışma yüzeylerinin adlarını göster" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "Yorumları göster" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Kılavuzları göster" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Renk paletini göster" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Piksel ızgarasını göster" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Cetvelleri göster" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Yazı tipi paletini göster" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Koyu temaya geç" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Açık temaya geç" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Sistem temasına geç" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Geri al" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "Kılavuzların kilidini aç" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Sıfırla" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:133 +msgid "workspace.header.retrying" +msgstr "Kaydetme yeniden deneniyor" + +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Kaydetme hatası" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Kaydedildi" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Kaydediliyor" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Paylaş" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Görünüm modu (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Yakınlaştırma" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Doldur - Doldurmak için ölçeklendir" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Sığdır - Sığdırmak için küçült" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Tümüne uydurmak için yakınlaştır" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Tam ekran" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Seçilene yakınlaştır" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Izgarayı düzenle" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Çıkış" @@ -6628,7 +6646,7 @@ msgstr "Çıkış" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Alt dolgu" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "4 taraflı dolgu seçeneklerini göster" @@ -6664,37 +6682,37 @@ msgstr "Bitti" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Bul" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Izgara düzenini bul" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 renk" msgstr[1] "%s renk" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "Kütüphaneye renk ekle" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Kütüphanenizde henüz renk stili yok" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "Henüz son kullanılan renkler yok" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Kütüphanenizde henüz tipografi stili yok" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Dosya kütüphanesi" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Son renkler" @@ -6706,75 +6724,99 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Renk biçimini kaydet" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "Renk paletini değiştir" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 bileşen" msgstr[1] "%s bileşen" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:864 +msgid "workspace.libraries.connected-libraries" +msgstr "Bağlantılı kütüphane" + +#: src/app/main/ui/workspace/libraries.cljs:235 +msgid "workspace.libraries.connected-through" +msgstr "Şununla bağlandı" + +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Bağlandı" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Veya denemek için bunlardan bazılarını ekleyin:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:834 +msgid "workspace.libraries.empty.no-assets" +msgstr "Henüz varlık yok" + +#: src/app/main/ui/workspace/libraries.cljs:918 +msgid "workspace.libraries.empty.no-connected-libraries" +msgstr "Henüz bağlantılı kütüphane yok." + +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "Takımınızda Paylaşılan Kütüphaneler bulunmuyor, burada bulunan" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "şablonlara bakabilirsiniz" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Dosya kütüphanesi" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 grafik" msgstr[1] "%s grafik" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "BU DOSYADAKİ KÜTÜPHANELER" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "KÜTÜPHANELER" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "KÜTÜPHANE GÜNCELLEMELERİ" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Yükleniyor…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Güncelleme gerektiren Paylaşılmış Kütüphane bulunmuyor" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "“%s“ için eşleşme bulunmadı" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Paylaşılmış kütüphane ara" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:849, src/app/main/ui/workspace/libraries.cljs:897 +msgid "workspace.libraries.set-as-tokens-source" +msgstr "Token kaynağı olarak ayarla" + +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "PAYLAŞILMIŞ KÜTÜPHANELER" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:674 +msgid "workspace.libraries.shared-library-added" +msgstr "Eklendi" + +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Kütüphaneyi bağla" @@ -6798,25 +6840,47 @@ msgstr "Tüm varlıkları ayır" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "Tüm tokenleri ayır" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:219, src/app/main/ui/workspace/libraries.cljs:263 +msgid "workspace.libraries.token-sets" +msgid_plural "workspace.libraries.token-sets" +msgstr[0] "1 küme" +msgstr[1] "%s küme" + +#: src/app/main/ui/workspace/libraries.cljs:222, src/app/main/ui/workspace/libraries.cljs:267 +msgid "workspace.libraries.token-themes" +msgid_plural "workspace.libraries.token-themes" +msgstr[0] "1 tema" +msgstr[1] "%s tema" + +#: src/app/main/ui/workspace/libraries.cljs:216, src/app/main/ui/workspace/libraries.cljs:259 +msgid "workspace.libraries.tokens" +msgid_plural "workspace.libraries.tokens" +msgstr[0] "1 token" +msgstr[1] "%s token" + +#: src/app/main/ui/workspace/libraries.cljs:842, src/app/main/ui/workspace/libraries.cljs:889 +msgid "workspace.libraries.tokens-source" +msgstr "Token kaynağı" + +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 tipografi" msgstr[1] "%s tipografi" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Kütüphanenin bağlantısını kes" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Güncelle" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "tüm değişiklikleri gör" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "GÜNCELLEMELER" @@ -6878,7 +6942,7 @@ msgstr "Bulanıklığı değiştir" msgid "workspace.options.blur-options.toggle-more-options" msgstr "Daha fazla seçenek göster/gizle" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "İptal" @@ -6886,117 +6950,121 @@ msgstr "İptal" msgid "workspace.options.canvas-background" msgstr "Tuval arka planı" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:75 +msgid "workspace.options.canvas-background-section" +msgstr "Tuval arka plan bölümü" + +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "İçeriği kırp" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Bileşen" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Açıklama" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Kopyala" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Bir açıklama oluştur" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Bir açıklamayı düzenle" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Ana bileşen" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Bileşeni değiştir" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "Bu kütüphanede henüz varlık yok" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Ayrıldı" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "Çakışan çeşitleri bul" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Bu bileşenin çakışan çeşitleri var. Her çeşidin kendine özgü özellik " "değerleri olduğundan emin olun." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Yinelenen çeşitleri bul" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Bazı çeşitler aynı özelliklere ve değerlere sahip" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "" "Bu çeşitler aynı özelliklere ve değerlere sahiptir. Değerleri, " "çağrılabilecekleri şekilde ayarlayın." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "" "Bu çeşit, başka bir çeşitle aynı özelliklere ve değerlere sahiptir. " "Değerleri, çağrılabilecekleri şekilde ayarlayın." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "" "Bu çeşitlerden bazıları aynı özelliklere ve değerlere sahiptir. Değerleri, " "geri çağrılabilecekleri şekilde ayarlayın." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Bu bileşenin geçersiz ada sahip çeşitleri var. Her çeşidin doğru yapıyı " "takip ettiğinden emin olun." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Geçersiz çeşitleri bul" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Bazı çeşitlerin adları geçersiz" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Geçersiz çeşitleri bul" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Bu çeşitlerin adları geçersiz." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Bu çeşidin adı geçersiz." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Bu çeşitlerden bazılarının adları geçersiz." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[özellik]=[değer], [özellik]=[değer]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Şu yapıyı kullanmayı deneyin:" @@ -7033,7 +7101,7 @@ msgstr "Aynı hiyerarşi düzeyine sahip." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Gruplar, çalışma yüzeyleri ve yerleşim düzenleri eş değer kabul edilir." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Çeşitler nasıl bağlantılı kalır" @@ -7081,11 +7149,11 @@ msgstr "Üst ve Alt" msgid "workspace.options.convert-to-typography" msgstr "Tipografi biçimi oluştur" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Tasarım" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Dışa aktar" @@ -7093,53 +7161,53 @@ msgstr "Dışa aktar" msgid "workspace.options.export-multiple" msgstr "Seçimi dışa aktar" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "1 ögeyi dışa aktar" msgstr[1] "%s ögeyi dışa aktar" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Dışa aktarım ekle" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Dışa aktarımı kaldır" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Son ek" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "Dışa aktarma hizmeti meşgul, lütfen daha sonra tekrar deneyin" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "Dışa aktarma iptal edildi" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "İptal ediliyor..." -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Dışa aktarma tamamlandı" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Dışa aktarılıyor…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Dışa aktarılamadı" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Dışa aktarma beklenmedik şekilde yavaş" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "Bekleniyor..." @@ -7159,7 +7227,7 @@ msgstr "Doldurmayı kaldır" msgid "workspace.options.fill.section" msgstr "Bölümü doldur" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Çalışma yüzeyini içeriğe sığacak şekilde yeniden boyutlandır" @@ -7179,71 +7247,77 @@ msgstr "Akış başlangıcı" msgid "workspace.options.flows.flow-starts" msgstr "Akış başlar" +#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:300, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:526 +msgid "workspace.options.font-not-available" +msgstr "" +"Bu metinde kullanılan yazı tipi (%s) artık kullanılabilir değil. Bir " +"tipografi biçimi oluşturmak için yazı tipini yeniden yükleyin" + #: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:34 msgid "workspace.options.grid.auto" msgstr "Otomatik" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Sütunlar" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Renk" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Aralık" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Yükseklik" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Kenar Boşluğu" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Öntanımlı olarak ayarla" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Alt" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Orta" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Sol" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Sağ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Ger" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Üst" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Öntanımlıyı kullan" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Genişlik" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Satırlar" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Kare" @@ -7255,7 +7329,7 @@ msgstr "Grubu doldur" msgid "workspace.options.group-stroke" msgstr "Grubu çiz" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Kılavuz ekle" @@ -7263,19 +7337,19 @@ msgstr "Kılavuz ekle" msgid "workspace.options.guides.remove-guide" msgstr "Kılavuzu kaldır" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Kılavuzlar" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Kılavuzu değiştir" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Yükseklik" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "İncele" @@ -7491,115 +7565,119 @@ msgstr "Etkileşimler" msgid "workspace.options.interactions.add-interaction" msgstr "Etkileşim ekle" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Renk" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Renk yanması" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Renk atlatması" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Karart" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Fark" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Dışla" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Sert ışık" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Ton" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Aydınlat" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Parlaklık" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Çoğalt" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Normal" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Üst katman" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Doygunluk" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Ekran" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Yumuşak ışık" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "Menü bölümü katmanı" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Katman görünürlüğünü değiştir" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Azami Yükseklik" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Azami Genişlik" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Asgari Yükseklik" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Asgari Genişlik" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Daha fazla renk" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Daha fazla kütüphane rengi" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "Daha fazla renk tokeni" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "Eşleşen boyut ön ayarı yok" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Opaklık" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/page.cljs:99, src/app/main/ui/workspace/sidebar/options/page.cljs:106 +msgid "workspace.options.pixel-grid-color" +msgstr "Piksel ızgara rengi" + +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Prototip" @@ -7635,23 +7713,23 @@ msgstr "Kenar yuvarlaklığı bölümü" msgid "workspace.options.radius.show-single-corners" msgstr "Bağımsız yarıçapı göster" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Son kullanılanlar" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Yeniden dene" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Döndür" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Yazı tipi ara" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "Boyut ön ayarı ara" @@ -7661,11 +7739,11 @@ msgstr "" "Diğer çalışma yüzeyine bağlantı taşımak için bir şekil, çalışma yüzeyi ya " "da grup seçin." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Seçilen renkler" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "Renk seçimi bölümü" @@ -7733,55 +7811,55 @@ msgstr "Gölgeyi değiştir" msgid "workspace.options.show-fill-on-export" msgstr "Dışa aktarmalarda göster" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Görünüm modunda göster" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Boyut" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Boyut ön ayarları" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Oranı kilitle" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Oranın kilidini kaldır" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Daire" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Elmas" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Ok" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Hiçbiri" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Yuvarlak" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Kare" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Dikdörtgen" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Üçgen" @@ -7789,31 +7867,31 @@ msgstr "Üçgen" msgid "workspace.options.stroke-color" msgstr "Çerçeve rengi" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "Çizgi" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "Boşluk" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Çerçeve genişliği" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "En alt (blok sonu)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "Sol (satır içi başlangıcı)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "Sağ (satır içi sonu)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "En üst (blok başlangıcı)" @@ -7821,49 +7899,49 @@ msgstr "En üst (blok başlangıcı)" msgid "workspace.options.stroke.add-stroke" msgstr "Çerçeve rengi ekle" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Merkez" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Çizgili" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Noktalı" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "İçinde" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Karışık" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Dışında" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "Kenar başına çerçeve" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "" "Kenar başına çerçeve özelliği yalnızca yeni görselleştirmede " "kullanılabilir. Kullanmak için görselleştirmeyi değiştirin." -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Çerçeveyi kaldır" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Katı" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "Çizgiyi aç/kapat" @@ -7899,11 +7977,11 @@ msgstr "Otomatik genişlik" msgid "workspace.options.text-options.grow-fixed" msgstr "Sabit" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Harf Aralıkları" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Satır yüksekliği" @@ -7951,221 +8029,225 @@ msgstr "Altı Çizili (%s)" msgid "workspace.options.use-play-button" msgstr "Prototip görünümünü çalıştırmak için başlıktaki oynatma düğmesini kullan." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Genişlik" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X ekseni" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y ekseni" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Düğüm çiz (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "Hizalandı" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "Bağımsız" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "Eşit" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "İşleyici davranışı" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Düğümleri birleştir (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Köşeye (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Eğriye (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Düğümleri birleştir (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Düğümleri taşı (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Düğümleri ayır (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Düğümleri tuttur (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Aç" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Keşfet [daha fazla eklenti](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Henüz eklenti kurulmadı" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Eklenti bildirimi yanlış." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Bu eklentiyi kullanmak için düzenleyici olmanız gerekir" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/data/plugins.cljs:160 +msgid "workspace.plugins.error.unreachable" +msgstr "Eklenti URL'sine erişilemedi." + +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Eklenti yok veya URL doğru değil." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Kur" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Kurulu eklentiler" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Eklenti yöneticisi" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Eklentiler" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "BU EKLENTİYİ GÜNCELLE" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Eklenti, son açtığınızdan beri değiştirildi. Artık şuraya da erişmek " "istiyor:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Dosya indirmesi başlat." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Tarayıcıda veri depola." -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "Panonun içeriğini oku." -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "Pano içeriğini oku ve yaz." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Yorumlarınızı ve yanıtlarınızı oku." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Yorumlarınızı oku, değiştir ve sizin adınıza yanıtla." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Kullanıcıların erişebildiği dosyaların içeriğini oku." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Kullanıcıların erişebildiği dosyaların içeriğini oku ve değiştir." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "Bu eklentinin harici bir tarafça oluşturulduğunu unutmayın." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Kütüphanelerinizi ve varlıklarınızı oku." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Kütüphanelerinizi ve varlıklarınızı oku ve değiştir." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "BU EKLENTİ ŞUNLARA ERİŞİM İSTİYOR:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Geçerli kullanıcının profil bilgilerini oku." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Eklenti Listesi" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Eklentiyi kaldır" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Bir eklenti URL'si yazın" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Eklentiler" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "ŞİMDİ DEĞİL" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Bir göz atmak ister misiniz? Geçerli takımınız için yeni bir taslakta " "açılacaktır. (İstemiyorsanız, onu herhangi bir dosyanın kurulu " "eklentilerinde her zaman bulabilirsiniz.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "'%s' EKLENTİSİ KULLANICINIZ İÇİN KURULDU!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "EKLENTİYİ DENE" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Esnek düzen ekle" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Izgara düzeni ekle" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Yerleşim düzeni ekle" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Çeşit oluştur" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Yeni özellik ekle" @@ -8177,11 +8259,11 @@ msgstr "En arkaya gönder" msgid "workspace.shape.menu.backward" msgstr "Arkaya gönder" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "Çalışma yüzeyi kılavuzlarını temizle" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Çeşit olarak birleştir" @@ -8189,7 +8271,7 @@ msgstr "Çeşit olarak birleştir" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Bileşenler aynı sayfada olmalıdır" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Kopyala" @@ -8233,27 +8315,27 @@ msgstr "Metin olarak kopyala" msgid "workspace.shape.menu.create-annotation" msgstr "Açıklama oluştur" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Çalışma yüzeyi için seçim" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Bileşen oluştur" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Çoklu bileşen oluştur" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Kes" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Sil" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Akış başlangıcını sil" @@ -8265,7 +8347,7 @@ msgstr "Örneği ayır" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Örnekleri ayır" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Fark" @@ -8273,15 +8355,15 @@ msgstr "Fark" msgid "workspace.shape.menu.duplicate" msgstr "Çoğalt" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Düzenle" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Dışla" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Düzleştir" @@ -8293,7 +8375,7 @@ msgstr "Yatay ters çevir" msgid "workspace.shape.menu.flip-vertical" msgstr "Dikey ters çevir" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Akış başlangıcı" @@ -8305,31 +8387,31 @@ msgstr "Öne getir" msgid "workspace.shape.menu.front" msgstr "En öne getir" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Grup" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Gizle" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Kullanıcı arayüzünü göster/gizle" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Kesişme" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Kilitle" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Maskele" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Yapıştır" @@ -8337,27 +8419,27 @@ msgstr "Yapıştır" msgid "workspace.shape.menu.paste-props" msgstr "Özellikleri yapıştır" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Yol" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Düzen esnekliğini kaldır" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Izgara düzenini kaldır" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Yerleşim düzenini kaldır" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Özelliği kaldır" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "Çeşidin en az bir özelliği olmalıdır" @@ -8381,11 +8463,11 @@ msgstr "Çeşidi geri yükle" msgid "workspace.shape.menu.select-layer" msgstr "Katman seç" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Göster" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Varlıklar panelinde göster" @@ -8393,7 +8475,7 @@ msgstr "Varlıklar panelinde göster" msgid "workspace.shape.menu.show-main" msgstr "Ana bileşeni göster" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "Yola çerçeve" @@ -8405,19 +8487,19 @@ msgstr "Küçük resmi kaldır" msgid "workspace.shape.menu.thumbnail-set" msgstr "Küçük resim olarak ayarla" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Grubu dağıt" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Birleşim" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Kilidi aç" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Maskelemeyi kaldır" @@ -8433,79 +8515,79 @@ msgstr "Kenar çubuğunu daralt" msgid "workspace.sidebar.expand" msgstr "Kenar çubuğunu genişlet" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Geçmiş" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Katmanlar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Bileşenler" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Filtrele" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Çalışma yüzeyleri" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Gruplar" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Görseller" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Maskeler" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "Eşleşme yok" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "Değiştir" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "Tümünü değiştir" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "Şununla değiştir..." -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Bul…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "Bul ve değiştir" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "Metin içeriği" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "Katman adları" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Şekiller" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Metinler" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "İçe Aktarılan SVG Öznitelikleri" @@ -8517,7 +8599,7 @@ msgstr "Sayfalar" msgid "workspace.sidebar.sitemap.add-page" msgstr "Sayfa ekle" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Site haritası" @@ -8537,6 +8619,10 @@ msgstr "%s etkin tema" msgid "workspace.tokens.add-new-theme" msgstr "Yeni tema ekle" +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:106 +msgid "workspace.tokens.add-set" +msgstr "Küme ekle" + #: src/app/main/ui/workspace/tokens/sets/context_menu.cljs:62 msgid "workspace.tokens.add-set-to-group" msgstr "Bu gruba küme ekle" @@ -8545,7 +8631,7 @@ msgstr "Bu gruba küme ekle" msgid "workspace.tokens.add-token" msgstr "Token ekle: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Uygulanan" @@ -8565,7 +8651,11 @@ msgstr "Temel yazı tipi boyutu" msgid "workspace.tokens.base-font-size.error" msgstr "Temel yazı tipi boyutu piksel cinsinden veya birimsiz bir değer olmalıdır." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:98 +msgid "workspace.tokens.change-token-source" +msgstr "Token kaynağını değiştir" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Renk" @@ -8577,7 +8667,7 @@ msgstr "Token yolunu kopyala" msgid "workspace.tokens.create-new-theme" msgstr "Şimdi ilk temanızı oluşturun." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Bir tane oluşturun." @@ -8585,6 +8675,10 @@ msgstr "Bir tane oluşturun." msgid "workspace.tokens.create-token" msgstr "Yeni %s tokeni oluştur" +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:101 +msgid "workspace.tokens.current-tokens-source-tooltip" +msgstr "Bu geçerli token kaynağıdır" + #: src/app/main/ui/workspace/tokens/management/context_menu.cljs:437 msgid "workspace.tokens.delete" msgstr "Tokeni sil" @@ -8601,7 +8695,7 @@ msgstr "Tokeni çoğalt" msgid "workspace.tokens.duplicate-group" msgstr "Token Gruplarının Kopyasını Oluştur" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "kopyala" @@ -8625,7 +8719,7 @@ msgstr "%s tokenini düzenle" msgid "workspace.tokens.enter-token-name" msgstr "%s token adını gir" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "Metni düzenlerken tokenler uygulanamaz. Bunun yerine metin katmanını seçin." @@ -8653,7 +8747,7 @@ msgstr "Ön izleme:" msgid "workspace.tokens.export.single-file" msgstr "Tek dosya" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Yazı tipi boyutu veya {alias}" @@ -8663,7 +8757,7 @@ msgstr "" "Yazı tipi kalınlığı/biçimi ayarlanırken hata oluştu. Bu yazı tipi biçimi " "geçerli yazı tipinde bulunmuyor" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Bir değer (300, Kalın, Normal İtalik...) veya bir {alias} girin" @@ -8701,7 +8795,7 @@ msgstr "" "Tek bir JSON dosyasında, birinci düzey anahtarlar token kümesi adları " "olmalıdır." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Tokenleri içe aktar" @@ -8711,17 +8805,17 @@ msgstr "" "Tokenleri içe aktarmak, geçerli tüm tokenlerinizi, kümelerinizi ve " "temalarınızı geçersiz kılacaktır." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Etkin değil" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Bu küme etkin değil. Görünüm alanında değişiklikleri görmek için temayı " "değiştirin veya bu kümeyi etkinleştirin" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Bireysel tokenler kullan" @@ -8741,11 +8835,11 @@ msgstr "Tema" msgid "workspace.tokens.label.theme-placeholder" msgstr "Tema ekle (örn. Açık)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Harf aralığı veya {alias} ekle" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Satır yüksekliği girin — çarpan, px, % veya {alias}" @@ -8761,11 +8855,11 @@ msgstr "Azami boyut" msgid "workspace.tokens.min-size" msgstr "Asgari boyut" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Eksik referans" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Seçenekleri görmek için sağ tıklayın" @@ -8783,7 +8877,7 @@ msgstr "" "Kümeleri etkinleştirmek / devre dışı bırakmak için düzenleyici olmanız " "gerekir" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Temaları kullanmak için düzenleyici olmanız gerekir" @@ -8795,7 +8889,7 @@ msgstr "Henüz tanımlanan küme yok. Önce bir tane oluşturun." msgid "workspace.tokens.no-sets-yet" msgstr "Henüz küme yok." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Tema yok." @@ -8807,11 +8901,25 @@ msgstr "Şu anda hiç temanız yok." msgid "workspace.tokens.not-remap" msgstr "Yeniden eşlenmesin" +#: src/app/main/data/workspace/libraries.cljs:848 +msgid "workspace.tokens.notifications.source-sets-or-themes-updated" +msgstr "Token kaynağı kümeleri veya temaları değiştirildi" + #: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:132 msgid "workspace.tokens.num-active-sets" msgstr "%s etkin küme" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/colorpicker.cljs:438, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:112 +msgid "workspace.tokens.only-first-fill-or-stroke" +msgstr "" +"Tasarım tokenleri, listedeki yalnızca ilk dolgu veya çerçeveye " +"uygulanabilir." + +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:104 +msgid "workspace.tokens.open-source-new-tab" +msgstr "Tokenlerin kaynağını yeni sekmede aç" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Orijinal değer: %s" @@ -8823,17 +8931,17 @@ msgstr "Dolgular" msgid "workspace.tokens.radius" msgstr "Yarıçap" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "" "{%s} içindeki referans geçerli değil veya herhangi bir etkin kümede " "bulunmuyor." -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Bir token tipografi takma adı girin" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Bir token gölge takma adı girin" @@ -8873,7 +8981,7 @@ msgstr "" "Tokenleriniz otomatik olarak %s.(sonEk).(tokeAdı) olarak yeniden " "adlandırılacak" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Çözülen değer: %s" @@ -8913,27 +9021,27 @@ msgstr "" msgid "workspace.tokens.settings" msgstr "Token ayarları" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Gölge Ekle" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Bulanıklık" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "İçeri" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Gölgeyi Kaldır" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Yayılım" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "Bulanıklık değeri sıfırdan küçük olamaz" @@ -8941,11 +9049,11 @@ msgstr "Bulanıklık değeri sıfırdan küçük olamaz" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "Yayılım değeri sıfırdan küçük olamaz" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -8953,11 +9061,19 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "Boyut" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:75 +msgid "workspace.tokens.source" +msgstr "Kaynak:" + +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:311, src/app/main/ui/workspace/tokens/management/context_menu.cljs:335 +msgid "workspace.tokens.stroke-width" +msgstr "Çerçeve Genişliği" + +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "Girin: none | uppercase | lowercase | capitalize veya {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "Metin süslemesi girin: none | underline | strike-through" @@ -8971,35 +9087,39 @@ msgstr "" msgid "workspace.tokens.themes-list" msgstr "Tema listesi" +#: src/app/main/ui/workspace/tokens/tokens_source.cljs:80 +msgid "workspace.tokens.this-file" +msgstr "Bu dosya" + #: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:334, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:335 msgid "workspace.tokens.token-description" msgstr "Açıklama" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Yazi tipi ailesi seç" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Yazı tipi ailesi" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Yazı tipi ailesi veya virgülle (,) ayrılan yazı tipi listesi" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Ad" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "%s yolunda veya bu yolun bir ön ekinde zaten bir token var." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "Ad en az 1 karakterden oluşmalıdır" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " geçerli bir token adı değil.\n" @@ -9010,11 +9130,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Değer" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "{alias} ile bir takma ad veya değer girin" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "TOKENLER - %s" @@ -9033,15 +9153,15 @@ msgstr "" msgid "workspace.tokens.unknown-token-type-section" msgstr "'%s' türü desteklenmiyor (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Referans kullan" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Değer geçerli değil" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "Ok (%s)" @@ -9049,75 +9169,75 @@ msgstr "Ok (%s)" msgid "workspace.toolbar.assets" msgstr "Varlıklar" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Renk Paketi (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Yorumlar (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Eğri (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Elips (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Çalışma Yüzeyi (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Görsel (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:362 +msgid "workspace.toolbar.label" +msgstr "Çizim araçları" + +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "Çizgi (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "Buradan bağlanın" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP bağlandı" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Taşı (%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "Palet çubuğu" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Yol (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Eklentiler (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Dikdörtgen (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Metin (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Tipografiler (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Araç çubuğunu aç/kapat" @@ -9140,11 +9260,11 @@ msgstr "" msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "Yenile" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Şu ana kadar değişim geçmişi yok" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "%s ile" @@ -9284,19 +9404,19 @@ msgstr "tipografi varlığı" msgid "workspace.undo.entry.unknown" msgstr "%s üstündeki işlem" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Yok say" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Daha fazla bilgi" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Paylaşılan kütüphaneler için güncellemeler var" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Güncelle" diff --git a/frontend/translations/ukr_UA.po b/frontend/translations/ukr_UA.po index 519ce16bf8..410477ee67 100644 --- a/frontend/translations/ukr_UA.po +++ b/frontend/translations/ukr_UA.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:14+0000\n" -"Last-Translator: Anonymous \n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Ukrainian " "\n" "Language: ukr_UA\n" @@ -10,9 +10,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "Уже маєте обліковий запис?" @@ -34,7 +34,7 @@ msgstr "" "Це демонстраційний варіант сервісу, не використовуйте для роботи, бо " "створені проєкти періодично очищуватимуться." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "Забули пароль?" @@ -42,37 +42,37 @@ msgstr "Забули пароль?" msgid "auth.fullname" msgstr "Повне ім'я" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "Увійти до мого облікового запису" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "Увійти тут." -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "" "Penpot це вільний інструмент з відкритим сирцевим кодом для спільної роботи " "над дизайном та кодом" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "з OpenID" @@ -94,7 +94,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "На вашу скриньку надіслано лист з посиланням на відновлення паролю." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "Успішно приєднано до команди" @@ -126,7 +126,7 @@ msgstr "Забули пароль?" msgid "auth.recovery-submit" msgstr "Змініть свій пароль" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "Не маєте облікового запису?" @@ -138,7 +138,7 @@ msgstr "Скажіть, як звертатись до Вас у панелі к msgid "auth.register-account-title" msgstr "Ваше ім'я" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "Створити обліковий запис" @@ -153,7 +153,7 @@ msgstr "" "Я погоджуюсь із [умовами користування](%s) та [політикою " "конфіденційності](%s)." -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "Умови користування" @@ -175,94 +175,94 @@ msgstr "" msgid "comments.mentions.not-found" msgstr "Не знайдено людей за @%s" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "Опублікувати" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "Усі користувачі Penpot" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "" "Ви впевнені, що хочете видалити це посилання? Це незворотна дія, після якої " "посилання стане недійсним" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(поточне)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "Стерти посилання" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "Отримати посилання" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "Посилання успішно скопійовано" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "Керувати дозволами" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "1 сторінку було поширено" msgstr[1] "%s сторінки було поширено" msgstr[2] "%s сторінок було поширено" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "Можна додавати коментарі" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "Можна переглядати код" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "Будь-хто з посиланням матиме доступ" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "Сторінок поширено" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "Посилання для спільного використання з'явиться тут" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "Лише учасники команди" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "Поділитись прототипами" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "Вибрати все" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "Скасувати публікацію" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "Керування командою" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "" "Penpot призначений для командної роботи. Запрошуйте учасників до спільної " "роботи над проєктами та файлами" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "Об'єднуйтесь!" @@ -278,31 +278,31 @@ msgstr "Відновити файл" msgid "dashboard.add-file" msgstr "Додати файл" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "Додати як Спільну Бібліотеку" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "Змінити пошту" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "Звільнити кошик" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(копія)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "Створити організацію" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "Створити нову команду" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "Пришпилити цю версію" @@ -310,7 +310,7 @@ msgstr "Пришпилити цю версію" msgid "dashboard.default-team-name" msgstr "Ваш Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "" "Ви впевнені що хочете назавжди позбутись видалених проєктів та файлів? Це " @@ -320,41 +320,41 @@ msgstr "" msgid "dashboard.delete-file-forever-confirmation.description" msgstr "Ви впевнені що хочете назавжди позбутись %s? Це незворотня дія." -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s файлів було успішно вилучено." -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "Позбутись назавжди" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "Вилучити проєкт" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "" "Ви впевнені що хочете назавжди позбутись %s проєктів? Ви позбудетесь їх та " "файлів, що в них містились, назавжди. Це незворотня дія." -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s було успішно вилучено." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "Видалити команду" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "Ваш смітник порожній. Видалені файли та проєкти зʼявлятимуться тут." -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "Буде вилучено %s" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "Завантажити файл Penpot (.penpot)" @@ -366,19 +366,19 @@ msgstr "Створити дублікат" msgid "dashboard.duplicate-multi" msgstr "Створити дублікат % файлів" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "Як тільки учасник проєкту створить чернетку, вона з'явиться тут." -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "Чернеток ще немає." -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "Як тільки учасник проєкту створить файл, він зʼявиться тут." -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "Файлів ще немає." @@ -426,23 +426,23 @@ msgstr "Імпортувати файл .penpot" msgid "dashboard.empty-project.start" msgstr "Почніть робити чудові речі" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "Сталася помилка під час вилучення файлів." -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "Сталася помилка під час вилучення проєкту %s." -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "Сталася помилка під час відновлення файлу %s." -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "Сталася помилка під час відновлення файлів." -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "Сталася помилка під час відновлення проєкту %s та його файлів." @@ -450,37 +450,37 @@ msgstr "Сталася помилка під час відновлення пр msgid "dashboard.export-binary-multi" msgstr "Завантажити %s файлів Penpot (.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "Експортувати дошки у PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "Експортувати у PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "Було виділено %s з %s елементів" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "Експорт" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "Ви можете додати налаштування експорту до елементів у властивостях дизайну " "(у нижній частині правої панелі)." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "Інформація про те, як налаштувати експорти в Penpot." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "У Вас немає елементів з налаштуваннями експорту." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "Вибір експорту" @@ -498,36 +498,36 @@ msgstr[0] "Відновити файл" msgstr[1] "Відновити кілька файлів" msgstr[2] "Відновити файли" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "Відсутній шрифт" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "Відхилити усе" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "Користувацькі шрифти, які Ви завантажите, з'являться тут." -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "Немає користувацьких шрифтів." -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "" "Як тільки учасник проєкту завантажить користувацький шрифт, він зʼявиться " "тут." -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "%s шрифт було додано" msgstr[1] "%s шрифта було додано" msgstr[2] "% s шрифтів було додано" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -537,7 +537,7 @@ msgstr "" "шрифтів**. Ви можете вивантажувати шрифти у таких форматах: **TTF, OTF і " "WOFF** (потрібен лише один)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -546,11 +546,11 @@ msgstr "" "[Умовах користування Penpot](https://penpot.app/terms.html). Ви також " "можете прочитати про [ліцензування шрифтів](https://www.typography.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "Вивантажити усе" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -561,15 +561,15 @@ msgstr "" "використовувати [Transfonter](https://transfonter.org/) для генерації " "веб-шрифтів і виправлення помилок. " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "Перейти до Адмін. Панелі" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "Імпортувати файли Penpot" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "Отакої! Ми не можемо імпортувати цей файл" @@ -577,38 +577,38 @@ msgstr "Отакої! Ми не можемо імпортувати цей фа msgid "dashboard.import.bad-url" msgstr "Імпортування не вдалось. URL шаблону неправильне" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "Не вдалось перевірити файл." -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "Файл здається пошкодженим." -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "Не всі файли було імортовано" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "Наступні файли мають помилки:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "Файли з помилками не буде вивантажено." -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "Щось пішло не так під час обробки файлу." -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "%s файл успішно імпортовано." msgstr[1] "%s файли успішно імпортовано." msgstr[2] "%s файлів успішно імпортовано." -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "Деякі файли містили помилкові об'єкти, які було вилучено." @@ -616,81 +616,53 @@ msgstr "Деякі файли містили помилкові об'єкти, msgid "dashboard.import.no-perms" msgstr "Ви не маєте права імпортувати в цю команду" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "Обробка кольорів" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "Обробка компонентів" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "Обробка медіа" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "Обробка сторінки: %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "Обробка типографік" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "Вивантаження даних на сервер (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "Вивантаження файлу: %s" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "Ви збираєтесь вилучити запрошення до:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "Ви збираєтесь перенадіслати запрошення до:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "Вилучити запрошення" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "Перенадіслати запрошення" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "" "Ви не маєте дозволів на створення, редагування чи видалення запрошень до " "команди." -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "Запросити людей" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "Покинути команду" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "Бібліотеки та Шаблони" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "Тут знаходяться Бібліотеки та Шаблони, які можна додати до проєкту" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "Дізнайтесь більше про них та як зробити внесок" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "Виникла проблема під час імпортування шаблону. Його не було імпортовано." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "Бібліотеки" @@ -698,7 +670,7 @@ msgstr "Бібліотеки" msgid "dashboard.loading-files" msgstr "завантаження ваших файлів…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "завантаження ваших шрифтів…" @@ -718,57 +690,57 @@ msgstr "Перемістити (%s) файлів до" msgid "dashboard.move-to-other-team" msgstr "Перенести в іншу команду" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ Новий файл" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "Новий файл" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ Новий проєкт" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "Новий проєкт" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "Жодних відповідностей за \"%s\" не знайдено" -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "" "Ви не маєте дозволів на створення команд у організації %s. Для подробиць " "зверніться до її власника." -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "" "Ви не маєте дозволів на видалення команд що є частиною організації %s. Для " "подробить зверніться до її власника." -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "" "Ви не маєте дозволів переміщати команди, що належать організації %s. Для " "подробиць зверніться до її власника." -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "Закріплені проєкти з'являться тут" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "Сповіщення" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "Адресу Вашої електронної пошти було успішно змінено" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "Адресу Вашої електронної пошти було успішно підтверджено" @@ -780,11 +752,11 @@ msgstr "Налаштування сповіщень оновлено" msgid "dashboard.notifications.password-saved" msgstr "Пароль успішно збережено!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "Показати сповіщення" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s учасників" @@ -792,15 +764,15 @@ msgstr "%s учасників" msgid "dashboard.open-in-new-tab" msgstr "Відкрити файл у новій вкладці" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "Параметри" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "Сортувати за роллю" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "Сортувати за статусом" @@ -824,7 +796,7 @@ msgstr "Відтепер Ви власник цієї команди." msgid "dashboard.permissions-change.viewer" msgstr "Відтепер Ви переглядач цієї команди." -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "Пришпилити/відшпилити" @@ -840,27 +812,27 @@ msgstr "Не вдається розібрати маніфест плагіну msgid "dashboard.plugins.try-plugin" msgstr "Спробуйте плагін: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "Вилучаю файли…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "Відновлюю файли…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "Вилучення триває неочікувано довго" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "Відновлення триває неочікувано довго" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "Проєкти" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "Ви хочете видалити свій обліковий запис?" @@ -868,71 +840,71 @@ msgstr "Ви хочете видалити свій обліковий запи msgid "dashboard.removed-from-team" msgstr "Ви більше не є частиною команди \"%s\"." -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "Ви збираєтесь відновити усі проєкти та їх файли. Це може зайняти деякий час." -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "Відновити усі проєкти й файли" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "Відновити Все" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "%s файлів було успішно відновлено." -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "Відновити проєкт" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "Ви збираєтесь відновити %s проєктів та всі їхні файли." -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "Відновити Проєкт" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s було успішно відновлено." -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "Зберегти налаштування" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "Пошук…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "Пошук за \"%s\"…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "Організації" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "Оберіть мову інтерфейсу" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "Оберіть тему" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot Темний (завмовчки)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot Світлий" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "Системна тема" @@ -996,11 +968,11 @@ msgstr "Оновити налаштування" msgid "dashboard.settings.notifications.title" msgstr "Сповіщення" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "Показати усі файли" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "Історія версій" @@ -1026,7 +998,7 @@ msgstr[2] "Файли були успішно дубліковані" msgid "dashboard.success-duplicate-project" msgstr "Ваш проєкт успішно дубліковано" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "Файл було успішно переміщено" @@ -1038,159 +1010,159 @@ msgstr "Файли було успішно переміщено" msgid "dashboard.success-move-project" msgstr "Ваш проєкт було успішно переміщено" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "Відомості про команду" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "Учасники команди" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "Команда організації" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "Додати до організації" -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "Змінити команду організації" -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "Ця команда не належить до жодної з організацій" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "Вилучити команду з організації" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "Проєкти команди" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "Додати до проєкту" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "Тема інтерфейсу" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "Результати пошуку" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "Вилучені файли лишатимуться у смітнику на" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s днів. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "Після цього, ви позбудетесь від них назавжди." -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "" "Якщо вирішете інакше, Ви маєте змогу відновити їх або позбутись їх назавжди " "у меню кожного з них." -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "Вкажіть для пошуку" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "Вилучити бібліотеку з публікації" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "Оновити налаштування" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "Бета" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "" "Відмальовування WebGL може покращити швидкодію, проте функція в беті, тому " "є менш стабільною. Можуть зʼявлятись візуальні відмінності між полотном, " "експортами (SVG/PDF) та режимом перегляду." -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "Вимкнено" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "Увімкнено" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "Зворотній звʼязок" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "Стан" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "Відмальовування WebGL" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "Активний" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "Після спрацювання хуку дані події будуть надіслані" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "Ви можете видаляти або редагувати лише створені Вами вебхуки." -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "Тип вмісту" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "Створити вебхук" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Вебхук успішно створено." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "Веб-хуки - це простий спосіб дозволити іншим веб-сайтам і застосункам " "отримувати сповіщення про певні події в Penpot. Ми надішлемо POST-запит на " "кожну з вказаних вами URL-адрес." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "Натисніть кнопку \"Додати вебхук\" щоб створити його." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "У вас поки що немає вебхуків." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "Ваш обліковий запис" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "Електронна пошта" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "Ваше ім'я" @@ -1206,11 +1178,11 @@ msgstr "Увага" msgid "ds.component-subtitle" msgstr "Компоненти для оновлення:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "Дозволити" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "Скасувати" @@ -1230,7 +1202,7 @@ msgstr "У активних наборах й темах не знайдено msgid "ds.inputs.numeric-input.no-matches" msgstr "Збігів не виявлено." -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "Відкрити список токенів" @@ -1242,15 +1214,15 @@ msgstr "Провайдер автентифікації не дозволени msgid "errors.auth-provider-not-configured" msgstr "Провайдер для автентифікації не налаштований." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "Схоже, Ви не автентифіковані, або термін дії сесії минув." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "Шрифт %s не можна завантажити" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "Шрифти %s не можна завантажити" @@ -1262,53 +1234,53 @@ msgstr "Не можна вивантажити медіа." msgid "errors.character-limit-exceeded" msgstr "Забагато символів" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "" "API буферу обміну недоступне. Надайте доступ до Penpot через HTTPS, щоб " "увімкнути доступ" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "Ваш браузер не може зробити це" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "" "Відмовлено у доступі до буферу обміну. Надайте дозвіл на використання " "буферу обміну у Вашому бровзері аби вставити вміст" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "З коментарем сталась помилка" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "" "Вибачте! Це старий файл, що використовує застарілий тип ресурсів Penpot, і " "їх не можна відкрити." -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "щоб ми Вам допомогли." -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "Оскільки Penpot не підтримує цей тип Ресурсів, Ви можете" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "звʼязатись з нами" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "Не можна кинути батьківський набір на приналежний дочірній." -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "Електронна пошта вже використовується" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "Електронна пошта вже підтверджена." @@ -1320,33 +1292,33 @@ msgstr "Ви не можете використати електронну ад msgid "errors.email-does-not-match-invitation" msgstr "Поштова адреса не відповідає тому що в запрошенню." -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "Цей домен не дозволений" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "" "Електронна пошта \"%s\" має багато скарг про постійне повернення " "повідомлень." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "" "Електронну адресу «%s» позначено як спам або вона постійно повертає " "повідомлення." -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "" "Схоже, Ви намагаєтесь відкрити файл, який має активовану функцію \"%s\", " "але поточна версія Penpot наразі не підтримує її або вона вимкнена." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Функція '%s' не підтримується." -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "Повинно містити щонайменше %s символів." @@ -1354,7 +1326,7 @@ msgstr "Повинно містити щонайменше %s символів." msgid "errors.field-min-length" msgstr "Має містити принаймні 1 символ." -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "Порожнє поле" @@ -1362,24 +1334,24 @@ msgstr "Порожнє поле" msgid "errors.field-not-all-whitespace" msgstr "Ім'я повинно містити якийсь символ замість пробілу." -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "" "Здається, існує невідповідність між увімкненими функціями та функціями " "файлу, який Ви намагаєтеся відкрити. Перш ніж відкрити файл, потрібно " "застосувати міграції для \"%s\"." -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "" "Імʼя сімейства шрифтів може мати лише літери, числа, пробіли, дефіси, нижні " "підкреслювання та крапки." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Виникла якась неполадка." -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Під час роботи веб-виконавця сталась помилка." @@ -1395,7 +1367,7 @@ msgstr "Помилкові дані" msgid "errors.invalid-email" msgstr "Будь ласка, вкажіть дійсну електрону адресу" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Підтвердження має відповідати електронній пошті" @@ -1429,11 +1401,11 @@ msgstr "Це запрошення могло бути відкликане аб msgid "errors.ldap-disabled" msgstr "Автентифікація через LDAP вимкнена." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "Ви досягли квоти \"%s\". Зв'яжіться із службою підтримки." -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "" "Дрсягнуто максимальної кількості поштових скриньок, на які можна надіслати " @@ -1451,21 +1423,21 @@ msgstr "Схоже, вміст зображення не відповідає р msgid "errors.media-type-not-allowed" msgstr "Схоже, це зображення не є дійсним." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Електронна пошта профілю, який Ви намагаєтесь запросити, має приглушення " "листів (скарги на спам або постійне пересилання)." -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "Міграція триває" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "Тільки власник версії може заблокувати її" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "Тільки власник версії може розблокувати її" @@ -1477,7 +1449,7 @@ msgstr "Підтверджувальний пароль має збігатис msgid "errors.password-too-short" msgstr "Пароль повинен мати, принаймні, 8 символів" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "Помилкові дані в буфері обміну" @@ -1485,7 +1457,7 @@ msgstr "Помилкові дані в буфері обміну" msgid "errors.profile-blocked" msgstr "Профіль заблоковано" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "" "Для Вашого профілю приглушено листи (скарги на спам або постійне " @@ -1495,25 +1467,25 @@ msgstr "" msgid "errors.registration-disabled" msgstr "Реєстрація наразі вимкнена." -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG недійсний або сформований неправильно" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "Виявлено несумісну функцію \"%s\"" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "" "Недостатньо учасників, щоб покинути команду, яку Ви, ймовірно, хочете " "видалити." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Учасник, якого Ви намагаєтеся призначити, не існує." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "" "Власник не може покинути команду, тому Ви повинні спершу призначити нового " @@ -1523,11 +1495,11 @@ msgstr "" msgid "errors.token-set-already-exists" msgstr "Набір з таким імʼям уже існує" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "Не вдалось перекинути, набір з таким імʼям вже існує." -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "Параметри Теми з таким імʼям вже існують" @@ -1562,11 +1534,11 @@ msgstr "" "Імена токену можуть містити в собі літери та цифри, розділені крапкою та не " "повинні починатись з $." -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "Помилкове значення токену: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "Відсутні посилання на токен: " @@ -1574,19 +1546,19 @@ msgstr "Відсутні посилання на токен: " msgid "errors.tokens.number-too-large" msgstr "Помилкове значення токену. Отримане значення завелике: %s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "Непрозорість має бути між 0 та 100% або ж між 0 та 1 (де 0.5 - 50%)." -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Токен має самопосилання" -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "Ширина обведення має бути більшим ніж 0 або дорівнювати 0." -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "Помилкове значення: Одиниці не дозволені." @@ -1594,57 +1566,57 @@ msgstr "Помилкове значення: Одиниці не дозволе msgid "errors.unexpected-error" msgstr "Виникла неочікувана помилка." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Невідомий токен" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "Ця версія вже заблокована" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "Ця версія заблокована і не може бути видалена іншими" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "Файл має несумісний номер версії" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "" "WebGL припинив працювати. Просимо Вас перезавантажити сторінку, щоб скинути " "його" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "От халепа! Полотно загубилось" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Помилка під'єднання, адреса недосяжна" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL не пройшов перевірку." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Остання спроба доставки була невдалою." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Помилка під час перевірки SSL." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Час вичерпано" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Неочікувана помилка під час перевірки" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Неочікуваний статус %s" @@ -1664,17 +1636,17 @@ msgstr "Опис" msgid "feedback.description-placeholder" msgstr "Просимо Вас описати причину відгуку" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Ми раді вас бачити. Якщо потрібна допомога, спешру спробуйте знайти перш " "ніж публікувати." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Спільнота Penpot" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "Інші способи звʼязатись з нами" @@ -1698,11 +1670,11 @@ msgstr "" msgid "feedback.title-contact-us" msgstr "Звʼяжіться з нами" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "Ми готові допомогти з вашими технічними питаннями." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Обліковий запис служби підтримки в X" @@ -1722,10 +1694,6 @@ msgstr "Ідея" msgid "feedback.type.issue" msgstr "Вада" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "Вивантажити файли" - #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "Виникла помилка" @@ -1762,7 +1730,7 @@ msgstr "Перегляд зображення заповнення фігури" msgid "inspect.attributes.shadow" msgstr "Тінь" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Розмір та розміщення" @@ -1770,39 +1738,27 @@ msgstr "Розмір та розміщення" msgid "inspect.attributes.stroke" msgstr "Обведення" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "По центру" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Всередину" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Назовні" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Типографія" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Сімейство Шрифта" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Розмір шрифта" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Стиль шрифта" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Відступ між літерами" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Висота лінії" @@ -1814,11 +1770,11 @@ msgstr "Немає" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Підкреслений" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Заголовковий регістр" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Нижній регістр" @@ -1830,7 +1786,7 @@ msgstr "Який є" msgid "inspect.attributes.typography.text-transform.unset" msgstr "Не встановлено" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Верхній регістр" @@ -1952,77 +1908,77 @@ msgstr "Знайдене значення:" msgid "inspect.tabs.styles.variants-panel" msgstr "Параметри Варіанту" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "" "Це URL серверу, для якого потрібно налаштувати MCP клієнт для того щоб " "зʼєднати його з сервером MCP Penpot'у." -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "Регенерувати ключ MCP" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "Ключ MCP" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "MCP ключ потрібен для налаштування клієнту MCP" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "Стан" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "Вимкнено" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "Увімкнено" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "Позначити все як прочитане" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Скорочення" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "Про Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "Прийняти" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Активне" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "Додати" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Додати користувацькі шрифти" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "Додавання..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Адміністратор" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe ExpDesign" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Всі" @@ -2030,17 +1986,17 @@ msgstr "Всі" msgid "labels.and" msgstr "і" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Назад" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Схоже, вам потрібно трохи почекати і спробувати ще раз; ми проводимо " "невелике технічне обслуговування наших серверів." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Поганий шлюз" @@ -2048,15 +2004,15 @@ msgstr "Поганий шлюз" msgid "labels.blur" msgstr "Розмиття" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Скасувати" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Канва" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Закрити" @@ -2064,7 +2020,7 @@ msgstr "Закрити" msgid "labels.code" msgstr "Код" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "Згорнути" @@ -2076,15 +2032,15 @@ msgstr "Колір" msgid "labels.comment" msgstr "Коментар" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "Коментарі" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Спільнота" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "Спільнота й Внески" @@ -2096,19 +2052,19 @@ msgstr "Обчислено" msgid "labels.confirm-password" msgstr "Підтвердити пароль" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "Звʼязатись з підтримкою" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "Звʼяжіться з нами" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Продовжити" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "Ви можете продовжувати з обліковим записом Penpot" @@ -2120,31 +2076,31 @@ msgstr "Копіювати" msgid "labels.copy-color" msgstr "Копіювати колір" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "Копіювати посилання" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019-дотепер" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Створити" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Створити нову команду" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Вкажіть ім'я нової команди" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Панель керування" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Видалити" @@ -2156,7 +2112,7 @@ msgstr "Видалити коментар" msgid "labels.delete-comment-thread" msgstr "Видалити гілку" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Видалити запрошення" @@ -2164,23 +2120,23 @@ msgstr "Видалити запрошення" msgid "labels.delete-multi-files" msgstr "Видалити %s файлів" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "Вилучено" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Розробка" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Відхилити" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "Завантажити %s" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Чернетки" @@ -2188,27 +2144,27 @@ msgstr "Чернетки" msgid "labels.duplicate" msgstr "Дублювати" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "Редагувати" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Редактор" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "Порожньо" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "Помилка" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Сплинув термін дії" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Експорт" @@ -2220,7 +2176,7 @@ msgstr "Відгук не доступний" msgid "labels.feedback-sent" msgstr "Відгук надіслано" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Фіґма" @@ -2228,55 +2184,55 @@ msgstr "Фіґма" msgid "labels.fill" msgstr "Заповнити" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Сімейство Шрифтів" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Стилі" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Шрифти" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Репозиторій Github" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Надішліть відгук" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Повернутись назад" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "Графічний дизайн" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Центр Допомоги" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "Допомога й Навчання" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "Приховати" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Приховати вирішені коментарі" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "Імпортувати" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "Не активний" @@ -2284,47 +2240,47 @@ msgstr "Не активний" msgid "labels.info" msgstr "Інформація" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Встановлені шрифти" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "Сталася халепа." -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "Спробуйте знову чи повідомте службу підтримки про помилку." -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Внутрішня помилка" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "ІнВіжн" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Запрошення" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Мова" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "Шаблон" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "Навчальний Центр" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Бібліотеки та Шаблони" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "Завантаження…" @@ -2332,7 +2288,7 @@ msgstr "Завантаження…" msgid "labels.lock" msgstr "Заблокувати" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "Увійти або зареєструватись" @@ -2340,19 +2296,19 @@ msgstr "Увійти або зареєструватись" msgid "labels.login" msgstr "Увійти" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Вийти" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Маркетинг" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Учасник" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Учасники" @@ -2364,45 +2320,45 @@ msgstr "Згадка" msgid "labels.new-password" msgstr "Новий пароль" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Далі" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "Ви все наздогнали! Нові повідомлення про коментарі з’являться тут." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "У вас, поки що, немає запрошень." -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "Збирайте людей та створюйте чудові речі разом." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Отакої!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "Сповіщення" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "%s файл" msgstr[1] "%s файли" msgstr[2] "%s файлів" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "%s дошка" msgstr[1] "%s дошок" msgstr[2] "%s дошок" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "%s проєкт" @@ -2413,39 +2369,39 @@ msgstr[2] "%s проєктів" msgid "labels.old-password" msgstr "Старий пароль" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "Параметри" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Інше (вкажіть)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "Інше" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Власник" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Пароль" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Очікування" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Список Змін Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Хаб Penpot" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "Пришпилені проєкти" @@ -2453,31 +2409,31 @@ msgstr "Пришпилені проєкти" msgid "labels.post" msgstr "Публікація" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Попередній" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "Розробка продуктів чи UX" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Керування продуктами" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Профіль" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Проєкти" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "Нещодавні" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "Посилання" @@ -2485,27 +2441,27 @@ msgstr "Посилання" msgid "labels.refresh" msgstr "Перезавантажити" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Примітки до випуску" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "Перезавантажити сторінку" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Видалити" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Вилучити учасника" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Перейменувати" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Перейменувати команду" @@ -2529,11 +2485,11 @@ msgstr "нова відповідь" msgid "labels.reply.thread" msgstr "Відповідь" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "Перенадіслати" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Повторно надіслати запрошення" @@ -2541,43 +2497,43 @@ msgstr "Повторно надіслати запрошення" msgid "labels.restore" msgstr "Відновити" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Повторити" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Роль" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Зберегти" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "Пошук" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Пошук шрифта" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Обрати варіант" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Надіслати" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Надсилаю…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "Ми проводимо програмне обслуговування наших систем." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Сервіс недосяжний" @@ -2585,55 +2541,55 @@ msgstr "Сервіс недосяжний" msgid "labels.sets" msgstr "Набори" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Налаштування" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "Тінь" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "Поділитись" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Бібліотеки" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "Показати" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Показати усі коментарі" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Показати список коментарів" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "Показувати лише ваші згадки" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Показати лише ваші коментарі" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Ескіз" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "Джерела" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Початок" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Статус" @@ -2641,7 +2597,7 @@ msgstr "Статус" msgid "labels.stroke" msgstr "Обрамлення" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Студент або викладач" @@ -2649,7 +2605,7 @@ msgstr "Студент або викладач" msgid "labels.styles" msgstr "Стилі" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "Перемикач" @@ -2657,19 +2613,19 @@ msgstr "Перемикач" msgid "labels.text" msgstr "Текст" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "Теми" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Посібники" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "Типографіка" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "Невідома помилка" @@ -2681,31 +2637,31 @@ msgstr "Розблокувати" msgid "labels.unpublish-multi-files" msgstr "Скасувати публікування %s файлів" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "Оновити" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Оновити команду" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Вивантажити" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Вивантажити користувацькі шрифти" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Вивантажую…" -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "Варіянт" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "Примітки версії %s" @@ -2713,7 +2669,7 @@ msgstr "Примітки версії %s" msgid "labels.view-only" msgstr "Лише для перегляду" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Глядач" @@ -2721,7 +2677,7 @@ msgstr "Глядач" msgid "labels.visibility" msgstr "Видимість" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Вебхуки" @@ -2729,11 +2685,11 @@ msgstr "Вебхуки" msgid "labels.write-new-comment" msgstr "Написати новий коментар" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(Ви)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Ваш обліковий запис" @@ -2874,61 +2830,61 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Додати \"%s\" як Спільну Бібліотеку" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Великий зсув" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Підтвердьте нову пошту" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "Ми надішлемо вам лист на вашу поточну електрону адресу \"%s\" для " "підтвердження вашої особи." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Нова електронна пошта" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Змінити електрону адресу" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Змінити свою електрону адресу" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Створити вебхук" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Створити вебхук" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Адреса пейлоду" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://savelife.in.ua/en" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Скасувати та лишити мій обліковий запис" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Так, видалити мій обліковий запис" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "Видаливши обліковий запис, Ви втратите усі свої проєкти та архіви." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Ви впевнені, що хочете видалити обліковий запис?" @@ -2978,31 +2934,31 @@ msgstr "Ви впевнені, що хочете видалити %s файлі msgid "modals.delete-file-multi-confirm.title" msgstr "Видалення %s файлів" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Ви впевнені, що хочете видалити стиль шрифта? Він не завантажиться якщо " "буде використаний у файлі." -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "Видалення стилю шрифта" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Ви впевнені, що хочете видалити цей шрифт? Він не завантажиться якщо буде " "використаний у файлі." -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "Видалення шрифта" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Ви впевнені, що хочете видалити цю сторінку?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Видалити сторінку" @@ -3018,35 +2974,35 @@ msgstr "Ви впевнені, що хочете видалити цей про msgid "modals.delete-project-confirm.title" msgstr "Видалити проєкт" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "Видалити файл" msgstr[1] "Видалити файли" msgstr[2] "Видалити файли" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "Це не було активовано у жодному з файлів." msgstr[1] "Вони не були активовані у жодному з файлів." msgstr[2] "Вони не були активовані у жодному з файлів." -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "Цю білбіотеку активовано тут: " msgstr[1] "Ці білбіотеки активовано тут: " msgstr[2] "Ці білбіотеки активовано тут: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "Ви впевнені, що хочете видалити цей файл?" msgstr[1] "Ви впевнені, що хочете видалити ці файли?" msgstr[2] "Ви впевнені, що хочете видалити ці файли?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "Видалення файлу" @@ -3067,19 +3023,19 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Видалення команди" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Видалити учасника" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Ви впевнені, що хочете видалити цього учасника з команди?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Видалити учасника команди" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "" @@ -3092,170 +3048,170 @@ msgstr[2] "" "Ресурси, які вже були використані в цих файлах, залишаться там (дизайн не " "буде порушено)." -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Видалити вебхук" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Ви впевнені, що хочете видалити цей вебхук?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Видалення вебхуку" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Змінити вебхук" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Змінити вебхук" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Надіслати запрошення" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Електроні адреси, розділені комами" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "Деякі учасники вже запрошені, ми запросимо решту." -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "" "Ви можете запросити учасників до команди для того щоб вони мали доступ до " "цього та інших файлів команди." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Запросити учасників до команди" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Так як Ви єдиний учасником цієї команди, її буде видалено разом з проєктами " "та файлами команди." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Ви впевнені, що хочете покинути команду \"%s\"?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "Ви не можете покинути команду, якщо немає іншого учасника, якого можна було " "б підвищити до власника. Ви можете видалити команду." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Ви власник цієї команди. Будь ласка, оберіть іншого учасника, щоб підвищити " "його до власника перш ніж покинути команду." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Підвищити та покинути" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Виберіть учасника для підвищення" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Перед тим як ви покинете" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Покинути команду" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Ви впевнені, що хочете покинути цю команду?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Покидання команди" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "Перемістити його" msgstr[1] "Перемістити їх" msgstr[2] "Перемістити їх" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "Ви впевнені, що хочете перемістити цю білбліотеку?" msgstr[1] "Ви впевнені, що хочете перемістити ці бібліотеки?" msgstr[2] "Ви впевнені, що хочете перемістити цю білбліотеку?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "Перемістити бібліотеку" msgstr[1] "Перемістити бібліотеки" msgstr[2] "Перемістити бібліотеки" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Розмір зсуву" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Передати права власності" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Якщо Ви передасте права власності, Ви зміните свою роль на Admin, втративши " "деякі повноваження над цією командою. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Ви поточний власник цієї команди. Ви впевнені, що хочете призначити %s " "новим власником цієї команди?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Новий власник команди" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Опублікувати" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Ваша бібліотека порожня. Ви впевнені, що хочете опублікувати її?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Опублікувати пусту бібліотеку" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Малий зсув" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "Зняти з публікації поширену сторінку" msgstr[1] "Зняти з публікації декілька поширених сторінок" msgstr[2] "Зняти з публікації поширені сторінки" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "Ви впевнені, що хочете зняти бібліотеку з публікації?" msgstr[1] "Ви впевнені, що хочете зняти бібліотеки з публікації?" msgstr[2] "Ви впевнені, що хочете зняти бібліотеки з публікації?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "Зняти бібліотеку з публікації" @@ -3280,11 +3236,11 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Оновити компонент спільної бібліотеки" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "Цієї сторінки не існує :/" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "Сторінка 404" @@ -3304,11 +3260,11 @@ msgstr "" "Перевірте вхідні листи та натисніть на посилання для створення нового " "паролю." -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "Зареєструватись безкоштовно" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "І почати використовувати Penpot у ту ж мить!" @@ -3316,67 +3272,67 @@ msgstr "І почати використовувати Penpot у ту ж мит msgid "not-found.made-with-love" msgstr "Створено з ЛЮБОВ'Ю та Open Source" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "Ви вже запитували дозвіл на доступ до цього файлу." -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "" "Ви вже запитували доступ до цього файлу або інших файлів чи проєктів цієї " "команди." -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "" "Ви вже запитували доступ до цього проєкту або інших проєктів чи файлів цієї " "команди." -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "Ви вже запитували дозвіл на доступ до цього проєкту." -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "ЗАПИТ НА ДОЗВІЛ" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "Запам'ятайте - якщо власник погодить, ви будете запрошені до команди." -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "Ваш запит надіслано успішно!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "Ви не маєте доступу до цього файлу." -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "Відвідати ваш Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "Якщо власник погодить, Ви будете запрошені до команди." -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Файл Penpot" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "Ви не маєте доступу до цього проєкту." -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "ПРОЄКТ" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "Щоб мати доступ до файлу, спершу запитайте власника команди." -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "Щоб мати доступ до цього проєкту, запитайте у власника команди." @@ -3386,29 +3342,29 @@ msgstr "" "Перерва на технічне обслуговування: ми закінчимо технічне обслуговування " "протягом 5 хвилин." -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "Запрошення вилучено успішно" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Запрощення успішно надіслано" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "Посилання на запрошення скопійовано" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "" "Ви не можете видалити свій профіль. Перш ніж продовжити, перепризначте свої " "команди." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "Профіль успішно збережено!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Лист для підтвердження надіслано на %s. Перевірте свою скриньку!" @@ -3440,78 +3396,78 @@ msgstr "" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "Створити команду" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Запрости учасників" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Запросити у якості:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Тож почнімо!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "Генерування реального коду з дизайнів" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "Створення дизайнерських систем" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "Прототипування" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "Розробка UI/UX для застосунків" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "Фреймворкінг" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "Для чого плануєте використовувати Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "" "Розкажіть нам трохи про себе, щоб допомогти нам зробити Penpot зручним для " "вас. Ваші відповіді допоможуть нам визначити пріоритетність нових функцій і " "вказати Вам потрібний напрямок для початку роботи." -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "Допоможіть нам познайомитися з вами ближче" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "Який з цих інструментів використовуєте найчастіше?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "Чим займаєтесь?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "Розкажіть про вашу роботу" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "Звідки хотіли б почати?" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "Навчання" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "Особисте" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "Робоче" @@ -3561,11 +3517,11 @@ msgstr "Почніть проєктувати" msgid "profile.recovery.go-to-login" msgstr "Повернутись на сторінку входу" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Від'єднати" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Змішаний" @@ -3577,35 +3533,35 @@ msgstr "Видалити колір" msgid "settings.select-this-color" msgstr "Оберіть елементи, використовуючи цей стиль" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Коментарі" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Вирівняти по низу" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Вирівняти по центру горизонтально" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Вирівняти по лівому краю" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Вирівняти по правому краю" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Вирівняти по верху" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Вирівняти по центру вертикально" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Створити дошку з вибраного" @@ -3613,55 +3569,55 @@ msgstr "Створити дошку з вибраного" msgid "shortcuts.bold" msgstr "Виділити жирним шрифтом" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Булева різниця" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Логічне виключення" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "Булева інтерсекція" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Булеве об'єднання" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Відсунути на задній план" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Посунути нижче" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Посунути вище" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Відсунути на передній план" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Скасувати відміну" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Скопіювати" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "Скопіювати посилання у буфер" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "Копіювати параметри" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "Створити компонент/варіянт" @@ -3669,35 +3625,35 @@ msgstr "Створити компонент/варіянт" msgid "shortcuts.create-new-project" msgstr "Створити новий" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Вирізати" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Зменшити" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Видалити" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Видалити вузол" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Від'єднати компонент" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Крива" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Еліпс" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "Рамка" @@ -3705,39 +3661,39 @@ msgstr "Рамка" msgid "shortcuts.draw-nodes" msgstr "Намалювати шлях" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Контур" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Прямокутник" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Текст" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Дублікат" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Відмінити" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Експортувати фігури" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Збільшити, щоб вмістити все" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Перевернути горизонтально" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Перевернути вертикально" @@ -3761,23 +3717,23 @@ msgstr "Відвідати спільні бібліотеки" msgid "shortcuts.go-to-search" msgstr "Пошук" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Група" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Розподілити горизонтально" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Показати/приховати інтерфейс" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Збільшити" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Вставити зображення" @@ -3785,7 +3741,7 @@ msgstr "Вставити зображення" msgid "shortcuts.italic" msgstr "Виділити курсивом" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Приєднати вузли" @@ -3793,39 +3749,39 @@ msgstr "Приєднати вузли" msgid "shortcuts.line-through" msgstr "Перемкнути рядок через" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Створити кут" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Створити криву" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Маска" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Об'єднання вузлів" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Перемістити" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Швидко перемістити униз" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Швидко перемістити ліворуч" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Швидко перемістити праворуч" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Швидко перемістити вгору" @@ -3833,333 +3789,333 @@ msgstr "Швидко перемістити вгору" msgid "shortcuts.move-nodes" msgstr "Перемістити вузол" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Перемістити вниз" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Перемістити ліворуч" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Перемістити праворуч" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Перемістити вгору" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Наступна дошка" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Скорочень не знайдено" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Непрозорість на 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Непрозорість на 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Непрозорість на 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Непрозорість на 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Непрозорість на 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Непрозорість на 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Непрозорість на 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Непрозорість на 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Непрозорість на 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Непрозорість на 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Підбір кольору" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "Перейти до коментарів глядача" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Перейти до панелі керування" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Перейти до інспекції глядача" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "Перейти до взаємодій глядача" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Перейти до взаємодій глядача" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "Перейти до робочого простору" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " або " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Вставити" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "Вставити параметри" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "Менеджер плагінів" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Попередня дошка" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Повернути внесені зміни" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "Перейменувати" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "Скинути збільшення" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "Масштабувати" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Основи" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "Панель управління" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Глядач" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Робоче поле" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Обрати все" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "Обрати наступний шар" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Обрати батьківський шар" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "Обрати попередній шар" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "Відокремити вузли" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "Показати / приховати піксельну сітку" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Показати / приховати скорочення" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Прив'язати до вузлів" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Прив'язати до піскельної сітки" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "Почати редагування" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Вирівнювання" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Редагувати" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "Загальні" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Головне меню" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Модифікувати шар" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Навігація" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Навігація" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Навігація" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Панелі" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Контури" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Форми" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Тексти" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Інструменти" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Масштабування" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Масштабування" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Вставити мініатюри" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "Клавіатурні скорочення" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Перемкнути динамічне вирівнювання" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Обрати ресурси" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Обрати палітру кольорів" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Обрати режим фокусування" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Перейти в повноекраний режим" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "Показати/приховати орієнтири" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "Обрати історію" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Обрати шари" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "Додати / вилучити гнучкий макет" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "Додати / вилучити макет сітки" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Блокувати / розблокувати" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "Заблокувати пропорції" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "Показати / приховати лінійки" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "Прив'язувати до орієнтирів" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "Прив'язувати до орієнтирів лінійок" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Обрати палітру тексту" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "Змінити тему" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Показати / приховати" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Обрати стиль збільшення" @@ -4167,31 +4123,31 @@ msgstr "Обрати стиль збільшення" msgid "shortcuts.underline" msgstr "Обрати нижнє підкреслення" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Скасувати попередню дію" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Розбити групу" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Зняти маску" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Розподілити вертикально" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Зменшити об'єктив лінзи" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Збільшити об'єктив лінзи" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Збільшити до обраного" @@ -4282,204 +4238,204 @@ msgstr "" msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "Запрошення людей з Безлімітним планом" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "Підписки" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "Додайте спосіб оплати, щоб продовжити після пробного періоду" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "Усі переваги Професійного плану разом з:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "Усі переваги безлімітного плану та:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s редакторів)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Корпоративний" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Корпоративний (пробний)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90-денні автозбережнні версії та відновлення файлів" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "Місячний рахунок з граничною сумою" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "Безлімітне сховище" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "Керувати підписками" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "Додати платіжні засоби" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "" "Додайте платіжні засоби щоб продовжити Вашу підписку після кінця пробного " "терміну й продовжити підтримувати наш відкритий проєкт. Плату поки не буде " "стягнуто." -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "Пропустити й розпочати пробний період" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "Допоможіть нам рости й зробіть свій пробний період простіше" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "Наразі Ви маєте %s людину, які можуть редагувати, поміж Ваших команд." msgstr[1] "Наразі Ви маєте %s людини, які можуть редагувати, поміж Ваших команд." msgstr[2] "Наразі Ви маєте %s людей, які можуть редагувати, поміж Ваших команд." -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "" "Увага: перехід на легший план зменшить обсяг сховища, тривалість резервних " "копій та історії версій." -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "Редактори" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(Власники, Адміни та Редаткори. Глядачі не є Редакторами)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "" "Ви не можете встановити менше редакторів ніж маєте зараз. Змініть роль " "(редактор/адмін на глядача) людей, які не редагують файли, в параметрах " "команди." -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "Плата опісля пробного терміну. Наразі кредитна карта не потрібна." -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s** на місяць" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "Застосувати %s до Вашої команди" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "" "Порада: Кількість місць можна збільшити зараз, щоб бути попереду запрошень. " "На 25+ редакторів поміж команд, Ви насолоджуватиметесь фіксованою платою у " "$175 на місяць." -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "Учасник Penpot з %s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "Більше інформації" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Інші плани Penpot" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "організацій на місяць" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Професійний" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "7-денні автозбережені версії та відновлення файлів" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10ГБ сховища" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "Безлімітні команди. До 8 редакторів поміж Ваших команд." -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "Рекомендовано" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "Ваша підписка" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "Розпочати безкоштовний пробний період" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "Оформити підписку" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "" "Ви можете змінити власні підписки у будь-який час зі сторінки 'Підписки', " "що у подробицях облікового запису." -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "Насолоджуйтесь планом!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "Дякуємо за вибір плану %s Penpot!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "Ви %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "Спробуйте перші 14 днів безкоштовно" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Безлімітний" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Безлімітний (пробний)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "30-денні автозбережені версії та відновлення файлів" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "Місячний рахунок з граничною сумою" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25ГБ сховища" @@ -4504,42 +4460,42 @@ msgstr "" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "Якщо бажаєте збільшити цей ліміт, [оновіть план|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "%s запрошення обрано" msgstr[1] "%s запрошення обрано" msgstr[2] "%s запрошень обрано" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Постачальники шрифтів - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Шрифти - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Проєкти - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Пошук - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "Спільні бібліотеки - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - свобода дизайну для команд" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Надіслати відгук - Penpot" @@ -4547,7 +4503,7 @@ msgstr "Надіслати відгук - Penpot" msgid "title.settings.notifications" msgstr "Сповіщення - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Налаштування - Penpot" @@ -4555,27 +4511,27 @@ msgstr "Налаштування - Penpot" msgid "title.settings.password" msgstr "Пароль - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Профіль - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Запрошення - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Учасники - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Налаштування - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Вебхуки - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - Режим перегляду - Penpot" @@ -4583,99 +4539,99 @@ msgstr "%s - Режим перегляду - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "На сторінці не знайдено дошок." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "Дошку не знайдено." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Коментарі (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Не показувати взаємодії" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Повноекранний режим" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Дослідити (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Взаємодії" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Взаємодії (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Копіювати посилання" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Показати взаємодії" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "Показати взаємодії при натискані" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Мапа сайту" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "Остання доставка була успішною." -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Вирівняти по центру горизонтально (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Розподілити горизонтальні проміжки (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Вирівняти по лівому краю (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Вирівняти по правому краю (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Вирівняти по низу (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Вирівняти по центру вертикально (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Розподілити вертикальні проміжки (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Вирівняти по верху (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "Додати бібліотеку" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Усі ресурси" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Кольори" @@ -4687,7 +4643,7 @@ msgstr "Додати колір" msgid "workspace.assets.component-group-options" msgstr "Параметри групи компонентів" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Компоненти" @@ -4695,7 +4651,7 @@ msgstr "Компоненти" msgid "workspace.assets.components.add-component" msgstr "Додати компонент" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s Варіантів" @@ -4703,15 +4659,15 @@ msgstr "%s Варіантів" msgid "workspace.assets.create-group" msgstr "Створити групу" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "Ваші елементи будуть автоматично названі як \"назва групи / назва елемента\"" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "Видалити" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "Створити дуплікат" @@ -4719,39 +4675,39 @@ msgstr "Створити дуплікат" msgid "workspace.assets.duplicate-main" msgstr "Дублювати головний" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Редагувати" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "Фільтрувати" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "Перегляд сіткою" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Група" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Ім'я групи" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "Перегляд списком" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "локальна бібліотека" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "Керувати бібліотекою" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Не було знайдено ресурсів" @@ -4759,7 +4715,7 @@ msgstr "Не було знайдено ресурсів" msgid "workspace.assets.open-library" msgstr "Відкрити файл бібліотеки" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Перейменувати" @@ -4767,11 +4723,11 @@ msgstr "Перейменувати" msgid "workspace.assets.rename-group" msgstr "Перейменувати групу" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Пошук ресурсів" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Спільна бібліотека" @@ -4782,43 +4738,43 @@ msgstr[0] "%s компонент" msgstr[1] "%s компоненти" msgstr[2] "%s компонентів" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "Сортувати" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Типографіки" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "Додати типографіку" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Розмір" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Стиль шрифта" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "Перейти до файлу бібліотеки стилів для редагування" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Міжлітеральний відступ" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Висота лінії" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Аа" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Перетворення тексту" @@ -4830,63 +4786,63 @@ msgstr "Розгрупувати" msgid "workspace.colorpicker.color-tokens" msgstr "Токени кольорів" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "Компоненти не можна вкладати в них же." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "Деякі копії не можна поміняти. Компоненти не можна вкладати в них же." -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "Створити ділянку" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "Створити дошку" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "Об'єднати клітинки" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "Додати 1 стовпець праворуч" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "Додати 1 стовпець ліворуч" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "Видалити стовпець" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "Видалити стовпець та фігури" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "Дублювати стовпець" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "Додати 1 рядок знизу" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "Додати 1 рядок вгорі" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "Видалити рядок" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "Видалити рядок та фігури" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "Дублювати рядок" @@ -4894,15 +4850,15 @@ msgstr "Дублювати рядок" msgid "workspace.debug.title" msgstr "Інструменти відлагодження" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Режим фокусування" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Розфокосувати" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Фокусувати" @@ -4914,191 +4870,191 @@ msgstr "Лінійний градієнт" msgid "workspace.gradients.radial" msgstr "Радіальний градієнт" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "Вимкнути динамічне вирівнювання" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Вимкнути пропорційне масштабування" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "Деактивувати прив'язку до орієнтирів" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Вимкнути прив'язку до пікселів" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "Деактивувати прив'язку до орієнтирів лінійок" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Увімкнути динамічне вирівнювання" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Увімкнути пропорційне масштабування" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Прив'язати до орієнтирів" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Увімкнути прив'язку до пікселів" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "Прив'язати до орієнтирів лінійок" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "Приховати імена дошок" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "Приховати орієнтири" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Приховати палітру кольорів" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Приховати піксельну сітку" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Приховати лінійки" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Приховати палітру шрифтів" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Редагувати" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Файл" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Допомога та інформація" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Налаштування" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Вигляд" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Повернутись до внесених змін" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Обрати все" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "Показати імена дошок" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "Показати орієнтири" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Показати палітру кольорів" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Показати піксельну сітку" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Показати лінійки" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Показати палітру шрифтів" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "Перемкнутись на темну тему" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "Перемкнутись на світлу тему" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "Перемкнутись на тему системи" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Відхилити зміни" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Скинути" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "Помилка при збереженні" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Збережено" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Збереження" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "Поділитись" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Режим перегляду (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "Збільшення" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Заповнення - розширити щоб заповнити" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Fit - зменшити масштаб до потрібного розміру" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Збільшити щоб вмістити все" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Повноекранний" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Наблизити до виділеного" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "Редагувати сітку" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "Вихід" @@ -5106,7 +5062,7 @@ msgstr "Вихід" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "Нижній внутрішній відступ" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "Показати параметри 4-стороннього внутрішнього відступу" @@ -5142,30 +5098,30 @@ msgstr "Все" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "Розмістити" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "Розмістити макет сітки" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "%s колір" msgstr[1] "%s кольори" msgstr[2] "%s кольорів" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "У вашій бібліотеці, поки що, немає стилів кольорів" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "У вашій бібліотеці, поки що, немає стилів типографіки" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "Файл бібліотеки" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Попередні кольори" @@ -5177,96 +5133,96 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Зберегти стиль кольору" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "%s компонент" msgstr[1] "%s компоненти" msgstr[2] "%s компонентів" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "Підʼєднано до" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "Або додайте кілька з них, щоб спробувати:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "У команді немає Спільних Бібліотек" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "деякі шаблони тут" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Бібліотека файлів" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "%s фігура" msgstr[1] "%s фігури" msgstr[2] "%s фігур" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "БІБЛІОТЕКИ В ЦЬОМУ ФАЙЛІ" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "БІБЛІОТЕКИ" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "ОНОВЛЕННЯ БІБЛІОТЕКИ" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "Завантаження…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Тут ще немає спільних бібліотек, які потребують оновлення" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "Не знайдено збігів для \"%s\"" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "Пошук спільних бібліотек" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "СПІЛЬНІ БІБЛІОТЕКИ" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "Під'єднати бібліотеку" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "%s типографіка" msgstr[1] "%s типографіки" msgstr[2] "%s типографік" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "Від'єднати бібліотеку" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "Оновити" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "побачити усі зміни" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "ОНОВЛЕННЯ" @@ -5302,111 +5258,111 @@ msgstr "Перемикання розмивання" msgid "workspace.options.canvas-background" msgstr "Фон полотна" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Зміст кліпу" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Компонент" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "Анотація" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Копія" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "Створіть анотацію" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Редагуйте анотацію" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Головний" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "Замінити компонент" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "У цій бібліотеці немає ресурсів" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "Розʼєднано" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "До головного компонента" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "" "Цей компонент має версії, що конфліктують. Впевніться, що кожна варіація " "має унікальний набір значень властивостей." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "Розташувати дубльовані варіанти" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "Деякі варіанти мають однакові властивості та значення" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "Ці варіанти мають однакові властивості та значення." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "Цей варіант має значення та властивості, які належать і до іншого." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "Деякі з цих варіантів мають однакові значення і властивості." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "" "Цей компонент містить варіянти з недійсними іменами. Впевніться що кожен з " "варіантів слідує відповідній структурі." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "Визначити помилкові варіанти" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "Деякі варіанти мають помилкові імена" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "Знайти недійсні варіанти" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "Ці варіанти мають помилкові імена." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "Цей варіант має помилкове імʼя." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "Деякі з цих варіантів мають помилкові імена." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[property] = [value], [property] = [value]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "Спробуйте використати таку структуру:" @@ -5440,7 +5396,7 @@ msgstr "Мають однаковий рівень у ієрархії." msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "Групи, дошки та шаблони вважаються рівними." -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "Як варіанти лишаються зʼєднаними" @@ -5484,11 +5440,11 @@ msgstr "По верху" msgid "workspace.options.constraints.topbottom" msgstr "По верху та низу" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Дизайн" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Експортувати" @@ -5496,38 +5452,38 @@ msgstr "Експортувати" msgid "workspace.options.export-multiple" msgstr "Експортувати обране" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "Експортувати %s елемент" msgstr[1] "Експортувати %s елементи" msgstr[2] "Експортувати %s елементів" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "Додати експортування" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "Видалити експортування" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "Суфікс" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Експортування завершено" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Експортую…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Не вдалось експортувати" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "Експортування відбувається неочікувано повільно" @@ -5543,7 +5499,7 @@ msgstr "Додати заливку" msgid "workspace.options.fill.remove-fill" msgstr "Видалити заливку" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "Змінити розмір щоб заповнити" @@ -5567,67 +5523,67 @@ msgstr "Початки потоку" msgid "workspace.options.grid.auto" msgstr "Автоматично" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "Стовпці" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Колір" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Проміжок" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Висота" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Зовнішній відступ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Встановити за вмовчанням" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "Низ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "По центру" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Ліво" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Право" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Розтягування" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "По верху" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Використовувати вмовчання" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Рядки" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Квадратна" @@ -5639,7 +5595,7 @@ msgstr "Заповнити групу" msgid "workspace.options.group-stroke" msgstr "Рамка групи" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "Додати орієнтир" @@ -5647,19 +5603,19 @@ msgstr "Додати орієнтир" msgid "workspace.options.guides.remove-guide" msgstr "Видалити орієнтир" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "Орієнтири" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "Перемкнути орієнтир" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Висота" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Дослідити" @@ -5875,107 +5831,107 @@ msgstr "Взаємодії" msgid "workspace.options.interactions.add-interaction" msgstr "Додати взаємодію" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Колір" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Вигорання кольору" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Освітлення кольором" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Потемніння" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Різниця" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Виключення" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Жорстке світло" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Відтінок" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Світліше" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Сяяння" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Множення" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Нормальний" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Накладення" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Насиченість" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Екран" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "М'яке світло" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "Перемкнути видимість шару" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Макс. Висота" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Макс. Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Мін.Висота" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Мін.Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Більше кольорів" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Більше кольорів бібліотеки" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "Більше токенів кольорів" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Непрозорість" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Прототип" @@ -6007,19 +5963,19 @@ msgstr "Згорнути незалежні радіуси" msgid "workspace.options.radius.show-single-corners" msgstr "Показати незалежні радіуси кутів" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Нещодавній" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Спробувати ще раз" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Обернення" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Пошук шрифту" @@ -6027,7 +5983,7 @@ msgstr "Пошук шрифту" msgid "workspace.options.select-a-shape" msgstr "Оберіть фігуру, дошку чи групу щоб перетягнути з'єднання до іншої дошки." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Обрані кольори" @@ -6095,55 +6051,55 @@ msgstr "Перемкнути тінь" msgid "workspace.options.show-fill-on-export" msgstr "Показати в експортованому" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Показати у режимі перегляду" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Розмір" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Шаблони розміру" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "Зафіксувати співвідношення" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "Зняти фіксацію співвідношення" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Коло" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Ромб" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Стрілка" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Жодний" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Округле" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Квадратне" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Прямокутне" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Трикутне" @@ -6151,7 +6107,7 @@ msgstr "Трикутне" msgid "workspace.options.stroke-color" msgstr "Колір обведення" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Довжина обведення" @@ -6159,35 +6115,35 @@ msgstr "Довжина обведення" msgid "workspace.options.stroke.add-stroke" msgstr "Додати колір обведення" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "По центру" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Пунктирний" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "Точковий" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Всередину" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Комбіноване" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Назовні" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "Видалити обведення" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Жирне" @@ -6223,11 +6179,11 @@ msgstr "Автоматична ширина" msgid "workspace.options.text-options.grow-fixed" msgstr "Фіксована" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Інтервал між літерами" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Висота рядка" @@ -6273,126 +6229,126 @@ msgstr "" "Використовуйте кнопку відтворення в заголовку, щоб запустити перегляд " "прототипів." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Ширина" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Вісь абсцис (X)" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Вісь ординат (Y)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Намалювати вузли (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Приєднати вузли (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "В кут (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "В криву (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Об'єднати вузли (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Перемістити вузли (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Розділити вузли (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Прив'язати вузли (%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "Відкрити" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "Дослідіть [більше плагінів](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "Ви поки не встановлювали жодного плагіна" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "Маніфест плагіна неправильний." -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "Щоб використовувати цей плагін, Ви маєте бути редактором" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "Плагін не існує або його URL недійсний." -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "Встановити" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "Встановлені плагіни" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "Керування плагінами" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "Плагіни" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "ОНОВИТИ ЦЕЙ ПЛАГІН" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "" "Плагін було модифіковано після останнього використання. Тепер він також " "хоче отримати доступ до:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "Завантаження файлів розпочато." -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "Зберігати дані в бровзері." -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "Читати ваші коментарі та відповіді." -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "Читати та змінювати ваші коментарі та відповіді від вашого імені." -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "Читати вміст файлів до яких користувачі мають доступ." -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "Читати та модифікувати вміст файлів, до яких користувачі мають доступ." -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "" "Зауважте, що плагін створено зовнішньою стороною, тож впевніться що Ви " @@ -6400,73 +6356,73 @@ msgstr "" "безпека Ваших даних. Якщо маєте якісь сумніви, звʼяжіться із службою " "підтримки." -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "Читати ваші бібліотеки та матеріали." -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "Читати та модифікувати ваші бібліотеки та матеріали." -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "'%s' ПЛАГІН ЗАПИТУЄ ДОСТУП НА:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "Читати інформацію профілю поточного користувача." -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "Список плагінів" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "Видалити плагін" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "Вкажіть URL плагіну" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "Плагіни" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "НЕ ЗАРАЗ" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "" "Хочете переглянути? Буде створено нову чернетку для цієї команди. (Якщо ні, " "ви можете завжди знайти це у встановлених плагінах будь-якого файлу.)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "'%s' ПЛАГІН ВСТАНОВЛЕНО ДЛЯ ВАШОГО КОРИСТУВАЧА!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "СПРОБУВАТИ ПЛАГІН" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Додати гнучкий макет" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Додати макет сітки" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "Додати макет" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "Створити варіант" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "Додати нову властивість" @@ -6478,7 +6434,7 @@ msgstr "Розмістити позаду" msgid "workspace.shape.menu.backward" msgstr "Перемістити назад" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "Поєднати як варіянти" @@ -6486,7 +6442,7 @@ msgstr "Поєднати як варіянти" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "Компонентам потрібно знаходитись на одній сторінці" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Копіювати" @@ -6522,27 +6478,27 @@ msgstr "Копіювати як текст" msgid "workspace.shape.menu.create-annotation" msgstr "Створити анотацію" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Виділене на дошку" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Створити компонент" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Створити кілька компонентів" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Вирізати" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Вилучити" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Видалити початок потоку" @@ -6554,7 +6510,7 @@ msgstr "Відокремити екземпляр" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Відокремити екземпляри" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Різниця" @@ -6562,15 +6518,15 @@ msgstr "Різниця" msgid "workspace.shape.menu.duplicate" msgstr "Дублювати" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Редагувати" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Виключити" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Вирівняти" @@ -6582,7 +6538,7 @@ msgstr "Перевернути це горизонтально" msgid "workspace.shape.menu.flip-vertical" msgstr "Перевернути це вертикально" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Початок потоку" @@ -6594,31 +6550,31 @@ msgstr "Посунути вище" msgid "workspace.shape.menu.front" msgstr "На передній план" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Група" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Приховати" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Показати / приховати інтерфейс" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Перехрестя" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Заблокувати" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Додати маску" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "Вставити" @@ -6626,27 +6582,27 @@ msgstr "Вставити" msgid "workspace.shape.menu.paste-props" msgstr "Вставити властивості" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Шлях" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Видалити гнучкий макет" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "Видалити макет сітки" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "Видалити макет" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "Видалити властивість" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "Варіант повинен містити принаймні один параметр" @@ -6670,11 +6626,11 @@ msgstr "Відновити варіант" msgid "workspace.shape.menu.select-layer" msgstr "Обрати шар" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Показати" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Показати в панелі ресурсів" @@ -6690,19 +6646,19 @@ msgstr "Видалити мініатюру" msgid "workspace.shape.menu.thumbnail-set" msgstr "Встановити як мініатюру" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Дегрупувати" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Об'єднання" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Розблокувати" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Зняти маску" @@ -6718,51 +6674,51 @@ msgstr "Приховати панель інструментів" msgid "workspace.sidebar.expand" msgstr "Розкрити панель інструментів" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Історія" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Шари" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Компоненти" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "Фільтр" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Дошки" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Групи" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Список зображень" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Маски" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Пошук шарів" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Фігури" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Тексти" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Імпортовані атрибути SVG" @@ -6774,7 +6730,7 @@ msgstr "Сторінки" msgid "workspace.sidebar.sitemap.add-page" msgstr "Додати сторінку" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Мапа сайту" @@ -6794,7 +6750,7 @@ msgstr "Додати набір до цієї групи" msgid "workspace.tokens.add-token" msgstr "Додати токен: %s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "Застосовано до" @@ -6816,7 +6772,7 @@ msgstr "" "Базовий розмір шрифту повинен бути значенням у пікселях або без одиниці " "виміру." -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "Колір" @@ -6824,7 +6780,7 @@ msgstr "Колір" msgid "workspace.tokens.create-new-theme" msgstr "Створити свою першу тему зараз." -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "Створити." @@ -6844,7 +6800,7 @@ msgstr "Видалити тему" msgid "workspace.tokens.duplicate" msgstr "Дублювати токен" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "копіювати" @@ -6892,7 +6848,7 @@ msgstr "Попередній перегляд:" msgid "workspace.tokens.export.single-file" msgstr "Один файл" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "Розмір шрифту чи {alias}" @@ -6902,7 +6858,7 @@ msgstr "" "Помилка під час встановлення розміру/стилю шрифта. Цей стиль шрифта не " "існує у поточному шрифті" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "Вага шрифту (300, Жирний Курсив...) чи {alias}" @@ -6940,7 +6896,7 @@ msgstr "" "У одному файлі JSON, ключі першого рівня повинні бути іменами набору " "токенів." -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "Імпортувати токени" @@ -6948,17 +6904,17 @@ msgstr "Імпортувати токени" msgid "workspace.tokens.import-warning" msgstr "Імпорт токенів перепише усі наявні токени, набори та теми." -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "Неактивний" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "" "Цей набір неактивний. Змініть тему чи активуйте набір щоб побачити зміни в " "області перегляду" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "Використовувати індивідуальні токени" @@ -6978,11 +6934,11 @@ msgstr "Тема" msgid "workspace.tokens.label.theme-placeholder" msgstr "Додати тему (приміром Світла)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "Літеральний відступ чи {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "Висота лінії (множник, px, %) чи {alias}" @@ -6998,11 +6954,11 @@ msgstr "Макс. розмір" msgid "workspace.tokens.min-size" msgstr "Мін. розмір" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "Відсутнє посилання" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "Праве натискання миші для показу параметрів" @@ -7018,7 +6974,7 @@ msgstr "Немає активної теми" msgid "workspace.tokens.no-permisions-set" msgstr "Щоб активувати / деактивувати набір, Ви маєте бути редактором" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "Щоб використовувати ці теми Ви маєте бути редактором" @@ -7030,7 +6986,7 @@ msgstr "Ще не оголошено жодного набору. Створіт msgid "workspace.tokens.no-sets-yet" msgstr "Тут ще немає наборів." -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "Тут немає тем." @@ -7046,7 +7002,7 @@ msgstr "Не перерозподіляти" msgid "workspace.tokens.num-active-sets" msgstr "%s активних наборів" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "Початкове значення: %s" @@ -7058,11 +7014,11 @@ msgstr "Внутрішні відступи" msgid "workspace.tokens.radius" msgstr "Радіус" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "Введіть псевдо токену типографіки" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "Введіть псевдо токену тіні" @@ -7078,7 +7034,7 @@ msgstr "Перерозподілити усі токени що використ msgid "workspace.tokens.remap-warning-time" msgstr "Ця дія може тривати довго." -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "Отримане значення: %s" @@ -7112,27 +7068,27 @@ msgstr "Тут можна налаштувати базовий розмір ш msgid "workspace.tokens.settings" msgstr "Налаштування токену" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "Додати Тінь" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "Розмиття" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "Вставка" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "Вилучити Тінь" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "Розкид" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "Значення розмиття не може бути відʼємним" @@ -7140,11 +7096,11 @@ msgstr "Значення розмиття не може бути відʼємн msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "Значення розкиду не може бути відʼємним" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "Х" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "" @@ -7152,11 +7108,11 @@ msgstr "" msgid "workspace.tokens.size" msgstr "Розмір" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "жоден | верхній регістр | нижній регістр | верхня на початку чи {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "жоден | підкреслення | закреслення чи {alias}" @@ -7174,31 +7130,31 @@ msgstr "Список тем" msgid "workspace.tokens.token-description" msgstr "Опис" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "Обрати сімейство шрифтів" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "Сімейство шрифта" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "Сімейство шрифта або список шрифтів, розділені комою (,)" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "Ім'я" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "Токен вже існує на шляху: %s" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "Імʼя повинне містити принаймні 1 символ" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " помилкове імʼя токену.\n" @@ -7209,11 +7165,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "Значення" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "Ведіть значення або псевдо з {псевдо}" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "ТОКЕНИ - %s" @@ -7229,11 +7185,11 @@ msgstr "Імпротування пройшло успішно. Деякі то msgid "workspace.tokens.unknown-token-type-section" msgstr "Тип \"%s\" непідтримуваний (%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "Використати посилання" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "Значення не є дійсним" @@ -7241,55 +7197,55 @@ msgstr "Значення не є дійсним" msgid "workspace.toolbar.assets" msgstr "Ресурси" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Палітра кольорів (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Коментарі (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Крива (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Еліпс (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Дошка (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Зображення (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Перемістити (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Шлях (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "Плагіни (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Прямокутник (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Текст (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Типографіки (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "Перемкнути панель інструментів" @@ -7302,7 +7258,7 @@ msgstr "Готово" msgid "workspace.top-bar.view-only" msgstr "**Інспектувати код** (Лише перегляд)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Тут, поки що, немає історії редагувань" @@ -7442,19 +7398,19 @@ msgstr "ресурс типографіки" msgid "workspace.undo.entry.unknown" msgstr "Операція над %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Відкинути" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Більше про" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Є деякі оновлення в спільній бібліотеці" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Оновити" diff --git a/frontend/translations/yo.po b/frontend/translations/yo.po index 23d4d7dfc4..7260cde389 100644 --- a/frontend/translations/yo.po +++ b/frontend/translations/yo.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-15 17:19+0000\n" -"Last-Translator: Alejandro Alonso \n" +"PO-Revision-Date: 2026-11-05 22:20+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Yoruba " "\n" "Language: yo\n" @@ -9,7 +9,7 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" #: src/app/main/ui/auth/recovery.cljs:83 msgid "auth.confirm-password" @@ -25,7 +25,7 @@ msgstr "" "\"\"This is a DEMO service, DO NOT USE for real work, the projects will be " "parẹ́ lóòrèkóòrè." -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "gbàgbé ọ̀rọ̀-ìgbaniwọlé ?" @@ -33,27 +33,27 @@ msgstr "gbàgbé ọ̀rọ̀-ìgbaniwọlé ?" msgid "auth.fullname" msgstr "orúkọ lẹ́kùnn-ún-rẹ́rẹ́" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "wọlé síbí" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "ibùdó gítì" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "láàbù gítì" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "ṣàwárí" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "ṣílẹ̀kuǹ ìdánimọ̀" @@ -75,7 +75,7 @@ msgstr "" msgid "auth.notifications.recovery-token-sent" msgstr "a ti fi ọ̀rọ̀-ìgbaniwọle aṣàtúnwárí ránṣẹ sínu àpótí rẹ." -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "o ti wọ inú ẹgbẹ́ bó ṣeyẹ" @@ -107,11 +107,11 @@ msgstr "gbàgbé ọ̀rọ̀-ìgbaniwọle ?" msgid "auth.recovery-submit" msgstr "yí ọ̀rọ̀-ìgbaniwọlé rẹ padà" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "kò tí ì sí àkáùntì?" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "ṣẹ̀dá àkáùntì" @@ -119,95 +119,95 @@ msgstr "ṣẹ̀dá àkáùntì" msgid "auth.register-title" msgstr "ṣẹ̀dá àkáùntì" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "òfin ibiṣẹ́" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "ìgbéjáde" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "gbogbo àwọn tó ń lo pẹ́ńpọtì" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(lọ́wọ́lọ́wọ́)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "pa ọ̀nà-àsopọ̀ run" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "gba ọ̀nà-àsopọ́" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "so àdàkọ pọ̀ bó ṣeyẹ" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "ṣàkóṣo ìgbaniláàye" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "o lè sọ̀rọ̀" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "ṣe àyẹ̀wò kóòdù" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "ẹnikẹ́ni tí ó bá ní ọ̀nà-àsopọ á ní àǹfààní àtiwọle" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "pín ojú ìwé" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "ọ̀nà-àsopọ̀ ojú ìwé tí a pín á hàn ní bí yìí" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "ọmọ ẹgbẹ́ nìkan ni" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "pín àwọn àpẹrẹ" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "yan gbogbo rẹ̀" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "ohun tí a kò tí ì kọ jáde" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "ìṣàkóṣo ọmọ ẹgbẹ" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "kẹ́gbẹ́ ṣera wọn lọ́kan" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "ṣe àpàpọ̀ bí i yàrá ìkàwe tí a pín" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "pàrọ̀ ímeèlí" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(dàákọ)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "ṣẹ̀dá ẹgbẹ́ tuntun" @@ -215,11 +215,11 @@ msgstr "ṣẹ̀dá ẹgbẹ́ tuntun" msgid "dashboard.default-team-name" msgstr "pẹ́ńpọtì rẹ" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "pa ẹgbẹ́ rẹ́" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "sọ fáìlí pẹ́ńpọtì kalẹ̀ (.penpot)" @@ -235,53 +235,53 @@ msgstr "ṣe ẹ̀dà %s fáìlì" msgid "dashboard.export-binary-multi" msgstr "sọ àwọn fáìlì pẹ́ẹ́pọtì kalẹ́ %s (.pẹ́ńpọtì)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "fi bọọdù ráńṣẹ bí i PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "fi ráńṣẹ́ bí i PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "% ti %s ẹ́límẹ́ǹti tí a yàn" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "fi ráńṣẹ" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "" "o lé ṣe àfikún ti ètò ìfiráńṣẹ si ẹ́límẹ́ǹtì láti bi àmì ohun ìní ( níbi " "ìsàlẹ̀ báà lẹ́gbẹ̀ẹ́ ọ̀tun )." -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "àlàyé bí a ti ṣètò ìfiráńṣẹ sí pẹ́ńpọtì ." -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "kòsí àwọn ẹ́límẹ́ǹtì pẹ̀lú ètò ìfirańńṣẹ." -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "àṣàyàn ìfiránṣẹ́" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "fọ́ǹtì tó sọnù" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "yọ gbogbo wọn kúrò" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "àwọn fọ́ǹtì tí o ọpulóòdù yó hàn níbí." -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -291,7 +291,7 @@ msgstr "" "soso**. O lè ọpulóòdù fọ́ǹtì pẹ̀lú àwọn ìlànà wọ̀nyí: **TTF, OTF and " "WOFF** (ẹyọ kan péré la ó nílò)." -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" @@ -300,11 +300,11 @@ msgstr "" "òfin pẹ́ńpọtìiṣẹ́](https://pẹ́ńpọtì.apù/àwọn òfin.html). o sì le fẹ́ kà " "nípa[ ìwé àṣẹ- pẹ́ńpọtì](https://www.taipogíráfì.com/faq)." -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "ọpulóòdù gbogbo rẹ̀" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -315,67 +315,39 @@ msgstr "" "Ní àfikún, a rẹkọmẹ́ǹdì lílo[tíráńsìfọtà(https://tíráńsìfọtà.org/) láti " "pilẹ̀ṣẹ̀ àwọn wẹ́bù fọ́ǹtì láti túnṣeàsìṣe. " -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "gbé àwọn fáìlì pẹ́ńpọtì wọlé" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "ooho! a kò lè gbé fáìlì yìí wọlé" -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "àwọn fáìlì tí ó ní nǹkan tí kò fẹsẹ múlẹ̀ ti di yíyọ kúro." -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "síṣe ètò àwọ̀" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "síṣe ètò àwọn irin iṣẹ́" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "síṣe ètò àwọn ohun ìgbéròyìn jáde" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "síṣe ètò ojú ìwe : %s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "síṣe ètò àkọsílẹ̀ ojú ìwé" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "ọpulóòdù dátà sínú sáfà (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "ọpulóòdù fáìlì: %s" - -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "pe àwọn ènìyàn" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "fẹgbẹ́ sílẹ̀" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "yàrá ìkàwé àti àwọn àwòṣe" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "ṣe ìwákiri ọ̀pọ̀ wọn kí o sì mọ bí ó ṣe sojúṣe tiẹ" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "wàhálà wà fún gbígbé tẹ́ńpílétì wọlé. tẹ́ńpílétì kò wọlé." -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "yàrá ìkàwé" @@ -383,7 +355,7 @@ msgstr "yàrá ìkàwé" msgid "dashboard.loading-files" msgstr "ṣe ìkójọpọ̀ àwọn fáìlí rẹ …" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "ṣèkójọpọ̀ àwọ fọ́ǹtì rẹ …" @@ -399,35 +371,35 @@ msgstr "gbé %s àwọn fáìlì lọ" msgid "dashboard.move-to-other-team" msgstr "gbé lọ sí ẹgbẹ́ mìíràn" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ fáìlì tuntun" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "fáìlì tuntun" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ iṣẹ́ àgbéṣe tuntun" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "iṣẹ́ àgbéṣe tuntun" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "kò sí èyí tó báramu fún un“%s“" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "àwon iṣẹ́ àgbéṣe tí a kànpọ á hàn níbí" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "a ti mú ímeèlì rẹ dójú ìwọn dáadáa" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "a ti ṣe ìwádìí ímeèlì rẹ dáadáa" @@ -435,7 +407,7 @@ msgstr "a ti ṣe ìwádìí ímeèlì rẹ dáadáa" msgid "dashboard.notifications.password-saved" msgstr "a ti fi ọ̀rọ̀ ìgbaniwọlé rẹ pamọ́ dáadáa!" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s àwọn ọmọ ẹgbẹ́" @@ -443,7 +415,7 @@ msgstr "%s àwọn ọmọ ẹgbẹ́" msgid "dashboard.open-in-new-tab" msgstr "ṣí fáìlì sínú tábù tuntun" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "àwọn àṣàyàn" @@ -451,39 +423,39 @@ msgstr "àwọn àṣàyàn" msgid "dashboard.password-change" msgstr "yí ọ̀rọ̀ ìgbaniwọlé padà" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "pínì/yọ pínì kúrò" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "àwọn iṣẹ́ àgbéṣe" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "o fẹ́ yọ àkáǹtì rẹ kúro?" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "fi àwọn eto pamọ́" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "ṣàwárí…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "ṣàwárí fún “%s“…" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "yan èdè ui" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "yan àkọlé" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "fi gbogbo àwn fáìlì hàn" @@ -495,7 +467,7 @@ msgstr "iṣẹ́ àgbéṣe rẹ ti parẹ́ dáadáa" msgid "dashboard.success-duplicate-project" msgstr "a ti ṣe ẹ̀dà iṣẹ́ àgbéṣe rẹ dáadáa" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "a ti gbé fáìlì rẹ kúro dáadáa" @@ -507,82 +479,82 @@ msgstr "a ti gbé àwọn fáìlì rẹ kúrò dáadáa" msgid "dashboard.success-move-project" msgstr "a ti gbé iṣẹ́ àgbéṣe rẹ kúrò dáadáa" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "àlàyé ẹgbẹ́" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "àwọn ọmọ ẹgbẹ́" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "iṣẹ́ àgbéṣe ọmọ ẹgbẹ́" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "àkọlé ui" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "ṣàwárí àwọn èsì" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "tẹ̀ láti rí àwọn èsì" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "yàrá ìkàwé tí a kòtí ì gbé jáde" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "mú àwọn ètò dójú ìwọ̀n" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "ti ń siṣẹ́" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "When this hook is triggered event details will be delivered" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "ẹ̀yà kọ́ńtẹ́ǹtì" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "ṣẹ̀dá ìkọ́-wẹ́bù" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "a ti ṣẹ̀dá ìkọ́-wẹ́bù dáadáa ." -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "" "àwọn ìkọ́-wẹ́bù jẹ́ ọ̀nà tó rọrùn láti fi ààyè sílẹ̀ fún àwọn ààyè " "ayélujára mìíràn àti awọn ápù ṣe filọ́ tí àwọn ìṣẹ̀lẹ̀ kan bá ní " "pẹ́ńpọtì’’ a ó fi ìbéèrè ráńṣẹ́ sí ọ̀kọ̀kan wọn tí urls tó o pèsè." -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "tẹ bọ́tínnì \"fikún ìkọ́- wẹ́bù\" láti fì kan sí i." -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "kò sí ìkọ́-wẹ́bù tí a ṣẹ̀da´dì sisìnyí." -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "àkáǹtì rẹ" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "ímeèlì" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "ímeèlì rẹ" @@ -598,7 +570,7 @@ msgstr "àkíyèsí" msgid "ds.component-subtitle" msgstr "ní láti ṣe ìmúdójú ìwọ̀n àwọn irin iṣẹ́:" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "fagilé e" @@ -610,15 +582,15 @@ msgstr "ó dára" msgid "errors.auth-provider-not-configured" msgstr "a kò tí ì fàṣẹ si àtúntò olùpèṣè." -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "ó dàbí pé o kò tí ì fàṣẹ sí i tàbí àkókò ti kọjá ." -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "fọ́ǹtì %s kò ṣe kójọpọ̀" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "àwọn fọ́ǹtì %s kò ṣe kójọpọ̀" @@ -626,15 +598,15 @@ msgstr "àwọn fọ́ǹtì %s kò ṣe kójọpọ̀" msgid "errors.cannot-upload" msgstr "kò le ọpulóòdù fáìlì ìgbéròyìn-jáde." -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "a ṣàwákiri rẹ kò le ṣe iṣẹ́ yìí" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "ímeèlì yìí ti jẹ́ lílò tẹ́lẹ" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "a ti fọwọ́ sí ímeèlì rẹ tẹ́lẹ̀." @@ -642,19 +614,19 @@ msgstr "a ti fọwọ́ sí ímeèlì rẹ tẹ́lẹ̀." msgid "errors.email-as-password" msgstr "o kò le lo ímeèlì rẹ gẹ́gẹ́ bí ọ̀rọ̀ ìgbaniwọlé" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "Ímeèlì «%s» ti ní ìjábọ̀ ọ̀pọ̀ọlọpọ̀ ìta-bọn-ọ̀n ti pẹ́." -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "Ímeèlì ti «%s» ń jábọ̀ gẹ́gẹ́ bí i awúrúju tàbí ìta-bọ̀n-ọnọǹ tipẹ́." -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "Àwọn ẹ̀yà ara ẹ̀rọ '%s' kò ní àtìlẹ́yìn." -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "Àsìṣe kan ti ṣẹlẹ̀." @@ -666,7 +638,7 @@ msgstr "àwọ̀ tó ti díbàjẹ́" msgid "errors.invalid-email" msgstr "Tẹ àti wọlé pẹ̀lú ímeèlì tó wúlo jọ̀wọ́" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "Ímeèlì tí a ti mọ̀dájú gbọ́dọ̀ báramu" @@ -682,7 +654,7 @@ msgstr "Èyí tí a pè le parẹ́ tàbí ki àkókò lekọjá ." msgid "errors.ldap-disabled" msgstr "Ìfàṣẹsí LDAP kò ṣe é ṣe ." -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "O ti débi '%s' ìfàmìsí. Késí i pẹ̀lú àtìlẹ́yìn ." @@ -694,7 +666,7 @@ msgstr "Àwòrán tóbi púpọ ju ohun tí a le fisí lọ." msgid "errors.media-type-not-allowed" msgstr "Ó dàbí pé èyí kì í ṣe àwòrán tó fẹsẹ́ múlẹ̀." -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "" "Pírófáìlì ímeèlì tí ò ǹ pè ti dákẹ (ti jábọ̀ àwúrúju tàbí ìtabọ̀n-ọ̀n tó " @@ -712,7 +684,7 @@ msgstr "Ọ̀rọ̀ ìgbaniwọlé kò gbọdọ̀ dín ní lẹ́tà mẹ́jọ msgid "errors.profile-blocked" msgstr "Ti dínà mọ́ pírófáìlì" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "Ímeèlì pírófáìlì rẹ ti ní ìdákẹ́ (ìjábọ̀ àwúrúju tàbí ìtabọ̀n-ọ̀n tó ga)." @@ -720,15 +692,15 @@ msgstr "Ímeèlì pírófáìlì rẹ ti ní ìdákẹ́ (ìjábọ̀ àwúrúju msgid "errors.registration-disabled" msgstr "Ìforúkọ-sílẹ̀ kò ṣe é ṣe lọ́wọ́lọ́wọ́." -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "kí àwọn ọmọ ẹgbẹ́ tí kò péye fi ẹgbẹ́ sílẹ̀, àbí bóyá o fẹ́ paárẹ́ ni ." -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "Kòsí ọmọ ẹgbẹ́ tí o fẹ́ yàn ." -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "Oní nǹkan kò le fi ẹgbẹ́ sílẹ̀ , o gbọ́dọ̀ tún ṣe àtúnyàn iṣẹ́ oní nǹkan." @@ -736,35 +708,35 @@ msgstr "Oní nǹkan kò le fi ẹgbẹ́ sílẹ̀ , o gbọ́dọ̀ tún ṣe msgid "errors.unexpected-error" msgstr "Àsìṣe tí a kòrò tẹ́lẹ̀ ti ṣẹlẹ." -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "Tókìnnì àìmọ̀" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "Àsìṣe nínú ètò ìsopọ̀, kò lè dé ibi URL" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "URL kò páásì ìfọwọ́sí." -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "Èyí tí a fijíṣẹ́ kẹ́yìn kò dára." -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "Àsìṣe nínú ìfẹwọ́sí SSL ." -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "Àkókò ti lọ" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "Unexpected error on validating àsìṣe àìròtẹ́lẹ̀ lórí ìfọwọ́sí" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "Ipò àìròtẹ́lẹ̀ %s" @@ -780,13 +752,13 @@ msgstr "Ọ̀rọ̀ ìgbaniwọlé ti àtijọ́ kò tọ̀nà" msgid "feedback.description" msgstr "Àpèjúwe" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "" "Inú wa dùn láti rí ẹ níbí. Tí o bá nílò ìrànlọ́wọ, please search before " "you jọ̀wọ́ ṣàwárí kí o tó firáńṣẹ́." -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Agbègbè pẹ́ńpọtì" @@ -801,11 +773,11 @@ msgstr "" "jẹ́ èrò tàbí síṣe iyèméjì. Ìkan lára àwọn ọmọ ẹgbẹ́ wa yóò dáhùn láìpẹ́ " "láìjìnnà." -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "A wà níbí láti ṣe ìrànlọ́wọ́ fún àwọn ìbéèrè tó bá wà lórí iṣẹ́ ọnà." -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "Àkáùnti àtìlẹ́yìn fún Tíwítà" @@ -837,7 +809,7 @@ msgstr "Danlóòdù orísun àwòrán" msgid "inspect.attributes.shadow" msgstr "Ibòòji" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "Ìwọ̀n àti ipò" @@ -845,39 +817,27 @@ msgstr "Ìwọ̀n àti ipò" msgid "inspect.attributes.stroke" msgstr "Stroke Sítírókì" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "Ààrin" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "Nínú" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "Ìtá" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "Taipogíráfì" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "Ẹbí Fọ́ǹtì" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "Ìwọ̀n Fọ́ǹtì" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "Àrà Fọ́ǹtì" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "Ìfàyè sí ààrin Lẹ́tà" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "Gíga Ìlà" @@ -889,11 +849,11 @@ msgstr "Kò sí" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "Fàlà sí" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "Àkọlé Lẹ́tà" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "Lẹ́tà Kékeré" @@ -901,7 +861,7 @@ msgstr "Lẹ́tà Kékeré" msgid "inspect.attributes.typography.text-transform.none" msgstr "Kò sí" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "Lẹ́tà ńlá" @@ -963,27 +923,27 @@ msgstr "SVG" msgid "inspect.tabs.code.selected.text" msgstr "Ọ̀rọ̀" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "Ọ̀nà àbùjá" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "Ń siṣẹ́" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "Ṣàfikún àṣà Fọ́ǹtì" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "Alábòjútó" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Àdòbí XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "Gbogbo ẹ̀" @@ -991,29 +951,29 @@ msgstr "Gbogbo ẹ̀" msgid "labels.and" msgstr "àti" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "Padá" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "" "Ó dàbí ẹnipé o ní láti dúró díẹ̀ kí o tún gbìyàjú ; à ń siṣẹ́ díẹ̀ síṣe " "ìtọ́jú sáfà wa." -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "Ọ̀nà-àbáwọle búburú" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "Párẹ́" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Káńfà" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "Tì í" @@ -1021,11 +981,11 @@ msgstr "Tì í" msgid "labels.code" msgstr "Kóòdù" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "dásí i" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "Agbègbè" @@ -1033,35 +993,35 @@ msgstr "Agbègbè" msgid "labels.confirm-password" msgstr "Jẹ́rìísí" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "Tẹ̀ síwájú" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "O lè tẹ̀ síwájú pẹ̀lú àkáúnti pẹ́ńpọtì" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "ṣà dàkọ ọ̀nà àsopọ̀" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "Ṣẹ̀dá" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "Ṣẹ̀dá ẹgbẹ́ tuntun" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "Fi orúkọ ẹgbẹ́ tuntun wọlé" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "Dáṣìbọọ̀dù" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "Paárẹ́" @@ -1073,7 +1033,7 @@ msgstr "Pa kọ́mẹ́ǹtì rẹ́" msgid "labels.delete-comment-thread" msgstr "Pa gbogbo kọ́mẹ́ǹtì rẹ́" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "Pa ìfìwépè rẹ́" @@ -1081,31 +1041,31 @@ msgstr "Pa ìfìwépè rẹ́" msgid "labels.delete-multi-files" msgstr "Pa gbogbo %s fáìlì rẹ́" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "Olùdásílẹ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "Yọ́ kúrò" -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "Kọ ọ́ sílẹ̀" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "sàtúnkà" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "Olóòtú" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "Kọjá àkókò" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "Fi ránṣẹ́" @@ -1117,43 +1077,43 @@ msgstr "Èsì kò siṣẹ́" msgid "labels.feedback-sent" msgstr "Èsì ti lọ" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Fígímà" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "Ẹbí fọ́ǹtì" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "Àrà" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "Fọ̀ǹtì" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "Báǹkì gítì-ọbù" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "Fèsì padà" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "Padà sẹ́yìn" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "Ibi ìranilọ́wọ́" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "Fi àwọn ọ̀rọ̀ tí a ti yanjú pamọ́" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "kò ja fáfá" @@ -1161,47 +1121,47 @@ msgstr "kò ja fáfá" msgid "labels.info" msgstr "Àlàyé" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "Fi àwọn fọ́ǹtì sí orí ẹ̀rọ" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "Àsìṣe ti abẹ́nú" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "Ìkógun wọ̀lú" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "Ìfiwépè" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "Èdè" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "Àwọn yàrá ìkàwé & Tẹ́ńpílétì" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "ṣí kí o wọle tàbí kí o ṣàmì wọlé" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "Padé kí ojáde" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "Títajà" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "Ẹgbẹ" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "Àwọn ọmọ Ẹgbẹ́" @@ -1209,19 +1169,19 @@ msgstr "Àwọn ọmọ Ẹgbẹ́" msgid "labels.new-password" msgstr "Ọ̀rọ̀ Ìgbaniwọlé tuntun" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "Èyí tó kàn" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "A ti gbé gbogbo yín dè! Àwọn ọ̀rọ̀ ìfitónilétí tuntun yí ó hàn níbí." -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "kò si àwọn ìfìwépè kankan nílẹ̀." -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "Óóho!" @@ -1229,135 +1189,135 @@ msgstr "Óóho!" msgid "labels.old-password" msgstr "Ọ̀rọ̀ ìgbaniwọlé àtijọ́" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "Òmíìràn(ní pàtó)" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "Oníǹkan" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "Ọ̀rọ̀ ìgbaniwọlé" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "Ńńdúró" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "Tàtẹ̀yìn wá" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "Ohun títà tàbí iṣẹ́ àgbéṣe Olùṣàkóso" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "Pírófáìlì" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "Àwọn iṣẹ́ àgbéṣe" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "Fi àkọsílẹ̀ rẹ sílẹ̀" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "Yọ ọ́ kúrò" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "Yọ ọmọ ẹgbẹ́ kúrò" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "Fún un lórúkọ mìíràn" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "Fún ẹgbẹ́ lórúkọ mìíràn" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "Tún fì ìwé ìpè ránńṣẹ́" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "Tún gbìyànjú" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "Ojúṣe" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "Fi pamọ́" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "Ṣàwárí fọ́ǹtì" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "Ṣe àṣàyàn" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "Fi ránńṣẹ́" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "Fífi-ránńṣẹ…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "A wà níbi ètò ìpèsè ìtọ́jú àwọn sísítẹ́mù wa." -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "Sáfísì kòsí" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "Àwọn ètò" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "Yàrá ìkáwe" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "Fi gbogbo kọ́mẹ́ǹtì hàn" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "Fi àkójọ àwọn kọ́mẹ́ǹtì hàn" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "Fi àwọn kọ́mẹ́ǹtì tìrẹ nìkan hàn" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Yàwòrán" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "Bẹ̀rẹ̀" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "Ipò" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "Akẹ́kọ̀ọ́ tàbí olùkọ̀ọ́" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "Ìbáṣepọ̀" @@ -1365,23 +1325,23 @@ msgstr "Ìbáṣepọ̀" msgid "labels.unpublish-multi-files" msgstr "%s àwọn fáìlì tí a kò tí kọ jáde" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "mú dójú ìwọ̀n" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "Mẹ́gbẹ́ dójú ìwọ̀n" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "Ọpulóòdù" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "Ọpulóòdù kọ́sítẹ́mù fọ́ǹtì" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "Ń ọpulóòdù…" @@ -1389,11 +1349,11 @@ msgstr "Ń ọpulóòdù…" msgid "labels.view-only" msgstr "Wò ó nìkan" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "Olùwo" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "àwọn ìwé ìkọ́ wẹ́bù" @@ -1401,11 +1361,11 @@ msgstr "àwọn ìwé ìkọ́ wẹ́bù" msgid "labels.write-new-comment" msgstr "Write new comment Kọ kọ́mẹ́ǹtì tuntun" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(ìwọ)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "Àkáùntì rẹ" @@ -1434,63 +1394,63 @@ msgstr "" msgid "modals.add-shared-confirm.message" msgstr "Ṣàfikún “%s” gẹ́gẹ́ bí yàrá ìkàwé pípí" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "Big nudge Nọ́ọ̀jì ńlá" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "Ṣàyẹ̀wò ímeèlì tuntun" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "" "A fi ímeèlì kan ránń ṣẹ́ sí inú ímeèlì tí ò ń lò lọ́wọ́lọ́wọ́“%s” láti ṣe " "àyẹ̀wò ìdánimọ̀ rẹ." -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "Ímeèlì tuntun" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "Pàrọ̀ ímeèlìl" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "Pàrọ̀ ímeèlì rẹ" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "Ṣẹ̀dá ìkọ́- wẹ́bù" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "Ṣẹ̀dá ìkọ́-wẹ́bù" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "Péloòdù URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://àpẹrẹ.com/postreceive ìgbà àtẹ̀yinwá" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "Fagilé e kí o sì fi àkáùntì mi pamọ́" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "Bẹ́ẹ̀ni, pa àkáùntì mi rẹ́" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "" "Nípa yíyọ àkáùntì rẹ kúrò o ’ pàdánù gbogbo àwọn iṣẹ́ àgbéṣe rẹ àti àwọn tí " "ó wà lápamọ́." -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "Ṣé ó dá ọ lójú pé o fẹ́ pa àkáùntì rẹ rẹ́ ?" @@ -1540,23 +1500,23 @@ msgstr "Ṣé ó dá ọ lójú pé o fẹ́ pa %s àwọn fáìlì rẹ́ ?" msgid "modals.delete-file-multi-confirm.title" msgstr "Pa fáìlì rẹ́ %s" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "" "Ṣé ó dá ọ lójú pé o fẹ́ pa fọ́ǹtì alárà yìí rẹ́ ? Kò ní gbé e tí a bá lò " "ó nínú fáìlì." -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "" "Ṣé ó dá ọ lójú pé o fẹ́ pa fẹ́ǹtì yí rẹ́? Kò ní le gbé e tí a bá lò ó nínú " "Fáìlì." -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "Ṣé ó dá ọ lójú pé o fẹ́ pa ojú ìwé yìí rẹ́?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "Pa ojú ìwé rẹ́" @@ -1586,140 +1546,140 @@ msgstr "" msgid "modals.delete-team-confirm.title" msgstr "Yọ ẹgbẹ́ kúrò" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "Yọ ọmọ ẹgbẹ́ kúrò" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "Ṣé ó dá ọ lójú pé o fẹ́ yọ ọmọ ẹgbẹ́ yìí kúrò nínú ẹgbẹ́?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "Yọ ọmọ ẹgbẹ́ kúrò" -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "Pa ìkọ́-wẹ́bù rẹ́" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "Ṣe ó dá ọ lójú pé o fẹ́ pa ìkọ́-wẹ́bù yìí rẹ́?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "Pa ìkọ́- wẹ́bù rẹ́" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "Ṣe àtúnṣe ìkọ́- wẹ́bù" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "Ṣe àtúnṣe ìkọ́-wẹ́bù" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "Fi ìfìwépè ránńṣẹ́" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "Ímeèlì, kí kọmá yà á sọ́tọ̀" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "" "Díẹ̀ nínú àwọn ímeèlì wá láti ọ̀dọ̀ àwọn tó jẹ́ ọmọ ẹgbẹ́ ní ìsinsìnyí. Ìwé " "ìfipé wọn a kò nífi ránńṣẹ́." -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "Pe àwọn ọmọ ẹgbẹ́ sínú ẹgbẹ́" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "" "Gẹ́gẹ́ bí o ṣe jẹ́ ìwọ nìkan ni ọmọ ẹgbẹ́ nínú ẹgbẹ́ yìí, a ó yọ ẹgbẹ́ " "kúrò pẹ̀lú iṣẹ́ àgbéṣe rẹ̀ àti àwọn fáìlì." -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "Ṣé ó dá ọ lójú pé o fẹ́ kúrò nínú %s ẹgbẹ́?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "" "O kò lè kúrò nínú ẹgbẹ́ tí kò bá sí ọmọ ẹgbẹ́ mìíràn tó ní ìgbéga sí oní " "ǹǹkan. O lè fẹ́ pa ẹgbẹ́ rẹ́." -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "" "Ìwọ ni ẹni tí ó ni ẹgbẹ́ yìí. Please select another member to promote to " "Jọ̀wọ́ yan ọmọ ẹgbẹ́ mìíràn tí yíò ní ìgbéga ẹni tí ó ni nǹkan kí o tó " "kúrò." -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "Ṣe ìgbéga kí o kúrò" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "Yan ọmọ ẹgbẹ́ kan tí o ó gbéga" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "Kí o tó kúrò" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "Fi ẹgbẹ́ sílẹ̀" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "Ṣé ó dá ọ lójú pé o fẹ́ fi ẹgbẹ́ yìí sílẹ̀?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "Ń fi ẹgbẹ́ sílẹ̀" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "Fi kún iye tó jẹ́" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "Gbé ẹni tó ni nǹkan" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "" "Tí o bá gbé ẹni tí ó ni nǹkan, o ó yìí ojúṣe rẹ pada sí Alábòjútó, losing " " àwọn ìgbà láàyè díẹ̀ á wà fún ẹgbẹ́ yìí. " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "" "Ìwọ ni ẹni tí ó ni ẹgbẹ́ yìí lọ́wọ́lọ́wọ́. Are you sure you want to make Ṣé " "ó dá ọ lójú pé o fẹ́ jẹ́ %s oní nǹkan tuntun fún ẹgbẹ́?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "Ẹni tó ni ẹgbẹ́ tuntun" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "Kọ ọ́ jáde" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "Yàrá ìkàwé rẹ ṣófìfo. Ṣé ó dá ọ lójú pé o fẹ́ kọ ọ́ jáde?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "Gbé yàrá ìkàwé tó ṣófìfo jáde" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "Fọwọ́ làn-ań díẹ̀" @@ -1741,23 +1701,23 @@ msgstr "" msgid "modals.update-remote-component.message" msgstr "Mú ẹ̀yà iyàrá ìkàwé pípín kan dójú ìwọ̀n" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "Ìfipè tí a fi ránńṣẹ́ ti lọ dáadáa" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "ti ṣe àdàkọ ìfipè ọ̀nà ìtọ́ka sí" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "O kò le è yọ pírófáìlì rẹ kúro. Ṣe àtún yàn àwọn ẹgbẹ́ kí o tó tẹ̀ síwájú." -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "A ti fi pírófáìlì pamọ́ dáadáa!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "Verification email sent to %s. Check your email!" @@ -1777,15 +1737,15 @@ msgstr "" "Lẹ́yìn tí o bá ti fún ẹgbẹ́ rẹ lórúkọ, Ó le pe àwọn ènìyàn láti darapọ̀ mọ́ " "ẹgbẹ́." -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "Fìwépè ọmọ àwọn ẹgbẹ́" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "Pè wọ́n pẹ̀lú ojúṣe wọn:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "Ẹjẹ́ 'ká bẹ̀rẹ̀ !" @@ -1832,43 +1792,43 @@ msgstr "Bẹ̀rẹ̀ sí ṣe ẹ̀yà alárà" msgid "profile.recovery.go-to-login" msgstr "Lọ láti wọlé" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "Yọ kúrò" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "Dàárú" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "Ọ́rọ̀íwòye" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "Sópọ sí ísàle" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "Páràpọ àárin naa" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "Sosi ósì" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "Sosi ọtún" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "Sopo si òké" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "Tẹle àárin ni inàró" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "Ṣẹ̀dá ọ̀kọ̀ lati yìyán" @@ -1876,43 +1836,43 @@ msgstr "Ṣẹ̀dá ọ̀kọ̀ lati yìyán" msgid "shortcuts.bold" msgstr "Yipàdà to han" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "Iyàtò Boolean" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "Boolean tà yọ̀kúro" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "ikòritá Boolean" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "Asépọ̀ Boolean" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "Fírànṣẹ́ si ẹ́hin" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "Ran pàdà sí ehin" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "Mù siwájù" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "Mù si wájù" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "Pa yipàdá rẹ" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "Dàákọ" @@ -1920,31 +1880,31 @@ msgstr "Dàákọ" msgid "shortcuts.create-new-project" msgstr "Ṣẹ̀dá títún" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "Ge" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "Sun-un jàdé" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "Pàrẹ" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "Pa ipàdé" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "Yà paati" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "Yìyí" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "Ẹ́lipsì" @@ -1952,39 +1912,39 @@ msgstr "Ẹ́lipsì" msgid "shortcuts.draw-nodes" msgstr "Fà ọnà" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "Ọnà" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "Ònìgún mèrìn" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "Ọ́rọ̀" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "Ṣe pidánpidán" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "Fàgílèè" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "Ókèéré apẹ̀rẹ̀" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "Sun-un látì ba gbògbò rẹ̀ mú" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "Yipadà ní pẹ̀tẹ̀lẹ̀" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "Yipadà ní ínàró" @@ -2008,23 +1968,23 @@ msgstr "Lọ si awọn ílé-Ikàwé tí a pín" msgid "shortcuts.go-to-search" msgstr "Wàá" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "Ẹgbẹ" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "Pinpín kàákírì pètèlè" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "Ífìhan / Tọju UI" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "Sùn-un sínú" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "Fí àwóran sii" @@ -2032,7 +1992,7 @@ msgstr "Fí àwóran sii" msgid "shortcuts.italic" msgstr "Yipàdà ítalikì" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "Dàràpọ̀ awọn ipàdé" @@ -2040,39 +2000,39 @@ msgstr "Dàràpọ̀ awọn ipàdé" msgid "shortcuts.line-through" msgstr "Íyìpàdà ílà to yọ̀rí" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "Ṣè ígùn" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "Ṣe ìsépọ" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "Ibòjú" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "Dàpọ̀ awọ́n apa" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "Gbé" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "Sàré lọ́ sí isàlẹ̀" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "Gbé sí osi kiákiá" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "Gbé si ọ̀tún kiákiá" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "Gbé si òké kiákiá" @@ -2080,241 +2040,241 @@ msgstr "Gbé si òké kiákiá" msgid "shortcuts.move-nodes" msgstr "Gbé ipàdé" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "Gbé sílè" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "Gbé osi" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "Gbé sọtùn" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "Gbé sóke" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "Ọ̀kọ̀ tókan" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "Kò sí awọ́n ọ̀nà abùjá ti à rii" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "Fí akomo sí ipò 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "Fi akomo si ipo 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "Fi akomo si ipo 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "Fi akomo si ipo 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "Fi akomo si ipo 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "Fi akomo si ipo 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "Fi akomo si ipo 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "Fi akomo si ipo 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "Fi akomo si ipo 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "Fi akomo si ipo 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "Ólùṣà àwọ̀" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "Lọ si dasibodu" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "Lọ si apaa ayewo olùwò" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "Lọ si apaa ibaraẹnisọrọ olùwò" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " tabi " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "Lẹẹmọ" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "Ọkọ ti tẹlẹ" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "Tunṣe" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "tun sun" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "Ipìlẹ̀" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "Ólùwò" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "Àaye ìṣẹ́" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "Ṣa gbogbo re" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "Yan Obii pẹlẹbẹ" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "Ifihan / Itọju ọna abùjá" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "Yaa si awon apa" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "Yaa si pikseli ilana" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "Titete" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "Sàtunkọ" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "Akojọ ólórì aṣyn" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "Yípàdá fẹ́lẹ́fẹ́lẹ́" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "Lìlọ́ kìrì" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "Lìlọ́ kìrì" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "Lìlọ́ kìrì" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "Àwọ̀n pánẹ̀lí" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "Àwọ́n ọna" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "Àwọ́n apẹ́rẹ́" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "Àwọ́n ọ́rọ̀" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "Àwọ́n irínṣẹ̀" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "Sun-un" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "Sun-un" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "Se étò awòn áwòran fun fidio" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "Iyipada titete ìmúdàgba" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "Yi dukia pada" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "Yi paleti awọ pada" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "Yi ipo idojukọ pada" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "Yi kuro ni fulscrin" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "Yi awon pelebe yii pada" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "Tiipa / Siilẹ" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "títìipa lápàkàn" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "Yíípàda ọrọ paleti" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "Fihan / Toju re" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "Yiipada asa sun-un" @@ -2322,63 +2282,63 @@ msgstr "Yiipada asa sun-un" msgid "shortcuts.underline" msgstr "Yii abẹlẹ" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "Yi pada" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "Tu ẹgbẹ" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "Aiboju" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "Pin ni inaro" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "Dín lẹnsì sisun kù" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "Lílọ̀soké lẹnsi sisun" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "Yiyan pelu sun-un" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "Pipese oniru fọnti - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "Fọnti - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "Awọn ísè agbèsè - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "Wà - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot – Se onìrú òmínìra fun awọ̀n ẹ̀gbẹ̀" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "Fi esi- Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "Awon eto - Penpot" @@ -2386,27 +2346,27 @@ msgstr "Awon eto - Penpot" msgid "title.settings.password" msgstr "Ọrọigbaníwólé - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "Profaili - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "Ifíwẹ́pe - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "Ọ́́mọ̀ ẹgbẹ - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "Awọn eto - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s –Ipo wíwò - Penpot" @@ -2414,91 +2374,91 @@ msgstr "%s –Ipo wíwò - Penpot" msgid "title.workspace" msgstr "%s - Penpot" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "No boards found on the page." -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "A kò ri ọ̀kọ̀." -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "Ọrọìwòye (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "Maa fi ìbaráẹ̀nìsepọ han" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "Fé ká gḅọgbọ síkírínì" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "Ayéwò (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "Ibaraenisépọ̀" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "Awọ̀n Ibaraenisépọ̀ (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "Da línkì kọ̀" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "Ifihan Ibaraenisépọ̀" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "Áwòran átọ̀kà ílẹ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "Paŕàpọ̀ sí petelè aarin (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "Pin aayè ni pètélè (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "Páràpọ̀ sí òsì (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "Páràpọ̀ sí ọtun (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "Páràpọ̀ sí isàlẹ̀ (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "Páràpọ̀ sí inàro aarin (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "Pin aayè inàró (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "Párápọ̀ sóké (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "Gbọ̀gbọ̀ awọ̀n dukia" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "Áwò" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "Awọ̀n eroja" @@ -2506,31 +2466,31 @@ msgstr "Awọ̀n eroja" msgid "workspace.assets.create-group" msgstr "Ṣẹ̀dá ẹ̀gbẹ̀ kan" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "páarẹ̀" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "ẹẹdà" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "Ṣatunkọ" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "Ẹgbẹ" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "Orúkọ̀ ẹgbẹ" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "Ílè-iwè agbegbe" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "Ko si ohun-ini ti a rii" @@ -2538,7 +2498,7 @@ msgstr "Ko si ohun-ini ti a rii" msgid "workspace.assets.open-library" msgstr "Ṣii faili ìlé-ikáwè" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "Lo oruko imiran" @@ -2546,39 +2506,39 @@ msgstr "Lo oruko imiran" msgid "workspace.assets.rename-group" msgstr "Lo oruko imiran fun ẹgbẹ" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "Wa dukia" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "Iĺẹ̀ iwekika ti a pln" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "Iwe itẹwe" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "Iwon" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "Àrà Fọ́ǹtì" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "Aaye leta" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "Giga ila" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "Iyipádà ọrọ" @@ -2586,15 +2546,15 @@ msgstr "Iyipádà ọrọ" msgid "workspace.assets.ungroup" msgstr "Títù ẹ̀gbẹ́" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "Ipo Idojukọ" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "Pa Ídòjúkọ" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "Tan Ídòjúkọ" @@ -2606,135 +2566,135 @@ msgstr "Itesiwaju gerege" msgid "workspace.gradients.radial" msgstr "Itesiwaju Radial" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "Pa asèkalẹ̀ iwọnbà" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "Pa yipada si piseli" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "Tan imudagba to Sedede" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "Tan asèkalẹ̀ iwọnbà" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "Yipada si itọ̀nìsọna" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "Tan Yipada si piseli" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "Tọ̀jú awọ palẹti" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "Tọ̀jú pisẹli ilana" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "Tọ̀jú awọn ólòrí" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "Tọ̀jú awọn fọnti paleti" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "Satunkọ" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "Faili" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "Iranlọwọ ati alaye" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "Awọn àyánfẹ̀" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "Wiwo" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "Tun ṣe" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "Sa gbogbo rẹ" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "Fi awo paleti han" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "Fi piseli ilana han" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "Ifihan awọ̀n ólórí" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "Ifihan fonti paleti" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "Yi pada" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "Tunto" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "àsisè lórì fíf́ipamọ" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "Ti a fipamọ" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "Fifipámọ" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "Ipo wiwo(%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "sun-un" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "Kun - óṣùwọn to kun" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "Báràmù - óṣùwọn to Báràmù" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "Suun kí o bá gbógbó rẹ̀ dọ́gbà" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "Áseàfihan to kún" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "Ṣuum lati yan" @@ -2742,15 +2702,15 @@ msgstr "Ṣuum lati yan" msgid "workspace.layout-grid.editor.title" msgstr "Ilànà àyípàdà" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "Kòyi si oniruiru awọ ni ilé-ikàwé re" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "Ko si oniruiru iwé itẹ̀wé ni ilé ikàwé rẹ̀" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "Awọn àwò tálò láipẹ̀" @@ -2762,35 +2722,35 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "Fi awọn oniru awọ pamọ" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "Faili ilé ikàwé" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "Awọn Ílè íkawè" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "Ílè íkawè ímùdojúiwọn" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "Kò si Ilé kàwé ti a pin si ímùdojúiwọn" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "Awọn ilé kàwé tí a pìn" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "ímùdojúiwọn" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "wò gbògbò awọn ayípàdà" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "Awọn ímùdojúiwọn" @@ -2810,23 +2770,23 @@ msgstr "Asayan Baìbaì" msgid "workspace.options.canvas-background" msgstr "Kánfásì abẹ́le" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "Ábẹrẹ́ ákoonù" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "Apakan" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "Ádàakọ" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "Ṣatunkọ àkọ̀silẹ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "Ólòrí" @@ -2866,27 +2826,27 @@ msgstr "Ókè" msgid "workspace.options.constraints.topbottom" msgstr "Ókè ati Ídí" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "Ónirú" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "Sí ilẹ́ òkèeré" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "afikun ọrọ niipárí" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "Gbígbè okeere ti pari" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "Gbígbè okeere …" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "Gbígbè okeere kuna" @@ -2910,63 +2870,63 @@ msgstr "íbẹrè awọn ṣiṣan" msgid "workspace.options.grid.auto" msgstr "Aifọ́wọyi" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "Awọ" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "Gota" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "Òkè" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "Àlà" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "Fi sípò abínibí" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "ìsàlẹ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "Àárín" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "Òsì" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "Ọ̀tún" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "Nà" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "Òkè" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "Lo ipò abínibí" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "Ìgbòrò" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "Àwọn orị ìlà ìbú" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "Alápoméjì" @@ -2978,11 +2938,11 @@ msgstr "Ẹ̀kún ẹgbẹ́" msgid "workspace.options.group-stroke" msgstr "Ẹgba ẹgbẹ́" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "Gíga" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "Ṣàyẹ̀wò" @@ -3194,99 +3154,99 @@ msgstr "Nígbàtị óhún tẹ̀" msgid "workspace.options.interactions" msgstr "Àwọn ìbáraẹniṣepọ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "Àwọ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "Ṣẹ̀dà àwọ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "Òpó àwọ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "Ṣókuǹkùn" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "Ìyàtọ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "Ìyàsọ́tọ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "Iná líle" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "Ọdà" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "Fẹ́ẹ́rẹ́fẹ́" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "Ìmọ́lẹ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "Ìsọdipúpọ̀" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "Déédé" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "Ìtẹ̀lẹ́ ìlànà" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "Ẹ̀kúnrẹ́rẹ́" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "Aṣàfihàn" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "Ìmọ́lẹ̀ rokoṣo" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "Ibi tó ga jùlọ" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "Ìgbòrò tó ga jù" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "Gíga to kéré jù lọ" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "Ìgbòrò tó kéré jù lọ" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "Àwọn àwọ̀ púpọ̀ sí i" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "Àwọn yàrá àwọ̀ púpọ̀ sí I" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "Àìríran" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "Ìtẹ̀lé ìlànà" @@ -3310,19 +3270,19 @@ msgstr "Òkè òsì" msgid "workspace.options.radius-top-right" msgstr "Òkè ọ̀tún" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "Àìpẹ́" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "Tún gbìyànjú" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "Yíyípo" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "Àdàkọ ìwádìí" @@ -3332,7 +3292,7 @@ msgstr "" "Yan ìrísi ,pátákó tàbí ẹgbẹ́ láti fa ìsopọ̀ mọ́ pátákó " "òmíràn." -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "Àwọn àwọ̀ tí a ti yàn" @@ -3388,47 +3348,47 @@ msgstr "Òjìjí tí a yàn" msgid "workspace.options.show-fill-on-export" msgstr "Fihàn nínụ àgbéjáde" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "Fihàn nínú ipò wíwò" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "Ìwọ̀n" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "Àwọn àtòjọ" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "Yíyíká" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "Dáyámọ́ǹdì" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "Ofà" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "Kòsí" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "Yíká" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "Alápoméjì" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "Onígun mẹ́rin" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "Onígun mẹ́ta" @@ -3436,35 +3396,35 @@ msgstr "Onígun mẹ́ta" msgid "workspace.options.stroke-color" msgstr "Àwọ ìlà" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "Ìgbòrò ìlà" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "Àárín" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "Tó ní ìlà-ìlà" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "àmì tó tó tó" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "Nínú" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "Tí a ti dàpọ̀" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "Ìta" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "Ṣinsin" @@ -3500,11 +3460,11 @@ msgstr "Gbígbòòrò aládáàṣiṣé" msgid "workspace.options.text-options.grow-fixed" msgstr "Tí kò yíyadà" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "Àlàfo lẹ́tà" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "Gíga ìlà" @@ -3550,55 +3510,55 @@ msgstr "" "Lo bọ́tìnì ìṣe tó wà lóori àkọsórí náà láti ṣakóso ìwò " "àkàwé." -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "Ìgbòrò" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "Ipò X" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Ipò Y" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "Fa ìpàdé (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "Da ìpàdé pọ̀ (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "Sí igun (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "Sí ẹkọ̀rọ̀ (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "Da ìpàdé pò (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "Gbé ìpàde (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "Ya ̣ìpàdé sọ́tọ̀ (%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "Ìmọ̀lára ìpàdé (%s)" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "Fi àrọ ètò ìrísí kun" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "Fi gírídì ètò ìrísị kun" @@ -3610,7 +3570,7 @@ msgstr "dáa padà séyiǹ" msgid "workspace.shape.menu.backward" msgstr "dá padà sọ́wọ èyiǹ" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "Ṣẹ̀dà" @@ -3618,27 +3578,27 @@ msgstr "Ṣẹ̀dà" msgid "workspace.shape.menu.create-annotation" msgstr "Ṣèda àlàyé" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "Yíyàn sí pátákó" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "Ṣẹ́dá àkóónú" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "Ṣẹ̀dá ọ̀pọ̀lọpọ̀ àkóónú" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "Gé" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "Parẹ́" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "Pa ìbẹ̀ẹ̀rè ìsàn rẹ́" @@ -3650,7 +3610,7 @@ msgstr "ṣẹ́ àpẹ̣ẹre yọ nípò" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "Ṣẹ́ àwọn àpẹẹrẹ yọ nípò" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "Ìyàtọ̀" @@ -3658,15 +3618,15 @@ msgstr "Ìyàtọ̀" msgid "workspace.shape.menu.duplicate" msgstr "Sẹ ẹ̀dà" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "Sàtúnṣe" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "Ṣà yọ sílè" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "Tẹ́ pẹrẹsẹ" @@ -3678,7 +3638,7 @@ msgstr "Ìyí padà oníbùú" msgid "workspace.shape.menu.flip-vertical" msgstr "Ìyí padà olóòró" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "Ìbẹ̀rè ìṣàn" @@ -3690,39 +3650,39 @@ msgstr "Mú wá sọ́wọ́ iwájú" msgid "workspace.shape.menu.front" msgstr "Mú wá sí wájú" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "Àwùjọ" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "Tọ́jú" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "Fihàn/ Tọ́jú UI" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "Ìkòríta" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "Tì" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "Ìbòjú" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "lẹ̀mọ́" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "Ipa ọ̀nà" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "Yọ àrọ ètò ìrísí kúrò" @@ -3738,11 +3698,11 @@ msgstr "Dạ àkóónú pàtàkì sí pò" msgid "workspace.shape.menu.select-layer" msgstr "Yan ìpele" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "Ṣà fihàn" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "Ṣafihàn nínú atọ́ka àwọn ìgbìmọ̀ ìní" @@ -3758,19 +3718,19 @@ msgstr "Yọ àwòrán aláṣojú kúrò" msgid "workspace.shape.menu.thumbnail-set" msgstr "Fi sípò àwòrań aláṣojú" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "Ṣe àìkójọ" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "Ìbáṣepò" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "Ṣíí" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "Sí ìbòjú" @@ -3782,47 +3742,47 @@ msgstr "Wó òpó ègbẹ́" msgid "workspace.sidebar.expand" msgstr "Fẹ òpó ẹ̀gbẹ́" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "Ìtàn" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "Àwọn ìpele" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "Àwọn àkóónú" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "Àwọn pátákó" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "Àkójọ" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "Àwọn àwòrán" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "Àwọn ìbòjú" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "Àwọn ìpele wíwá" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "Àwọn ìrísí" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "Àwọn àyọkà" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "Gbígbé àwọn ànímọ́ SVG wọlé wá" @@ -3830,7 +3790,7 @@ msgstr "Gbígbé àwọn ànímọ́ SVG wọlé wá" msgid "workspace.sidebar.sitemap" msgstr "Àwọn abala" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "Máàpù ojú ìlà" @@ -3838,51 +3798,51 @@ msgstr "Máàpù ojú ìlà" msgid "workspace.toolbar.assets" msgstr "Àwọn ohun ìní" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "Pálètì àwọ̀ (%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "Àwọn ìdáásí (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "Ìṣépo (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "Àlàfo (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "Pátákó (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "Àwòrán(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "Gbé (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "Pópó ọ̀nà (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "Onígun mẹ́rin (%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "Àyọkà (%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "Àwọn ohun ìtẹ̀wé (%s)" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "Di ìgbà yìí kò sí àyípadà nínú ìtàn" @@ -4022,18 +3982,18 @@ msgstr "Ohun ìyì ìwé títẹ̀" msgid "workspace.undo.entry.unknown" msgstr "Iṣẹ́ parí %s" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "Ta nù" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "Àlàyé síi" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "Àwọn imúdójúìwọ̀n wà nínú àwọn yàrá ìkàwẹ tí a pín" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "Ìmúdójúìwọ̀n" diff --git a/frontend/translations/zh_CN.po b/frontend/translations/zh_CN.po index f17366656f..d9fce3851c 100644 --- a/frontend/translations/zh_CN.po +++ b/frontend/translations/zh_CN.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:13+0000\n" -"Last-Translator: DoubleCat \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Chinese (Simplified Han script) " "\n" "Language: zh_CN\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "已经有账号了?" @@ -31,7 +31,7 @@ msgstr "创建演示账号" msgid "auth.demo-warning" msgstr "这是演示服务,请勿用于实际工作。项目会被定期清除。" -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "忘记密码?" @@ -39,35 +39,35 @@ msgstr "忘记密码?" msgid "auth.fullname" msgstr "全名" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "登录我的账号" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "在这里登录。" -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot 是一款用于设计与代码协作的自由开源设计工具" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "使用 GitHub 登录" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "使用 GitLab 登录" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "使用 Google 登录" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "使用 LDAP 登录" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "使用 OpenID 登录" @@ -87,7 +87,7 @@ msgstr "个人资料尚未验证,请验证后再继续。" msgid "auth.notifications.recovery-token-sent" msgstr "找回密码链接已发至你的收件箱。" -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "成功加入团队" @@ -119,7 +119,7 @@ msgstr "忘记密码?" msgid "auth.recovery-submit" msgstr "修改密码" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "现在还没有账号?" @@ -131,7 +131,7 @@ msgstr "请告诉我们在仪表板和邮件中该如何称呼你。" msgid "auth.register-account-title" msgstr "你的姓名" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "创建账号" @@ -144,7 +144,7 @@ msgstr "创建账号" msgid "auth.terms-and-privacy-agreement" msgstr "我同意[服务条款](%s)和[隐私政策](%s)。" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "服务条款" @@ -164,89 +164,89 @@ msgstr "没有可用的颜色令牌。请检查已启用的令牌集或主题, msgid "comments.mentions.not-found" msgstr "未找到 @%s 相关人员" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "发布" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "所有 Penpot 用户" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "确定要移除此链接吗?移除后,任何人都无法再通过此链接访问" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(当前)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "移除链接" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "获取链接" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "链接已复制" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "权限管理" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "已共享 1 个页面" msgstr[1] "已共享 %s 个页面" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "可评论" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "可检查代码" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "任何人通过此链接都可访问" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "页面已共享" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "可分享的链接会在此处显示" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "仅团队成员" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "分享原型" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "全选" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "取消发布" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "团队管理" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "Penpot 专为团队协作而设计。邀请成员共同处理项目和文件" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "组建团队!" @@ -262,27 +262,27 @@ msgstr "恢复文件" msgid "dashboard.add-file" msgstr "添加文件" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "添加为共享库" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "修改电子邮件" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "清空回收站" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(复制)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "创建新团队" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "固定此版本" @@ -290,7 +290,7 @@ msgstr "固定此版本" msgid "dashboard.default-team-name" msgstr "你的 Penpot" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "确定要永久删除回收站中的所有项目和文件吗?此操作无法撤销。" @@ -298,39 +298,39 @@ msgstr "确定要永久删除回收站中的所有项目和文件吗?此操作 msgid "dashboard.delete-file-forever-confirmation.description" msgstr "确定要永久删除 %s 吗?此操作无法撤销。" -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "已成功删除 %s 个文件。" -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "永久删除" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "删除项目" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "确定要永久删除项目 %s 吗?该项目及其包含的所有文件都将被永久删除。此操作无法撤销。" -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "已成功删除 %s。" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "删除团队" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "回收站为空。删除的文件和项目会显示在这里。" -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "将于 %s 删除" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "下载 Penpot 文件(.penpot)" @@ -342,19 +342,19 @@ msgstr "复制" msgid "dashboard.duplicate-multi" msgstr "复制 %s 个文件" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "项目成员创建草稿后,它将显示在这里。" -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "还没有草稿。" -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "项目成员创建文件后,它将显示在这里。" -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "还没有文件。" @@ -401,23 +401,23 @@ msgstr "导入 .penpot 文件" msgid "dashboard.empty-project.start" msgstr "开始打造惊人的作品" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "删除文件时出错。" -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "删除项目 %s 时出错。" -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "恢复文件 %s 时出错。" -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "恢复文件时出错。" -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "恢复项目 %s 及其文件时出错。" @@ -425,35 +425,35 @@ msgstr "恢复项目 %s 及其文件时出错。" msgid "dashboard.export-binary-multi" msgstr "下载 %s 个 Penpot 文件(.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "将画板导出为 PDF 格式" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "导出为 PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "已选择 %s/%s 个元素" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "导出" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "你可以在设计属性中为元素添加导出设置(位于右侧边栏底部)。" -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "了解如何在 Penpot 中设置导出。" -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "没有设置为导出的元素。" -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "导出所选内容" @@ -469,33 +469,33 @@ msgid_plural "dashboard.file-menu.restore-files-option" msgstr[0] "恢复文件" msgstr[1] "恢复文件" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "缺失字体" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "全部忽略" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "你上传的自定义字体会显示在这里。" -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "还没有自定义字体。" -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "项目成员上传自定义字体后,字体会显示在这里。" -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "已添加 1 个字体" msgstr[1] "已添加 %s 个字体" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" @@ -503,18 +503,18 @@ msgstr "" "字体都会添加到此团队所有文件的文本属性中的可用字体家族列表。字体家族名称相同的字体会归为**一个字体家族**。你可以上传以下格式的字体:**" "TTF、OTF 和 WOFF**(只需上传其中一种格式)。" -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" "你应当只向 Penpot 上传你拥有或获准使用的字体。请参阅 [Penpot " "服务条款](%s)中的“内容权利”章节了解更多信息。你也可以阅读有关[字体许可](https://www.typography.com/faq)的信息。" -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "全部上传" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -522,15 +522,15 @@ msgstr "" "netlify.app/)进行检查。此外,我们建议使用 " "[Transfonter](https://transfonter.org/)生成网络字体并修复错误。 " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "前往管理控制台" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "导入 Penpot 文件" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "糟糕!无法导入此文件" @@ -538,37 +538,37 @@ msgstr "糟糕!无法导入此文件" msgid "dashboard.import.bad-url" msgstr "导入失败。模板 URL 不正确" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "无法验证此文件。" -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "这个文件似乎已损坏。" -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "部分文件未能导入" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "下面的文件有错误:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "有错误的文件将不会被上传。" -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "处理该文件的时候出错了。" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgid_plural "dashboard.import.import-message" msgstr[0] "已成功导入 1 个文件。" msgstr[1] "已成功导入 %s 个文件。" -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "部分文件包含无效对象,这些对象已被移除。" @@ -576,79 +576,51 @@ msgstr "部分文件包含无效对象,这些对象已被移除。" msgid "dashboard.import.no-perms" msgstr "你没有权限将文件导入此团队" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "正在处理颜色" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "正在处理组件" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "正在处理媒体" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "正在处理页面:%s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "正在处理排版" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "正在将数据上传到服务器(%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "正在上传文件:%s" - -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "你将删除发送给以下人员的邀请:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "你将向以下人员重新发送邀请:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "删除邀请" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "重新发送邀请" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "你没有权限邀请他人加入此团队,也没有权限编辑或删除邀请。" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "邀请成员" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "退出团队" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "库和模板" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "这里列出了可以添加到项目的库和模板" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "探索更多内容,了解如何做出贡献" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "导入模板时发生错误。模板未导入成功。" -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "库" @@ -656,7 +628,7 @@ msgstr "库" msgid "dashboard.loading-files" msgstr "正在加载文件…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "正在加载字体…" @@ -676,51 +648,51 @@ msgstr "移动 %s 个文件到" msgid "dashboard.move-to-other-team" msgstr "移动到其他团队" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ 新文件" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "新建文件" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ 新项目" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "新建项目" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "没有找到“%s”的匹配项" -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "你无权在 %s 组织内创建团队。如需更多信息,请联系该组织的负责人。" -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "你无权在 %s 组织内删除团队。如需更多信息,请联系该组织的负责人。" -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "你无权在 %s 组织内移动团队。如需更多信息,请联系该组织的负责人。" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "固定的项目会显示在这里" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "通知" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "电子邮箱地址已成功更新" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "电子邮箱地址已成功验证" @@ -732,11 +704,11 @@ msgstr "通知设置已更新" msgid "dashboard.notifications.password-saved" msgstr "密码已成功保存!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "查看通知" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s 名成员" @@ -744,15 +716,15 @@ msgstr "%s 名成员" msgid "dashboard.open-in-new-tab" msgstr "在新标签页打开文件" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "选项" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "按角色排序" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "按状态排序" @@ -776,7 +748,7 @@ msgstr "你现在是此团队的所有者。" msgid "dashboard.permissions-change.viewer" msgstr "你现在是此团队的查看者。" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "固定到侧边栏/取消固定" @@ -792,27 +764,27 @@ msgstr "不能解析插件清单" msgid "dashboard.plugins.try-plugin" msgstr "尝试插件: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "删除文件中…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "恢复文件中…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "删除速度出乎意料地慢" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "恢复速度出乎意料地慢" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "项目" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "想要删除账号?" @@ -820,71 +792,71 @@ msgstr "想要删除账号?" msgid "dashboard.removed-from-team" msgstr "你现在不再是此团队“%s”的成员。" -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "你将恢复所有项目和文件。这可能需要一些时间。" -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "恢复所有的项目及其文件" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "恢复所有" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "已成功恢复 %s 个文件。" -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "恢复项目" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "你将恢复项目 %s 及其中的所有文件。" -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "恢复项目" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "已成功恢复 %s。" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "保存设置" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "搜索…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "正在搜索“%s”…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "组织" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "选择界面语言" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "选择界面主题" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot 深色模式(默认)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot 浅色模式" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "系统主题" @@ -948,11 +920,11 @@ msgstr "更新设置" msgid "dashboard.settings.notifications.title" msgstr "通知" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "显示所有文件" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "版本历史" @@ -976,7 +948,7 @@ msgstr[1] "文件已成功复制" msgid "dashboard.success-duplicate-project" msgstr "成功创建了项目副本" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "成功移动了文件" @@ -988,151 +960,151 @@ msgstr "成功移动了文件" msgid "dashboard.success-move-project" msgstr "成功移动了项目" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "团队信息" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "团队成员" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "团队组织" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "添加到组织" -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "更改团队所属组织" -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "这个团队不属于任何组织" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "从组织中移除团队" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "团队项目" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "添加到你的项目" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "界面主题" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "搜索结果" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "删除的文件会在回收站中保留" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s 天。 " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "之后,它们将被永久删除。" -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "如果你改变主意,可以在每个文件的菜单中恢复或永久删除它们。" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "输入内容以搜索" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "取消发布库" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "更新设置" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "测试版" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "WebGL 渲染可以提升性能,但目前仍处于测试阶段,稳定性可能较低。画布、导出内容(SVG/PDF)和查看模式之间可能存在一些视觉差异。" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "已关闭" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "已开启" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "提供反馈" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "状态" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "WebGL 渲染" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "已启用" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "触发此 Webhook 时会传递事件详情" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "你只能删除或修改自己创建的 Webhook。" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "内容类型" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "创建 Webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "Webhook 创建成功。" -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "Webhook 是一种让其他网站和应用在 Penpot 中发生特定事件时收到通知的简便方式。我们会向你提供的每个 URL 发送 POST 请求。" -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "点击“添加 Webhook”按钮即可添加。" -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "尚未创建 Webhook。" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "你的账号" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "电子邮件" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "你的姓名" @@ -1148,11 +1120,11 @@ msgstr "注意" msgid "ds.component-subtitle" msgstr "待更新的组件:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "允许" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "取消" @@ -1172,7 +1144,7 @@ msgstr "已启用的令牌集或主题中没有适用的令牌。" msgid "ds.inputs.numeric-input.no-matches" msgstr "无匹配项。" -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "打开令牌列表" @@ -1188,15 +1160,15 @@ msgstr "此个人资料不允许使用该身份验证提供商" msgid "errors.auth-provider-not-configured" msgstr "未配置身份验证提供商。" -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "你似乎还没有登录或会话已过期。" -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "无法加载字体 %s" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "无法加载字体 %s" @@ -1208,47 +1180,47 @@ msgstr "无法上传该媒体文件。" msgid "errors.character-limit-exceeded" msgstr "超出字符限制" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "剪贴板 API 不可用。请通过 HTTPS 提供 Penpot 服务以启用剪贴板访问" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "你的浏览器不支持此操作" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "剪贴板访问遭拒。请在浏览器中授予剪贴板权限以粘贴内容" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "处理评论时出错" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "抱歉!这是一个使用已弃用 Penpot 素材类型的旧文件,你无法打开它。" -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "链接并向我们说明情况。" -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "虽然 Penpot 不再支持此类素材,但如需帮助,请点击下方的" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "联系我们" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "无法将父集放置到自己的子路径中。" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "此电子邮箱已被使用" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "电子邮箱已验证。" @@ -1260,27 +1232,27 @@ msgstr "密码不能为邮箱地址" msgid "errors.email-does-not-match-invitation" msgstr "电子邮箱与邀请不匹配。" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "不允许使用此域名" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "电子邮箱 «%s» 有多次永久退信记录。" -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "电子邮箱 «%s» 已被举报为垃圾邮件来源或有永久退信记录。" -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "你打开的文件启用了“%s”功能,但当前 Penpot 版本不支持或未启用此功能。" -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "不支持功能“%s”。" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "最多可包含 %s 个字符。" @@ -1288,7 +1260,7 @@ msgstr "最多可包含 %s 个字符。" msgid "errors.field-min-length" msgstr "至少需要包含 1 个字符。" -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "空字段" @@ -1296,19 +1268,19 @@ msgstr "空字段" msgid "errors.field-not-all-whitespace" msgstr "名称必须包含除空格外的其他字符。" -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "当前启用的功能与要打开的文件所需功能不匹配。必须先对“%s”应用迁移,才能打开此文件。" -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "字体家族名称只能包含字母、数字、空格、连字符、下划线和句点。" -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "出现了问题。" -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Web Worker 出错。" @@ -1324,7 +1296,7 @@ msgstr "无效数据" msgid "errors.invalid-email" msgstr "请输入有效的电子邮件" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "两次输入的电子邮箱必须一致" @@ -1356,11 +1328,11 @@ msgstr "此邀请可能已取消或已过期。" msgid "errors.ldap-disabled" msgstr "LDAP 身份验证已禁用。" -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "你已达到“%s”配额。请联系支持团队。" -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "已达到单次请求可邀请的电子邮箱数量上限(%s)" @@ -1376,19 +1348,19 @@ msgstr "图片内容好像与文档扩展名不匹配。" msgid "errors.media-type-not-allowed" msgstr "该图片好像不可用。" -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "受邀用户已停用邮件通知(曾举报垃圾邮件或多次发生退信)。" -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "迁移正在进行中" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "只有版本创建者可以锁定" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "只有版本创建者可以解除锁定" @@ -1400,7 +1372,7 @@ msgstr "两次输入的密码必须一致" msgid "errors.password-too-short" msgstr "密码至少需要 8 个字符" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "剪贴板中的数据无效" @@ -1408,7 +1380,7 @@ msgstr "剪贴板中的数据无效" msgid "errors.profile-blocked" msgstr "此账号已被封禁" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "你的邮件通知已停用(曾举报垃圾邮件或多次发生退信)。" @@ -1416,19 +1388,19 @@ msgstr "你的邮件通知已停用(曾举报垃圾邮件或多次发生退信 msgid "errors.registration-disabled" msgstr "当前禁止注册。" -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG 无效或格式错误" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "检测到不兼容功能“%s”" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "团队成员不足,无法退出。你可能需要删除此团队。" -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "你尝试指定的成员不存在。" @@ -1436,7 +1408,7 @@ msgstr "你尝试指定的成员不存在。" msgid "errors.team-leave.only-owner-can-delete" msgstr "只有团队所有者可以删除团队。" -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "所有者无法退出团队,你必须先转让所有者角色。" @@ -1448,11 +1420,11 @@ msgstr "团队名称不能包含以下字符:'.'、':' 或 '/'" msgid "errors.token-set-already-exists" msgstr "已存在同名令牌集" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "无法完成放置:此路径中已存在同名令牌集。" -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "已存在同名令牌主题" @@ -1464,11 +1436,11 @@ msgstr "此主题引用了不存在的令牌集:%s" msgid "errors.tokens.circular-reference" msgstr "令牌存在循环引用" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "行高依赖字号。请添加字号以获取解析值。" -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "此字段不能为空" @@ -1488,11 +1460,11 @@ msgstr "导入错误:" msgid "errors.tokens.invalid-color" msgstr "无效的颜色值:%s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "令牌值无效:只能引用字体家族令牌" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "字重值无效:请使用数值(100-950)或标准名称(thin、light、regular、bold 等),后面可加“Italic”" @@ -1510,35 +1482,35 @@ msgstr "" "“%s”不是有效的令牌名称。\n" "令牌名称只能包含以英文句点(.)分隔的字母和数字,且不能以美元符号($)开头。" -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "阴影类型无效:仅接受 'innerShadow' 或 'dropShadow'" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "令牌值无效:仅接受以下字面值:none、uppercase、lowercase 或 capitalize" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "令牌值无效:仅接受以下字面值:none、underline 或 strike-through" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "值无效:必须引用复合阴影令牌。" -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "值无效:必须引用复合排版令牌。" -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "无效的令牌值:%s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "缺少令牌引用: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "%s 的组名与另一个已启用令牌集中的同名令牌冲突。" @@ -1550,31 +1522,31 @@ msgstr "此文件中未找到任何令牌、令牌集或令牌主题。" msgid "errors.tokens.number-too-large" msgstr "令牌值无效。解析值过大:%s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "不透明度必须介于 0 和 100% 之间或介于 0 和 1 之间(例如 50% 或 0.5)。" -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "令牌存在自引用" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "阴影模糊值必须大于或等于 0。" -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "阴影扩展值必须大于或等于 0。" -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "描边宽度必须大于或等于 0。" -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "值无效:不允许使用 %。" -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "值无效:不允许使用单位。" @@ -1582,55 +1554,55 @@ msgstr "值无效:不允许使用单位。" msgid "errors.unexpected-error" msgstr "发生了意料之外的错误。" -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "未知的令牌" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "此版本已被锁定" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "此版本被锁定,不能被其他人删除" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "文件具有不兼容的版本号" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL 已停止工作。请重新加载页面以将其重置" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "糟糕!画布上下文已丢失" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "连接错误,无法访问 URL" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "网址未通过验证。" -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "上次交付不成功。" -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "SSL 验证时出错。" -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "超时" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "验证时出现意外错误" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "意外状态 %s" @@ -1650,15 +1622,15 @@ msgstr "描述" msgid "feedback.description-placeholder" msgstr "请说明反馈原因" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "很高兴在这里见到你。如需帮助,请先搜索再发帖。" -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot 社区" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "其他联系方式" @@ -1678,11 +1650,11 @@ msgstr "请说明你发邮件的原因,详细说明这是一个问题反馈、 msgid "feedback.title-contact-us" msgstr "联系我们" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "帮助你解决技术问题。" -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X 支持账号" @@ -1702,10 +1674,6 @@ msgstr "建议" msgid "feedback.type.issue" msgstr "问题" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "下载文件" - #: src/app/main/ui/settings/password.cljs:45 msgid "generic.error" msgstr "发生错误" @@ -1742,7 +1710,7 @@ msgstr "形状填充图像的预览" msgid "inspect.attributes.shadow" msgstr "阴影" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "尺寸和位置" @@ -1750,43 +1718,31 @@ msgstr "尺寸和位置" msgid "inspect.attributes.stroke" msgstr "描边" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "居中" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "内侧" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "外侧" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "排版" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "字体家族" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "字号" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "字体样式" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "字间距" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "行高" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "混合字体家族" @@ -1802,11 +1758,11 @@ msgstr "删除线" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "下划线" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "单词首字母大写" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "小写" @@ -1818,7 +1774,7 @@ msgstr "无" msgid "inspect.attributes.typography.text-transform.unset" msgstr "未设置" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "大写" @@ -1942,23 +1898,23 @@ msgstr "解析值:" msgid "inspect.tabs.styles.variants-panel" msgstr "变体属性" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "创建新访问令牌" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "点击“创建新访问令牌”按钮即可生成令牌。" -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "你还没有访问令牌。" -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "个人访问令牌" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "个人访问令牌可替代账号密码认证,让应用访问 Penpot 内部 API" @@ -2038,47 +1994,47 @@ msgstr "MCP 密钥将于 %s 过期" msgid "integrations.mcp-key.will-not-expire" msgstr "MCP 密钥没有到期日期" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "Penpot MCP 服务器让 MCP 客户端能够直接与 Penpot 设计文件交互。" -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "如何配置 MCP 客户端" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "你需要使用此服务器 URL 配置 MCP 客户端,以连接到 Penpot MCP 服务器。" -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "重新生成 MCP 密钥" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "MCP 密钥" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "配置 MCP 客户端时需要 MCP 密钥" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "状态" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "已停用" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "已启用" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "用于连接 MCP 服务器的 MCP 密钥已过期,因此无法建立连接。" -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "请重新生成 MCP 密钥,并在客户端配置中更新为新密钥。" @@ -2086,10 +2042,6 @@ msgstr "请重新生成 MCP 密钥,并在客户端配置中更新为新密钥 msgid "integrations.mcp-server.title" msgstr "MCP 服务器" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "测试版" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "名称" @@ -2138,7 +2090,7 @@ msgstr "重新生成 MCP 密钥" msgid "integrations.regenerate-mcp-key.title.created" msgstr "MCP 密钥已重新生成" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "集成" @@ -2154,47 +2106,47 @@ msgstr "令牌将于 %s 过期" msgid "integrations.token.will-not-expire" msgstr "令牌没有到期日期" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "标记全部为已读" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "快捷方式" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "关于 Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "接受" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "启用" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "新增" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "添加自定义字体" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "正在添加…" -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "管理员" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "全部" @@ -2202,15 +2154,15 @@ msgstr "全部" msgid "labels.and" msgstr "和" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "后退" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "请过会儿再来试试,我们正在对服务器进行一些简单维护。" -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "网关错误" @@ -2222,15 +2174,15 @@ msgstr "模糊" msgid "labels.blur-effects" msgstr "模糊效果" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "取消" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "Canva" -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "关闭" @@ -2238,7 +2190,7 @@ msgstr "关闭" msgid "labels.code" msgstr "代码" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "折叠" @@ -2250,15 +2202,15 @@ msgstr "颜色" msgid "labels.comment" msgstr "评论" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "评论" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "社区" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "社区与贡献" @@ -2270,19 +2222,19 @@ msgstr "计算结果" msgid "labels.confirm-password" msgstr "确认密码" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "联系支持团队" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "联系我们" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "继续" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "你可以使用 Penpot 账号继续" @@ -2294,31 +2246,31 @@ msgstr "复制" msgid "labels.copy-color" msgstr "复制颜色" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "复制链接" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019 - 至今" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "创建" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "创建新团队" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "输入新的团队名称" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "仪表板" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "删除" @@ -2330,7 +2282,7 @@ msgstr "删除评论" msgid "labels.delete-comment-thread" msgstr "删除该讨论串" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "删除邀请" @@ -2338,39 +2290,35 @@ msgstr "删除邀请" msgid "labels.delete-multi-files" msgstr "删除 %s 个文件" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "确定要删除你的头像吗?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "删除头像" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "已删除" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "开发" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "丢弃" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "下载 %s" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "下载" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "正在下载文件…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "草稿" @@ -2378,19 +2326,19 @@ msgstr "草稿" msgid "labels.duplicate" msgstr "复制" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "编辑" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "编辑者" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "空" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "错误" @@ -2398,11 +2346,11 @@ msgstr "错误" msgid "labels.exit" msgstr "退出" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "已到期" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "导出" @@ -2414,7 +2362,7 @@ msgstr "反馈已禁用" msgid "labels.feedback-sent" msgstr "反馈已发出" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2422,63 +2370,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "填充" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "Flex 布局" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "字体家族" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "样式" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "字体" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "GitHub 仓库" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "提交反馈" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "返回" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "平面设计" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "网格布局" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "帮助中心" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "帮助与学习" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "隐藏" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "隐藏已解决的评论" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "导入" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "未激活" @@ -2486,51 +2434,51 @@ msgstr "未激活" msgid "labels.info" msgstr "信息" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "已安装字体" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "集成" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "出现了问题。" -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "你可以重试此操作,或联系支持团队报告错误。" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "内部错误" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "邀请" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "语言" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "布局" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "学习中心" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "库和模板" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "加载中…" @@ -2538,7 +2486,7 @@ msgstr "加载中…" msgid "labels.lock" msgstr "锁定" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "登录或注册" @@ -2546,19 +2494,19 @@ msgstr "登录或注册" msgid "labels.login" msgstr "登录" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "退出登录" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "市场营销" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "成员" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "成员" @@ -2570,23 +2518,23 @@ msgstr "提及" msgid "labels.new-password" msgstr "新密码" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "下一项" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "由于系统问题,Penpot 暂时不可用。请稍后重试。" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "所有评论都已处理完毕!新的评论通知会显示在这里。" -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "没有待处理的邀请。" -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "邀请大家加入,一起创造精彩成果。" @@ -2594,27 +2542,27 @@ msgstr "邀请大家加入,一起创造精彩成果。" msgid "labels.no-matches" msgstr "没有匹配项" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "哎呀!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "通知" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "1 个文件" msgstr[1] "%s 个文件" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "1 个画板" msgstr[1] "%s 个画板" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "1 个项目" @@ -2624,43 +2572,43 @@ msgstr[1] "%s 个项目" msgid "labels.old-password" msgstr "旧密码" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "打开" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "选项" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "其他(请注明)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "其他" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "所有者" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "密码" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "待处理" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Penpot 更新日志" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot 中心" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "固定项目" @@ -2668,31 +2616,31 @@ msgstr "固定项目" msgid "labels.post" msgstr "发帖" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "前一项" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "产品或 UX 设计" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "产品管理" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "个人资料" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "项目" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "最近" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "参考" @@ -2700,27 +2648,27 @@ msgstr "参考" msgid "labels.refresh" msgstr "刷新" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "发布说明" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "重新加载页面" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "移除" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "删除成员" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "重命名" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "重命名团队" @@ -2744,11 +2692,11 @@ msgstr "新回复" msgid "labels.reply.thread" msgstr "回复" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "重新发送" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "重新发送邀请" @@ -2756,43 +2704,43 @@ msgstr "重新发送邀请" msgid "labels.restore" msgstr "还原" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "重试" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "角色" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "保存" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "搜索" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "搜索字体" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "选择选项" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "发送" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "正在发送…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "系统正在按计划维护。" -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "服务不可用" @@ -2800,55 +2748,55 @@ msgstr "服务不可用" msgid "labels.sets" msgstr "集" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "设置" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "阴影" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "分享" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "库" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "显示" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "显示所有评论" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "显示评论列表" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "只显示提及你的评论" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "只显示你的评论" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "Sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "来源" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "开始" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "状态" @@ -2856,7 +2804,7 @@ msgstr "状态" msgid "labels.stroke" msgstr "描边" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "学生/教师" @@ -2868,7 +2816,7 @@ msgstr "样式" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "切换" @@ -2876,19 +2824,19 @@ msgstr "切换" msgid "labels.text" msgstr "文本" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "主题" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "教程" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "排版" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "未知错误" @@ -2900,35 +2848,31 @@ msgstr "解锁" msgid "labels.unpublish-multi-files" msgstr "取消发布 %s 个文件" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "更新" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "更新团队" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "上传" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "上传自定义字体" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "正在上传…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "正在上传文件…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "变体" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "版本 %s 说明" @@ -2936,7 +2880,7 @@ msgstr "版本 %s 说明" msgid "labels.view-only" msgstr "仅查看" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "查看者" @@ -2950,7 +2894,7 @@ msgid_plural "labels.warning-count" msgstr[0] "%s 条警告" msgstr[1] "%s 条警告" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhook" @@ -2958,11 +2902,11 @@ msgstr "Webhook" msgid "labels.write-new-comment" msgstr "写一条新评论" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(你)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "你的账号" @@ -3090,59 +3034,59 @@ msgstr "添加为共享库后,此文件库中的素材可供其他文件使用 msgid "modals.add-shared-confirm.message" msgstr "将“%s”添加为共享库" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "大幅微调" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "确认新电子邮箱" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "我们会向你当前的电子邮箱“%s”发送一封邮件,以验证你的身份。" -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "新电子邮箱" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "更改电子邮箱" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "更改电子邮箱" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "创建 Webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "创建 Webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "有效载荷 URL" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "取消操作并保留我的账号" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "是的,删除我的账号" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "删除账号后,你会失去所有项目和存档。" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "你确定想要删除你的账号?" @@ -3200,27 +3144,27 @@ msgstr "确定要删除这 %s 个文件吗?" msgid "modals.delete-file-multi-confirm.title" msgstr "删除 %s 个文件" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "确定要删除此字体样式吗?如果文件使用了该样式,将无法加载它。" -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "删除字体样式" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "确定要删除此字体吗?如果文件使用了该字体,将无法加载它。" -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "删除字体" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "你确定想要删除这个页面?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "删除页面" @@ -3236,31 +3180,31 @@ msgstr "你确定想要删除这个项目?" msgid "modals.delete-project-confirm.title" msgstr "删除项目" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "删除文件" msgstr[1] "删除文件" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "所选库未在任何文件中启用。" msgstr[1] "所选库未在任何文件中启用。" -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "所选库已在以下文件中启用: " msgstr[1] "所选库已在以下文件中启用: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "确定要删除所选文件吗?" msgstr[1] "确定要删除所选文件吗?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "删除文件" @@ -3278,41 +3222,41 @@ msgstr "确定要删除此团队吗?与该团队关联的所有项目和文件 msgid "modals.delete-team-confirm.title" msgstr "删除团队" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "删除成员" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "你确定想要从团队中删除这个成员?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "删除团队成员" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "已在这些文件中使用的素材会保留在原处(不会破坏现有设计)。" msgstr[1] "已在这些文件中使用的素材会保留在原处(不会破坏现有设计)。" -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "删除 Webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "确定要删除此 Webhook 吗?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "删除 Webhook" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "编辑 Webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "编辑 Webhook" @@ -3328,15 +3272,15 @@ msgstr "导入令牌" msgid "modals.import-library-tokens.title" msgstr "从库中导入令牌?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "发送邀请" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "电子邮件,以逗号分隔" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "有些成员已经在团队中,我们将邀请其余成员。" @@ -3348,145 +3292,145 @@ msgstr "只能邀请组织的现有成员。由于列表中的人员都不是组 msgid "modals.invite-restricted-members.all-blocked-title" msgstr "未发送任何邀请" -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "不会收到邀请的地址" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "取消" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "只能邀请组织的现有成员。" -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "发送邀请" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "部分邀请无法发送" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "列表中的部分人员不会收到邀请。" -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "你可以邀请成员加入团队,以便他们访问此文件及团队的所有文件。" -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "邀请成员加入团队" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "由于你是这个团队的唯一成员,这个团队将连同其项目和文件一起被删除。" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "确定要离开 %s 团队吗?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "如果不能推选另一个成员作为团队所有者,你就无法离开团队。你或许想要删除该团队。" -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "你是此团队的所有者。在你离开团队之前,请选择其他成员晋升为所有者。" -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "晋升并退出" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "选择一位成员晋升" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "在你离开之前" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "退出团队" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "你确定要离开本团队吗?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "正在退出团队" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "移动" msgstr[1] "移动" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "确定要移动所选共享库吗?" msgstr[1] "确定要移动所选共享库吗?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "移动共享库" msgstr[1] "移动共享库" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "微调量" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "转让所有权" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "转让所有权后,你的角色将变为管理员,并失去对此团队的部分权限。 " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "你是此团队的当前所有者。确定要将 %s 设为团队的新所有者吗?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "新团队所有者" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "发布" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "你的库是空的。你确定想要发布它?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "发布空库" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "小幅微调" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "取消发布" msgstr[1] "取消发布" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "确定要取消发布所选库吗?" msgstr[1] "确定要取消发布所选库吗?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "取消发布库" @@ -3548,27 +3492,27 @@ msgstr "激活" msgid "nitrate.code-activation.title" msgstr "激活 Nitrate" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "联系销售团队" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "如需切换到此方案,请联系我们的销售团队。我们会协助你更新订阅并确保一切设置妥当。" -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "你的总存储空间、自动版本历史记录和文件恢复期限将受到限制。" -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "团队、项目和文件将不再属于任何组织,但仍可继续访问。" -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "降级后:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "要切换到 %s 方案吗?" @@ -3596,7 +3540,7 @@ msgstr "通过专用管理控制台,集中管理人员在各处可执行的操 msgid "nitrate.form.enterprise-feature-3" msgstr "保留完整的开源自由,同时满足组织的治理需求" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "为团队安全地规模化协作提供所需的一切:" @@ -3612,7 +3556,7 @@ msgstr "查看当前方案" msgid "nitrate.form.start-enterprise" msgstr "开始使用 Enterprise" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "解锁 Enterprise 功能" @@ -3620,7 +3564,7 @@ msgstr "解锁 Enterprise 功能" msgid "nitrate.form.try-free" msgstr "免费试用 14 天" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "有效期至 %s" @@ -3628,19 +3572,19 @@ msgstr "有效期至 %s" msgid "nitrate.subscription.renew-success" msgstr "你的 Nitrate 订阅已续订。" -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "取消订阅" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "使用激活码续订" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "此页面不存在" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 错误" @@ -3656,11 +3600,11 @@ msgstr "我们已将恢复电子邮件发送至" msgid "not-found.login.sent-recovery-check" msgstr "请检查电子邮箱,然后点击链接创建新密码。" -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "免费注册" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "并在数秒内开始使用 Penpot!" @@ -3668,63 +3612,63 @@ msgstr "并在数秒内开始使用 Penpot!" msgid "not-found.made-with-love" msgstr "用爱和开源制作" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "你已申请访问此文件。" -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "你已申请访问此文件,或此团队的其他文件或项目。" -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "你已申请访问此项目,或此团队的其他项目或文件。" -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "你已申请访问此项目。" -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "请求访问权限" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "请注意,如果所有者批准请求,你将受邀加入此团队。" -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "你的请求已成功发送!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "你无权访问此文件。" -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "前往你的 Penpot" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "如果所有者批准请求,你将受邀加入此团队。" -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot 文件" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "你无权访问此项目。" -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "项目" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "要访问此文件,你可以向团队所有者申请。" -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "要访问此项目,你可以向团队所有者申请。" @@ -3736,27 +3680,27 @@ msgstr "维护通知:系统将在 5 分钟内短暂停机维护。" msgid "notifications.by-code.upgrade-version" msgstr "有新版本可用。" -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "邀请已成功删除" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "成功发送邀请" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "邀请链接已复制" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "你无法删除个人资料。请先转让你拥有的团队。" -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "个人资料保存成功!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "验证邮件已发送至 %s。请检查电子邮箱!" @@ -3784,99 +3728,99 @@ msgstr "为团队命名后,你就可以邀请其他人加入。" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "创建团队" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "邀请成员" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "邀请成员担任以下角色:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "让我们开始吧!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "从设计中生成真实代码" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "创建设计系统" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "原型设计" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "设计应用的 UI/UX" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "线框设计" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "你打算使用 Penpot 做什么?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "请告诉我们一些关于你的信息,帮助我们优化 Penpot。你的回答将帮助我们确定新功能的优先级,并指引你开始使用。" -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "帮助我们了解你" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "你最常使用以下哪个工具?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "你从事什么工作?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "你的公司规模有多大?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "你想从哪里开始?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1,001 - 5,000 人" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 - 500 人" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 - 100 人" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 - 1,000 人" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "只有我" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5,001 人以上" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "教育" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "个人" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "工作" @@ -3928,11 +3872,11 @@ msgstr "字段 %s 无效:%s" msgid "profile.recovery.go-to-login" msgstr "去登录" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "分离" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "混合" @@ -3944,35 +3888,35 @@ msgstr "移除颜色" msgid "settings.select-this-color" msgstr "选择使用此样式的项目" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "评论" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "底部对齐" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "水平居中对齐" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "靠左对齐" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "靠右对齐" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "顶部对齐" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "垂直居中对齐" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "以所选内容创建画板" @@ -3980,55 +3924,55 @@ msgstr "以所选内容创建画板" msgid "shortcuts.bold" msgstr "切换粗体" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "布尔差" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "布尔排除" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "布尔交集" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "布尔合并" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "移至最下层" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "移至下一层" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "移至上一层" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "移至最上层" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "清除撤销历史" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "拷贝" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "复制链接" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "复制属性" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "创建组件/变体" @@ -4036,35 +3980,35 @@ msgstr "创建组件/变体" msgid "shortcuts.create-new-project" msgstr "新建" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "剪切" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "缩小" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "删除" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "删除节点" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "拆分组件" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "曲线" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "椭圆" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "画板" @@ -4072,47 +4016,47 @@ msgstr "画板" msgid "shortcuts.draw-nodes" msgstr "绘制路径" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "路径" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "矩形" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "文本" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "复制" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "取消" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "导出形状" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "查找" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "查找和替换" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "缩放以显示全部" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "水平翻转" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "垂直翻转" @@ -4136,23 +4080,23 @@ msgstr "前往共享库" msgid "shortcuts.go-to-search" msgstr "搜索" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "编组" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "水平分布" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "显示/隐藏 UI" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "放大" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "插入图片" @@ -4160,7 +4104,7 @@ msgstr "插入图片" msgid "shortcuts.italic" msgstr "切换斜体" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "连接节点" @@ -4168,39 +4112,39 @@ msgstr "连接节点" msgid "shortcuts.line-through" msgstr "切换删除线" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "转换为角点" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "转换为曲线" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "遮罩" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "合并节点" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "移动" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "快速下移" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "快速向左移动" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "快速向右移动" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "快速上移" @@ -4208,337 +4152,337 @@ msgstr "快速上移" msgid "shortcuts.move-nodes" msgstr "移动节点" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "向下移动" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "左移" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "右移" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "上移" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "下个画板" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "未找到快捷键" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "将不透明度设为 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "将不透明度设为 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "将不透明度设为 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "将不透明度设为 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "将不透明度设为 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "将不透明度设为 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "将不透明度设为 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "将不透明度设为 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "将不透明度设为 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "将不透明度设为 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "色彩拾取器" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "前往阅读器的评论区" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "前往仪表板" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "前往阅读器的检查区域" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "前往阅读器的交互区域" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "打开阅读器" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "前往工作区" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " 或 " -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "粘贴" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "粘贴属性" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "粘贴并替换" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "插件管理器" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "前一画板" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "重做" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "重命名" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "重置缩放" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "缩放" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "基础" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "仪表板" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "阅读器" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "工作区" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "选择所有" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "选择下一个图层" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "选择上级图层" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "选择上一个图层" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "分离节点" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "显示/隐藏像素网格" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "显示/隐藏快捷方式" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "对齐到节点" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "对齐像素网格" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "开始编辑" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "对齐" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "编辑" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "通用" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "主菜单" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "修改图层" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "导航" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "导航" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "导航" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "面板" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "路径" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "形状" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "文本" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "工具" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "缩放" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "缩放" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "设置缩略图" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "快捷键" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "切换动态对齐" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "切换素材面板" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "切换调色板" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "切换焦点模式" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "切换全屏" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "显示/隐藏参考线" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "切换历史" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "切换图层面板" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "添加/移除弹性布局" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "添加/移除网格布局" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "锁定/解锁" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "锁定比例" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "显示/隐藏标尺" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "吸附到参考线" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "吸附到标尺参考线" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "切换文本调色板" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "更改主题" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "显示/隐藏" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "切换缩放样式" @@ -4546,31 +4490,31 @@ msgstr "切换缩放样式" msgid "shortcuts.underline" msgstr "切换下划线" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "撤销" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "取消组合" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "取消遮罩" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "垂直分布" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "降低缩放镜倍率" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "提高缩放镜倍率" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "缩放至所选对象" @@ -4582,7 +4526,7 @@ msgstr "查看 Enterprise 实际效果" msgid "subscription.current-plan.enterprise" msgstr "Enterprise" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "Enterprise" @@ -4594,7 +4538,7 @@ msgstr "Enterprise 试用版" msgid "subscription.current-plan.professional" msgstr "Professional" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "你的订阅" @@ -4706,218 +4650,218 @@ msgstr "只有你拥有的团队中新增的编辑者才会计入后续账单。 msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "使用 Unlimited 方案时邀请成员" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "付款尚未完成。准备好后可随时重试。" -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "无法开始结算流程,请重试。如果问题仍然存在,请联系我们:support@penpot.app。" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "订阅" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "输入激活码" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "添加付款方式以便在试用后继续" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "包含 Professional 方案的所有权益,此外还有:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "包含 Unlimited 方案的所有权益,此外还有:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(%s 名编辑者)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise(试用版)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "90 天自动保存版本和文件恢复" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "每月固定费用" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "无限存储空间" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "管理你的订阅" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "添加付款信息" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "现在添加付款信息,即可在试用期结束后顺畅续订,并继续支持我们的开源项目。目前不会向你收费。" -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "暂时跳过并开始试用" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "助力我们成长,也让你的试用更轻松" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "目前,你的各团队中共有 %s 人可以编辑。" msgstr[1] "目前,你的各团队中共有 %s 人可以编辑。" -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "请注意:切换到较低级别的方案后,存储空间会减少,文件备份和版本历史记录的保留期限也会缩短。" -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "编辑者" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(包括所有者、管理员和编辑者,不包括查看者)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "设置的编辑者人数不能少于当前人数。请在团队设置中,将实际不编辑文件的人员角色从编辑者或管理员改为查看者。" -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "试用期结束后收费,目前无需信用卡。" -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "每月 **$%s**" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "将 %s 应用于你的团队" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "提示:你可以现在增加席位数,为后续邀请预留空间。当各团队共有 25 名或更多编辑者时,每月固定收费 175 美元。" -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "加入 Penpot 的时间:%s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "更多信息" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "每名组织成员每月" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "Penpot 的其他方案" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "每个组织每月" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "Professional" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "保留 7 天的自动保存版本和文件恢复" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "所有设计和原型功能,以及社区支持。" -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "完全掌控数据和基础设施。" -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "用户、团队和文件数量不限。" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10GB 存储空间" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "团队数量不限。你拥有的团队中最多可有 8 名编辑者。" -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "推荐" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "你的订阅" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "开始免费试用" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "订阅" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "你可以随时在账号详情的“订阅”页面编辑订阅。" -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "尽情使用你的方案吧!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "感谢你选择 Penpot %s 方案!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "你已开通 %s!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "免费试用 14 天" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "Unlimited" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unlimited(试用版)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "保留 30 天的自动保存版本和文件恢复" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "每月账单上限为 175 美元" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GB 存储空间" @@ -4940,45 +4884,45 @@ msgstr "如需提高此限额,请联系团队所有者:[%s](mailto)" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "如需提高此限额,请[升级你的方案|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "已选中 1 个邀请" msgstr[1] "已选中 %s 个邀请" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "字体提供者 - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "字体 - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "项目 - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "搜索 - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "共享库 - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot - 面向团队,设计自由" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "提交反馈 - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "集成 - Penpot" @@ -4986,7 +4930,7 @@ msgstr "集成 - Penpot" msgid "title.settings.notifications" msgstr "通知 - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "设置 - Penpot" @@ -4994,27 +4938,27 @@ msgstr "设置 - Penpot" msgid "title.settings.password" msgstr "密码 - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "个人资料 - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "邀请函 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "成员 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "设置 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhook - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - 预览模式 - Penpot" @@ -5026,51 +4970,51 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "分离令牌" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "此页面上未找到画板。" -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "未找到画板。" -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "评论(%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "不显示交互" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "全屏" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "检查(%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "交互" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "交互(%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "复制链接" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "显示交互" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "点击时显示交互" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "站点地图" @@ -5102,67 +5046,67 @@ msgstr "请参阅我们的 " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "WebGL 故障排除指南" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "WebGL 渲染已禁用" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "WebGL 渲染已启用" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "WebGL 上下文已丢失" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "WebGL 上下文已恢复" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "上次交付成功。" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "水平居中对齐(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "水平等距分布(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "左对齐(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "右对齐(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "底部对齐(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "垂直居中对齐(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "垂直等距分布(%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "顶部对齐(%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "新增库" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "所有素材" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "颜色" @@ -5174,7 +5118,7 @@ msgstr "新增颜色" msgid "workspace.assets.component-group-options" msgstr "组件组选项" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "组件" @@ -5182,7 +5126,7 @@ msgstr "组件" msgid "workspace.assets.components.add-component" msgstr "新增组件" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s 变体" @@ -5190,11 +5134,11 @@ msgstr "%s 变体" msgid "workspace.assets.create-group" msgstr "创建组" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "这些项目将自动命名为“组名/项目名”" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "删除" @@ -5202,7 +5146,7 @@ msgstr "删除" msgid "workspace.assets.delete-group" msgstr "删除组" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "创建副本" @@ -5210,39 +5154,39 @@ msgstr "创建副本" msgid "workspace.assets.duplicate-main" msgstr "复制主组件" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "编辑" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "筛选" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "网格视图" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "组" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "组名" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "列表视图" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "本地库" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "管理库" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "未找到素材" @@ -5250,7 +5194,7 @@ msgstr "未找到素材" msgid "workspace.assets.open-library" msgstr "打开库文件" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "重命名" @@ -5258,11 +5202,11 @@ msgstr "重命名" msgid "workspace.assets.rename-group" msgstr "重命名组" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "搜索素材" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "共享库" @@ -5272,43 +5216,43 @@ msgid_plural "workspace.assets.sidebar.components" msgstr[0] "1 个组件" msgstr[1] "%s 个组件" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "排序" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "排版" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "新增排版" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "字号" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "字体样式" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "前往样式库文件进行编辑" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "字间距" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "行高" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "文本变换" @@ -5316,15 +5260,15 @@ msgstr "文本变换" msgid "workspace.assets.ungroup" msgstr "解组" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "正在复制图片…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "图片已复制到剪贴板" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "复制图片时出错" @@ -5332,83 +5276,83 @@ msgstr "复制图片时出错" msgid "workspace.colorpicker.color-tokens" msgstr "颜色令牌" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "组件不能嵌套在自身内部。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "部分组件实例无法切换。组件不能嵌套在自身内部。" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "新建区域" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "复制列" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "复制行" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "新建画板" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "合并单元格" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "粘贴" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "在右侧添加一列" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "在左侧添加一列" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "删除此列" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "删除此列及其内容" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "复制此列" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "在下方添加一行" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "在上方添加一行" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "删除此行" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "删除此行及其内容" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "复制此行" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "参考线颜色" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "移除参考线" @@ -5416,15 +5360,15 @@ msgstr "移除参考线" msgid "workspace.debug.title" msgstr "调试工具" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "专注模式" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "退出专注模式" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "进入专注模式" @@ -5436,243 +5380,243 @@ msgstr "线性渐变" msgid "workspace.gradients.radial" msgstr "径向渐变" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "禁用动态对齐" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "禁用比例缩放" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "禁用与参考线对齐" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "禁用像素对齐" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "禁用“吸附到标尺参考线”" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "禁用 WebGL 渲染(测试版)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "启用动态对齐" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "启用比例缩放" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "与参考线对齐" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "启用像素对齐" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "启用“吸附到标尺参考线”" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "启用 WebGL 渲染(测试版)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "查找" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "查找和替换" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "隐藏画板名称" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "隐藏评论" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "隐藏参考线" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "隐藏调色板" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "隐藏像素网格" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "隐藏标尺" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "隐藏字体调色板" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "锁定参考线" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "连接" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "断开连接" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "管理(状态:已禁用)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "管理(状态:已启用)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "编辑" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "文件" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "帮助与信息" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "MCP 服务器" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "偏好设置" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "视图" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "重做" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "全选" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "显示画板名称" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "显示评论" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "显示参考线" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "显示调色板" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "显示像素网格" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "显示标尺" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "显示字体调色板" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "切换到深色主题" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "切换到浅色主题" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "切换至系统主题" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "撤销" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "解锁参考线" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "重置" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "保存时发生错误" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "已保存" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "正在保存" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "分享" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "预览模式(%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "缩放" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "填充 - 缩放以填满" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "适合 - 缩小以适应" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "缩放以显示全部" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "全屏" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "缩放至所选对象" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "编辑网格" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "退出" @@ -5680,7 +5624,7 @@ msgstr "退出" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "下内边距" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "显示四边内边距选项" @@ -5716,37 +5660,37 @@ msgstr "完成" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "定位" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "定位网格布局" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "1 种颜色" msgstr[1] "%s 种颜色" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "将颜色添加到库" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "你的库中还没有颜色样式" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "还没有最近使用的颜色" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "你的库中还没有排版样式" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "文件库" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "最近使用的颜色" @@ -5758,75 +5702,75 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "保存颜色样式" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "显示或隐藏调色板" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "1 个组件" msgstr[1] "%s 个组件" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "已连接到" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "也可以添加以下内容试用:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "你的团队中没有共享库,你可以查找" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "这里的一些模板" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "文件库" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "1 个图形" msgstr[1] "%s 个图形" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "本文档中的库" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "库" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "共享库更新" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "加载中…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "没有需要更新的共享库" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "未找到与“%s”匹配的结果" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "搜索共享库" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "共享库" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "连接库" @@ -5850,25 +5794,25 @@ msgstr "取消链接所有素材" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "分离所有令牌" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "1 个排版素材" msgstr[1] "%s 个排版素材" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "断开库连接" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "更新" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "查看所有更改" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "更新" @@ -5932,107 +5876,107 @@ msgstr "显示或隐藏更多选项" msgid "workspace.options.canvas-background" msgstr "画布背景" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "裁剪内容" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "组件" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "注释" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "复制" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "创建注释" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "编辑注释" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "主组件" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "替换组件" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "你的库中还没有素材" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "未链接" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "定位冲突的变体" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "此组件包含冲突的变体。请确保每个变体都有一组独一无二的属性值。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "定位重复的变体" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "部分变体的属性和值完全相同" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "这些变体的属性和值完全相同。请调整属性值,以便正确检索这些变体。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "此变体与另一个变体的属性和值完全相同。请调整属性值,以便正确检索此变体。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "其中部分变体的属性和值完全相同。请调整属性值,以便正确检索这些变体。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "此组件包含名称无效的变体。请确保每个变体的名称都遵循正确的结构。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "定位无效变体" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "部分变体的名称无效" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "定位无效变体" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "这些变体的名称无效。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "此变体的名称无效。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "其中部分变体的名称无效。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[property]=[value], [property]=[value]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "尝试使用下面的结构:" @@ -6064,7 +6008,7 @@ msgstr "位于同一层级。" msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "组、画板和布局视为等效。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "变体如何保持连接" @@ -6112,11 +6056,11 @@ msgstr "上下固定" msgid "workspace.options.convert-to-typography" msgstr "创建排版样式" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "设计" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "导出" @@ -6124,37 +6068,37 @@ msgstr "导出" msgid "workspace.options.export-multiple" msgstr "导出所选对象" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "导出 1 个元素" msgstr[1] "导出 %s 个元素" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "新增导出" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "移除导出" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "后缀" -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "导出完成" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "正在导出…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "导出失败" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "导出速度意外缓慢" @@ -6174,7 +6118,7 @@ msgstr "移除填充" msgid "workspace.options.fill.section" msgstr "填充区域" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "调整画板适应内容大小" @@ -6198,67 +6142,67 @@ msgstr "所有流程起点" msgid "workspace.options.grid.auto" msgstr "自动" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "列" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "颜色" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "间距" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "高" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "外边距" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "设为默认" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "底" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "居中" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "左" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "右" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "拉伸" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "顶" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "使用默认" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "宽" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "行" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "正方形" @@ -6270,7 +6214,7 @@ msgstr "编组填充" msgid "workspace.options.group-stroke" msgstr "组描边" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "添加参考线" @@ -6278,19 +6222,19 @@ msgstr "添加参考线" msgid "workspace.options.guides.remove-guide" msgstr "移除参考线" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "参考线" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "切换参考线" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "高度" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "检查" @@ -6506,115 +6450,115 @@ msgstr "交互" msgid "workspace.options.interactions.add-interaction" msgstr "新增交互" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "颜色" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "颜色加深" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "颜色减淡" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "变暗" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "差值" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "排除" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "强光" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "色相" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "变亮" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "亮度" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "正片叠底" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "正常" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "叠加" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "饱和度" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "滤色" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "柔光" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "图层菜单区域" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "显示/隐藏图层" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "最大高度" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "最大宽度" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "最小高度" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "最小宽度" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "更多颜色" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "更多库颜色" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "更多颜色令牌" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "没有匹配的尺寸预设" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "不透明度" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "原型" @@ -6650,23 +6594,23 @@ msgstr "圆角半径区域" msgid "workspace.options.radius.show-single-corners" msgstr "显示独立的圆角" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "最近的" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "重试" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "旋转" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "搜索字体" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "搜索尺寸预设" @@ -6674,11 +6618,11 @@ msgstr "搜索尺寸预设" msgid "workspace.options.select-a-shape" msgstr "选择一个形状、画板或编组,拖至另一个画板,以创建关联。" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "已选颜色" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "颜色选择区域" @@ -6746,55 +6690,55 @@ msgstr "切换阴影" msgid "workspace.options.show-fill-on-export" msgstr "在导出中显示" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "在预览模式显示" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "尺寸" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "尺寸预设" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "锁定比例" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "解锁比例" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "圆形" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "菱形" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "箭头" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "无" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "圆头" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "方头" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "矩形" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "三角形" @@ -6802,15 +6746,15 @@ msgstr "三角形" msgid "workspace.options.stroke-color" msgstr "描边颜色" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "虚线长度" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "间隙" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "描边宽度" @@ -6818,39 +6762,39 @@ msgstr "描边宽度" msgid "workspace.options.stroke.add-stroke" msgstr "新增描边颜色" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "居中" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "虚线" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "点线" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "内侧" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "混合" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "外侧" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "移除描边" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "实线" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "启用或禁用描边" @@ -6886,11 +6830,11 @@ msgstr "自动宽度" msgid "workspace.options.text-options.grow-fixed" msgstr "固定" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "字距" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "行高" @@ -6938,200 +6882,200 @@ msgstr "下划线(%s)" msgid "workspace.options.use-play-button" msgstr "点击页面顶端的播放按钮预览原型。" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "宽度" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X 轴" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y 轴" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "绘制节点(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "连接节点(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "转换为角点(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "转换为曲线(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "合并节点(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "移动节点(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "拆分节点(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "吸附节点(%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "打开" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "发现[更多插件](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "未安装插件" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "插件清单不正确。" -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "你需要拥有编辑权限才能使用此插件" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "插件不存在或 URL 不正确。" -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "安装" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "已安装的插件" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "插件管理器" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "插件" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "更新此插件" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "自你上次打开以来,此插件已被修改。它现在还希望访问:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "开始文件下载。" -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "保存数据至浏览器。" -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "读取剪贴板中的内容。" -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "读取和写入剪贴板内容。" -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "读取你的评论和回复。" -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "读取和修改你的评论,并以你的名义回复。" -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "读取用户有权访问的文件内容。" -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "读取和修改用户有权访问的文件内容。" -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "请注意,此插件由第三方创建,因此请在授予访问权限前确认你信任它。你的数据隐私和安全对我们至关重要。如有任何疑虑,请联系支持团队。" -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "读取你的库和素材。" -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "读取和修改你的库和素材。" -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "“%s”插件需要访问:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "读取当前用户的个人资料信息。" -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "插件列表" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "移除插件" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "输入插件 URL" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "插件" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "暂不" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "想看看吗?它将在当前团队的新草稿中打开。(如果不想,你之后也可以在任意文件的已安装插件中找到它。)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "“%s”插件已为你的账号安装!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "尝试插件" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "添加 Flex 布局" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "添加网格布局" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "新增布局" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "创建变体" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "添加新属性" @@ -7143,11 +7087,11 @@ msgstr "移至底层" msgid "workspace.shape.menu.backward" msgstr "向下移动一层" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "清除画板参考线" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "合并为变体" @@ -7155,7 +7099,7 @@ msgstr "合并为变体" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "组件需要在同一个页面中" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "复制" @@ -7195,27 +7139,27 @@ msgstr "复制为文本" msgid "workspace.shape.menu.create-annotation" msgstr "创建注释" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "转为画板" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "创建组件" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "创建多个组件" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "剪切" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "删除" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "删除流程起点" @@ -7227,7 +7171,7 @@ msgstr "解绑实例" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "解绑实例" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "差集" @@ -7235,15 +7179,15 @@ msgstr "差集" msgid "workspace.shape.menu.duplicate" msgstr "创建副本" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "编辑" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "排除重叠" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "展平" @@ -7255,7 +7199,7 @@ msgstr "水平翻转" msgid "workspace.shape.menu.flip-vertical" msgstr "垂直翻转" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "流程起点" @@ -7267,31 +7211,31 @@ msgstr "向上移动一层" msgid "workspace.shape.menu.front" msgstr "移至顶层" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "编组" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "隐藏" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "显示/隐藏界面" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "交集" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "锁定" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "蒙版" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "粘贴" @@ -7299,27 +7243,27 @@ msgstr "粘贴" msgid "workspace.shape.menu.paste-props" msgstr "粘贴属性" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "路径" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "移除 Flex 布局" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "移除网格布局" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "移除布局" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "移除属性" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "变体至少需要有一个属性" @@ -7343,11 +7287,11 @@ msgstr "恢复变体" msgid "workspace.shape.menu.select-layer" msgstr "选择图层" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "显示" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "在素材面板中显示" @@ -7355,7 +7299,7 @@ msgstr "在素材面板中显示" msgid "workspace.shape.menu.show-main" msgstr "显示主组件" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "将描边转换为路径" @@ -7367,19 +7311,19 @@ msgstr "移除缩略图" msgid "workspace.shape.menu.thumbnail-set" msgstr "设为缩略图" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "取消编组" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "并集" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "取消锁定" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "取消蒙版" @@ -7395,79 +7339,79 @@ msgstr "收起侧边栏" msgid "workspace.sidebar.expand" msgstr "展开侧边栏" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "历史" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "图层" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "组件" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "筛选" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "画板" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "编组" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "图片" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "蒙版" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "无匹配结果" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "替换" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "全部替换" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "替换为…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "搜索…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "查找和替换" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "文本内容" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "图层名称" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "形状" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "文本" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "已导入的 SVG 属性" @@ -7479,7 +7423,7 @@ msgstr "页面" msgid "workspace.sidebar.sitemap.add-page" msgstr "添加页面" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "站点地图" @@ -7507,7 +7451,7 @@ msgstr "向此组添加令牌集" msgid "workspace.tokens.add-token" msgstr "添加令牌:%s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "应用于" @@ -7527,7 +7471,7 @@ msgstr "基准字号" msgid "workspace.tokens.base-font-size.error" msgstr "基准字号必须是像素值或无单位数值。" -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "颜色" @@ -7539,7 +7483,7 @@ msgstr "复制令牌路径" msgid "workspace.tokens.create-new-theme" msgstr "立即创建你的第一个令牌主题。" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "创建一个。" @@ -7563,7 +7507,7 @@ msgstr "复制令牌" msgid "workspace.tokens.duplicate-group" msgstr "复制令牌组" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "复制" @@ -7587,7 +7531,7 @@ msgstr "编辑 %s 令牌" msgid "workspace.tokens.enter-token-name" msgstr "输入 %s 令牌名称" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "编辑文本时无法应用令牌。请改为选择文本图层。" @@ -7615,7 +7559,7 @@ msgstr "预览:" msgid "workspace.tokens.export.single-file" msgstr "单文件" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "字号或 {alias}" @@ -7623,7 +7567,7 @@ msgstr "字号或 {alias}" msgid "workspace.tokens.font-variant-not-found" msgstr "设置字重/样式时出错。当前字体中不存在此字体样式" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "字重(300、Bold Italic...)或 {alias}" @@ -7659,7 +7603,7 @@ msgstr "导入多个文件时,文件名/路径将作为令牌集名称。" msgid "workspace.tokens.import-single-file" msgstr "导入单个 JSON 文件时,一级键应为令牌集名称。" -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "导入令牌" @@ -7667,15 +7611,15 @@ msgstr "导入令牌" msgid "workspace.tokens.import-warning" msgstr "导入令牌将覆盖你当前的所有令牌、令牌集和令牌主题。" -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "未启用" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "此令牌集未启用。请更改令牌主题或启用此令牌集,以在视口中查看更改" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "使用单独的令牌" @@ -7695,11 +7639,11 @@ msgstr "令牌主题" msgid "workspace.tokens.label.theme-placeholder" msgstr "添加令牌主题(例如浅色)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "字间距或 {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "行高(倍数、px、%)或 {alias}" @@ -7715,11 +7659,11 @@ msgstr "最大尺寸" msgid "workspace.tokens.min-size" msgstr "最小尺寸" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "缺少引用" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "右键单击查看选项" @@ -7735,7 +7679,7 @@ msgstr "没有已启用的令牌主题" msgid "workspace.tokens.no-permisions-set" msgstr "你需要拥有编辑权限才能启用或停用令牌集" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "你需要拥有编辑权限才能使用令牌主题" @@ -7747,7 +7691,7 @@ msgstr "尚未定义任何令牌集。请先创建一个。" msgid "workspace.tokens.no-sets-yet" msgstr "尚无令牌集。" -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "尚无令牌主题。" @@ -7763,7 +7707,7 @@ msgstr "不重新映射" msgid "workspace.tokens.num-active-sets" msgstr "%s 个已启用的令牌集" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "原始值:%s" @@ -7775,15 +7719,15 @@ msgstr "内边距" msgid "workspace.tokens.radius" msgstr "圆角半径" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "{%s} 中的引用无效,或不属于任何活动令牌集" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "输入排版令牌别名" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "输入阴影令牌别名" @@ -7819,7 +7763,7 @@ msgstr "重命名令牌组" msgid "workspace.tokens.rename-group-name-hint" msgstr "你的令牌将自动重命名为 %s.(suffix).(tokenName)" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "解析值:%s" @@ -7855,27 +7799,27 @@ msgstr "你可以在此配置基准字号,它定义了 1rem 的值:" msgid "workspace.tokens.settings" msgstr "令牌设置" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "添加阴影" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "模糊" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "内阴影" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "移除阴影" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "扩散" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "模糊值不能为负数" @@ -7883,11 +7827,11 @@ msgstr "模糊值不能为负数" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "扩散值不能为负数" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -7895,11 +7839,11 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "大小" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize 或 {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through 或 {alias}" @@ -7915,31 +7859,31 @@ msgstr "令牌主题列表" msgid "workspace.tokens.token-description" msgstr "描述" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "选择字体家族" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "字体家族" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "字体家族,或以英文逗号(,)分隔的字体列表" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "名称" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "路径 %s 或其前缀处已存在令牌。" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "名称至少应包含 1 个字符" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " 不是有效的令牌名称。\n" @@ -7949,11 +7893,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "值" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "输入值或使用 {alias} 输入别名" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "令牌 - %s" @@ -7969,11 +7913,11 @@ msgstr "导入成功,但部分令牌因使用不受支持的 $type 值而被 msgid "workspace.tokens.unknown-token-type-section" msgstr "不支持类型“%s”(%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "使用引用" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "值无效" @@ -7981,71 +7925,67 @@ msgstr "值无效" msgid "workspace.toolbar.assets" msgstr "素材" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "调色盘(%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "评论(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "曲线(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "椭圆(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "画板(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "图片(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "在此连接" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP 已连接" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "移动(%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "调色板栏" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "路径(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "插件(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "矩形(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "文本(%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "排版(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "切换工具栏" @@ -8066,11 +8006,11 @@ msgstr "无法渲染。刷新页面以恢复编辑。" msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "刷新" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "目前没有历史修改" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "由 %s 操作" @@ -8210,19 +8150,19 @@ msgstr "排版素材" msgid "workspace.undo.entry.unknown" msgstr "对 %s 执行操作" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "忽略" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "更多信息" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "共享库有更新" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "更新" diff --git a/frontend/translations/zh_Hant.po b/frontend/translations/zh_Hant.po index 56f41a99d3..2127902f2d 100644 --- a/frontend/translations/zh_Hant.po +++ b/frontend/translations/zh_Hant.po @@ -1,7 +1,7 @@ msgid "" msgstr "" -"PO-Revision-Date: 2026-09-28 08:13+0000\n" -"Last-Translator: Andy Li \n" +"PO-Revision-Date: 2026-11-05 22:19+0000\n" +"Last-Translator: Andrey Antukh \n" "Language-Team: Chinese (Traditional Han script) " "\n" "Language: zh_Hant\n" @@ -9,9 +9,9 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 2026.10.dev0\n" +"X-Generator: Weblate 2026.11.2.dev0\n" -#: src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:191, src/app/main/ui/viewer/login.cljs:119 +#: src/app/main/ui/auth/login_modal.cljs:125, src/app/main/ui/auth/register.cljs:227, src/app/main/ui/static.cljs:193 msgid "auth.already-have-account" msgstr "已經有帳號了嗎?" @@ -31,7 +31,7 @@ msgstr "建立試用帳號" msgid "auth.demo-warning" msgstr "此服務僅供展示,請勿用於實際工作,專案內容將定期清除。" -#: src/app/main/ui/auth/login.cljs:203, src/app/main/ui/viewer/login.cljs:105 +#: src/app/main/ui/auth/login.cljs:204, src/app/main/ui/auth/login_modal.cljs:109 msgid "auth.forgot-password" msgstr "忘記密碼?" @@ -39,35 +39,35 @@ msgstr "忘記密碼?" msgid "auth.fullname" msgstr "全名" -#: src/app/main/ui/auth/login.cljs:294 +#: src/app/main/ui/auth/login.cljs:295 msgid "auth.login-account-title" msgstr "登入我的帳號" -#: src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:194, src/app/main/ui/viewer/login.cljs:122 +#: src/app/main/ui/auth/login_modal.cljs:130, src/app/main/ui/auth/register.cljs:231, src/app/main/ui/static.cljs:199 msgid "auth.login-here" msgstr "在此登入。" -#: src/app/main/ui/auth/login.cljs:297 +#: src/app/main/ui/auth/login.cljs:298 msgid "auth.login-tagline" msgstr "Penpot 是用於設計與開發協作,免費且開源的設計工具" -#: src/app/main/ui/auth/login.cljs:254 +#: src/app/main/ui/auth/login.cljs:255 msgid "auth.login-with-github-submit" msgstr "GitHub" -#: src/app/main/ui/auth/login.cljs:260 +#: src/app/main/ui/auth/login.cljs:261 msgid "auth.login-with-gitlab-submit" msgstr "GitLab" -#: src/app/main/ui/auth/login.cljs:248 +#: src/app/main/ui/auth/login.cljs:249 msgid "auth.login-with-google-submit" msgstr "Google" -#: src/app/main/ui/auth/login.cljs:213 +#: src/app/main/ui/auth/login.cljs:214 msgid "auth.login-with-ldap-submit" msgstr "LDAP" -#: src/app/main/ui/auth/login.cljs:266 +#: src/app/main/ui/auth/login.cljs:267 msgid "auth.login-with-oidc-submit" msgstr "OpenID" @@ -75,7 +75,7 @@ msgstr "OpenID" msgid "auth.new-password" msgstr "輸入新密碼" -#: src/app/main/ui/auth/verify_token.cljs:81 +#: src/app/main/ui/auth/verify_token.cljs:54 msgid "auth.notifications.organization-invitation-accepted" msgstr "您已成為 %s 的一員" @@ -91,7 +91,7 @@ msgstr "個人檔案尚未驗證,請先完成驗證再繼續。" msgid "auth.notifications.recovery-token-sent" msgstr "密碼重設連結已寄至您的信箱。" -#: src/app/main/ui/auth/verify_token.cljs:85 +#: src/app/main/ui/auth/verify_token.cljs:58 msgid "auth.notifications.team-invitation-accepted" msgstr "已成功加入團隊" @@ -123,7 +123,7 @@ msgstr "忘記密碼?" msgid "auth.recovery-submit" msgstr "變更密碼" -#: src/app/main/ui/auth/login.cljs:310, src/app/main/ui/static.cljs:177, src/app/main/ui/viewer/login.cljs:109 +#: src/app/main/ui/auth/login.cljs:311, src/app/main/ui/auth/login_modal.cljs:113, src/app/main/ui/static.cljs:177 msgid "auth.register" msgstr "還沒有帳號嗎?" @@ -135,7 +135,7 @@ msgstr "請告訴我們該如何稱呼您,這個名稱會顯示在控制台與 msgid "auth.register-account-title" msgstr "您的名稱" -#: src/app/main/ui/auth/login.cljs:314, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:181, src/app/main/ui/viewer/login.cljs:112 +#: src/app/main/ui/auth/login.cljs:315, src/app/main/ui/auth/login_modal.cljs:118, src/app/main/ui/auth/register.cljs:199, src/app/main/ui/auth/register.cljs:345, src/app/main/ui/static.cljs:183 msgid "auth.register-submit" msgstr "建立帳號" @@ -148,7 +148,7 @@ msgstr "建立帳號" msgid "auth.terms-and-privacy-agreement" msgstr "我同意[服務條款](%s)與[隱私權政策](%s)。" -#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:542, src/app/main/ui/workspace/main_menu.cljs:210 +#: src/app/main/ui/auth/register.cljs:264, src/app/main/ui/dashboard/sidebar.cljs:535, src/app/main/ui/workspace/main_menu.cljs:211 msgid "auth.terms-of-service" msgstr "服務條款" @@ -168,88 +168,88 @@ msgstr "沒有可用的色彩 tokens。請檢查已啟用的集合或主題, msgid "comments.mentions.not-found" msgstr "找不到符合 @%s 的使用者" -#: src/app/main/ui/workspace/libraries.cljs:437, src/app/main/ui/workspace/libraries.cljs:838 +#: src/app/main/ui/workspace/libraries.cljs:489, src/app/main/ui/workspace/libraries.cljs:860 msgid "common.publish" msgstr "發佈" -#: src/app/main/ui/viewer/share_link.cljs:304, src/app/main/ui/viewer/share_link.cljs:314 +#: src/app/main/ui/viewer/share_link.cljs:309, src/app/main/ui/viewer/share_link.cljs:319 msgid "common.share-link.all-users" msgstr "所有 Penpot 使用者" -#: src/app/main/ui/viewer/share_link.cljs:198 +#: src/app/main/ui/viewer/share_link.cljs:202 msgid "common.share-link.confirm-deletion-link-description" msgstr "確定要移除此連結嗎?移除後,任何人都無法再使用此連結" -#: src/app/main/ui/viewer/share_link.cljs:259, src/app/main/ui/viewer/share_link.cljs:289 +#: src/app/main/ui/viewer/share_link.cljs:264, src/app/main/ui/viewer/share_link.cljs:294 msgid "common.share-link.current-tag" msgstr "(目前)" -#: src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/viewer/share_link.cljs:214 +#: src/app/main/ui/viewer/share_link.cljs:211, src/app/main/ui/viewer/share_link.cljs:217 msgid "common.share-link.destroy-link" msgstr "刪除連結" -#: src/app/main/ui/viewer/share_link.cljs:221 +#: src/app/main/ui/viewer/share_link.cljs:223 msgid "common.share-link.get-link" msgstr "取得連結" -#: src/app/main/ui/viewer/share_link.cljs:137 +#: src/app/main/ui/viewer/share_link.cljs:140 msgid "common.share-link.link-copied-success" msgstr "已複製連結" -#: src/app/main/ui/viewer/share_link.cljs:231 +#: src/app/main/ui/viewer/share_link.cljs:236 msgid "common.share-link.manage-ops" msgstr "管理權限" -#: src/app/main/ui/viewer/share_link.cljs:277 +#: src/app/main/ui/viewer/share_link.cljs:282 msgid "common.share-link.page-shared" msgid_plural "common.share-link.page-shared" msgstr[0] "已分享 %s 個頁面" -#: src/app/main/ui/viewer/share_link.cljs:298 +#: src/app/main/ui/viewer/share_link.cljs:303 msgid "common.share-link.permissions-can-comment" msgstr "可留言" -#: src/app/main/ui/viewer/share_link.cljs:308 +#: src/app/main/ui/viewer/share_link.cljs:313 msgid "common.share-link.permissions-can-inspect" msgstr "可檢視程式碼" -#: src/app/main/ui/viewer/share_link.cljs:193 +#: src/app/main/ui/viewer/share_link.cljs:197 msgid "common.share-link.permissions-hint" msgstr "任何持有連結的人都能存取" -#: src/app/main/ui/viewer/share_link.cljs:241 +#: src/app/main/ui/viewer/share_link.cljs:246 msgid "common.share-link.permissions-pages" msgstr "已分享頁面" -#: src/app/main/ui/viewer/share_link.cljs:183 +#: src/app/main/ui/viewer/share_link.cljs:187 msgid "common.share-link.placeholder" msgstr "可分享的連結會顯示在這裡" -#: src/app/main/ui/viewer/share_link.cljs:303, src/app/main/ui/viewer/share_link.cljs:313 +#: src/app/main/ui/viewer/share_link.cljs:308, src/app/main/ui/viewer/share_link.cljs:318 msgid "common.share-link.team-members" msgstr "僅限團隊成員" -#: src/app/main/ui/viewer/share_link.cljs:171 +#: src/app/main/ui/viewer/share_link.cljs:174 msgid "common.share-link.title" msgstr "分享原型" -#: src/app/main/ui/viewer/share_link.cljs:269 +#: src/app/main/ui/viewer/share_link.cljs:274 msgid "common.share-link.view-all" msgstr "全選" -#: src/app/main/ui/workspace/libraries.cljs:432, src/app/main/ui/workspace/libraries.cljs:833 +#: src/app/main/ui/workspace/libraries.cljs:484, src/app/main/ui/workspace/libraries.cljs:855 msgid "common.unpublish" msgstr "取消發布" -#: src/app/main/ui/dashboard/projects.cljs:90 +#: src/app/main/ui/dashboard/projects.cljs:98 msgid "dasboard.team-hero.management" msgstr "團隊管理" -#: src/app/main/ui/dashboard/projects.cljs:89 +#: src/app/main/ui/dashboard/projects.cljs:97 msgid "dasboard.team-hero.text" msgstr "Penpot 專為團隊打造。邀請成員一起協作處理專案與檔案" -#: src/app/main/ui/dashboard/projects.cljs:87 +#: src/app/main/ui/dashboard/projects.cljs:95 msgid "dasboard.team-hero.title" msgstr "組隊!" @@ -265,31 +265,31 @@ msgstr "還原檔案" msgid "dashboard.add-file" msgstr "新增檔案" -#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:700 +#: src/app/main/ui/dashboard/file_menu.cljs:324, src/app/main/ui/workspace/main_menu.cljs:701 msgid "dashboard.add-shared" msgstr "新增為共享資料庫" -#: src/app/main/ui/settings/profile.cljs:75 +#: src/app/main/ui/settings/profile.cljs:79 msgid "dashboard.change-email" msgstr "變更電子郵件地址" -#: src/app/main/data/nitrate.cljs:602 +#: src/app/main/data/nitrate.cljs:614 msgid "dashboard.change-organization-modal.accept" msgstr "移動團隊" -#: src/app/main/data/nitrate.cljs:600 +#: src/app/main/data/nitrate.cljs:612 msgid "dashboard.change-organization-modal.choose" msgstr "移動至:" -#: src/app/main/data/nitrate.cljs:599 +#: src/app/main/data/nitrate.cljs:611 msgid "dashboard.change-organization-modal.description" msgstr "團隊成員仍可存取原有的專案與檔案。團隊將套用新組織的設定。" -#: src/app/main/data/nitrate.cljs:601 +#: src/app/main/data/nitrate.cljs:613 msgid "dashboard.change-organization-modal.select" msgstr "選擇組織" -#: src/app/main/data/nitrate.cljs:591, src/app/main/data/nitrate.cljs:598, src/app/main/ui/dashboard/team_form.cljs:159 +#: src/app/main/data/nitrate.cljs:603, src/app/main/data/nitrate.cljs:610, src/app/main/ui/dashboard/team_form.cljs:163 msgid "dashboard.change-organization-modal.title" msgstr "變更團隊所屬組織" @@ -345,23 +345,23 @@ msgstr "檢視完整變更紀錄" msgid "dashboard.check-updates.view-release-notes" msgstr "檢視版本說明" -#: src/app/main/ui/dashboard/deleted.cljs:322 +#: src/app/main/ui/dashboard/deleted.cljs:328 msgid "dashboard.clear-trash-button" msgstr "清空垃圾桶" -#: src/app/main/data/dashboard.cljs:300, src/app/main/data/dashboard.cljs:535, src/app/main/data/workspace/pages.cljs:201 +#: src/app/main/data/dashboard.cljs:317, src/app/main/data/dashboard.cljs:553, src/app/main/data/workspace/pages.cljs:206 msgid "dashboard.copy-suffix" msgstr "(複製)" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:223, src/app/main/ui/dashboard/organization_team_switch.cljs:304 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:311, src/app/main/ui/dashboard/organization_team_switch.cljs:402 msgid "dashboard.create-new-organization" msgstr "建立組織" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:295 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:393 msgid "dashboard.create-new-team" msgstr "建立新團隊" -#: src/app/main/ui/workspace/main_menu.cljs:711 +#: src/app/main/ui/workspace/main_menu.cljs:712 msgid "dashboard.create-version-menu" msgstr "固定此版本" @@ -369,7 +369,7 @@ msgstr "固定此版本" msgid "dashboard.default-team-name" msgstr "個人專案" -#: src/app/main/ui/dashboard/deleted.cljs:268 +#: src/app/main/ui/dashboard/deleted.cljs:274 msgid "dashboard.delete-all-forever-confirmation.description" msgstr "確定要永久刪除所有已刪除的專案與檔案嗎?此操作無法復原。" @@ -377,39 +377,39 @@ msgstr "確定要永久刪除所有已刪除的專案與檔案嗎?此操作無 msgid "dashboard.delete-file-forever-confirmation.description" msgstr "確定要永久刪除 %s 嗎?此操作無法復原。" -#: src/app/main/data/dashboard.cljs:792 +#: src/app/main/data/dashboard.cljs:810 msgid "dashboard.delete-files-success-notification" msgstr "%s 個檔案已成功刪除。" -#: src/app/main/ui/dashboard/deleted.cljs:54, src/app/main/ui/dashboard/deleted.cljs:56, src/app/main/ui/dashboard/deleted.cljs:267, src/app/main/ui/dashboard/deleted.cljs:269, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 +#: src/app/main/ui/dashboard/deleted.cljs:55, src/app/main/ui/dashboard/deleted.cljs:57, src/app/main/ui/dashboard/deleted.cljs:273, src/app/main/ui/dashboard/deleted.cljs:275, src/app/main/ui/dashboard/file_menu.cljs:246, src/app/main/ui/dashboard/file_menu.cljs:248 msgid "dashboard.delete-forever-confirmation.title" msgstr "永久刪除" -#: src/app/main/ui/dashboard/deleted.cljs:90 +#: src/app/main/ui/dashboard/deleted.cljs:95 msgid "dashboard.delete-project-button" msgstr "刪除專案" -#: src/app/main/ui/dashboard/deleted.cljs:55 +#: src/app/main/ui/dashboard/deleted.cljs:56 msgid "dashboard.delete-project-forever-confirmation.description" msgstr "確定要永久刪除 %s 專案嗎?此專案及其中的所有檔案都將永久刪除,這個動作無法復原。" -#: src/app/main/data/dashboard.cljs:791, src/app/main/data/dashboard.cljs:825 +#: src/app/main/data/dashboard.cljs:809, src/app/main/data/dashboard.cljs:843 msgid "dashboard.delete-success-notification" msgstr "%s 已成功刪除。" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:153 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:227, src/app/main/ui/dashboard/team_form.cljs:157 msgid "dashboard.delete-team" msgstr "刪除團隊" -#: src/app/main/ui/dashboard/deleted.cljs:337 +#: src/app/main/ui/dashboard/deleted.cljs:343 msgid "dashboard.deleted.empty-state-description" msgstr "垃圾桶目前是空的。已刪除的檔案與專案會顯示在這裡。" -#: src/app/main/ui/dashboard/grid.cljs:248 +#: src/app/main/ui/dashboard/grid.cljs:249 msgid "dashboard.deleted.will-be-deleted-at" msgstr "將於 %s 刪除" -#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:737 +#: src/app/main/ui/dashboard/file_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:738 msgid "dashboard.download-binary-file" msgstr "下載 Penpot 檔案(.penpot)" @@ -421,19 +421,19 @@ msgstr "建立複本" msgid "dashboard.duplicate-multi" msgstr "建立 %s 個檔案的複本" -#: src/app/main/ui/dashboard/files.cljs:233, src/app/main/ui/dashboard/projects.cljs:309 +#: src/app/main/ui/dashboard/files.cljs:243, src/app/main/ui/dashboard/projects.cljs:318 msgid "dashboard.empty-placeholder-drafts-subtitle" msgstr "專案成員建立草稿後,就會顯示在這裡。" -#: src/app/main/ui/dashboard/files.cljs:228, src/app/main/ui/dashboard/projects.cljs:304 +#: src/app/main/ui/dashboard/files.cljs:238, src/app/main/ui/dashboard/projects.cljs:313 msgid "dashboard.empty-placeholder-drafts-title" msgstr "尚無草稿。" -#: src/app/main/ui/dashboard/deleted.cljs:180, src/app/main/ui/dashboard/files.cljs:234, src/app/main/ui/dashboard/projects.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:186, src/app/main/ui/dashboard/files.cljs:244, src/app/main/ui/dashboard/projects.cljs:319 msgid "dashboard.empty-placeholder-files-subtitle" msgstr "專案成員建立檔案後,就會顯示在這裡。" -#: src/app/main/ui/dashboard/deleted.cljs:177, src/app/main/ui/dashboard/files.cljs:229, src/app/main/ui/dashboard/projects.cljs:305 +#: src/app/main/ui/dashboard/deleted.cljs:183, src/app/main/ui/dashboard/files.cljs:239, src/app/main/ui/dashboard/projects.cljs:314 msgid "dashboard.empty-placeholder-files-title" msgstr "尚無檔案。" @@ -480,23 +480,23 @@ msgstr "匯入 .penpot 檔案" msgid "dashboard.empty-project.start" msgstr "開始打造精彩作品" -#: src/app/main/data/dashboard.cljs:795 +#: src/app/main/data/dashboard.cljs:813 msgid "dashboard.errors.error-on-delete-files" msgstr "刪除檔案時發生錯誤。" -#: src/app/main/data/dashboard.cljs:828 +#: src/app/main/data/dashboard.cljs:846 msgid "dashboard.errors.error-on-delete-project" msgstr "刪除專案 %s 時發生錯誤。" -#: src/app/main/data/dashboard.cljs:923, src/app/main/ui/dashboard/file_menu.cljs:227 +#: src/app/main/data/dashboard.cljs:941, src/app/main/ui/dashboard/file_menu.cljs:227 msgid "dashboard.errors.error-on-restore-file" msgstr "還原檔案 %s 時發生錯誤。" -#: src/app/main/data/dashboard.cljs:924 +#: src/app/main/data/dashboard.cljs:942 msgid "dashboard.errors.error-on-restore-files" msgstr "還原檔案時發生錯誤。" -#: src/app/main/data/dashboard.cljs:956 +#: src/app/main/data/dashboard.cljs:974 msgid "dashboard.errors.error-on-restoring-project" msgstr "還原專案 %s 及其檔案時發生錯誤。" @@ -504,35 +504,35 @@ msgstr "還原專案 %s 及其檔案時發生錯誤。" msgid "dashboard.export-binary-multi" msgstr "下載 %s 個 Penpot 檔案(.penpot)" -#: src/app/main/ui/workspace/main_menu.cljs:745 +#: src/app/main/ui/workspace/main_menu.cljs:746 msgid "dashboard.export-frames" msgstr "將畫板匯出為 PDF" -#: src/app/main/ui/exports/assets.cljs:202 +#: src/app/main/ui/exports/assets.cljs:194 msgid "dashboard.export-frames.title" msgstr "匯出為 PDF" -#: src/app/main/ui/exports/assets.cljs:108 +#: src/app/main/ui/exports/assets.cljs:103 msgid "dashboard.export-multiple.selected" msgstr "'已選取 %s / %s 個元素'" -#: src/app/main/ui/workspace/main_menu.cljs:728 +#: src/app/main/ui/workspace/main_menu.cljs:729 msgid "dashboard.export-shapes" msgstr "匯出" -#: src/app/main/ui/exports/assets.cljs:179 +#: src/app/main/ui/exports/assets.cljs:171 msgid "dashboard.export-shapes.how-to" msgstr "你可以在設計屬性(右側欄底部)中,為元素新增匯出設定。" -#: src/app/main/ui/exports/assets.cljs:183 +#: src/app/main/ui/exports/assets.cljs:175 msgid "dashboard.export-shapes.how-to-link" msgstr "瞭解如何在 Penpot 中設定匯出。" -#: src/app/main/ui/exports/assets.cljs:178 +#: src/app/main/ui/exports/assets.cljs:170 msgid "dashboard.export-shapes.no-elements" msgstr "沒有任何元素設有匯出設定。" -#: src/app/main/ui/exports/assets.cljs:189 +#: src/app/main/ui/exports/assets.cljs:181 msgid "dashboard.export-shapes.title" msgstr "匯出所選項目" @@ -554,50 +554,50 @@ msgstr "方格檢視" msgid "dashboard.files-layout.list" msgstr "清單檢視" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:608 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:652 msgid "dashboard.fonts.deleted-placeholder" msgstr "缺少字型" -#: src/app/main/ui/dashboard/fonts.cljs:235 +#: src/app/main/ui/dashboard/fonts.cljs:251 msgid "dashboard.fonts.dismiss-all" msgstr "全部關閉" -#: src/app/main/ui/dashboard/fonts.cljs:495 +#: src/app/main/ui/dashboard/fonts.cljs:528 msgid "dashboard.fonts.empty-placeholder" msgstr "你上傳的自訂字型會顯示在這裡。" -#: src/app/main/ui/dashboard/fonts.cljs:498 +#: src/app/main/ui/dashboard/fonts.cljs:531 msgid "dashboard.fonts.empty-placeholder-viewer" msgstr "尚無自訂字型。" -#: src/app/main/ui/dashboard/fonts.cljs:499 +#: src/app/main/ui/dashboard/fonts.cljs:532 msgid "dashboard.fonts.empty-placeholder-viewer-sub" msgstr "專案成員上傳自訂字型後,就會顯示在這裡。" -#: src/app/main/ui/dashboard/fonts.cljs:223 +#: src/app/main/ui/dashboard/fonts.cljs:237 msgid "dashboard.fonts.fonts-added" msgid_plural "dashboard.fonts.fonts-added" msgstr[0] "'已新增 %s 個字型'" -#: src/app/main/ui/dashboard/fonts.cljs:198 +#: src/app/main/ui/dashboard/fonts.cljs:210 #, markdown msgid "dashboard.fonts.hero-text1" msgstr "" "在這裡上傳的網頁字型會加入此團隊檔案中文字屬性的字型家族清單。字型家族名稱相同的字型會合併為**同一個字型家族**。您可以上傳以下格式的字型:**" "TTF、OTF 和 WOFF**(只需上傳其中一種格式)。" -#: src/app/main/ui/dashboard/fonts.cljs:211 +#: src/app/main/ui/dashboard/fonts.cljs:225 #, markdown msgid "dashboard.fonts.hero-text2" msgstr "" "請僅上傳您擁有或已獲授權在 Penpot 中使用的字型。詳情請參閱 [Penpot " "服務條款](%s)的內容權利章節。您也可以閱讀[字型授權相關資訊](https://www.typography.com/faq)。" -#: src/app/main/ui/dashboard/fonts.cljs:231 +#: src/app/main/ui/dashboard/fonts.cljs:245 msgid "dashboard.fonts.upload-all" msgstr "全部上傳" -#: src/app/main/ui/dashboard/fonts.cljs:216 +#: src/app/main/ui/dashboard/fonts.cljs:230 #, markdown msgid "dashboard.fonts.warning-text" msgstr "" @@ -605,23 +605,23 @@ msgstr "" "app/)等字型垂直度量檢查工具來確認。此外,我們建議使用 [Transfonter](https://transfonter.org/) " "產生網頁字型並修正錯誤。 " -#: src/app/main/ui/dashboard/organization_team_switch.cljs:240 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:305 msgid "dashboard.go-to-admin-console" msgstr "前往管理員控制台" -#: src/app/main/ui/dashboard/grid.cljs:252 +#: src/app/main/ui/dashboard/grid.cljs:253 msgid "dashboard.grid.last-modified-at" msgstr "上次修改時間:%s" -#: src/app/main/ui/dashboard/import.cljs:995, src/app/main/ui/dashboard/project_menu.cljs:110 +#: src/app/main/ui/dashboard/project_menu.cljs:110 msgid "dashboard.import" msgstr "匯入 Penpot 檔案" -#: src/app/main/ui/dashboard/import.cljs:343, src/app/worker/import.cljs:144, src/app/worker/import.cljs:147 +#: src/app/main/data/imports.cljs:272, src/app/main/data/imports.cljs:275, src/app/main/ui/dashboard/import.cljs:343 msgid "dashboard.import.analyze-error" msgstr "糟糕!無法匯入此檔案" -#: src/app/main/ui/dashboard/import.cljs:597 +#: src/app/main/ui/dashboard/import.cljs:698 msgid "dashboard.import.auto-linked-libraries" msgid_plural "dashboard.import.auto-linked-libraries" msgstr[0] "已依名稱自動連結 %s 個資料庫。" @@ -630,39 +630,39 @@ msgstr[0] "已依名稱自動連結 %s 個資料庫。" msgid "dashboard.import.bad-url" msgstr "匯入失敗。範本網址有誤" -#: src/app/main/ui/dashboard/import.cljs:733 +#: src/app/main/ui/dashboard/import.cljs:838 msgid "dashboard.import.confirm-library-links" msgstr "確認資料庫連結" -#: src/app/main/ui/dashboard/import.cljs:619 +#: src/app/main/ui/dashboard/import.cljs:721 msgid "dashboard.import.import-error.check-error" msgstr "無法驗證此檔案。" -#: src/app/main/ui/dashboard/import.cljs:623 +#: src/app/main/ui/dashboard/import.cljs:725 msgid "dashboard.import.import-error.corrupt-file" msgstr "此檔案似乎已損毀。" -#: src/app/main/ui/dashboard/import.cljs:603 +#: src/app/main/ui/dashboard/import.cljs:704 msgid "dashboard.import.import-error.disclaimer" msgstr "部分檔案未能匯入" -#: src/app/main/ui/dashboard/import.cljs:607 +#: src/app/main/ui/dashboard/import.cljs:709 msgid "dashboard.import.import-error.message1" msgstr "下列檔案有錯誤:" -#: src/app/main/ui/dashboard/import.cljs:627 +#: src/app/main/ui/dashboard/import.cljs:729 msgid "dashboard.import.import-error.message2" msgstr "有錯誤的檔案將不會上傳。" -#: src/app/main/ui/dashboard/import.cljs:626 +#: src/app/main/ui/dashboard/import.cljs:728 msgid "dashboard.import.import-error.unknown-error" msgstr "處理此檔案時發生錯誤。" -#: src/app/main/ui/dashboard/import.cljs:593 +#: src/app/main/ui/dashboard/import.cljs:694 msgid "dashboard.import.import-message" msgstr "已成功匯入 %s 個檔案。" -#: src/app/main/ui/dashboard/import.cljs:587 +#: src/app/main/ui/dashboard/import.cljs:688 msgid "dashboard.import.import-warning" msgstr "部分檔案含有無效物件,這些物件已移除。" @@ -670,75 +670,47 @@ msgstr "部分檔案含有無效物件,這些物件已移除。" msgid "dashboard.import.no-perms" msgstr "您沒有權限將檔案匯入此團隊" -#: src/app/main/ui/dashboard/import.cljs:134 -msgid "dashboard.import.progress.process-colors" -msgstr "正在處理色彩" - -#: src/app/main/ui/dashboard/import.cljs:143, src/app/main/ui/dashboard/import.cljs:146 -msgid "dashboard.import.progress.process-components" -msgstr "正在處理元件" - -#: src/app/main/ui/dashboard/import.cljs:140 -msgid "dashboard.import.progress.process-media" -msgstr "正在處理媒體" - -#: src/app/main/ui/dashboard/import.cljs:131 -msgid "dashboard.import.progress.process-page" -msgstr "正在處理頁面:%s" - -#: src/app/main/ui/dashboard/import.cljs:137 -msgid "dashboard.import.progress.process-typographies" -msgstr "正在處理文字樣式" - -#: src/app/main/ui/dashboard/import.cljs:125 -msgid "dashboard.import.progress.upload-data" -msgstr "正在將資料上傳至伺服器 (%s/%s)" - -#: src/app/main/ui/dashboard/import.cljs:128 -msgid "dashboard.import.progress.upload-media" -msgstr "正在上傳檔案:%s" - -#: src/app/main/ui/dashboard/import.cljs:443 +#: src/app/main/ui/dashboard/import.cljs:431 msgid "dashboard.import.resolve-libraries" msgstr "部分資料庫無法自動連結。請為每一項選擇正確的資料庫:" -#: src/app/main/ui/dashboard/import.cljs:565 +#: src/app/main/ui/dashboard/import.cljs:553 msgid "dashboard.import.resolve-libraries-summary" msgstr "確認前,請檢查資料庫連結:" -#: src/app/main/ui/dashboard/import.cljs:521 +#: src/app/main/ui/dashboard/import.cljs:509 msgid "dashboard.import.summary.linked" msgstr "已連結" -#: src/app/main/ui/dashboard/import.cljs:558 +#: src/app/main/ui/dashboard/import.cljs:546 msgid "dashboard.import.summary.no-selection" msgstr "尚未選取資料庫" -#: src/app/main/ui/dashboard/team.cljs:881 +#: src/app/main/ui/dashboard/team.cljs:879 msgid "dashboard.invitation-modal.delete" msgstr "你即將刪除寄給以下對象的邀請:" -#: src/app/main/ui/dashboard/team.cljs:882 +#: src/app/main/ui/dashboard/team.cljs:880 msgid "dashboard.invitation-modal.resend" msgstr "你即將重新寄送邀請給以下對象:" -#: src/app/main/ui/dashboard/team.cljs:872 +#: src/app/main/ui/dashboard/team.cljs:867 msgid "dashboard.invitation-modal.title.delete-invitations" msgstr "刪除邀請" -#: src/app/main/ui/dashboard/team.cljs:873 +#: src/app/main/ui/dashboard/team.cljs:868 msgid "dashboard.invitation-modal.title.resend-invitations" msgstr "重新寄送邀請" -#: src/app/main/ui/dashboard/team.cljs:861, src/app/main/ui/dashboard/team.cljs:1202, src/app/main/ui/dashboard/team_form.cljs:156 +#: src/app/main/ui/dashboard/team.cljs:856, src/app/main/ui/dashboard/team.cljs:1203, src/app/main/ui/dashboard/team_form.cljs:160 msgid "dashboard.invitations.no-permission" msgstr "你沒有權限邀請他人加入此團隊,也無法編輯或刪除邀請。" -#: src/app/main/ui/dashboard/team.cljs:149, src/app/main/ui/dashboard/team.cljs:860 +#: src/app/main/ui/dashboard/team.cljs:133, src/app/main/ui/dashboard/team.cljs:855 msgid "dashboard.invite-profile" msgstr "邀請人員" -#: src/app/main/ui/dashboard/team.cljs:152 +#: src/app/main/ui/dashboard/team.cljs:136 msgid "dashboard.invite-profile-disabled" msgstr "你沒有權限邀請他人加入此團隊" @@ -750,27 +722,27 @@ msgstr "離開組織" msgid "dashboard.leave-organization.toast" msgstr "你已不再是 %s 組織的成員。" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:464 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:210, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:216, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:221, src/app/main/ui/dashboard/team.cljs:455 msgid "dashboard.leave-team" msgstr "離開團隊" -#: src/app/main/ui/dashboard/templates.cljs:83, src/app/main/ui/dashboard/templates.cljs:166 +#: src/app/main/ui/dashboard/templates.cljs:78, src/app/main/ui/dashboard/templates.cljs:160 msgid "dashboard.libraries-and-templates" msgstr "資料庫與範本" -#: src/app/main/ui/dashboard/templates.cljs:263 +#: src/app/main/ui/dashboard/templates.cljs:257 msgid "dashboard.libraries-and-templates.description" msgstr "這裡提供一些可加入專案的資料庫與範本" -#: src/app/main/ui/dashboard/templates.cljs:167 +#: src/app/main/ui/dashboard/templates.cljs:161 msgid "dashboard.libraries-and-templates.explore" msgstr "探索更多資料庫與範本,並了解如何貢獻" -#: src/app/main/ui/dashboard/import.cljs:816, src/app/main/ui/workspace/libraries.cljs:229 +#: src/app/main/ui/dashboard/import.cljs:937, src/app/main/ui/workspace/libraries.cljs:281 msgid "dashboard.libraries-and-templates.import-error" msgstr "匯入範本時發生問題,範本未匯入。" -#: src/app/main/ui/dashboard/libraries.cljs:71 +#: src/app/main/ui/dashboard/libraries.cljs:76 msgid "dashboard.libraries-title" msgstr "資料庫" @@ -778,7 +750,7 @@ msgstr "資料庫" msgid "dashboard.loading-files" msgstr "正在載入你的檔案…" -#: src/app/main/ui/dashboard/fonts.cljs:489 +#: src/app/main/ui/dashboard/fonts.cljs:520 msgid "dashboard.loading-fonts" msgstr "正在載入你的字型…" @@ -798,55 +770,55 @@ msgstr "將 %s 個檔案移至" msgid "dashboard.move-to-other-team" msgstr "移至其他團隊" -#: src/app/main/ui/dashboard/files.cljs:117, src/app/main/ui/dashboard/projects.cljs:259 +#: src/app/main/ui/dashboard/files.cljs:127, src/app/main/ui/dashboard/projects.cljs:268 msgid "dashboard.new-file" msgstr "+ 新增檔案" -#: src/app/main/data/dashboard.cljs:506, src/app/main/data/dashboard.cljs:618 +#: src/app/main/data/dashboard.cljs:524, src/app/main/data/dashboard.cljs:636 msgid "dashboard.new-file-prefix" msgstr "新檔案" -#: src/app/main/ui/dashboard/projects.cljs:59 +#: src/app/main/ui/dashboard/projects.cljs:67 msgid "dashboard.new-project" msgstr "+ 新增專案" -#: src/app/main/data/dashboard.cljs:259, src/app/main/data/dashboard.cljs:621 +#: src/app/main/data/dashboard.cljs:269, src/app/main/data/dashboard.cljs:639 msgid "dashboard.new-project-prefix" msgstr "新專案" -#: src/app/main/ui/dashboard/search.cljs:76 +#: src/app/main/ui/dashboard/search.cljs:90 msgid "dashboard.no-matches-for" msgstr "找不到符合「%s」的項目" -#: src/app/main/ui/dashboard/team_form.cljs:158 +#: src/app/main/ui/dashboard/team_form.cljs:162 msgid "dashboard.no-organization-allows-create-team.message" msgstr "你沒有權限在所屬的任何組織中新增團隊。" -#: src/app/main/ui/dashboard/team_form.cljs:152 +#: src/app/main/ui/dashboard/team_form.cljs:156 msgid "dashboard.no-permission-create-team.message" msgstr "你沒有權限在 %s 組織中建立團隊。如需更多資訊,請聯絡組織擁有者。" -#: src/app/main/ui/dashboard/team_form.cljs:154 +#: src/app/main/ui/dashboard/team_form.cljs:158 msgid "dashboard.no-permission-delete-team.message" msgstr "你沒有權限刪除隸屬於 %s 組織的團隊。如需更多資訊,請聯絡組織擁有者。" -#: src/app/main/ui/dashboard/team_form.cljs:160 +#: src/app/main/ui/dashboard/team_form.cljs:164 msgid "dashboard.no-permission-move-team.message" msgstr "你沒有權限移動隸屬於 %s 組織的團隊。如需更多資訊,請聯絡組織擁有者。" -#: src/app/main/ui/dashboard/sidebar.cljs:400 +#: src/app/main/ui/dashboard/sidebar.cljs:393 msgid "dashboard.no-projects-placeholder" msgstr "釘選的專案會顯示在這裡" -#: src/app/main/ui/dashboard/comments.cljs:89 +#: src/app/main/ui/dashboard/comments.cljs:85 msgid "dashboard.notifications" msgstr "通知" -#: src/app/main/ui/auth/verify_token.cljs:38 +#: src/app/main/ui/auth/verify_token.cljs:36 msgid "dashboard.notifications.email-changed-successfully" msgstr "您的電子郵件地址已成功更新" -#: src/app/main/ui/auth/verify_token.cljs:32 +#: src/app/main/ui/auth/verify_token.cljs:30 msgid "dashboard.notifications.email-verified-successfully" msgstr "您的電子郵件地址已成功驗證" @@ -858,11 +830,11 @@ msgstr "通知設定已更新" msgid "dashboard.notifications.password-saved" msgstr "密碼已成功儲存!" -#: src/app/main/ui/dashboard/comments.cljs:43 +#: src/app/main/ui/dashboard/comments.cljs:39 msgid "dashboard.notifications.view" msgstr "查看通知" -#: src/app/main/ui/dashboard/team.cljs:1725 +#: src/app/main/ui/dashboard/team.cljs:1740 msgid "dashboard.num-of-members" msgstr "%s 位成員" @@ -870,23 +842,23 @@ msgstr "%s 位成員" msgid "dashboard.open-in-new-tab" msgstr "在新分頁開啟檔案" -#: src/app/main/ui/dashboard/deleted.cljs:161, src/app/main/ui/dashboard/deleted.cljs:162, src/app/main/ui/dashboard/files.cljs:143, src/app/main/ui/dashboard/grid.cljs:427, src/app/main/ui/dashboard/grid.cljs:438, src/app/main/ui/dashboard/grid.cljs:480, src/app/main/ui/dashboard/projects.cljs:217, src/app/main/ui/dashboard/projects.cljs:274, src/app/main/ui/dashboard/sidebar.cljs:156 +#: src/app/main/ui/dashboard/deleted.cljs:167, src/app/main/ui/dashboard/files.cljs:153, src/app/main/ui/dashboard/grid.cljs:441, src/app/main/ui/dashboard/grid.cljs:452, src/app/main/ui/dashboard/grid.cljs:494, src/app/main/ui/dashboard/projects.cljs:226, src/app/main/ui/dashboard/projects.cljs:283, src/app/main/ui/dashboard/sidebar.cljs:143 msgid "dashboard.options" msgstr "選項" -#: src/app/main/ui/dashboard/team.cljs:1224 +#: src/app/main/ui/dashboard/team.cljs:1225 msgid "dashboard.order-invitations-by-role" msgstr "依角色排序" -#: src/app/main/ui/dashboard/team.cljs:1233 +#: src/app/main/ui/dashboard/team.cljs:1234 msgid "dashboard.order-invitations-by-status" msgstr "依狀態排序" -#: src/app/main/data/dashboard.cljs:703 +#: src/app/main/data/dashboard.cljs:721 msgid "dashboard.organization-deleted" msgstr "%s 組織已刪除。" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:208, src/app/main/ui/dashboard/organization_team_switch.cljs:209 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:236, src/app/main/ui/dashboard/organization_team_switch.cljs:237 msgid "dashboard.other-teams" msgstr "其他團隊" @@ -910,11 +882,11 @@ msgstr "您現在是此團隊的擁有者。" msgid "dashboard.permissions-change.viewer" msgstr "您現在是此團隊的檢視者。" -#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:46, src/app/main/ui/dashboard/libraries.cljs:58, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:50, src/app/main/ui/dashboard/projects.cljs:364, src/app/main/ui/dashboard/search.cljs:47, src/app/main/ui/dashboard/team.cljs:647, src/app/main/ui/dashboard/team.cljs:1257, src/app/main/ui/dashboard/team.cljs:1537, src/app/main/ui/dashboard/team.cljs:1644 +#: src/app/main/ui/dashboard/file_menu.cljs:38, src/app/main/ui/dashboard/fonts.cljs:51, src/app/main/ui/dashboard/libraries.cljs:60, src/app/main/ui/dashboard/organization_team_switch.cljs:57, src/app/main/ui/dashboard/projects.cljs:57, src/app/main/ui/dashboard/projects.cljs:373, src/app/main/ui/dashboard/search.cljs:49, src/app/main/ui/dashboard/team.cljs:638, src/app/main/ui/dashboard/team.cljs:1258, src/app/main/ui/dashboard/team.cljs:1548, src/app/main/ui/dashboard/team.cljs:1655 msgid "dashboard.personal-projects" msgstr "個人專案" -#: src/app/main/ui/dashboard/files.cljs:122, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:251 +#: src/app/main/ui/dashboard/files.cljs:132, src/app/main/ui/dashboard/project_menu.cljs:98, src/app/main/ui/dashboard/projects.cljs:260 msgid "dashboard.pin-unpin" msgstr "釘選/取消釘選" @@ -930,27 +902,27 @@ msgstr "無法解析外掛資訊清單" msgid "dashboard.plugins.try-plugin" msgstr "試用外掛: " -#: src/app/main/data/dashboard.cljs:736 +#: src/app/main/data/dashboard.cljs:754 msgid "dashboard.progress-notification.deleting-files" msgstr "正在刪除檔案…" -#: src/app/main/data/dashboard.cljs:857 +#: src/app/main/data/dashboard.cljs:875 msgid "dashboard.progress-notification.restoring-files" msgstr "正在還原檔案…" -#: src/app/main/data/dashboard.cljs:737 +#: src/app/main/data/dashboard.cljs:755 msgid "dashboard.progress-notification.slow-delete" msgstr "刪除所需時間比預期長" -#: src/app/main/data/dashboard.cljs:858 +#: src/app/main/data/dashboard.cljs:876 msgid "dashboard.progress-notification.slow-restore" msgstr "還原所需時間比預期長" -#: src/app/main/ui/dashboard/deleted.cljs:65, src/app/main/ui/dashboard/projects.cljs:51 +#: src/app/main/ui/dashboard/deleted.cljs:69, src/app/main/ui/dashboard/projects.cljs:58 msgid "dashboard.projects-title" msgstr "專案" -#: src/app/main/ui/settings/profile.cljs:86 +#: src/app/main/ui/settings/profile.cljs:92 msgid "dashboard.remove-account" msgstr "想要移除您的帳號嗎?" @@ -958,107 +930,107 @@ msgstr "想要移除您的帳號嗎?" msgid "dashboard.removed-from-team" msgstr "您已不再是「%s」團隊的成員。" -#: src/app/main/ui/dashboard/deleted.cljs:280 +#: src/app/main/ui/dashboard/deleted.cljs:286 msgid "dashboard.restore-all-confirmation.description" msgstr "即將還原您所有的專案與檔案,這可能需要一些時間。" -#: src/app/main/ui/dashboard/deleted.cljs:279 +#: src/app/main/ui/dashboard/deleted.cljs:285 msgid "dashboard.restore-all-confirmation.title" msgstr "還原所有專案與檔案" -#: src/app/main/ui/dashboard/deleted.cljs:317 +#: src/app/main/ui/dashboard/deleted.cljs:323 msgid "dashboard.restore-all-deleted-button" msgstr "全部還原" -#: src/app/main/data/dashboard.cljs:917 +#: src/app/main/data/dashboard.cljs:935 msgid "dashboard.restore-files-success-notification" msgstr "已成功還原 %s 個檔案。" -#: src/app/main/ui/dashboard/deleted.cljs:87 +#: src/app/main/ui/dashboard/deleted.cljs:92 msgid "dashboard.restore-project-button" msgstr "還原專案" -#: src/app/main/ui/dashboard/deleted.cljs:44 +#: src/app/main/ui/dashboard/deleted.cljs:45 msgid "dashboard.restore-project-confirmation.description" msgstr "即將還原「%s」專案及其中的所有檔案。" -#: src/app/main/ui/dashboard/deleted.cljs:43 +#: src/app/main/ui/dashboard/deleted.cljs:44 msgid "dashboard.restore-project-confirmation.title" msgstr "還原專案" -#: src/app/main/data/dashboard.cljs:889, src/app/main/data/dashboard.cljs:916, src/app/main/data/dashboard.cljs:953, src/app/main/ui/dashboard/file_menu.cljs:224 +#: src/app/main/data/dashboard.cljs:907, src/app/main/data/dashboard.cljs:934, src/app/main/data/dashboard.cljs:971, src/app/main/ui/dashboard/file_menu.cljs:224 msgid "dashboard.restore-success-notification" msgstr "%s 已成功還原。" -#: src/app/main/ui/settings/profile.cljs:78 +#: src/app/main/ui/settings/profile.cljs:82 msgid "dashboard.save-settings" msgstr "儲存設定" -#: src/app/main/ui/dashboard/sidebar.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:229, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 +#: src/app/main/ui/dashboard/sidebar.cljs:215, src/app/main/ui/dashboard/sidebar.cljs:216, src/app/main/ui/ds/controls/shared/searchable_options_dropdown.cljs:140 msgid "dashboard.search-placeholder" msgstr "搜尋…" -#: src/app/main/ui/dashboard/search.cljs:71 +#: src/app/main/ui/dashboard/search.cljs:82 msgid "dashboard.searching-for" msgstr "正在搜尋「%s」…" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:180 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:263 msgid "dashboard.section.organizations" msgstr "組織" -#: src/app/main/ui/dashboard/organization_team_switch.cljs:249 +#: src/app/main/ui/dashboard/organization_team_switch.cljs:363 msgid "dashboard.section.teams" msgstr "團隊" -#: src/app/main/data/nitrate.cljs:517 +#: src/app/main/data/nitrate.cljs:529 msgid "dashboard.select-organization-modal.accept" msgstr "加入組織" -#: src/app/main/data/nitrate.cljs:515 +#: src/app/main/data/nitrate.cljs:527 msgid "dashboard.select-organization-modal.choose" msgstr "選擇組織:" -#: src/app/main/ui/dashboard/team.cljs:1041 +#: src/app/main/ui/dashboard/team.cljs:1042 msgid "dashboard.select-organization-modal.external-invitations-warning" msgstr "「%s」僅允許組織成員加入旗下團隊。所有寄給組織外部人員且尚未接受的邀請都將撤銷。" -#: src/app/main/ui/dashboard/team.cljs:1037 +#: src/app/main/ui/dashboard/team.cljs:1038 msgid "dashboard.select-organization-modal.external-invitations-will-be-canceled" msgstr "寄給外部使用者且尚未接受的邀請將會取消。" -#: src/app/main/data/nitrate.cljs:583 +#: src/app/main/data/nitrate.cljs:595 msgid "dashboard.select-organization-modal.permission-info" msgstr "這裡會列出您所屬且有權將團隊移入的組織。" -#: src/app/main/data/nitrate.cljs:507 +#: src/app/main/data/nitrate.cljs:519 msgid "dashboard.select-organization-modal.permission-info-add" msgstr "這裡會列出您所屬且有權在其中建立團隊或將現有團隊加入的組織。" -#: src/app/main/data/nitrate.cljs:516 +#: src/app/main/data/nitrate.cljs:528 msgid "dashboard.select-organization-modal.select" msgstr "選擇組織" -#: src/app/main/data/nitrate.cljs:514, src/app/main/data/nitrate.cljs:534, src/app/main/ui/dashboard/team_form.cljs:157 +#: src/app/main/data/nitrate.cljs:526, src/app/main/data/nitrate.cljs:546, src/app/main/ui/dashboard/team_form.cljs:161 msgid "dashboard.select-organization-modal.title" msgstr "將團隊加入組織" -#: src/app/main/ui/settings/options.cljs:66 +#: src/app/main/ui/settings/options.cljs:69 msgid "dashboard.select-ui-language" msgstr "選擇介面語言" -#: src/app/main/ui/settings/options.cljs:73 +#: src/app/main/ui/settings/options.cljs:79 msgid "dashboard.select-ui-theme" msgstr "選擇主題" -#: src/app/main/ui/settings/options.cljs:76 +#: src/app/main/ui/settings/options.cljs:82 msgid "dashboard.select-ui-theme.dark" msgstr "Penpot 深色(預設)" -#: src/app/main/ui/settings/options.cljs:77 +#: src/app/main/ui/settings/options.cljs:83 msgid "dashboard.select-ui-theme.light" msgstr "Penpot 淺色" -#: src/app/main/ui/settings/options.cljs:78 +#: src/app/main/ui/settings/options.cljs:84 msgid "dashboard.select-ui-theme.system" msgstr "系統主題" @@ -1122,11 +1094,11 @@ msgstr "更新設定" msgid "dashboard.settings.notifications.title" msgstr "通知" -#: src/app/main/ui/dashboard/projects.cljs:288 +#: src/app/main/ui/dashboard/projects.cljs:297 msgid "dashboard.show-all-files" msgstr "顯示所有檔案" -#: src/app/main/ui/workspace/main_menu.cljs:718 +#: src/app/main/ui/workspace/main_menu.cljs:719 msgid "dashboard.show-version-history" msgstr "版本歷史記錄" @@ -1148,7 +1120,7 @@ msgstr[0] "已成功建立您檔案的複本" msgid "dashboard.success-duplicate-project" msgstr "已成功建立您專案的複本" -#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:715, src/app/main/ui/dashboard/sidebar.cljs:143 +#: src/app/main/ui/dashboard/file_menu.cljs:162, src/app/main/ui/dashboard/grid.cljs:729, src/app/main/ui/dashboard/sidebar.cljs:130 msgid "dashboard.success-move-file" msgstr "已成功移動您的檔案" @@ -1160,174 +1132,174 @@ msgstr "已成功移動您的檔案" msgid "dashboard.success-move-project" msgstr "已成功移動您的專案" -#: src/app/main/data/common.cljs:266 +#: src/app/main/data/common.cljs:268 #, backend msgid "dashboard.team-belong-organization" msgstr "%s 現已隸屬於 %s" -#: src/app/main/ui/dashboard/team.cljs:1669 +#: src/app/main/ui/dashboard/team.cljs:1682 msgid "dashboard.team-info" msgstr "團隊資訊" -#: src/app/main/ui/dashboard/team.cljs:1714 +#: src/app/main/ui/dashboard/team.cljs:1728 msgid "dashboard.team-members" msgstr "團隊成員" -#: src/app/main/data/common.cljs:267 +#: src/app/main/data/common.cljs:269 #, backend msgid "dashboard.team-no-longer-belong-organization" msgstr "%s 已不再隸屬於 %s" -#: src/app/main/ui/dashboard/team.cljs:1676 +#: src/app/main/ui/dashboard/team.cljs:1689 msgid "dashboard.team-organization" msgstr "團隊所屬組織" -#: src/app/main/ui/dashboard/team.cljs:1710 +#: src/app/main/ui/dashboard/team.cljs:1724 msgid "dashboard.team-organization.add" msgstr "加入組織" -#: src/app/main/data/nitrate.cljs:533, src/app/main/data/nitrate.cljs:590 +#: src/app/main/data/nitrate.cljs:545, src/app/main/data/nitrate.cljs:602 msgid "dashboard.team-organization.add.no-valid-organizations" msgstr "您沒有權限將此團隊加入您所屬的任何組織。" -#: src/app/main/ui/dashboard/team.cljs:1699 +#: src/app/main/ui/dashboard/team.cljs:1713 msgid "dashboard.team-organization.change" msgstr "變更團隊所屬組織" -#: src/app/main/ui/dashboard/team.cljs:948 +#: src/app/main/ui/dashboard/team.cljs:946 msgid "dashboard.team-organization.disabled-organization-tooltip" msgstr "只能邀請您所屬組織內的人員。" -#: src/app/main/ui/dashboard/team.cljs:1706 +#: src/app/main/ui/dashboard/team.cljs:1720 msgid "dashboard.team-organization.none" msgstr "此團隊不隸屬於任何組織" -#: src/app/main/ui/dashboard/team.cljs:1702 +#: src/app/main/ui/dashboard/team.cljs:1716 msgid "dashboard.team-organization.remove" msgstr "將團隊移出組織" -#: src/app/main/ui/dashboard/team.cljs:1729 +#: src/app/main/ui/dashboard/team.cljs:1744 msgid "dashboard.team-projects" msgstr "團隊專案" -#: src/app/main/ui/dashboard/templates.cljs:132 +#: src/app/main/ui/dashboard/templates.cljs:125 msgid "dashboard.template.add-to-project" msgstr "新增至你的專案" -#: src/app/main/ui/settings/options.cljs:71 +#: src/app/main/ui/settings/options.cljs:77 msgid "dashboard.theme-change" msgstr "介面主題" -#: src/app/main/ui/dashboard/search.cljs:58 +#: src/app/main/ui/dashboard/search.cljs:63 msgid "dashboard.title-search" msgstr "搜尋結果" -#: src/app/main/ui/dashboard/deleted.cljs:307 +#: src/app/main/ui/dashboard/deleted.cljs:313 msgid "dashboard.trash-info-text-part1" msgstr "已刪除的檔案會在垃圾桶中保留" -#: src/app/main/ui/dashboard/deleted.cljs:309 +#: src/app/main/ui/dashboard/deleted.cljs:315 msgid "dashboard.trash-info-text-part2" msgstr " %s 天. " -#: src/app/main/ui/dashboard/deleted.cljs:310 +#: src/app/main/ui/dashboard/deleted.cljs:316 msgid "dashboard.trash-info-text-part3" msgstr "之後就會永久刪除。" -#: src/app/main/ui/dashboard/deleted.cljs:312 +#: src/app/main/ui/dashboard/deleted.cljs:318 msgid "dashboard.trash-info-text-part4" msgstr "如果改變心意,你可以從各個檔案的選單還原檔案,或將其永久刪除。" -#: src/app/main/ui/dashboard/search.cljs:66 +#: src/app/main/ui/dashboard/search.cljs:74 msgid "dashboard.type-something" msgstr "輸入以搜尋" -#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:692 +#: src/app/main/ui/dashboard/file_menu.cljs:322, src/app/main/ui/workspace/main_menu.cljs:693 msgid "dashboard.unpublish-shared" msgstr "取消發布資料庫" -#: src/app/main/ui/settings/options.cljs:82 +#: src/app/main/ui/settings/options.cljs:88 msgid "dashboard.update-settings" msgstr "更新設定" -#: src/app/main/data/dashboard.cljs:671 +#: src/app/main/data/dashboard.cljs:689 #, backend msgid "dashboard.user-no-longer-belong-organization" msgstr "你已不再是 %s 組織的成員" -#: src/app/main/ui/settings/options.cljs:109 +#: src/app/main/ui/settings/options.cljs:115 msgid "dashboard.webgl-switch.beta" msgstr "測試版" -#: src/app/main/ui/settings/options.cljs:110 +#: src/app/main/ui/settings/options.cljs:116 msgid "dashboard.webgl-switch.description" msgstr "WebGL 算繪可提升效能,但目前仍在測試階段,可能較不穩定。畫布、匯出檔案(SVG/PDF)與檢視模式的顯示效果可能略有差異。" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.disabled" msgstr "已停用" -#: src/app/main/ui/settings/options.cljs:113 +#: src/app/main/ui/settings/options.cljs:119 msgid "dashboard.webgl-switch.enabled" msgstr "已啟用" -#: src/app/main/ui/settings/options.cljs:116 +#: src/app/main/ui/settings/options.cljs:122 msgid "dashboard.webgl-switch.feedback" msgstr "提供意見回饋" -#: src/app/main/ui/settings/options.cljs:112 +#: src/app/main/ui/settings/options.cljs:118 msgid "dashboard.webgl-switch.status" msgstr "狀態" -#: src/app/main/ui/settings/options.cljs:108 +#: src/app/main/ui/settings/options.cljs:114 msgid "dashboard.webgl-switch.title" msgstr "WebGL 渲染" -#: src/app/main/ui/dashboard/team.cljs:1404 +#: src/app/main/ui/dashboard/team.cljs:1408 msgid "dashboard.webhooks.active" msgstr "啟用" -#: src/app/main/ui/dashboard/team.cljs:1405 +#: src/app/main/ui/dashboard/team.cljs:1409 msgid "dashboard.webhooks.active.explain" msgstr "觸發此 webhook 時,將傳送事件的詳細資訊" -#: src/app/main/ui/dashboard/team.cljs:1447 +#: src/app/main/ui/dashboard/team.cljs:1456 msgid "dashboard.webhooks.cant-edit" msgstr "您只能刪除或修改自己建立的 webhook。" -#: src/app/main/ui/dashboard/team.cljs:1395 +#: src/app/main/ui/dashboard/team.cljs:1399 msgid "dashboard.webhooks.content-type" msgstr "內容類型" -#: src/app/main/ui/dashboard/team.cljs:1427 +#: src/app/main/ui/dashboard/team.cljs:1433 msgid "dashboard.webhooks.create" msgstr "建立 webhook" -#: src/app/main/ui/dashboard/team.cljs:1330 +#: src/app/main/ui/dashboard/team.cljs:1331 msgid "dashboard.webhooks.create.success" msgstr "已成功建立 webhook。" -#: src/app/main/ui/dashboard/team.cljs:1424 +#: src/app/main/ui/dashboard/team.cljs:1428 msgid "dashboard.webhooks.description" msgstr "透過 webhook,您可以輕鬆讓其他網站和應用程式在 Penpot 發生特定事件時收到通知。我們會向您提供的每個網址傳送 POST 請求。" -#: src/app/main/ui/dashboard/team.cljs:1551 +#: src/app/main/ui/dashboard/team.cljs:1562 msgid "dashboard.webhooks.empty.add-one" msgstr "按一下「新增 webhook」按鈕即可新增。" -#: src/app/main/ui/dashboard/team.cljs:1550 +#: src/app/main/ui/dashboard/team.cljs:1561 msgid "dashboard.webhooks.empty.no-webhooks" msgstr "尚未建立任何 webhook。" -#: src/app/main/ui/settings.cljs:36 +#: src/app/main/ui/settings.cljs:42 msgid "dashboard.your-account-title" msgstr "您的帳號" -#: src/app/main/ui/settings/profile.cljs:70 +#: src/app/main/ui/settings/profile.cljs:72 msgid "dashboard.your-email" msgstr "電子郵件" -#: src/app/main/ui/settings/profile.cljs:62 +#: src/app/main/ui/settings/profile.cljs:64 msgid "dashboard.your-name" msgstr "您的姓名" @@ -1343,11 +1315,11 @@ msgstr "注意" msgid "ds.component-subtitle" msgstr "待更新的元件:" -#: src/app/main/ui/workspace/plugins.cljs:379, src/app/main/ui/workspace/plugins.cljs:442 +#: src/app/main/ui/workspace/plugins.cljs:443, src/app/main/ui/workspace/plugins.cljs:508 msgid "ds.confirm-allow" msgstr "允許" -#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:327, src/app/main/ui/settings/subscription.cljs:360, src/app/main/ui/settings/subscription.cljs:844, src/app/main/ui/settings/subscription.cljs:893, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/workspace/plugins.cljs:373, src/app/main/ui/workspace/plugins.cljs:436 +#: src/app/main/ui/comments.cljs:719, src/app/main/ui/confirm.cljs:43, src/app/main/ui/settings/subscription.cljs:331, src/app/main/ui/settings/subscription.cljs:364, src/app/main/ui/settings/subscription.cljs:869, src/app/main/ui/settings/subscription.cljs:920, src/app/main/ui/settings/subscription.cljs:930, src/app/main/ui/workspace/plugins.cljs:438, src/app/main/ui/workspace/plugins.cljs:503 msgid "ds.confirm-cancel" msgstr "取消" @@ -1367,7 +1339,7 @@ msgstr "已啟用的集合或主題中沒有適用的 tokens。" msgid "ds.inputs.numeric-input.no-matches" msgstr "找不到符合的項目。" -#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:270 +#: src/app/main/ui/ds/controls/numeric_input.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:519, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:177, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:267 msgid "ds.inputs.numeric-input.open-token-list-dropdown" msgstr "開啟 token 清單" @@ -1383,15 +1355,15 @@ msgstr "此帳號不允許使用此驗證服務提供者" msgid "errors.auth-provider-not-configured" msgstr "尚未設定驗證服務提供者。" -#: src/app/main/errors.cljs:580 +#: src/app/main/errors.cljs:742 msgid "errors.auth.unable-to-login" msgstr "您似乎尚未登入,或登入階段已過期。" -#: src/app/main/data/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:134, src/app/main/ui/dashboard/fonts.cljs:178 +#: src/app/main/data/fonts.cljs:310, src/app/main/ui/dashboard/fonts.cljs:143, src/app/main/ui/dashboard/fonts.cljs:187 msgid "errors.bad-font" msgstr "無法載入字型 %s" -#: src/app/main/data/fonts.cljs:255 +#: src/app/main/data/fonts.cljs:309 msgid "errors.bad-font-plural" msgstr "無法載入以下字型:%s" @@ -1403,51 +1375,51 @@ msgstr "無法上傳媒體檔案。" msgid "errors.character-limit-exceeded" msgstr "已超過字元數上限" -#: src/app/main/data/workspace/libraries.cljs:1691 +#: src/app/main/data/workspace/libraries.cljs:1778 msgid "errors.circular-library-reference" msgstr "無法新增資料庫:這會造成循環相依" -#: src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:248 +#: src/app/main/ui/components/mcp_menu.cljs:34, src/app/main/ui/settings/integrations.cljs:93, src/app/util/clipboard.cljs:274 msgid "errors.clipboard-api-unavailable" msgstr "剪貼簿 API 無法使用。請透過 HTTPS 提供 Penpot 服務,以啟用剪貼簿存取功能" -#: src/app/main/data/workspace/clipboard.cljs:514 +#: src/app/main/data/workspace/clipboard.cljs:554 msgid "errors.clipboard-not-implemented" msgstr "你的瀏覽器無法執行此操作" -#: src/app/util/clipboard.cljs:245 +#: src/app/util/clipboard.cljs:271 msgid "errors.clipboard-permission-denied" msgstr "剪貼簿存取遭拒。請在瀏覽器中允許存取剪貼簿,才能貼上內容" -#: src/app/main/errors.cljs:773 +#: src/app/main/errors.cljs:944 msgid "errors.comment-error" msgstr "留言發生錯誤" -#: src/app/main/errors.cljs:842 +#: src/app/main/errors.cljs:1013 msgid "errors.deprecated" msgstr "很抱歉!這是舊版檔案,使用了已停用的 Penpot 資產類型,因此無法開啟。" -#: src/app/main/errors.cljs:845 +#: src/app/main/errors.cljs:1016 msgid "errors.deprecated.contact.after" msgstr "讓我們協助你。" -#: src/app/main/errors.cljs:843 +#: src/app/main/errors.cljs:1014 msgid "errors.deprecated.contact.before" msgstr "雖然 Penpot 已不再支援這類資產,你仍可" -#: src/app/main/errors.cljs:844 +#: src/app/main/errors.cljs:1015 msgid "errors.deprecated.contact.text" msgstr "聯絡我們" -#: src/app/main/data/workspace/tokens/library_edit.cljs:515 +#: src/app/main/data/workspace/tokens/library_edit.cljs:549 msgid "errors.drop-token-set-parent-to-child" msgstr "無法將父集合拖放到自身的子路徑下。" -#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:148, src/app/main/ui/settings/change_email.cljs:29 +#: src/app/main/ui/auth/register.cljs:111, src/app/main/ui/auth/verify_token.cljs:121, src/app/main/ui/settings/change_email.cljs:30 msgid "errors.email-already-exists" msgstr "此電子郵件地址已被使用" -#: src/app/main/ui/auth/verify_token.cljs:153 +#: src/app/main/ui/auth/verify_token.cljs:126 msgid "errors.email-already-validated" msgstr "此電子郵件地址已完成驗證。" @@ -1459,27 +1431,27 @@ msgstr "不能使用你的電子郵件地址作為密碼" msgid "errors.email-does-not-match-invitation" msgstr "電子郵件地址與邀請中的地址不符。" -#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:243 +#: src/app/main/data/auth.cljs:346, src/app/main/ui/auth/register.cljs:99, src/app/main/ui/dashboard/team.cljs:227 msgid "errors.email-domain-not-allowed" msgstr "不允許使用此網域" -#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:740, src/app/main/ui/settings/change_email.cljs:37 +#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:102, src/app/main/ui/auth/register.cljs:105, src/app/main/ui/dashboard/team.cljs:735, src/app/main/ui/settings/change_email.cljs:38 msgid "errors.email-has-permanent-bounces" msgstr "電子郵件地址「%s」已有多次永久退信紀錄。" -#: src/app/main/ui/dashboard/team.cljs:239, src/app/main/ui/dashboard/team.cljs:1128, src/app/main/ui/onboarding/team_choice.cljs:122 +#: src/app/main/ui/dashboard/team.cljs:223, src/app/main/ui/dashboard/team.cljs:1129, src/app/main/ui/onboarding/team_choice.cljs:122 msgid "errors.email-spam-or-permanent-bounces" msgstr "電子郵件地址 «%s» 已被回報為垃圾郵件來源,或寄往此地址的郵件遭到永久退信。" -#: src/app/main/errors.cljs:819 +#: src/app/main/errors.cljs:990 msgid "errors.feature-mismatch" msgstr "您正要開啟的檔案已啟用「%s」功能,但目前的 Penpot 版本不支援此功能,或已停用此功能。" -#: src/app/main/errors.cljs:823, src/app/main/errors.cljs:837 +#: src/app/main/errors.cljs:994, src/app/main/errors.cljs:1008 msgid "errors.feature-not-supported" msgstr "不支援「%s」功能。" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:302, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:234, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:370, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:305, ../common/src/app/common/files/tokens.cljc:217, ../common/src/app/common/files/tokens.cljc:298, ../common/src/app/common/files/tokens.cljc:339, ../common/src/app/common/schema.cljc:1063 msgid "errors.field-max-length" msgstr "最多只能包含 %s 個字元。" @@ -1487,7 +1459,7 @@ msgstr "最多只能包含 %s 個字元。" msgid "errors.field-min-length" msgstr "至少須包含 1 個字元。" -#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:471, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 +#: src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:477, ../common/src/app/common/schema/messages.cljc:64, ../common/src/app/common/schema.cljc:1072 msgid "errors.field-missing" msgstr "欄位不可留空" @@ -1495,19 +1467,19 @@ msgstr "欄位不可留空" msgid "errors.field-not-all-whitespace" msgstr "名稱至少須包含一個非空格字元。" -#: src/app/main/errors.cljs:815 +#: src/app/main/errors.cljs:986 msgid "errors.file-feature-mismatch" msgstr "目前啟用的功能似乎與您要開啟的檔案所使用的功能不一致。必須先完成「%s」的資料遷移,才能開啟檔案。" -#: src/app/main/ui/dashboard/fonts.cljs:341 +#: src/app/main/ui/dashboard/fonts.cljs:358 msgid "errors.font-family-invalid-chars" msgstr "字型家族名稱只能包含英文字母、數字、空格、連字號、底線和句點。" -#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:479, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:158, src/app/main/ui/dashboard/fonts.cljs:386, src/app/main/ui/dashboard/team.cljs:250, src/app/main/ui/dashboard/team.cljs:1131, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:966 +#: src/app/main/data/auth.cljs:352, src/app/main/errors.cljs:564, src/app/main/ui/auth/login.cljs:116, src/app/main/ui/auth/register.cljs:131, src/app/main/ui/auth/register.cljs:317, src/app/main/ui/auth/verify_token.cljs:131, src/app/main/ui/dashboard/fonts.cljs:125, src/app/main/ui/dashboard/fonts.cljs:403, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1132, src/app/main/ui/onboarding/team_choice.cljs:125, src/app/main/ui/settings/feedback.cljs:84, src/app/main/ui/settings/integrations.cljs:143, src/app/main/ui/workspace/main_menu.cljs:967 msgid "errors.generic" msgstr "發生錯誤。" -#: src/app/main/errors.cljs:752 +#: src/app/main/errors.cljs:923 msgid "errors.internal-worker-error" msgstr "Web Worker 發生錯誤。" @@ -1523,7 +1495,7 @@ msgstr "資料無效" msgid "errors.invalid-email" msgstr "請輸入有效的電子郵件地址" -#: src/app/main/ui/settings/change_email.cljs:62 +#: src/app/main/ui/settings/change_email.cljs:63 msgid "errors.invalid-email-confirmation" msgstr "兩次輸入的電子郵件地址必須一致" @@ -1555,11 +1527,11 @@ msgstr "此邀請可能已取消或過期。" msgid "errors.ldap-disabled" msgstr "LDAP 驗證已停用。" -#: src/app/main/errors.cljs:831, src/app/main/ui/dashboard/team.cljs:234, src/app/main/ui/dashboard/team.cljs:1123, src/app/main/ui/onboarding/team_choice.cljs:117 +#: src/app/main/errors.cljs:1002, src/app/main/ui/dashboard/team.cljs:218, src/app/main/ui/dashboard/team.cljs:1124, src/app/main/ui/onboarding/team_choice.cljs:117 msgid "errors.max-quota-reached" msgstr "您已達到「%s」的配額上限。請聯絡支援團隊。" -#: src/app/main/ui/dashboard/team.cljs:230, src/app/main/ui/dashboard/team.cljs:1119, src/app/main/ui/onboarding/team_choice.cljs:113 +#: src/app/main/ui/dashboard/team.cljs:214, src/app/main/ui/dashboard/team.cljs:1120, src/app/main/ui/onboarding/team_choice.cljs:113 msgid "errors.maximum-invitations-by-request-reached" msgstr "已達到單次可邀請的電子郵件地址數量上限(%s)" @@ -1575,19 +1547,19 @@ msgstr "圖片內容似乎與副檔名不符。" msgid "errors.media-type-not-allowed" msgstr "這似乎不是有效的圖片。" -#: src/app/main/ui/dashboard/team.cljs:735 +#: src/app/main/ui/dashboard/team.cljs:730 msgid "errors.member-is-muted" msgstr "您邀請的使用者已因垃圾郵件檢舉或退信率過高而暫停接收電子郵件。" -#: src/app/main/errors.cljs:805 +#: src/app/main/errors.cljs:976 msgid "errors.migration-in-progress" msgstr "正在進行資料遷移" -#: src/app/main/errors.cljs:707 +#: src/app/main/errors.cljs:869 msgid "errors.only-creator-can-lock" msgstr "只有此版本的建立者可以鎖定此版本" -#: src/app/main/errors.cljs:715 +#: src/app/main/errors.cljs:877 msgid "errors.only-creator-can-unlock" msgstr "只有此版本的建立者可以解除鎖定" @@ -1599,7 +1571,7 @@ msgstr "退出組織時發生問題,請再試一次。" msgid "errors.organization-leave.organization-owner-cannot-leave" msgstr "組織擁有者無法退出組織。" -#: src/app/main/ui/auth/verify_token.cljs:125 +#: src/app/main/ui/auth/verify_token.cljs:98 msgid "errors.organization-not-found" msgstr "該組織不存在" @@ -1611,7 +1583,7 @@ msgstr "確認密碼必須與密碼一致" msgid "errors.password-too-short" msgstr "密碼至少須包含 8 個字元" -#: src/app/main/errors.cljs:688 +#: src/app/main/errors.cljs:850 msgid "errors.paste-data-validation" msgstr "剪貼簿中的資料無效" @@ -1619,7 +1591,7 @@ msgstr "剪貼簿中的資料無效" msgid "errors.profile-blocked" msgstr "此帳號已遭封鎖" -#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:225, src/app/main/ui/dashboard/team.cljs:731, src/app/main/ui/dashboard/team.cljs:1114, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:33 +#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:209, src/app/main/ui/dashboard/team.cljs:726, src/app/main/ui/dashboard/team.cljs:1115, src/app/main/ui/onboarding/team_choice.cljs:109, src/app/main/ui/settings/change_email.cljs:34 msgid "errors.profile-is-muted" msgstr "由於垃圾郵件檢舉或退信率過高,系統已停止寄送電子郵件至您的帳號。" @@ -1627,23 +1599,23 @@ msgstr "由於垃圾郵件檢舉或退信率過高,系統已停止寄送電子 msgid "errors.registration-disabled" msgstr "目前未開放註冊。" -#: src/app/main/errors.cljs:538 +#: src/app/main/errors.cljs:700 msgid "errors.save-failed" msgstr "尚未確認您的最新變更是否已儲存,且儲存功能可能不會自動恢復。請勿關閉或重新載入此分頁,以免遺失變更。請聯絡支援人員尋求協助。" -#: src/app/main/errors.cljs:764 +#: src/app/main/errors.cljs:935 msgid "errors.svg-parser.invalid-svg" msgstr "SVG 無效或格式錯誤" -#: src/app/main/errors.cljs:810 +#: src/app/main/errors.cljs:981 msgid "errors.team-feature-mismatch" msgstr "偵測到不相容的功能「%s」" -#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:505 +#: src/app/main/data/nitrate.cljs:192, src/app/main/ui/dashboard/team.cljs:496 msgid "errors.team-leave.insufficient-members" msgstr "團隊成員不足,無法離開團隊。您可能需要刪除團隊。" -#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:508 +#: src/app/main/data/nitrate.cljs:193, src/app/main/ui/dashboard/team.cljs:499 msgid "errors.team-leave.member-does-not-exists" msgstr "您要指派的成員不存在。" @@ -1651,7 +1623,7 @@ msgstr "您要指派的成員不存在。" msgid "errors.team-leave.only-owner-can-delete" msgstr "只有團隊擁有者可以刪除團隊。" -#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:511 +#: src/app/main/data/nitrate.cljs:194, src/app/main/ui/dashboard/team.cljs:502 msgid "errors.team-leave.owner-cant-leave" msgstr "擁有者無法離開團隊,請先將擁有者角色指派給其他成員。" @@ -1663,11 +1635,11 @@ msgstr "團隊名稱不能包含下列任一字元:'.'、':' 或 '/'" msgid "errors.token-set-already-exists" msgstr "已存在同名的集合" -#: src/app/main/data/workspace/tokens/library_edit.cljs:514 +#: src/app/main/data/workspace/tokens/library_edit.cljs:548 msgid "errors.token-set-exists-on-drop" msgstr "無法完成拖放,此路徑已有同名集合。" -#: src/app/main/data/workspace/tokens/library_edit.cljs:274, src/app/main/data/workspace/tokens/library_edit.cljs:293, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 +#: src/app/main/data/workspace/tokens/library_edit.cljs:298, src/app/main/data/workspace/tokens/library_edit.cljs:318, ../common/src/app/common/files/tokens.cljc:318, ../common/src/app/common/files/tokens.cljc:332 msgid "errors.token-theme-already-exists" msgstr "已有同名的主題選項" @@ -1679,11 +1651,11 @@ msgstr "此主題參照了不存在的集合:%s" msgid "errors.tokens.circular-reference" msgstr "Token 存在循環參照" -#: src/app/main/data/workspace/tokens/errors.cljs:116, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:245 +#: src/app/main/data/workspace/tokens/errors.cljs:132, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:316 msgid "errors.tokens.composite-line-height-needs-font-size" msgstr "行高取決於字型大小。請新增字型大小以取得解析後的值。" -#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:350, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:436 +#: src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:347, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:442 msgid "errors.tokens.empty-field" msgstr "此欄位不可空白" @@ -1703,11 +1675,11 @@ msgstr "匯入錯誤:" msgid "errors.tokens.invalid-color" msgstr "無效的色彩值:%s" -#: src/app/main/data/workspace/tokens/errors.cljs:108 +#: src/app/main/data/workspace/tokens/errors.cljs:124 msgid "errors.tokens.invalid-font-family-token-value" msgstr "無效的 token 值:只能參照 font-family token" -#: src/app/main/data/workspace/tokens/errors.cljs:104, ../common/src/app/common/files/tokens.cljc:79 +#: src/app/main/data/workspace/tokens/errors.cljs:120, ../common/src/app/common/files/tokens.cljc:79 msgid "errors.tokens.invalid-font-weight-token-value" msgstr "無效的字重值:請使用數值(100-950)或標準名稱(thin、light、regular、bold 等),後方可加上 'Italic'" @@ -1725,35 +1697,35 @@ msgstr "" "\"%s\" 不是有效的 token 名稱。\n" "Token 名稱只能包含以 . 字元分隔的字母與數字,且不得以 $ 符號開頭。" -#: src/app/main/data/workspace/tokens/errors.cljs:120 +#: src/app/main/data/workspace/tokens/errors.cljs:136 msgid "errors.tokens.invalid-shadow-type-token-value" msgstr "無效的陰影類型:僅接受 'innerShadow' 或 'dropShadow'" -#: src/app/main/data/workspace/tokens/errors.cljs:96 +#: src/app/main/data/workspace/tokens/errors.cljs:112 msgid "errors.tokens.invalid-text-case-token-value" msgstr "無效的 token 值:僅接受 none、Uppercase、Lowercase 或 Capitalize" -#: src/app/main/data/workspace/tokens/errors.cljs:100 +#: src/app/main/data/workspace/tokens/errors.cljs:116 msgid "errors.tokens.invalid-text-decoration-token-value" msgstr "無效的 token 值:僅接受 none、underline 和 strike-through" -#: src/app/main/data/workspace/tokens/errors.cljs:132 +#: src/app/main/data/workspace/tokens/errors.cljs:148 msgid "errors.tokens.invalid-token-value-shadow" msgstr "無效的值:必須參照複合陰影 token。" -#: src/app/main/data/workspace/tokens/errors.cljs:112 +#: src/app/main/data/workspace/tokens/errors.cljs:128 msgid "errors.tokens.invalid-token-value-typography" msgstr "無效的值:必須參照複合文字樣式 token。" -#: src/app/main/data/workspace/tokens/errors.cljs:76, src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/errors.cljs:92, ../common/src/app/common/files/tokens.cljc:42 +#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/errors.cljs:108, ../common/src/app/common/files/tokens.cljc:42 msgid "errors.tokens.invalid-value" msgstr "無效的 token 值:%s" -#: src/app/main/data/workspace/tokens/errors.cljs:72 +#: src/app/main/data/workspace/tokens/errors.cljs:88 msgid "errors.tokens.missing-references" msgstr "缺少 token 參照: " -#: src/app/main/data/workspace/tokens/errors.cljs:68, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 +#: src/app/main/data/workspace/tokens/errors.cljs:84, src/app/main/ui/workspace/tokens/management/forms/controls/utils.cljs:126 msgid "errors.tokens.name-collision" msgstr "群組名稱 %s 與另一個啟用中的集合內的同名 token 衝突。" @@ -1765,31 +1737,31 @@ msgstr "此檔案中找不到任何 tokens、集合或主題。" msgid "errors.tokens.number-too-large" msgstr "無效的 token 值。解析後的值過大:%s" -#: src/app/main/data/workspace/tokens/errors.cljs:88, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 +#: src/app/main/data/workspace/tokens/errors.cljs:104, src/app/main/data/workspace/tokens/warnings.cljs:15, ../common/src/app/common/files/tokens.cljc:65 msgid "errors.tokens.opacity-range" msgstr "不透明度必須介於 0 到 100% 或 0 到 1 之間(例如 50% 或 0.5)。" -#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:305, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:237, ../common/src/app/common/files/tokens.cljc:235 +#: src/app/main/data/workspace/tokens/errors.cljs:46, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:373, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:308, ../common/src/app/common/files/tokens.cljc:235 msgid "errors.tokens.self-reference" msgstr "Token 參照了自身" -#: src/app/main/data/workspace/tokens/errors.cljs:124 +#: src/app/main/data/workspace/tokens/errors.cljs:140 msgid "errors.tokens.shadow-blur-range" msgstr "陰影模糊值必須大於或等於 0。" -#: src/app/main/data/workspace/tokens/errors.cljs:128 +#: src/app/main/data/workspace/tokens/errors.cljs:144 msgid "errors.tokens.shadow-spread-range" msgstr "陰影擴張值必須大於或等於 0。" -#: src/app/main/data/workspace/tokens/errors.cljs:92, src/app/main/data/workspace/tokens/warnings.cljs:19 +#: src/app/main/data/workspace/tokens/errors.cljs:108, src/app/main/data/workspace/tokens/warnings.cljs:19 msgid "errors.tokens.stroke-width-range" msgstr "筆畫寬度必須大於或等於 0。" -#: src/app/main/data/workspace/tokens/errors.cljs:84, ../common/src/app/common/files/tokens.cljc:52 +#: src/app/main/data/workspace/tokens/errors.cljs:100, ../common/src/app/common/files/tokens.cljc:52 msgid "errors.tokens.value-with-percent" msgstr "數值無效:不可使用 %。" -#: src/app/main/data/workspace/tokens/errors.cljs:80 +#: src/app/main/data/workspace/tokens/errors.cljs:96 msgid "errors.tokens.value-with-units" msgstr "數值無效:不可包含單位。" @@ -1797,19 +1769,19 @@ msgstr "數值無效:不可包含單位。" msgid "errors.unexpected-error" msgstr "發生非預期的錯誤。" -#: src/app/main/ui/auth/verify_token.cljs:95 +#: src/app/main/ui/auth/verify_token.cljs:68 msgid "errors.unexpected-token" msgstr "未知的 token" -#: src/app/main/errors.cljs:723 +#: src/app/main/errors.cljs:894 msgid "errors.version-already-locked" msgstr "此版本已鎖定" -#: src/app/main/errors.cljs:699 +#: src/app/main/errors.cljs:861 msgid "errors.version-locked" msgstr "此版本已鎖定,其他人無法刪除" -#: src/app/main/errors.cljs:827 +#: src/app/main/errors.cljs:998 msgid "errors.version-not-supported" msgstr "檔案版本不相容" @@ -1842,39 +1814,39 @@ msgstr "至少包含 1 個大寫字母" msgid "errors.weak-password.too-short" msgstr "至少 8 個字元" -#: src/app/main/ui/static.cljs:380 +#: src/app/main/ui/static.cljs:385 msgid "errors.webgl-context-lost.desc-message" msgstr "WebGL 已停止運作。請重新載入頁面以重設 WebGL" -#: src/app/main/ui/static.cljs:379 +#: src/app/main/ui/static.cljs:384 msgid "errors.webgl-context-lost.main-message" msgstr "糟糕!畫布的繪圖環境已遺失" -#: src/app/main/ui/dashboard/team.cljs:1306, src/app/main/ui/dashboard/team.cljs:1312 +#: src/app/main/ui/dashboard/team.cljs:1307, src/app/main/ui/dashboard/team.cljs:1313 msgid "errors.webhooks.connection" msgstr "連線錯誤,無法連上此網址" -#: src/app/main/ui/dashboard/team.cljs:1297 +#: src/app/main/ui/dashboard/team.cljs:1298 msgid "errors.webhooks.invalid-uri" msgstr "網址未通過驗證。" -#: src/app/main/ui/dashboard/team.cljs:1490 +#: src/app/main/ui/dashboard/team.cljs:1499 msgid "errors.webhooks.last-delivery" msgstr "上次傳送失敗。" -#: src/app/main/ui/dashboard/team.cljs:1300, src/app/main/ui/dashboard/team.cljs:1493 +#: src/app/main/ui/dashboard/team.cljs:1301, src/app/main/ui/dashboard/team.cljs:1502 msgid "errors.webhooks.ssl-validation" msgstr "SSL 驗證發生錯誤。" -#: src/app/main/ui/dashboard/team.cljs:1303 +#: src/app/main/ui/dashboard/team.cljs:1304 msgid "errors.webhooks.timeout" msgstr "逾時" -#: src/app/main/ui/dashboard/team.cljs:1315, src/app/main/ui/dashboard/team.cljs:1499 +#: src/app/main/ui/dashboard/team.cljs:1316, src/app/main/ui/dashboard/team.cljs:1508 msgid "errors.webhooks.unexpected" msgstr "驗證時發生非預期的錯誤" -#: src/app/main/ui/dashboard/team.cljs:1309, src/app/main/ui/dashboard/team.cljs:1496 +#: src/app/main/ui/dashboard/team.cljs:1310, src/app/main/ui/dashboard/team.cljs:1505 msgid "errors.webhooks.unexpected-status" msgstr "非預期的狀態 %s" @@ -1894,15 +1866,15 @@ msgstr "說明" msgid "feedback.description-placeholder" msgstr "請說明您提供意見回饋的原因" -#: src/app/main/ui/settings/feedback.cljs:151 +#: src/app/main/ui/settings/feedback.cljs:153 msgid "feedback.discourse-subtitle1" msgstr "很高興您加入我們。如果需要協助,請先搜尋相關內容,再發文提問。" -#: src/app/main/ui/settings/feedback.cljs:150 +#: src/app/main/ui/settings/feedback.cljs:152 msgid "feedback.discourse-title" msgstr "Penpot 社群" -#: src/app/main/ui/settings/feedback.cljs:144 +#: src/app/main/ui/settings/feedback.cljs:146 msgid "feedback.other-ways-contact" msgstr "其他聯絡方式" @@ -1922,11 +1894,11 @@ msgstr "請說明來信原因,並註明是遇到問題、提出想法,還是 msgid "feedback.title-contact-us" msgstr "聯絡我們" -#: src/app/main/ui/settings/feedback.cljs:157 +#: src/app/main/ui/settings/feedback.cljs:159 msgid "feedback.twitter-subtitle1" msgstr "為您解答技術問題。" -#: src/app/main/ui/settings/feedback.cljs:156 +#: src/app/main/ui/settings/feedback.cljs:158 msgid "feedback.twitter-title" msgstr "X 支援帳號" @@ -1946,39 +1918,35 @@ msgstr "想法" msgid "feedback.type.issue" msgstr "問題" -#: src/app/main/ui/exports/files.cljs:140 -msgid "files-download-modal.title" -msgstr "下載檔案" - -#: src/app/main/ui/exports/files.cljs:183 +#: src/app/main/ui/exports/files.cljs:431 msgid "files-export-modal.options.detach-libraries.message" msgstr "檔案將不包含已連結資料庫中的資產。" -#: src/app/main/ui/exports/files.cljs:177 +#: src/app/main/ui/exports/files.cljs:425 msgid "files-export-modal.options.detach-libraries.title" msgstr "將資產視為基本物件" -#: src/app/main/ui/exports/files.cljs:181 +#: src/app/main/ui/exports/files.cljs:429 msgid "files-export-modal.options.include-libraries.message" msgstr "匯出內容將包含檔案及其連結的資料庫,並保留彼此的連結關係。 " -#: src/app/main/ui/exports/files.cljs:175 +#: src/app/main/ui/exports/files.cljs:423 msgid "files-export-modal.options.include-libraries.title" msgstr "匯出檔案 + 資料庫" -#: src/app/main/ui/exports/files.cljs:184 +#: src/app/main/ui/exports/files.cljs:432 msgid "files-export-modal.options.link-later.message" msgstr "匯入時,你可以連結名稱相符的現有資料庫。" -#: src/app/main/ui/exports/files.cljs:178 +#: src/app/main/ui/exports/files.cljs:426 msgid "files-export-modal.options.link-later.title" msgstr "匯入時連結名稱相符的資料庫" -#: src/app/main/ui/exports/files.cljs:182 +#: src/app/main/ui/exports/files.cljs:430 msgid "files-export-modal.options.merge-libraries.message" msgstr "匯出檔案時,資料庫資產將合併至檔案內的資料庫。" -#: src/app/main/ui/exports/files.cljs:176 +#: src/app/main/ui/exports/files.cljs:424 msgid "files-export-modal.options.merge-libraries.title" msgstr "將資料庫資產嵌入檔案" @@ -1986,7 +1954,7 @@ msgstr "將資料庫資產嵌入檔案" msgid "generic.error" msgstr "發生錯誤" -#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:161 +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:171 msgid "import-shortcuts.apply" msgstr "套用" @@ -2034,7 +2002,7 @@ msgstr "形狀填充圖片預覽" msgid "inspect.attributes.shadow" msgstr "陰影" -#: src/app/main/ui/inspect/attributes/geometry.cljs:50, src/app/main/ui/inspect/styles/style_box.cljs:27 +#: src/app/main/ui/inspect/attributes/geometry.cljs:46, src/app/main/ui/inspect/styles/style_box.cljs:27 msgid "inspect.attributes.size" msgstr "大小與位置" @@ -2042,43 +2010,31 @@ msgstr "大小與位置" msgid "inspect.attributes.stroke" msgstr "筆畫" -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.center" -msgstr "置中" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.inner" -msgstr "內側" - -#, permanent, unused -msgid "inspect.attributes.stroke.alignment.outer" -msgstr "外側" - #: src/app/main/ui/inspect/attributes/text.cljs:53, src/app/main/ui/inspect/attributes/text.cljs:158 msgid "inspect.attributes.typography" msgstr "文字樣式" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:588 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:632 msgid "inspect.attributes.typography.font-family" msgstr "字型家族" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:612, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:618 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:662 msgid "inspect.attributes.typography.font-size" msgstr "字型大小" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:629 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:673 msgid "inspect.attributes.typography.font-style" msgstr "字型樣式" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:685, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:696 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:729, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:740 msgid "inspect.attributes.typography.letter-spacing" msgstr "字距" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:677 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:711, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:721 msgid "inspect.attributes.typography.line-height" msgstr "行高" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:594 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:638 msgid "inspect.attributes.typography.mixed-font-family" msgstr "混合字型家族" @@ -2090,11 +2046,11 @@ msgstr "無" msgid "inspect.attributes.typography.text-decoration.underline" msgstr "底線" -#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:726 +#: src/app/main/ui/inspect/attributes/text.cljs:128, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:770 msgid "inspect.attributes.typography.text-transform.capitalize" msgstr "每個單字首字母大寫" -#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:730 +#: src/app/main/ui/inspect/attributes/text.cljs:126, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:774 msgid "inspect.attributes.typography.text-transform.lowercase" msgstr "全部小寫" @@ -2106,7 +2062,7 @@ msgstr "無" msgid "inspect.attributes.typography.text-transform.unset" msgstr "取消設定" -#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:720 +#: src/app/main/ui/inspect/attributes/text.cljs:129, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:764 msgid "inspect.attributes.typography.text-transform.uppercase" msgstr "全部大寫" @@ -2226,23 +2182,23 @@ msgstr "解析後的值:" msgid "inspect.tabs.styles.variants-panel" msgstr "變體屬性" -#: src/app/main/ui/settings/integrations.cljs:616 +#: src/app/main/ui/settings/integrations.cljs:612 msgid "integrations.access-tokens.create" msgstr "建立新的存取權杖" -#: src/app/main/ui/settings/integrations.cljs:632 +#: src/app/main/ui/settings/integrations.cljs:628 msgid "integrations.access-tokens.empty.add-one" msgstr "按一下「建立新的存取權杖」按鈕即可產生權杖。" -#: src/app/main/ui/settings/integrations.cljs:631 +#: src/app/main/ui/settings/integrations.cljs:627 msgid "integrations.access-tokens.empty.no-access-tokens" msgstr "您目前沒有任何權杖。" -#: src/app/main/ui/settings/integrations.cljs:606 +#: src/app/main/ui/settings/integrations.cljs:602 msgid "integrations.access-tokens.personal" msgstr "個人存取權杖" -#: src/app/main/ui/settings/integrations.cljs:611 +#: src/app/main/ui/settings/integrations.cljs:607 msgid "integrations.access-tokens.personal.description" msgstr "個人存取權杖可替代帳號與密碼驗證,讓應用程式存取 Penpot 內部 API" @@ -2322,47 +2278,47 @@ msgstr "MCP 金鑰將於 %s 到期" msgid "integrations.mcp-key.will-not-expire" msgstr "MCP 金鑰沒有到期日" -#: src/app/main/ui/settings/integrations.cljs:498 +#: src/app/main/ui/settings/integrations.cljs:494 msgid "integrations.mcp-server.description" msgstr "Penpot MCP 伺服器可讓 MCP 用戶端直接與 Penpot 設計檔案互動。" -#: src/app/main/ui/settings/integrations.cljs:581 +#: src/app/main/ui/settings/integrations.cljs:577 msgid "integrations.mcp-server.mcp-keys.help" msgstr "如何設定 MCP 用戶端" -#: src/app/main/ui/settings/integrations.cljs:568 +#: src/app/main/ui/settings/integrations.cljs:564 msgid "integrations.mcp-server.mcp-keys.info" msgstr "設定 MCP 用戶端時,需要使用此伺服器網址才能連線至 Penpot MCP 伺服器。" -#: src/app/main/ui/settings/integrations.cljs:548 +#: src/app/main/ui/settings/integrations.cljs:544 msgid "integrations.mcp-server.mcp-keys.regenerate" msgstr "重新產生 MCP 金鑰" -#: src/app/main/ui/settings/integrations.cljs:541 +#: src/app/main/ui/settings/integrations.cljs:537 msgid "integrations.mcp-server.mcp-keys.title" msgstr "MCP 金鑰" -#: src/app/main/ui/settings/integrations.cljs:549 +#: src/app/main/ui/settings/integrations.cljs:545 msgid "integrations.mcp-server.mcp-keys.tootip" msgstr "設定 MCP 用戶端時需要 MCP 金鑰" -#: src/app/main/ui/settings/integrations.cljs:504 +#: src/app/main/ui/settings/integrations.cljs:500 msgid "integrations.mcp-server.status" msgstr "狀態" -#: src/app/main/ui/settings/integrations.cljs:524 +#: src/app/main/ui/settings/integrations.cljs:520 msgid "integrations.mcp-server.status.disabled" msgstr "已停用" -#: src/app/main/ui/settings/integrations.cljs:523 +#: src/app/main/ui/settings/integrations.cljs:519 msgid "integrations.mcp-server.status.enabled" msgstr "已啟用" -#: src/app/main/ui/settings/integrations.cljs:514 +#: src/app/main/ui/settings/integrations.cljs:510 msgid "integrations.mcp-server.status.expired.0" msgstr "用於連線至 MCP 伺服器的 MCP 金鑰已過期,因此無法建立連線。" -#: src/app/main/ui/settings/integrations.cljs:519 +#: src/app/main/ui/settings/integrations.cljs:515 msgid "integrations.mcp-server.status.expired.1" msgstr "請重新產生 MCP 金鑰,並使用新金鑰更新用戶端設定。" @@ -2370,10 +2326,6 @@ msgstr "請重新產生 MCP 金鑰,並使用新金鑰更新用戶端設定。" msgid "integrations.mcp-server.title" msgstr "MCP 伺服器" -#: src/app/main/ui/settings/integrations.cljs:493 -msgid "integrations.mcp-server.title.beta" -msgstr "測試版" - #: src/app/main/ui/settings/integrations.cljs:200 msgid "integrations.name.label" msgstr "名稱" @@ -2422,7 +2374,7 @@ msgstr "重新產生 MCP 金鑰" msgid "integrations.regenerate-mcp-key.title.created" msgstr "已重新產生 MCP 金鑰" -#: src/app/main/ui/settings/integrations.cljs:655 +#: src/app/main/ui/settings/integrations.cljs:651 msgid "integrations.title" msgstr "整合" @@ -2438,47 +2390,47 @@ msgstr "token 將於 %s 到期" msgid "integrations.token.will-not-expire" msgstr "token 沒有到期日" -#: src/app/main/ui/dashboard/comments.cljs:94 +#: src/app/main/ui/dashboard/comments.cljs:90 msgid "label.mark-all-as-read" msgstr "全部標示為已讀" -#: src/app/main/ui/settings/shortcuts.cljs:473, src/app/main/ui/settings/sidebar.cljs:112, src/app/main/ui/workspace/main_menu.cljs:219 +#: src/app/main/ui/settings/shortcuts.cljs:524, src/app/main/ui/settings/sidebar.cljs:108, src/app/main/ui/workspace/main_menu.cljs:220 msgid "label.shortcuts" msgstr "快速鍵" -#: src/app/main/ui/dashboard/sidebar.cljs:740 +#: src/app/main/ui/dashboard/sidebar.cljs:731 msgid "labels.about-penpot" msgstr "關於 Penpot" -#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:671, src/app/main/ui/dashboard/import.cljs:677 +#: src/app/main/data/common.cljs:92, src/app/main/data/team.cljs:193, src/app/main/data/team.cljs:650, src/app/main/ui/dashboard/import.cljs:776, src/app/main/ui/dashboard/import.cljs:782 msgid "labels.accept" msgstr "接受" -#: src/app/main/ui/dashboard/team.cljs:1511 +#: src/app/main/ui/dashboard/team.cljs:1522 msgid "labels.active" msgstr "已啟用" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.add" msgstr "新增" -#: src/app/main/ui/dashboard/fonts.cljs:203 +#: src/app/main/ui/dashboard/fonts.cljs:217 msgid "labels.add-custom-font" msgstr "新增自訂字型" -#: src/app/main/ui/workspace/libraries.cljs:255 +#: src/app/main/ui/workspace/libraries.cljs:307 msgid "labels.adding" msgstr "正在新增..." -#: src/app/main/ui/dashboard/team.cljs:166, src/app/main/ui/dashboard/team.cljs:404, src/app/main/ui/dashboard/team.cljs:433, src/app/main/ui/dashboard/team.cljs:676, src/app/main/ui/dashboard/team.cljs:706, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:72 +#: src/app/main/ui/dashboard/team.cljs:150, src/app/main/ui/dashboard/team.cljs:391, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:667, src/app/main/ui/dashboard/team.cljs:701, src/app/main/ui/dashboard/team.cljs:887, src/app/main/ui/onboarding/team_choice.cljs:72 msgid "labels.admin" msgstr "管理員" -#: src/app/main/ui/onboarding/questions.cljs:158 +#: src/app/main/ui/onboarding/questions.cljs:160 msgid "labels.adobe-xd" msgstr "Adobe XD 軟體" -#: src/app/main/ui/settings/shortcuts.cljs:294, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:127 +#: src/app/main/ui/settings/shortcuts.cljs:341, src/app/main/ui/workspace/tokens/management/context_menu.cljs:101, src/app/main/ui/workspace/tokens/management/context_menu.cljs:139, src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 msgid "labels.all" msgstr "全部" @@ -2486,15 +2438,15 @@ msgstr "全部" msgid "labels.and" msgstr "及" -#: src/app/main/ui/dashboard/import.cljs:728 +#: src/app/main/ui/dashboard/import.cljs:833 msgid "labels.back" msgstr "返回" -#: src/app/main/ui/static.cljs:331 +#: src/app/main/ui/static.cljs:336 msgid "labels.bad-gateway.desc-message" msgstr "我們正在進行伺服器的小規模維護,請稍候再試。" -#: src/app/main/ui/static.cljs:330 +#: src/app/main/ui/static.cljs:335 msgid "labels.bad-gateway.main-message" msgstr "閘道錯誤" @@ -2506,23 +2458,23 @@ msgstr "模糊" msgid "labels.blur-effects" msgstr "模糊效果" -#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:518, src/app/main/data/nitrate.cljs:603, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:657, src/app/main/ui/dashboard/team.cljs:902, src/app/main/ui/dashboard/team.cljs:1411, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:199, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:156, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:651, src/app/main/ui/viewer/share_link.cljs:203, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 +#: src/app/main/data/common.cljs:121, src/app/main/data/nitrate.cljs:530, src/app/main/data/nitrate.cljs:615, src/app/main/ui/dashboard/change_owner.cljs:73, src/app/main/ui/dashboard/change_owner.cljs:180, src/app/main/ui/dashboard/fonts.cljs:459, src/app/main/ui/dashboard/import.cljs:757, src/app/main/ui/dashboard/import.cljs:771, src/app/main/ui/dashboard/team.cljs:900, src/app/main/ui/dashboard/team.cljs:1415, src/app/main/ui/delete_shared.cljs:38, src/app/main/ui/exports/assets.cljs:158, src/app/main/ui/exports/files.cljs:447, src/app/main/ui/mcp_authorize.cljs:160, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:166, src/app/main/ui/settings/integrations.cljs:220, src/app/main/ui/settings/restore_shortcuts_modal.cljs:165, src/app/main/ui/shortcuts.cljs:783, src/app/main/ui/viewer/share_link.cljs:207, src/app/main/ui/workspace/sidebar/assets/groups.cljs:181, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:360, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:243 msgid "labels.cancel" msgstr "取消" -#: src/app/main/ui/onboarding/questions.cljs:160 +#: src/app/main/ui/onboarding/questions.cljs:162 msgid "labels.canva" msgstr "油畫" -#: src/app/main/ui/dashboard/sidebar.cljs:553 +#: src/app/main/ui/dashboard/sidebar.cljs:546 msgid "labels.check-for-updates" msgstr "檢查更新" -#: src/app/main/ui/dashboard/sidebar.cljs:552 +#: src/app/main/ui/dashboard/sidebar.cljs:545 msgid "labels.checking-for-updates" msgstr "正在檢查更新..." -#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:101, src/app/main/ui/dashboard/import.cljs:997, src/app/main/ui/dashboard/projects.cljs:97, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/files.cljs:142, src/app/main/ui/exports/files.cljs:225, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:407, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:229, src/app/main/ui/viewer/login.cljs:91, src/app/main/ui/viewer/share_link.cljs:174, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1155, src/app/main/ui/workspace/sidebar/layers.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:825, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 +#: src/app/main/data/common.cljs:98, src/app/main/ui/alert.cljs:63, src/app/main/ui/auth/login_modal.cljs:92, src/app/main/ui/confirm.cljs:78, src/app/main/ui/dashboard/change_owner.cljs:52, src/app/main/ui/dashboard/change_owner.cljs:156, src/app/main/ui/dashboard/check_updates.cljs:217, src/app/main/ui/dashboard/check_updates.cljs:256, src/app/main/ui/dashboard/check_updates.cljs:272, src/app/main/ui/dashboard/check_updates.cljs:311, src/app/main/ui/dashboard/comments.cljs:97, src/app/main/ui/dashboard/import.cljs:1132, src/app/main/ui/dashboard/projects.cljs:105, src/app/main/ui/dashboard/team.cljs:312, src/app/main/ui/dashboard/team.cljs:872, src/app/main/ui/dashboard/team.cljs:1003, src/app/main/ui/dashboard/team.cljs:1386, src/app/main/ui/dashboard/team_form.cljs:124, src/app/main/ui/dashboard/team_form.cljs:171, src/app/main/ui/delete_shared.cljs:104, src/app/main/ui/ds/layout/modal.cljs:96, src/app/main/ui/ds/product/panel_title.cljs:32, src/app/main/ui/exports/assets.cljs:81, src/app/main/ui/exports/assets.cljs:341, src/app/main/ui/exports/files.cljs:383, src/app/main/ui/nitrate/nitrate_code_activation_modal.cljs:82, src/app/main/ui/settings/change_email.cljs:94, src/app/main/ui/settings/delete_account.cljs:68, src/app/main/ui/settings/import_shortcuts_diff_modal.cljs:93, src/app/main/ui/settings/integrations.cljs:128, src/app/main/ui/settings/integrations.cljs:247, src/app/main/ui/settings/integrations.cljs:287, src/app/main/ui/settings/integrations.cljs:329, src/app/main/ui/settings/restore_shortcuts_modal.cljs:85, src/app/main/ui/settings/subscription.cljs:269, src/app/main/ui/settings/subscription.cljs:391, src/app/main/ui/settings/subscription.cljs:414, src/app/main/ui/settings/subscription.cljs:847, src/app/main/ui/settings/subscription.cljs:903, src/app/main/ui/settings/subscription.cljs:958, src/app/main/ui/static.cljs:159, src/app/main/ui/static.cljs:234, src/app/main/ui/viewer/share_link.cljs:177, src/app/main/ui/viewer/thumbnails.cljs:101, src/app/main/ui/workspace/components_debugger.cljs:1102, src/app/main/ui/workspace/libraries.cljs:1179, src/app/main/ui/workspace/nudge.cljs:50, src/app/main/ui/workspace/plugins.cljs:213, src/app/main/ui/workspace/plugins.cljs:421, src/app/main/ui/workspace/plugins.cljs:487, src/app/main/ui/workspace/plugins.cljs:544, src/app/main/ui/workspace/sidebar/assets/groups.cljs:164, src/app/main/ui/workspace/sidebar/layers.cljs:627, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:869, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:890, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:43, src/app/main/ui/workspace/sidebar.cljs:265, src/app/main/ui/workspace/tokens/export.cljs:25, src/app/main/ui/workspace/tokens/import.cljs:19, src/app/main/ui/workspace/tokens/import_from_library.cljs:63, src/app/main/ui/workspace/tokens/management/forms/modals.cljs:99, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:114, src/app/main/ui/workspace/tokens/remapping_modal.cljs:93, src/app/main/ui/workspace/tokens/settings/menu.cljs:77, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:62, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:145, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:476, src/app/main/ui/workspace/webgl_unavailable_modal.cljs:56 msgid "labels.close" msgstr "關閉" @@ -2530,7 +2482,7 @@ msgstr "關閉" msgid "labels.code" msgstr "程式碼" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:184 +#: src/app/main/ui/viewer/thumbnails.cljs:95, src/app/main/ui/workspace/tokens/sets/lists.cljs:184 msgid "labels.collapse" msgstr "收合" @@ -2542,15 +2494,15 @@ msgstr "色彩" msgid "labels.comment" msgstr "留言" -#: src/app/main/ui/viewer/comments.cljs:72, src/app/main/ui/workspace/comments.cljs:130 +#: src/app/main/ui/viewer/comments.cljs:76, src/app/main/ui/workspace/comments.cljs:133 msgid "labels.comments" msgstr "留言" -#: src/app/main/ui/dashboard/sidebar.cljs:479, src/app/main/ui/workspace/main_menu.cljs:165 +#: src/app/main/ui/dashboard/sidebar.cljs:472, src/app/main/ui/workspace/main_menu.cljs:166 msgid "labels.community" msgstr "社群" -#: src/app/main/ui/dashboard/sidebar.cljs:726 +#: src/app/main/ui/dashboard/sidebar.cljs:717 msgid "labels.community-contributions" msgstr "社群與貢獻" @@ -2562,19 +2514,19 @@ msgstr "計算結果" msgid "labels.confirm-password" msgstr "確認密碼" -#: src/app/main/ui/static.cljs:433 +#: src/app/main/ui/static.cljs:441 msgid "labels.contact-support" msgstr "聯絡支援" -#: src/app/main/ui/settings/sidebar.cljs:147, src/app/main/ui/settings/subscription.cljs:944 +#: src/app/main/ui/settings/sidebar.cljs:145, src/app/main/ui/settings/subscription.cljs:973 msgid "labels.contact-us" msgstr "聯絡我們" -#: src/app/main/ui/auth/login.cljs:208, src/app/main/ui/dashboard/deleted.cljs:46, src/app/main/ui/dashboard/deleted.cljs:281, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:664, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/exports/files.cljs:204, src/app/main/ui/settings/subscription.cljs:333, src/app/main/ui/settings/subscription.cljs:367, src/app/main/ui/settings/subscription.cljs:896 +#: src/app/main/ui/auth/login.cljs:209, src/app/main/ui/dashboard/deleted.cljs:47, src/app/main/ui/dashboard/deleted.cljs:287, src/app/main/ui/dashboard/file_menu.cljs:235, src/app/main/ui/dashboard/import.cljs:764, src/app/main/ui/dashboard/team.cljs:907, src/app/main/ui/exports/files.cljs:452, src/app/main/ui/settings/subscription.cljs:337, src/app/main/ui/settings/subscription.cljs:371, src/app/main/ui/settings/subscription.cljs:923 msgid "labels.continue" msgstr "繼續" -#: src/app/main/ui/viewer/login.cljs:88 +#: src/app/main/ui/auth/login_modal.cljs:89 msgid "labels.continue-with-penpot" msgstr "你可以使用 Penpot 帳號繼續" @@ -2586,23 +2538,23 @@ msgstr "複製" msgid "labels.copy-color" msgstr "複製色彩" -#: src/app/main/ui/dashboard/team.cljs:765 +#: src/app/main/ui/dashboard/team.cljs:760 msgid "labels.copy-invitation-link" msgstr "複製連結" -#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:352, src/app/main/ui/static.cljs:373 +#: src/app/main/ui/static.cljs:72, src/app/main/ui/static.cljs:357, src/app/main/ui/static.cljs:378 msgid "labels.copyright-period" msgstr "Kaleidos © 2019 至今" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:202 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:206 msgid "labels.create" msgstr "建立" -#: src/app/main/ui/dashboard/team_form.cljs:119, src/app/main/ui/dashboard/team_form.cljs:139, src/app/main/ui/dashboard/team_form.cljs:151 +#: src/app/main/ui/dashboard/team_form.cljs:120, src/app/main/ui/dashboard/team_form.cljs:143, src/app/main/ui/dashboard/team_form.cljs:155 msgid "labels.create-team" msgstr "建立新團隊" -#: src/app/main/ui/dashboard/team_form.cljs:131 +#: src/app/main/ui/dashboard/team_form.cljs:135 msgid "labels.create-team.placeholder" msgstr "輸入新團隊名稱" @@ -2610,7 +2562,7 @@ msgstr "輸入新團隊名稱" msgid "labels.current" msgstr "目前" -#: src/app/main/ui/settings/sidebar.cljs:86 +#: src/app/main/ui/settings/sidebar.cljs:82 msgid "labels.dashboard" msgstr "控制台" @@ -2618,7 +2570,7 @@ msgstr "控制台" msgid "labels.default" msgstr "預設" -#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:286, src/app/main/ui/dashboard/fonts.cljs:363, src/app/main/ui/dashboard/fonts.cljs:397, src/app/main/ui/dashboard/import.cljs:332, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1445, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:117, src/app/main/ui/settings/profile.cljs:128, src/app/main/ui/settings/profile.cljs:129, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 +#: src/app/main/ui/dashboard/file_menu.cljs:335, src/app/main/ui/dashboard/fonts.cljs:303, src/app/main/ui/dashboard/fonts.cljs:380, src/app/main/ui/dashboard/fonts.cljs:414, src/app/main/ui/dashboard/import.cljs:319, src/app/main/ui/dashboard/project_menu.cljs:116, src/app/main/ui/dashboard/team.cljs:1454, src/app/main/ui/settings/integrations.cljs:376, src/app/main/ui/settings/profile.cljs:123, src/app/main/ui/settings/profile.cljs:137, src/app/main/ui/settings/profile.cljs:138, src/app/main/ui/workspace/sidebar/assets/common.cljs:231, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:228, src/app/main/ui/workspace/sidebar/versions.cljs:257, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:353, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:111, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:66, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:394 msgid "labels.delete" msgstr "刪除" @@ -2630,7 +2582,7 @@ msgstr "刪除留言" msgid "labels.delete-comment-thread" msgstr "刪除討論串" -#: src/app/main/ui/dashboard/team.cljs:1216 +#: src/app/main/ui/dashboard/team.cljs:1217 msgid "labels.delete-invitation" msgstr "刪除邀請" @@ -2638,39 +2590,35 @@ msgstr "刪除邀請" msgid "labels.delete-multi-files" msgstr "刪除 %s 個檔案" -#: src/app/main/ui/settings/profile.cljs:116 +#: src/app/main/ui/settings/profile.cljs:122 msgid "labels.delete-profile-photo.message" msgstr "確定要刪除你的個人照片嗎?" -#: src/app/main/ui/settings/profile.cljs:115 +#: src/app/main/ui/settings/profile.cljs:121 msgid "labels.delete-profile-photo.title" msgstr "刪除個人照片" -#: src/app/main/ui/dashboard/deleted.cljs:219 +#: src/app/main/ui/dashboard/deleted.cljs:225 msgid "labels.deleted" msgstr "已刪除" -#: src/app/main/ui/onboarding/questions.cljs:83 +#: src/app/main/ui/onboarding/questions.cljs:85 msgid "labels.developer" msgstr "開發" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:210 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:214 msgid "labels.discard" msgstr "捨棄" -#: src/app/main/errors.cljs:484, src/app/main/ui/settings/feedback.cljs:135, src/app/main/ui/static.cljs:427 +#: src/app/main/errors.cljs:559, src/app/main/ui/settings/feedback.cljs:137, src/app/main/ui/static.cljs:435 msgid "labels.download" msgstr "下載 %s" -#: src/app/main/ui/dashboard/fonts.cljs:283 +#: src/app/main/ui/dashboard/fonts.cljs:300 msgid "labels.download-simple" msgstr "下載" -#: src/app/main/ui/exports/files.cljs:217 -msgid "labels.downloading-file" -msgstr "正在下載檔案…" - -#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:86, src/app/main/ui/dashboard/files.cljs:207, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:228, src/app/main/ui/dashboard/sidebar.cljs:362 +#: src/app/main/ui/dashboard/file_menu.cljs:28, src/app/main/ui/dashboard/files.cljs:92, src/app/main/ui/dashboard/files.cljs:217, src/app/main/ui/dashboard/projects.cljs:234, src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/sidebar.cljs:355 msgid "labels.drafts" msgstr "草稿" @@ -2678,19 +2626,19 @@ msgstr "草稿" msgid "labels.duplicate" msgstr "建立複本" -#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:280, src/app/main/ui/dashboard/import.cljs:326, src/app/main/ui/dashboard/team.cljs:1443, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:217, src/app/main/ui/workspace/tokens/themes.cljs:52 +#: src/app/main/ui/comments.cljs:1043, src/app/main/ui/dashboard/fonts.cljs:297, src/app/main/ui/dashboard/import.cljs:313, src/app/main/ui/dashboard/team.cljs:1452, src/app/main/ui/viewer/header.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:222, src/app/main/ui/workspace/tokens/themes.cljs:55 msgid "labels.edit" msgstr "編輯" -#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:405, src/app/main/ui/dashboard/team.cljs:430, src/app/main/ui/dashboard/team.cljs:677, src/app/main/ui/dashboard/team.cljs:710, src/app/main/ui/dashboard/team.cljs:890, src/app/main/ui/onboarding/team_choice.cljs:71 +#: src/app/main/ui/dashboard/team.cljs:148, src/app/main/ui/dashboard/team.cljs:392, src/app/main/ui/dashboard/team.cljs:418, src/app/main/ui/dashboard/team.cljs:668, src/app/main/ui/dashboard/team.cljs:705, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/onboarding/team_choice.cljs:71 msgid "labels.editor" msgstr "編輯者" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:295 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:302 msgid "labels.empty" msgstr "空白" -#: src/app/main/ui/dashboard/import.cljs:353, src/app/worker/import.cljs:204, src/app/worker/import.cljs:245 +#: src/app/main/data/imports.cljs:57, src/app/main/data/imports.cljs:392, src/app/main/ui/dashboard/import.cljs:344, src/app/main/ui/exports/files.cljs:212 msgid "labels.error" msgstr "錯誤" @@ -2698,11 +2646,11 @@ msgstr "錯誤" msgid "labels.exit" msgstr "離開" -#: src/app/main/ui/dashboard/team.cljs:782 +#: src/app/main/ui/dashboard/team.cljs:777 msgid "labels.expired-invitation" msgstr "已過期" -#: src/app/main/ui/exports/assets.cljs:172, src/app/main/ui/settings/shortcuts.cljs:548, src/app/main/ui/workspace/tokens/sidebar.cljs:145 +#: src/app/main/ui/exports/assets.cljs:165, src/app/main/ui/settings/shortcuts.cljs:599, src/app/main/ui/workspace/tokens/sidebar.cljs:145 msgid "labels.export" msgstr "匯出" @@ -2714,7 +2662,7 @@ msgstr "已停用意見回饋" msgid "labels.feedback-sent" msgstr "已送出意見回饋" -#: src/app/main/ui/onboarding/questions.cljs:154 +#: src/app/main/ui/onboarding/questions.cljs:156 msgid "labels.figma" msgstr "Figma" @@ -2722,63 +2670,63 @@ msgstr "Figma" msgid "labels.fill" msgstr "填充" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1284 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 msgid "labels.flex-layout" msgstr "彈性版面佈局" -#: src/app/main/ui/dashboard/fonts.cljs:472 +#: src/app/main/ui/dashboard/fonts.cljs:501 msgid "labels.font-family" msgstr "字型家族" -#: src/app/main/ui/dashboard/fonts.cljs:473 +#: src/app/main/ui/dashboard/fonts.cljs:502 msgid "labels.font-variants" msgstr "樣式" -#: src/app/main/ui/dashboard/fonts.cljs:74, src/app/main/ui/dashboard/sidebar.cljs:375 +#: src/app/main/ui/dashboard/fonts.cljs:82, src/app/main/ui/dashboard/sidebar.cljs:367 msgid "labels.fonts" msgstr "字型" -#: src/app/main/ui/dashboard/sidebar.cljs:473, src/app/main/ui/workspace/main_menu.cljs:201 +#: src/app/main/ui/dashboard/sidebar.cljs:466, src/app/main/ui/workspace/main_menu.cljs:202 msgid "labels.github-repo" msgstr "GitHub 儲存庫" -#: src/app/main/ui/dashboard/sidebar.cljs:447, src/app/main/ui/workspace/main_menu.cljs:230 +#: src/app/main/ui/dashboard/sidebar.cljs:440, src/app/main/ui/workspace/main_menu.cljs:231 msgid "labels.give-feedback" msgstr "提供意見回饋" -#: src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:208, src/app/main/ui/viewer/login.cljs:132 +#: src/app/main/ui/auth/login_modal.cljs:140, src/app/main/ui/auth/recovery_request.cljs:103, src/app/main/ui/auth/register.cljs:365, src/app/main/ui/static.cljs:213 msgid "labels.go-back" msgstr "返回" -#: src/app/main/ui/onboarding/questions.cljs:85 +#: src/app/main/ui/onboarding/questions.cljs:87 msgid "labels.graphic-design" msgstr "平面設計" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1288 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1302 msgid "labels.grid-layout" msgstr "網格版面佈局" -#: src/app/main/ui/dashboard/sidebar.cljs:430, src/app/main/ui/workspace/main_menu.cljs:156, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1353, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1381, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1570 +#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/workspace/main_menu.cljs:157, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1367, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1395, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1584 msgid "labels.help-center" msgstr "說明中心" -#: src/app/main/ui/dashboard/sidebar.cljs:714 +#: src/app/main/ui/dashboard/sidebar.cljs:705 msgid "labels.help-learning" msgstr "說明與學習資源" -#: src/app/main/ui/dashboard/templates.cljs:90 +#: src/app/main/ui/dashboard/templates.cljs:86 msgid "labels.hide" msgstr "隱藏" -#: src/app/main/ui/viewer/comments.cljs:115, src/app/main/ui/workspace/comments.cljs:76 +#: src/app/main/ui/viewer/comments.cljs:119, src/app/main/ui/workspace/comments.cljs:78 msgid "labels.hide-resolved-comments" msgstr "隱藏已解決的留言" -#: src/app/main/ui/settings/shortcuts.cljs:544, src/app/main/ui/workspace/tokens/sidebar.cljs:142 +#: src/app/main/ui/settings/shortcuts.cljs:595, src/app/main/ui/workspace/tokens/sidebar.cljs:142 msgid "labels.import" msgstr "匯入" -#: src/app/main/ui/dashboard/team.cljs:1512 +#: src/app/main/ui/dashboard/team.cljs:1523 msgid "labels.inactive" msgstr "未啟用" @@ -2786,51 +2734,51 @@ msgstr "未啟用" msgid "labels.info" msgstr "資訊" -#: src/app/main/ui/dashboard/fonts.cljs:470 +#: src/app/main/ui/dashboard/fonts.cljs:499 msgid "labels.installed-fonts" msgstr "已安裝的字型" -#: src/app/main/ui/settings/sidebar.cljs:134 +#: src/app/main/ui/settings/sidebar.cljs:130 msgid "labels.integrations" msgstr "整合" -#: src/app/main/ui/static.cljs:423 +#: src/app/main/ui/static.cljs:428 msgid "labels.internal-error.desc-message-first" msgstr "發生錯誤。" -#: src/app/main/ui/static.cljs:424 +#: src/app/main/ui/static.cljs:429 msgid "labels.internal-error.desc-message-second" msgstr "您可以重試此操作,或聯絡技術支援回報錯誤。" -#: src/app/main/ui/static.cljs:420 +#: src/app/main/ui/static.cljs:425 msgid "labels.internal-error.main-message" msgstr "內部錯誤" -#: src/app/main/ui/onboarding/questions.cljs:162 +#: src/app/main/ui/onboarding/questions.cljs:164 msgid "labels.invision" msgstr "InVision 軟體" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:127, src/app/main/ui/dashboard/team.cljs:135, src/app/main/ui/dashboard/team.cljs:1219 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:186, src/app/main/ui/dashboard/team.cljs:103, src/app/main/ui/dashboard/team.cljs:115, src/app/main/ui/dashboard/team.cljs:1220 msgid "labels.invitations" msgstr "邀請" -#: src/app/main/ui/settings/options.cljs:61 +#: src/app/main/ui/settings/options.cljs:64 msgid "labels.language" msgstr "語言" -#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1298 +#: src/app/main/ui/inspect/styles/style_box.cljs:35, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 msgid "labels.layout" msgstr "版面佈局" -#: src/app/main/ui/dashboard/sidebar.cljs:436 +#: src/app/main/ui/dashboard/sidebar.cljs:429 msgid "labels.learning-center" msgstr "學習中心" -#: src/app/main/ui/workspace/main_menu.cljs:192 +#: src/app/main/ui/workspace/main_menu.cljs:193 msgid "labels.libraries-and-templates" msgstr "資料庫與範本" -#: src/app/main/ui/auth/verify_token.cljs:164, src/app/main/ui/dashboard/grid.cljs:126, src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/import.cljs:289, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/exports/files.cljs:69, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:665, src/app/main/ui/viewer.cljs:641, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 +#: src/app/main/ui/auth/verify_token.cljs:137, src/app/main/ui/dashboard/fonts.cljs:519, src/app/main/ui/dashboard/grid.cljs:127, src/app/main/ui/dashboard/grid.cljs:143, src/app/main/ui/dashboard/placeholder.cljs:139, src/app/main/ui/ds/product/loader.cljs:85, src/app/main/ui/mcp_authorize.cljs:140, src/app/main/ui/nitrate/entry.cljs:26, src/app/main/ui/static.cljs:694, src/app/main/ui/viewer.cljs:652, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:263, src/app/main/ui/workspace.cljs:132 msgid "labels.loading" msgstr "載入中…" @@ -2838,7 +2786,7 @@ msgstr "載入中…" msgid "labels.lock" msgstr "鎖定" -#: src/app/main/ui/viewer/header.cljs:208 +#: src/app/main/ui/viewer/header.cljs:220 msgid "labels.log-or-sign" msgstr "登入或註冊" @@ -2846,19 +2794,19 @@ msgstr "登入或註冊" msgid "labels.login" msgstr "登入" -#: src/app/main/ui/dashboard/sidebar.cljs:750 +#: src/app/main/ui/dashboard/sidebar.cljs:742 msgid "labels.logout" msgstr "登出" -#: src/app/main/ui/onboarding/questions.cljs:86 +#: src/app/main/ui/onboarding/questions.cljs:88 msgid "labels.marketing" msgstr "行銷" -#: src/app/main/ui/dashboard/team.cljs:623 +#: src/app/main/ui/dashboard/team.cljs:614 msgid "labels.member" msgstr "成員" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:133 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:181, src/app/main/ui/dashboard/team.cljs:101, src/app/main/ui/dashboard/team.cljs:111 msgid "labels.members" msgstr "成員" @@ -2870,39 +2818,39 @@ msgstr "提及" msgid "labels.new-password" msgstr "新密碼" -#: src/app/main/ui/dashboard/templates.cljs:297, src/app/main/ui/onboarding/questions.cljs:52, src/app/main/ui/viewer.cljs:112, src/app/main/ui/workspace/sidebar/layers.cljs:614 +#: src/app/main/ui/dashboard/templates.cljs:293, src/app/main/ui/onboarding/questions.cljs:54, src/app/main/ui/viewer.cljs:118, src/app/main/ui/workspace/sidebar/layers.cljs:647 msgid "labels.next" msgstr "下一個" -#: src/app/main/ui/static.cljs:362 +#: src/app/main/ui/static.cljs:367 msgid "labels.nitrate-not-configured.desc-message" msgstr "此執行個體需要完成最新版本新增的設定步驟。" -#: src/app/main/ui/static.cljs:365 +#: src/app/main/ui/static.cljs:370 msgid "labels.nitrate-not-configured.learn-more" msgstr "請參閱我們的" -#: src/app/main/ui/static.cljs:360 +#: src/app/main/ui/static.cljs:365 msgid "labels.nitrate-not-configured.main-message" msgstr "Penpot 尚無法啟動。" -#: src/app/main/ui/static.cljs:370 +#: src/app/main/ui/static.cljs:375 msgid "labels.nitrate-not-configured.technical-guide" msgstr "技術指南" -#: src/app/main/ui/static.cljs:350 +#: src/app/main/ui/static.cljs:355 msgid "labels.nitrate-unavailable.main-message" msgstr "Penpot 因系統問題暫時無法使用,請稍後再試。" -#: src/app/main/ui/dashboard/comments.cljs:120, src/app/main/ui/workspace/comments.cljs:164 +#: src/app/main/ui/dashboard/comments.cljs:118, src/app/main/ui/workspace/comments.cljs:171 msgid "labels.no-comments-available" msgstr "您已看完所有通知!新的留言通知會顯示在這裡。" -#: src/app/main/ui/dashboard/team.cljs:852 +#: src/app/main/ui/dashboard/team.cljs:847 msgid "labels.no-invitations" msgstr "沒有待處理的邀請。" -#: src/app/main/ui/dashboard/team.cljs:855 +#: src/app/main/ui/dashboard/team.cljs:850 msgid "labels.no-invitations-gather-people" msgstr "邀請夥伴加入,一起打造精彩作品。" @@ -2910,25 +2858,25 @@ msgstr "邀請夥伴加入,一起打造精彩作品。" msgid "labels.no-matches" msgstr "沒有符合的結果" -#: src/app/main/ui/static.cljs:320 +#: src/app/main/ui/static.cljs:325 msgid "labels.not-found.main-message" msgstr "糟糕!" -#: src/app/main/ui/settings/sidebar.cljs:106 +#: src/app/main/ui/settings/sidebar.cljs:102 msgid "labels.notifications" msgstr "通知" -#: src/app/main/ui/dashboard/projects.cljs:238, src/app/main/ui/dashboard/team.cljs:1739 +#: src/app/main/ui/dashboard/projects.cljs:247, src/app/main/ui/dashboard/team.cljs:1756 msgid "labels.num-of-files" msgid_plural "labels.num-of-files" msgstr[0] "%s 個檔案" -#: src/app/main/ui/viewer/thumbnails.cljs:82 +#: src/app/main/ui/viewer/thumbnails.cljs:91 msgid "labels.num-of-frames" msgid_plural "labels.num-of-frames" msgstr[0] "%s 個畫板" -#: src/app/main/ui/dashboard/team.cljs:1734 +#: src/app/main/ui/dashboard/team.cljs:1750 msgid "labels.num-of-projects" msgid_plural "labels.num-of-projects" msgstr[0] "%s 個專案" @@ -2937,43 +2885,43 @@ msgstr[0] "%s 個專案" msgid "labels.old-password" msgstr "舊密碼" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:994 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1038 msgid "labels.open" msgstr "開啟" -#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:92 +#: src/app/main/ui/comments.cljs:961, src/app/main/ui/comments.cljs:1026, src/app/main/ui/dashboard/fonts.cljs:466, src/app/main/ui/dashboard/team.cljs:443, src/app/main/ui/dashboard/team.cljs:1444, src/app/main/ui/settings/integrations.cljs:400, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:910, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:398, src/app/main/ui/workspace/sidebar/options/menus/text.cljs:590, src/app/main/ui/workspace/top_toolbar.cljs:90 msgid "labels.options" msgstr "選項" -#: src/app/main/ui/onboarding/questions.cljs:130, src/app/main/ui/onboarding/questions.cljs:201, src/app/main/ui/onboarding/questions.cljs:313, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 +#: src/app/main/ui/onboarding/questions.cljs:132, src/app/main/ui/onboarding/questions.cljs:203, src/app/main/ui/onboarding/questions.cljs:315, src/app/main/ui/workspace/colorpicker/libraries.cljs:129 msgid "labels.other" msgstr "其他(請註明)" -#: src/app/main/ui/onboarding/questions.cljs:88, src/app/main/ui/onboarding/questions.cljs:164, src/app/main/ui/onboarding/questions.cljs:277 +#: src/app/main/ui/onboarding/questions.cljs:90, src/app/main/ui/onboarding/questions.cljs:166, src/app/main/ui/onboarding/questions.cljs:279 msgid "labels.other-short" msgstr "其他" -#: src/app/main/ui/dashboard/team.cljs:403, src/app/main/ui/dashboard/team.cljs:437, src/app/main/ui/dashboard/team.cljs:675, src/app/main/ui/dashboard/team.cljs:888, src/app/main/ui/dashboard/team.cljs:1720 +#: src/app/main/ui/dashboard/team.cljs:390, src/app/main/ui/dashboard/team.cljs:425, src/app/main/ui/dashboard/team.cljs:666, src/app/main/ui/dashboard/team.cljs:886, src/app/main/ui/dashboard/team.cljs:1734 msgid "labels.owner" msgstr "擁有者" -#: src/app/main/ui/settings/sidebar.cljs:101 +#: src/app/main/ui/settings/sidebar.cljs:97 msgid "labels.password" msgstr "密碼" -#: src/app/main/ui/dashboard/team.cljs:783 +#: src/app/main/ui/dashboard/team.cljs:778 msgid "labels.pending-invitation" msgstr "待接受" -#: src/app/main/ui/dashboard/sidebar.cljs:536 +#: src/app/main/ui/dashboard/sidebar.cljs:529 msgid "labels.penpot-changelog" msgstr "Penpot 更新紀錄" -#: src/app/main/ui/dashboard/sidebar.cljs:442 +#: src/app/main/ui/dashboard/sidebar.cljs:435 msgid "labels.penpot-hub" msgstr "Penpot Hub" -#: src/app/main/ui/dashboard/sidebar.cljs:388 +#: src/app/main/ui/dashboard/sidebar.cljs:379 msgid "labels.pinned-projects" msgstr "已釘選的專案" @@ -2981,31 +2929,31 @@ msgstr "已釘選的專案" msgid "labels.post" msgstr "發布" -#: src/app/main/ui/dashboard/import.cljs:698, src/app/main/ui/onboarding/questions.cljs:48, src/app/main/ui/viewer.cljs:105, src/app/main/ui/workspace/sidebar/layers.cljs:612 +#: src/app/main/ui/dashboard/import.cljs:803, src/app/main/ui/dashboard/templates.cljs:284, src/app/main/ui/onboarding/questions.cljs:50, src/app/main/ui/viewer.cljs:108, src/app/main/ui/workspace/sidebar/layers.cljs:645 msgid "labels.previous" msgstr "上一個" -#: src/app/main/ui/onboarding/questions.cljs:82 +#: src/app/main/ui/onboarding/questions.cljs:84 msgid "labels.product-design" msgstr "產品或使用者體驗設計" -#: src/app/main/ui/onboarding/questions.cljs:87 +#: src/app/main/ui/onboarding/questions.cljs:89 msgid "labels.product-management" msgstr "產品管理" -#: src/app/main/ui/settings/profile.cljs:149, src/app/main/ui/settings/sidebar.cljs:96 +#: src/app/main/ui/settings/profile.cljs:162, src/app/main/ui/settings/sidebar.cljs:92 msgid "labels.profile" msgstr "個人檔案" -#: src/app/main/ui/dashboard/sidebar.cljs:355 +#: src/app/main/ui/dashboard/sidebar.cljs:348 msgid "labels.projects" msgstr "專案" -#: src/app/main/ui/dashboard/deleted.cljs:212 +#: src/app/main/ui/dashboard/deleted.cljs:218 msgid "labels.recent" msgstr "最近" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:209, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:179 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:243, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:216 msgid "labels.reference" msgstr "參照" @@ -3013,27 +2961,27 @@ msgstr "參照" msgid "labels.refresh" msgstr "重新整理" -#: src/app/main/ui/settings/sidebar.cljs:140, src/app/main/ui/workspace/main_menu.cljs:183 +#: src/app/main/ui/settings/sidebar.cljs:136, src/app/main/ui/workspace/main_menu.cljs:184 msgid "labels.release-notes" msgstr "版本更新說明" -#: src/app/main/ui/static.cljs:383 +#: src/app/main/errors.cljs:663, src/app/main/ui/static.cljs:388 msgid "labels.reload-page" msgstr "重新載入頁面" -#: src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 +#: src/app/main/ui/dashboard/fonts.cljs:288, src/app/main/ui/forms.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:402 msgid "labels.remove" msgstr "移除" -#: src/app/main/ui/dashboard/team.cljs:468 +#: src/app/main/ui/dashboard/team.cljs:459 msgid "labels.remove-member" msgstr "移除成員" -#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:186, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 +#: src/app/main/ui/dashboard/file_menu.cljs:298, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:204, src/app/main/ui/dashboard/project_menu.cljs:90, src/app/main/ui/workspace/sidebar/assets/groups.cljs:187, src/app/main/ui/workspace/sidebar/versions.cljs:228, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:76, src/app/main/ui/workspace/tokens/management/node_context_menu.cljs:101, src/app/main/ui/workspace/tokens/sets/context_menu.cljs:63 msgid "labels.rename" msgstr "重新命名" -#: src/app/main/ui/dashboard/team_form.cljs:117 +#: src/app/main/ui/dashboard/team_form.cljs:118 msgid "labels.rename-team" msgstr "重新命名團隊" @@ -3057,11 +3005,11 @@ msgstr "新回覆" msgid "labels.reply.thread" msgstr "回覆" -#: src/app/main/ui/dashboard/team.cljs:910 +#: src/app/main/ui/dashboard/team.cljs:908 msgid "labels.resend" msgstr "重新傳送" -#: src/app/main/ui/dashboard/team.cljs:1213 +#: src/app/main/ui/dashboard/team.cljs:1214 msgid "labels.resend-invitation" msgstr "重新傳送邀請" @@ -3069,43 +3017,43 @@ msgstr "重新傳送邀請" msgid "labels.restore" msgstr "還原" -#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:333, src/app/main/ui/static.cljs:342, src/app/main/ui/static.cljs:437 +#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:338, src/app/main/ui/static.cljs:347, src/app/main/ui/static.cljs:445 msgid "labels.retry" msgstr "重試" -#: src/app/main/ui/dashboard/team.cljs:624, src/app/main/ui/dashboard/team.cljs:1220 +#: src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/dashboard/team.cljs:1221 msgid "labels.role" msgstr "角色" -#: src/app/main/ui/dashboard/fonts.cljs:435, src/app/main/ui/shortcuts.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:203, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 +#: src/app/main/ui/dashboard/fonts.cljs:457, src/app/main/ui/shortcuts.cljs:788, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:207, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/settings/menu.cljs:109 msgid "labels.save" msgstr "儲存" -#: src/app/main/ui/workspace/sidebar/layers.cljs:763, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:787 +#: src/app/main/ui/dashboard/sidebar.cljs:236, src/app/main/ui/workspace/sidebar/layers.cljs:796, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:795 msgid "labels.search" msgstr "搜尋" -#: src/app/main/ui/dashboard/fonts.cljs:475 +#: src/app/main/ui/dashboard/fonts.cljs:505 msgid "labels.search-font" msgstr "搜尋字型" -#: src/app/main/ui/onboarding/questions.cljs:81, src/app/main/ui/onboarding/questions.cljs:215 +#: src/app/main/ui/onboarding/questions.cljs:83, src/app/main/ui/onboarding/questions.cljs:217 msgid "labels.select-option" msgstr "選擇選項" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.send" msgstr "傳送" -#: src/app/main/ui/settings/feedback.cljs:138 +#: src/app/main/ui/settings/feedback.cljs:140 msgid "labels.sending" msgstr "正在傳送…" -#: src/app/main/ui/static.cljs:340 +#: src/app/main/ui/static.cljs:345 msgid "labels.service-unavailable.desc-message" msgstr "系統正在進行排定的維護作業。" -#: src/app/main/ui/static.cljs:339 +#: src/app/main/ui/static.cljs:344 msgid "labels.service-unavailable.main-message" msgstr "服務暫時無法使用" @@ -3113,67 +3061,67 @@ msgstr "服務暫時無法使用" msgid "labels.sets" msgstr "集合" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:140, src/app/main/ui/settings/options.cljs:133, src/app/main/ui/settings/sidebar.cljs:91, src/app/main/ui/settings/sidebar.cljs:118 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:196, src/app/main/ui/dashboard/team.cljs:102, src/app/main/ui/dashboard/team.cljs:124, src/app/main/ui/settings/options.cljs:139, src/app/main/ui/settings/sidebar.cljs:87, src/app/main/ui/settings/sidebar.cljs:114 msgid "labels.settings" msgstr "設定" -#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:233 +#: src/app/main/ui/inspect/styles/style_box.cljs:34, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:300 msgid "labels.shadow" msgstr "陰影" -#: src/app/main/ui/viewer/header.cljs:204 +#: src/app/main/ui/viewer/header.cljs:215 msgid "labels.share" msgstr "分享" -#: src/app/main/ui/dashboard/sidebar.cljs:382 +#: src/app/main/ui/dashboard/sidebar.cljs:374 msgid "labels.shared-libraries" msgstr "資料庫" -#: src/app/main/ui/dashboard/templates.cljs:86 +#: src/app/main/ui/dashboard/templates.cljs:81 msgid "labels.show" msgstr "顯示" -#: src/app/main/ui/viewer/comments.cljs:84, src/app/main/ui/workspace/comments.cljs:58, src/app/main/ui/workspace/comments.cljs:138 +#: src/app/main/ui/viewer/comments.cljs:90, src/app/main/ui/workspace/comments.cljs:57, src/app/main/ui/workspace/comments.cljs:144 msgid "labels.show-all-comments" msgstr "顯示所有留言" -#: src/app/main/ui/viewer/comments.cljs:127 +#: src/app/main/ui/viewer/comments.cljs:130 msgid "labels.show-comments-list" msgstr "顯示留言清單" -#: src/app/main/ui/viewer/comments.cljs:103, src/app/main/ui/workspace/comments.cljs:70, src/app/main/ui/workspace/comments.cljs:140 +#: src/app/main/ui/viewer/comments.cljs:108, src/app/main/ui/workspace/comments.cljs:71, src/app/main/ui/workspace/comments.cljs:146 msgid "labels.show-mentions" msgstr "僅顯示提及您的留言" -#: src/app/main/ui/viewer/comments.cljs:93, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:139 +#: src/app/main/ui/viewer/comments.cljs:99, src/app/main/ui/workspace/comments.cljs:64, src/app/main/ui/workspace/comments.cljs:145 msgid "labels.show-your-comments" msgstr "僅顯示您的留言" -#: src/app/main/ui/onboarding/questions.cljs:156 +#: src/app/main/ui/onboarding/questions.cljs:158 msgid "labels.sketch" msgstr "sketch" -#: src/app/main/ui/dashboard/sidebar.cljs:367 +#: src/app/main/ui/dashboard/sidebar.cljs:359 msgid "labels.sources" msgstr "來源" -#: src/app/main/ui/static.cljs:554 +#: src/app/main/ui/static.cljs:562 msgid "labels.sso-error.desc-message" msgstr "未能透過您組織的身分識別提供者完成登入。該服務可能暫時無法使用,或您的帳號尚未加入其目錄。" -#: src/app/main/ui/static.cljs:555 +#: src/app/main/ui/static.cljs:563 msgid "labels.sso-error.retry" msgstr "再試一次" -#: src/app/main/ui/static.cljs:553 +#: src/app/main/ui/static.cljs:561 msgid "labels.sso-error.title" msgstr "無法將您登入 %s" -#: src/app/main/ui/onboarding/questions.cljs:53 +#: src/app/main/ui/onboarding/questions.cljs:55 msgid "labels.start" msgstr "開始" -#: src/app/main/ui/dashboard/team.cljs:1229 +#: src/app/main/ui/dashboard/team.cljs:1230 msgid "labels.status" msgstr "狀態" @@ -3181,7 +3129,7 @@ msgstr "狀態" msgid "labels.stroke" msgstr "筆畫" -#: src/app/main/ui/onboarding/questions.cljs:84 +#: src/app/main/ui/onboarding/questions.cljs:86 msgid "labels.student-teacher" msgstr "學生或教師" @@ -3193,7 +3141,7 @@ msgstr "樣式" msgid "labels.svg" msgstr "SVG" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:570 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:593 msgid "labels.switch" msgstr "切換" @@ -3201,19 +3149,19 @@ msgstr "切換" msgid "labels.text" msgstr "文字" -#: src/app/main/ui/workspace/tokens/themes.cljs:36 +#: src/app/main/ui/workspace/tokens/themes.cljs:39 msgid "labels.themes" msgstr "佈景主題" -#: src/app/main/ui/workspace/main_menu.cljs:174 +#: src/app/main/ui/workspace/main_menu.cljs:175 msgid "labels.tutorials" msgstr "教學" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:189 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:259 msgid "labels.typography" msgstr "文字樣式" -#: src/app/main/data/workspace/tokens/errors.cljs:136, src/app/main/data/workspace/tokens/errors.cljs:163, src/app/main/data/workspace/tokens/errors.cljs:175 +#: src/app/main/data/workspace/tokens/errors.cljs:152, src/app/main/data/workspace/tokens/errors.cljs:179, src/app/main/data/workspace/tokens/errors.cljs:191 msgid "labels.unknown-error" msgstr "未知錯誤" @@ -3225,35 +3173,31 @@ msgstr "解除鎖定" msgid "labels.unpublish-multi-files" msgstr "取消發布 %s 個檔案" -#: src/app/main/ui/settings/profile.cljs:123 +#: src/app/main/ui/settings/profile.cljs:131 msgid "labels.update" msgstr "更新" -#: src/app/main/ui/dashboard/team_form.cljs:138 +#: src/app/main/ui/dashboard/team_form.cljs:142 msgid "labels.update-team" msgstr "更新團隊" -#: src/app/main/ui/dashboard/fonts.cljs:270 +#: src/app/main/ui/dashboard/fonts.cljs:286 msgid "labels.upload" msgstr "上傳" -#: src/app/main/ui/dashboard/fonts.cljs:197 +#: src/app/main/ui/dashboard/fonts.cljs:209 msgid "labels.upload-custom-fonts" msgstr "上傳自訂字型" -#: src/app/main/ui/dashboard/fonts.cljs:269 +#: src/app/main/ui/dashboard/fonts.cljs:285 msgid "labels.uploading" msgstr "正在上傳…" -#: src/app/main/ui/dashboard/import.cljs:648 -msgid "labels.uploading-file" -msgstr "正在上傳檔案…" - -#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034 +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046 msgid "labels.variant" msgstr "變體" -#: src/app/main/ui/dashboard/sidebar.cljs:530 +#: src/app/main/ui/dashboard/sidebar.cljs:523 msgid "labels.version-notes" msgstr "版本 %s 說明" @@ -3261,7 +3205,7 @@ msgstr "版本 %s 說明" msgid "labels.view-only" msgstr "僅供檢視" -#: src/app/main/ui/dashboard/team.cljs:163, src/app/main/ui/dashboard/team.cljs:406, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:678, src/app/main/ui/dashboard/team.cljs:714, src/app/main/ui/dashboard/team.cljs:891, src/app/main/ui/onboarding/team_choice.cljs:70 +#: src/app/main/ui/dashboard/team.cljs:147, src/app/main/ui/dashboard/team.cljs:393, src/app/main/ui/dashboard/team.cljs:415, src/app/main/ui/dashboard/team.cljs:669, src/app/main/ui/dashboard/team.cljs:709, src/app/main/ui/dashboard/team.cljs:889, src/app/main/ui/onboarding/team_choice.cljs:70 msgid "labels.viewer" msgstr "檢視者" @@ -3274,7 +3218,7 @@ msgid "labels.warning-count" msgid_plural "labels.warning-count" msgstr[0] "%s 則警告" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:138, src/app/main/ui/dashboard/team.cljs:1422 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:191, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:120, src/app/main/ui/dashboard/team.cljs:1426 msgid "labels.webhooks" msgstr "Webhook" @@ -3282,11 +3226,11 @@ msgstr "Webhook" msgid "labels.write-new-comment" msgstr "撰寫新留言" -#: src/app/main/ui/dashboard/team.cljs:381 +#: src/app/main/ui/dashboard/team.cljs:368 msgid "labels.you" msgstr "(你)" -#: src/app/main/ui/dashboard/sidebar.cljs:700 +#: src/app/main/ui/dashboard/sidebar.cljs:692 msgid "labels.your-account" msgstr "你的帳戶" @@ -3418,7 +3362,7 @@ msgstr "將「%s」新增為共享資料庫" msgid "modals.before-leave-organization.message" msgstr "您是部分團隊中唯一的成員。這些團隊及其專案與檔案都將被刪除。" -#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:151 +#: src/app/main/data/nitrate.cljs:319, src/app/main/ui/dashboard/change_owner.cljs:153 msgid "modals.before-leave-organization.title" msgstr "離開組織前" @@ -3426,75 +3370,75 @@ msgstr "離開組織前" msgid "modals.before-leave-organization.warning" msgstr "所有只有您一位成員的團隊都將被刪除。" -#: src/app/main/ui/workspace/nudge.cljs:59 +#: src/app/main/ui/workspace/nudge.cljs:63 msgid "modals.big-nudge" msgstr "大幅移動" -#: src/app/main/ui/settings/change_email.cljs:109 +#: src/app/main/ui/settings/change_email.cljs:113 msgid "modals.change-email.confirm-email" msgstr "確認新的電子郵件地址" -#: src/app/main/ui/settings/change_email.cljs:97 +#: src/app/main/ui/settings/change_email.cljs:101 msgid "modals.change-email.info" msgstr "我們會寄送郵件至您目前的電子郵件地址「%s」,以驗證您的身分。" -#: src/app/main/ui/settings/change_email.cljs:102 +#: src/app/main/ui/settings/change_email.cljs:106 msgid "modals.change-email.new-email" msgstr "新的電子郵件地址" -#: src/app/main/ui/settings/change_email.cljs:117 +#: src/app/main/ui/settings/change_email.cljs:121 msgid "modals.change-email.submit" msgstr "變更電子郵件地址" -#: src/app/main/ui/settings/change_email.cljs:90 +#: src/app/main/ui/settings/change_email.cljs:91 msgid "modals.change-email.title" msgstr "變更您的電子郵件地址" -#: src/app/main/ui/dashboard/team.cljs:1416 +#: src/app/main/ui/dashboard/team.cljs:1420 msgid "modals.create-webhook.submit-label" msgstr "建立 webhook" -#: src/app/main/ui/dashboard/team.cljs:1381 +#: src/app/main/ui/dashboard/team.cljs:1382 msgid "modals.create-webhook.title" msgstr "建立 webhook" -#: src/app/main/ui/dashboard/team.cljs:1392 +#: src/app/main/ui/dashboard/team.cljs:1396 msgid "modals.create-webhook.url.label" msgstr "資料接收網址" -#: src/app/main/ui/dashboard/team.cljs:1393 +#: src/app/main/ui/dashboard/team.cljs:1397 msgid "modals.create-webhook.url.placeholder" msgstr "https://example.com/postreceive" -#: src/app/main/ui/settings/delete_account.cljs:105 +#: src/app/main/ui/settings/delete_account.cljs:110 msgid "modals.delete-account.cancel" msgstr "取消並保留我的帳號" -#: src/app/main/ui/settings/delete_account.cljs:110 +#: src/app/main/ui/settings/delete_account.cljs:115 msgid "modals.delete-account.confirm" msgstr "是,刪除我的帳號" -#: src/app/main/ui/settings/delete_account.cljs:74 +#: src/app/main/ui/settings/delete_account.cljs:78 msgid "modals.delete-account.info" msgstr "刪除帳號後,你將失去目前所有的專案與封存資料。" -#: src/app/main/ui/settings/delete_account.cljs:73 +#: src/app/main/ui/settings/delete_account.cljs:77 msgid "modals.delete-account.info.with-organizations" msgstr "此操作無法復原。你的帳號、訂閱及組織將立即刪除,你也將無法再存取目前的專案與檔案。" -#: src/app/main/ui/settings/delete_account.cljs:83 +#: src/app/main/ui/settings/delete_account.cljs:87 msgid "modals.delete-account.owned-organizations.list-title" msgstr "即將刪除的組織" -#: src/app/main/ui/settings/delete_account.cljs:98 +#: src/app/main/ui/settings/delete_account.cljs:102 msgid "modals.delete-account.owned-organizations.members-count" msgstr "%s 位成員" -#: src/app/main/ui/settings/delete_account.cljs:96 +#: src/app/main/ui/settings/delete_account.cljs:100 msgid "modals.delete-account.owned-organizations.teams-count" msgstr "%s 個團隊" -#: src/app/main/ui/settings/delete_account.cljs:64 +#: src/app/main/ui/settings/delete_account.cljs:65 msgid "modals.delete-account.title" msgstr "確定要刪除您的帳號嗎?" @@ -3551,19 +3495,19 @@ msgstr "確定要刪除 %s 個檔案嗎?" msgid "modals.delete-file-multi-confirm.title" msgstr "刪除 %s 個檔案" -#: src/app/main/ui/dashboard/fonts.cljs:396 +#: src/app/main/ui/dashboard/fonts.cljs:413 msgid "modals.delete-font-variant.message" msgstr "確定要刪除這個字型樣式嗎?刪除後,使用此樣式的檔案將無法載入此樣式。" -#: src/app/main/ui/dashboard/fonts.cljs:395 +#: src/app/main/ui/dashboard/fonts.cljs:412 msgid "modals.delete-font-variant.title" msgstr "刪除字型樣式" -#: src/app/main/ui/dashboard/fonts.cljs:362 +#: src/app/main/ui/dashboard/fonts.cljs:379 msgid "modals.delete-font.message" msgstr "確定要刪除這個字型嗎?刪除後,使用此字型的檔案將無法載入此字型。" -#: src/app/main/ui/dashboard/fonts.cljs:361 +#: src/app/main/ui/dashboard/fonts.cljs:378 msgid "modals.delete-font.title" msgstr "刪除字型" @@ -3571,19 +3515,19 @@ msgstr "刪除字型" msgid "modals.delete-organization-team-confirm.message" msgstr "確定要刪除隸屬於 %s 組織的這個團隊嗎?" -#: src/app/main/ui/workspace/context_menu.cljs:808, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 +#: src/app/main/ui/workspace/context_menu.cljs:816, src/app/main/ui/workspace/sidebar/sitemap.cljs:125 msgid "modals.delete-page.body" msgstr "確定要刪除此頁面嗎?" -#: src/app/main/ui/workspace/context_menu.cljs:807, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 +#: src/app/main/ui/workspace/context_menu.cljs:815, src/app/main/ui/workspace/sidebar/sitemap.cljs:124, src/app/main/ui/workspace/sidebar/sitemap.cljs:242 msgid "modals.delete-page.title" msgstr "刪除頁面" -#: src/app/main/ui/workspace/context_menu.cljs:814 +#: src/app/main/ui/workspace/context_menu.cljs:822 msgid "modals.delete-pages.body" msgstr "確定要刪除所選頁面嗎?" -#: src/app/main/ui/workspace/context_menu.cljs:813 +#: src/app/main/ui/workspace/context_menu.cljs:821 msgid "modals.delete-pages.title" msgstr "刪除頁面" @@ -3599,27 +3543,27 @@ msgstr "確定要刪除此專案嗎?" msgid "modals.delete-project-confirm.title" msgstr "刪除專案" -#: src/app/main/ui/delete_shared.cljs:52 +#: src/app/main/ui/delete_shared.cljs:54 msgid "modals.delete-shared-confirm.accept" msgid_plural "modals.delete-shared-confirm.accept" msgstr[0] "刪除檔案" -#: src/app/main/ui/delete_shared.cljs:56 +#: src/app/main/ui/delete_shared.cljs:58 msgid "modals.delete-shared-confirm.activated.no-files-message" msgid_plural "modals.delete-shared-confirm.activated.no-files-message" msgstr[0] "這些資料庫未在任何檔案中啟用。" -#: src/app/main/ui/delete_shared.cljs:58 +#: src/app/main/ui/delete_shared.cljs:60 msgid "modals.delete-shared-confirm.activated.scd-message" msgid_plural "modals.delete-shared-confirm.activated.scd-message" msgstr[0] "這些資料庫已在下列位置啟用: " -#: src/app/main/ui/delete_shared.cljs:47 +#: src/app/main/ui/delete_shared.cljs:49 msgid "modals.delete-shared-confirm.message" msgid_plural "modals.delete-shared-confirm.message" msgstr[0] "確定要刪除這些檔案嗎?" -#: src/app/main/ui/delete_shared.cljs:42 +#: src/app/main/ui/delete_shared.cljs:44 msgid "modals.delete-shared-confirm.title" msgid_plural "modals.delete-shared-confirm.title" msgstr[0] "刪除檔案" @@ -3636,40 +3580,40 @@ msgstr "確定要刪除這個團隊嗎?與此團隊相關的所有專案和檔 msgid "modals.delete-team-confirm.title" msgstr "刪除團隊" -#: src/app/main/ui/dashboard/team.cljs:573 +#: src/app/main/ui/dashboard/team.cljs:564 msgid "modals.delete-team-member-confirm.accept" msgstr "移除成員" -#: src/app/main/ui/dashboard/team.cljs:572 +#: src/app/main/ui/dashboard/team.cljs:563 msgid "modals.delete-team-member-confirm.message" msgstr "確定要將這位成員從團隊中移除嗎?" -#: src/app/main/ui/dashboard/team.cljs:571 +#: src/app/main/ui/dashboard/team.cljs:562 msgid "modals.delete-team-member-confirm.title" msgstr "移除團隊成員" -#: src/app/main/ui/delete_shared.cljs:60 +#: src/app/main/ui/delete_shared.cljs:62 msgid "modals.delete-unpublish-shared-confirm.activated.hint" msgid_plural "modals.delete-unpublish-shared-confirm.activated.hint" msgstr[0] "這些檔案中已使用的資產仍會保留(不會破壞任何設計)。" -#: src/app/main/ui/dashboard/team.cljs:1483 +#: src/app/main/ui/dashboard/team.cljs:1492 msgid "modals.delete-webhook.accept" msgstr "刪除 webhook" -#: src/app/main/ui/dashboard/team.cljs:1482 +#: src/app/main/ui/dashboard/team.cljs:1491 msgid "modals.delete-webhook.message" msgstr "確定要刪除此 webhook 嗎?" -#: src/app/main/ui/dashboard/team.cljs:1481 +#: src/app/main/ui/dashboard/team.cljs:1490 msgid "modals.delete-webhook.title" msgstr "刪除 webhook" -#: src/app/main/ui/dashboard/team.cljs:1415 +#: src/app/main/ui/dashboard/team.cljs:1419 msgid "modals.edit-webhook.submit-label" msgstr "編輯 webhook" -#: src/app/main/ui/dashboard/team.cljs:1380 +#: src/app/main/ui/dashboard/team.cljs:1381 msgid "modals.edit-webhook.title" msgstr "編輯 webhook" @@ -3685,15 +3629,15 @@ msgstr "匯入 tokens" msgid "modals.import-library-tokens.title" msgstr "要從資料庫匯入 tokens 嗎?" -#: src/app/main/ui/dashboard/team.cljs:308 +#: src/app/main/ui/dashboard/team.cljs:292 msgid "modals.invite-member-confirm.accept" msgstr "傳送邀請" -#: src/app/main/ui/dashboard/team.cljs:302, src/app/main/ui/onboarding/team_choice.cljs:222 +#: src/app/main/ui/dashboard/team.cljs:286, src/app/main/ui/onboarding/team_choice.cljs:222 msgid "modals.invite-member.emails" msgstr "電子郵件地址,以逗號分隔" -#: src/app/main/ui/dashboard/team.cljs:286 +#: src/app/main/ui/dashboard/team.cljs:270 msgid "modals.invite-member.repeated-invitation" msgstr "部分成員已加入團隊,我們會邀請其餘成員。" @@ -3709,87 +3653,87 @@ msgstr "未寄出任何邀請" msgid "modals.invite-restricted-members.all-organization-members-in-team" msgstr "只有「%s」組織的成員才能受邀加入此團隊,而他們都已經是團隊成員。如需更多資訊,請聯絡組織擁有者。" -#: src/app/main/ui/dashboard/team.cljs:340 +#: src/app/main/ui/dashboard/team.cljs:327 msgid "modals.invite-restricted-members.blocked-addresses" msgstr "不會收到邀請的電子郵件地址" -#: src/app/main/ui/dashboard/team.cljs:357 +#: src/app/main/ui/dashboard/team.cljs:344 msgid "modals.invite-restricted-members.cancel" msgstr "取消" -#: src/app/main/ui/dashboard/team.cljs:330 +#: src/app/main/ui/dashboard/team.cljs:317 msgid "modals.invite-restricted-members.description" msgstr "只能邀請組織的現有成員。" -#: src/app/main/ui/dashboard/team.cljs:365 +#: src/app/main/ui/dashboard/team.cljs:352 msgid "modals.invite-restricted-members.send" msgstr "寄送邀請" -#: src/app/main/ui/dashboard/team.cljs:325 +#: src/app/main/ui/dashboard/team.cljs:309 msgid "modals.invite-restricted-members.title" msgstr "部分邀請無法寄出" -#: src/app/main/ui/dashboard/team.cljs:331 +#: src/app/main/ui/dashboard/team.cljs:318 msgid "modals.invite-restricted-members.warning" msgstr "清單上的部分人員不會收到邀請。" -#: src/app/main/ui/dashboard/team.cljs:278 +#: src/app/main/ui/dashboard/team.cljs:262 msgid "modals.invite-team-member.text" msgstr "您可以邀請成員加入團隊,讓他們存取此檔案及團隊的所有檔案。" -#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:272, src/app/main/ui/dashboard/team_form.cljs:155 +#: src/app/main/data/team.cljs:195, src/app/main/ui/dashboard/team.cljs:256, src/app/main/ui/dashboard/team_form.cljs:159 msgid "modals.invite-team-member.title" msgstr "邀請成員加入團隊" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:539 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:148, src/app/main/ui/dashboard/team.cljs:530 msgid "modals.leave-and-close-confirm.hint" msgstr "由於您是這個團隊的唯一成員,團隊及其所有專案和檔案都將一併刪除。" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:538 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:147, src/app/main/ui/dashboard/team.cljs:529 msgid "modals.leave-and-close-confirm.message" msgstr "您確定要離開 %s 團隊嗎?" -#: src/app/main/ui/dashboard/change_owner.cljs:57, src/app/main/ui/dashboard/change_owner.cljs:92 +#: src/app/main/ui/dashboard/change_owner.cljs:62, src/app/main/ui/dashboard/change_owner.cljs:94 msgid "modals.leave-and-reassign.forbidden" msgstr "沒有其他成員可接任擁有者,因此您無法離開團隊。您可以考慮刪除團隊。" -#: src/app/main/ui/dashboard/change_owner.cljs:53 +#: src/app/main/ui/dashboard/change_owner.cljs:58 msgid "modals.leave-and-reassign.hint1" msgstr "您是這個團隊的擁有者。離開前,請先選擇另一位成員接任擁有者。" -#: src/app/main/ui/dashboard/change_owner.cljs:76, src/app/main/ui/dashboard/change_owner.cljs:183 +#: src/app/main/ui/dashboard/change_owner.cljs:79, src/app/main/ui/dashboard/change_owner.cljs:186 msgid "modals.leave-and-reassign.promote-and-leave" msgstr "轉移擁有權並離開" -#: src/app/main/ui/dashboard/change_owner.cljs:33 +#: src/app/main/ui/dashboard/change_owner.cljs:35 msgid "modals.leave-and-reassign.select-member-to-promote" msgstr "選擇接任擁有者的成員" -#: src/app/main/ui/dashboard/change_owner.cljs:47 +#: src/app/main/ui/dashboard/change_owner.cljs:49 msgid "modals.leave-and-reassign.title" msgstr "離開之前" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:540, src/app/main/ui/dashboard/team.cljs:562 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:124, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:149, src/app/main/ui/dashboard/team.cljs:531, src/app/main/ui/dashboard/team.cljs:553 msgid "modals.leave-confirm.accept" msgstr "離開團隊" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:561 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:123, src/app/main/ui/dashboard/team.cljs:552 msgid "modals.leave-confirm.message" msgstr "您確定要離開這個團隊嗎?" -#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:537, src/app/main/ui/dashboard/team.cljs:560 +#: src/app/main/ui/dashboard/organization_team_switch_menus.cljs:122, src/app/main/ui/dashboard/organization_team_switch_menus.cljs:146, src/app/main/ui/dashboard/team.cljs:528, src/app/main/ui/dashboard/team.cljs:551 msgid "modals.leave-confirm.title" msgstr "離開團隊" -#: src/app/main/ui/dashboard/change_owner.cljs:158 +#: src/app/main/ui/dashboard/change_owner.cljs:163 msgid "modals.leave-organization-and-reassign.hint" msgstr "您是組織中部分團隊的擁有者。請將其他成員設為擁有者。" -#: src/app/main/ui/dashboard/change_owner.cljs:161 +#: src/app/main/ui/dashboard/change_owner.cljs:166 msgid "modals.leave-organization-and-reassign.hint-delete" msgstr "您是部分團隊中唯一的成員。這些團隊將連同其中的專案與檔案一併刪除。" -#: src/app/main/ui/dashboard/change_owner.cljs:163 +#: src/app/main/ui/dashboard/change_owner.cljs:168 msgid "modals.leave-organization-and-reassign.hint-promote" msgstr "此外,您也是組織中部分團隊的擁有者。請將其他成員設為擁有者。" @@ -3805,50 +3749,50 @@ msgstr "您將永久失去此組織內所有團隊、專案與檔案的存取權 msgid "modals.leave-organization-confirm.title" msgstr "要離開 %s 組織嗎?" -#: src/app/main/ui/delete_shared.cljs:54 +#: src/app/main/ui/delete_shared.cljs:56 msgid "modals.move-shared-confirm.accept" msgid_plural "modals.move-shared-confirm.accept" msgstr[0] "移動" -#: src/app/main/ui/delete_shared.cljs:49 +#: src/app/main/ui/delete_shared.cljs:51 msgid "modals.move-shared-confirm.message" msgid_plural "modals.move-shared-confirm.message" msgstr[0] "確定要移動這些資料庫嗎?" -#: src/app/main/ui/delete_shared.cljs:44 +#: src/app/main/ui/delete_shared.cljs:46 msgid "modals.move-shared-confirm.title" msgid_plural "modals.move-shared-confirm.title" msgstr[0] "移動資料庫" -#: src/app/main/ui/workspace/main_menu.cljs:317, src/app/main/ui/workspace/nudge.cljs:46 +#: src/app/main/ui/workspace/main_menu.cljs:318, src/app/main/ui/workspace/nudge.cljs:47 msgid "modals.nudge-title" msgstr "微調幅度" -#: src/app/main/ui/dashboard/team.cljs:492 +#: src/app/main/ui/dashboard/team.cljs:483 msgid "modals.promote-owner-confirm.accept" msgstr "轉移所有權" -#: src/app/main/ui/dashboard/team.cljs:491 +#: src/app/main/ui/dashboard/team.cljs:482 msgid "modals.promote-owner-confirm.hint" msgstr "轉移所有權後,您的角色將變更為管理員,並失去此團隊的部分權限。 " -#: src/app/main/ui/dashboard/team.cljs:490 +#: src/app/main/ui/dashboard/team.cljs:481 msgid "modals.promote-owner-confirm.message" msgstr "您目前是此團隊的擁有者。確定要讓 %s 成為團隊的新擁有者嗎?" -#: src/app/main/ui/dashboard/team.cljs:489 +#: src/app/main/ui/dashboard/team.cljs:480 msgid "modals.promote-owner-confirm.title" msgstr "新的團隊擁有者" -#: src/app/main/ui/workspace/libraries.cljs:397, src/app/main/ui/workspace/libraries.cljs:775 +#: src/app/main/ui/workspace/libraries.cljs:449, src/app/main/ui/workspace/libraries.cljs:797 msgid "modals.publish-empty-library.accept" msgstr "發布" -#: src/app/main/ui/workspace/libraries.cljs:396, src/app/main/ui/workspace/libraries.cljs:774 +#: src/app/main/ui/workspace/libraries.cljs:448, src/app/main/ui/workspace/libraries.cljs:796 msgid "modals.publish-empty-library.message" msgstr "您的資料庫目前沒有內容。確定要發布嗎?" -#: src/app/main/ui/workspace/libraries.cljs:395, src/app/main/ui/workspace/libraries.cljs:773 +#: src/app/main/ui/workspace/libraries.cljs:447, src/app/main/ui/workspace/libraries.cljs:795 msgid "modals.publish-empty-library.title" msgstr "發布空白資料庫" @@ -3868,21 +3812,21 @@ msgstr "確定要將「%s」團隊從「%s」組織中移除嗎?" msgid "modals.remove-team-organization.title" msgstr "從組織中移除團隊" -#: src/app/main/ui/workspace/nudge.cljs:52 +#: src/app/main/ui/workspace/nudge.cljs:56 msgid "modals.small-nudge" msgstr "小幅微調" -#: src/app/main/ui/delete_shared.cljs:53 +#: src/app/main/ui/delete_shared.cljs:55 msgid "modals.unpublish-shared-confirm.accept" msgid_plural "modals.unpublish-shared-confirm.accept" msgstr[0] "取消發布" -#: src/app/main/ui/delete_shared.cljs:48 +#: src/app/main/ui/delete_shared.cljs:50 msgid "modals.unpublish-shared-confirm.message" msgid_plural "modals.unpublish-shared-confirm.message" msgstr[0] "確定要取消發布這些資料庫嗎?" -#: src/app/main/ui/delete_shared.cljs:43 +#: src/app/main/ui/delete_shared.cljs:45 msgid "modals.unpublish-shared-confirm.title" msgid_plural "modals.unpublish-shared-confirm.title" msgstr[0] "取消發布資料庫" @@ -3967,31 +3911,31 @@ msgstr "啟用" msgid "nitrate.code-activation.title" msgstr "啟用企業版" -#: src/app/main/ui/settings/subscription.cljs:906 +#: src/app/main/ui/settings/subscription.cljs:933 msgid "nitrate.contact-sales.button" msgstr "聯絡業務團隊" -#: src/app/main/ui/settings/subscription.cljs:899 +#: src/app/main/ui/settings/subscription.cljs:926 msgid "nitrate.contact-sales.downgrade-contact-info" msgstr "如要切換至此方案,請聯絡我們的業務團隊。我們會協助您更新訂閱,並確保所有設定皆正確無誤。" -#: src/app/main/ui/settings/subscription.cljs:885 +#: src/app/main/ui/settings/subscription.cljs:912 msgid "nitrate.contact-sales.downgrade-organization-deleted" msgstr "您的組織將遭到刪除。" -#: src/app/main/ui/settings/subscription.cljs:887 +#: src/app/main/ui/settings/subscription.cljs:914 msgid "nitrate.contact-sales.downgrade-storage-limited" msgstr "您的儲存空間總容量、自動儲存的版本紀錄及檔案復原期限都將受到限制。" -#: src/app/main/ui/settings/subscription.cljs:886 +#: src/app/main/ui/settings/subscription.cljs:913 msgid "nitrate.contact-sales.downgrade-teams-available" msgstr "團隊、專案和檔案將不再隸屬於任何組織,但仍可繼續使用。" -#: src/app/main/ui/settings/subscription.cljs:883 +#: src/app/main/ui/settings/subscription.cljs:910 msgid "nitrate.contact-sales.downgrade-title" msgstr "降級後:" -#: src/app/main/ui/settings/subscription.cljs:880 +#: src/app/main/ui/settings/subscription.cljs:907 msgid "nitrate.contact-sales.title" msgstr "要切換至 %s 方案嗎?" @@ -4007,15 +3951,15 @@ msgstr "請聯絡我們,試用企業版 14 天:" msgid "nitrate.form.contact-upgrade" msgstr "聯絡我們,升級至企業版:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-free-trial" msgstr "聯絡我們,試用企業方案 14 天:" -#: src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.contact-us-upgrade" msgstr "聯絡我們,升級至企業方案:" -#: src/app/main/ui/settings/subscription.cljs:837 +#: src/app/main/ui/settings/subscription.cljs:862 msgid "nitrate.form.enterprise-description" msgstr "目前不會向您收費,試用期結束後才會扣款。您可以隨時取消。" @@ -4031,7 +3975,7 @@ msgstr "透過專屬管理控制台,集中控管每位成員可在哪裡執行 msgid "nitrate.form.enterprise-feature-3" msgstr "享有完整的開源自由,同時滿足組織的治理需求" -#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:832, src/app/main/ui/settings/subscription.cljs:854 +#: src/app/main/ui/nitrate/nitrate_form.cljs:80, src/app/main/ui/settings/subscription.cljs:857, src/app/main/ui/settings/subscription.cljs:879 msgid "nitrate.form.enterprise-intro" msgstr "滿足團隊大規模安全協作的一切需求%s" @@ -4039,7 +3983,7 @@ msgstr "滿足團隊大規模安全協作的一切需求%s" msgid "nitrate.form.enterprise.price" msgstr "每位成員每月" -#: src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "nitrate.form.free-trial-button" msgstr "免費試用 14 天" @@ -4055,7 +3999,7 @@ msgstr "開始使用 Enterprise 方案" msgid "nitrate.form.subscribe-with-code" msgstr "使用啟用碼訂閱" -#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:826 +#: src/app/main/ui/nitrate/nitrate_form.cljs:77, src/app/main/ui/settings/subscription.cljs:851 msgid "nitrate.form.title" msgstr "解鎖 Enterprise 功能" @@ -4067,7 +4011,7 @@ msgstr "免費試用 14 天" msgid "nitrate.modal-success.title" msgstr "歡迎使用 Enterprise 方案!" -#: src/app/main/ui/settings/subscription.cljs:618 +#: src/app/main/ui/settings/subscription.cljs:634 msgid "nitrate.subscription.active-until" msgstr "有效期限至 %s" @@ -4075,23 +4019,23 @@ msgstr "有效期限至 %s" msgid "nitrate.subscription.renew-success" msgstr "您的 Enterprise 方案已續訂。" -#: src/app/main/ui/settings/subscription.cljs:630, src/app/main/ui/settings/subscription.cljs:933 +#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:962 msgid "nitrate.subscription.settings.manual-cancel" msgstr "取消訂閱" -#: src/app/main/ui/settings/subscription.cljs:937 +#: src/app/main/ui/settings/subscription.cljs:966 msgid "nitrate.subscription.settings.manual-contact-us" msgstr "如要取消企業版訂閱,請透過以下方式聯絡我們:" -#: src/app/main/ui/settings/subscription.cljs:111 +#: src/app/main/ui/settings/subscription.cljs:113 msgid "nitrate.subscription.settings.renew-with-code" msgstr "使用啟用碼續訂" -#: src/app/main/ui/static.cljs:322 +#: src/app/main/ui/static.cljs:327 msgid "not-found.desc-message.doesnt-exist" msgstr "此頁面不存在" -#: src/app/main/ui/static.cljs:321 +#: src/app/main/ui/static.cljs:326 msgid "not-found.desc-message.error" msgstr "404 錯誤" @@ -4107,11 +4051,11 @@ msgstr "我們已將密碼重設郵件寄至" msgid "not-found.login.sent-recovery-check" msgstr "請查看電子郵件,並點選連結以設定新密碼。" -#: src/app/main/ui/static.cljs:185 +#: src/app/main/ui/static.cljs:187 msgid "not-found.login.signup-free" msgstr "免費註冊" -#: src/app/main/ui/static.cljs:186 +#: src/app/main/ui/static.cljs:188 msgid "not-found.login.start-using" msgstr "只要幾秒鐘,就能開始使用 Penpot!" @@ -4119,63 +4063,63 @@ msgstr "只要幾秒鐘,就能開始使用 Penpot!" msgid "not-found.made-with-love" msgstr "以愛與開源精神打造" -#: src/app/main/ui/static.cljs:282 +#: src/app/main/ui/static.cljs:287 msgid "not-found.no-permission.already-requested.file" msgstr "您已申請此檔案的存取權限。" -#: src/app/main/ui/static.cljs:283 +#: src/app/main/ui/static.cljs:288 msgid "not-found.no-permission.already-requested.or-others.file" msgstr "您已申請此檔案或此團隊其他檔案或專案的存取權限。" -#: src/app/main/ui/static.cljs:289 +#: src/app/main/ui/static.cljs:294 msgid "not-found.no-permission.already-requested.or-others.project" msgstr "您已申請此專案或此團隊其他專案或檔案的存取權限。" -#: src/app/main/ui/static.cljs:288 +#: src/app/main/ui/static.cljs:293 msgid "not-found.no-permission.already-requested.project" msgstr "您已申請此專案的存取權限。" -#: src/app/main/ui/static.cljs:303, src/app/main/ui/static.cljs:312 +#: src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:317 msgid "not-found.no-permission.ask" msgstr "申請存取權限" -#: src/app/main/ui/static.cljs:295 +#: src/app/main/ui/static.cljs:300 msgid "not-found.no-permission.done.remember" msgstr "請留意,擁有者核准後,您將收到加入團隊的邀請。" -#: src/app/main/ui/static.cljs:294 +#: src/app/main/ui/static.cljs:299 msgid "not-found.no-permission.done.success" msgstr "您的申請已成功送出!" -#: src/app/main/ui/static.cljs:300 +#: src/app/main/ui/static.cljs:305 msgid "not-found.no-permission.file" msgstr "您沒有此檔案的存取權限。" -#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:278, src/app/main/ui/static.cljs:284, src/app/main/ui/static.cljs:290, src/app/main/ui/static.cljs:296, src/app/main/ui/static.cljs:305, src/app/main/ui/static.cljs:314, src/app/main/ui/static.cljs:557 +#: src/app/main/ui/static.cljs:61, src/app/main/ui/static.cljs:283, src/app/main/ui/static.cljs:289, src/app/main/ui/static.cljs:295, src/app/main/ui/static.cljs:301, src/app/main/ui/static.cljs:310, src/app/main/ui/static.cljs:319, src/app/main/ui/static.cljs:565 msgid "not-found.no-permission.go-dashboard" msgstr "前往個人專案" -#: src/app/main/ui/static.cljs:302, src/app/main/ui/static.cljs:311 +#: src/app/main/ui/static.cljs:307, src/app/main/ui/static.cljs:316 msgid "not-found.no-permission.if-approves" msgstr "擁有者核准後,您將收到加入團隊的邀請。" -#: src/app/main/ui/static.cljs:477, src/app/main/ui/static.cljs:490 +#: src/app/main/ui/static.cljs:485, src/app/main/ui/static.cljs:498 msgid "not-found.no-permission.penpot-file" msgstr "Penpot 檔案" -#: src/app/main/ui/static.cljs:277, src/app/main/ui/static.cljs:309 +#: src/app/main/ui/static.cljs:282, src/app/main/ui/static.cljs:314 msgid "not-found.no-permission.project" msgstr "您沒有此專案的存取權限。" -#: src/app/main/ui/static.cljs:476, src/app/main/ui/static.cljs:488 +#: src/app/main/ui/static.cljs:484, src/app/main/ui/static.cljs:496 msgid "not-found.no-permission.project-name" msgstr "專案" -#: src/app/main/ui/static.cljs:301 +#: src/app/main/ui/static.cljs:306 msgid "not-found.no-permission.you-can-ask.file" msgstr "如需存取此檔案,請向團隊擁有者申請權限。" -#: src/app/main/ui/static.cljs:310 +#: src/app/main/ui/static.cljs:315 msgid "not-found.no-permission.you-can-ask.project" msgstr "如需存取此專案,請向團隊擁有者申請權限。" @@ -4187,31 +4131,31 @@ msgstr "維護通知:我們將在 5 分鐘內進行短暫維護,期間服務 msgid "notifications.by-code.upgrade-version" msgstr "已有新版本可用。" -#: src/app/main/ui/auth/verify_token.cljs:133, src/app/main/ui/auth/verify_token.cljs:136 +#: src/app/main/ui/auth/verify_token.cljs:106, src/app/main/ui/auth/verify_token.cljs:109 msgid "notifications.invitation-canceled" msgstr "此邀請已失效。" -#: src/app/main/ui/dashboard/team.cljs:1096 +#: src/app/main/ui/dashboard/team.cljs:1097 msgid "notifications.invitation-deleted" msgstr "已成功刪除邀請" -#: src/app/main/ui/dashboard/team.cljs:213, src/app/main/ui/dashboard/team.cljs:1137 +#: src/app/main/ui/dashboard/team.cljs:197, src/app/main/ui/dashboard/team.cljs:1138 msgid "notifications.invitation-email-sent" msgstr "已成功寄出邀請" -#: src/app/main/ui/dashboard/team.cljs:750 +#: src/app/main/ui/dashboard/team.cljs:745 msgid "notifications.invitation-link-copied" msgstr "已複製邀請連結" -#: src/app/main/ui/settings/delete_account.cljs:28 +#: src/app/main/ui/settings/delete_account.cljs:29 msgid "notifications.profile-deletion-not-allowed" msgstr "目前無法刪除您的個人資料。請先移轉您所擁有的團隊,再繼續操作。" -#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:34 +#: src/app/main/ui/settings/options.cljs:36, src/app/main/ui/settings/profile.cljs:36 msgid "notifications.profile-saved" msgstr "個人資料已成功儲存!" -#: src/app/main/ui/settings/change_email.cljs:46 +#: src/app/main/ui/settings/change_email.cljs:47 msgid "notifications.validation-email-sent" msgstr "已寄送驗證信至 %s。請查看您的信箱!" @@ -4247,7 +4191,7 @@ msgstr "團隊名稱" msgid "onboarding.choice.team-up.create-team-without-invite" msgstr "建立團隊" -#: src/app/main/ui/dashboard/projects.cljs:93, src/app/main/ui/onboarding/team_choice.cljs:207 +#: src/app/main/ui/dashboard/projects.cljs:101, src/app/main/ui/onboarding/team_choice.cljs:207 msgid "onboarding.choice.team-up.invite-members" msgstr "邀請成員" @@ -4255,95 +4199,95 @@ msgstr "邀請成員" msgid "onboarding.choice.team-up.invite-members-desc" msgstr "開發人員、設計師、管理者……歡迎所有人加入。您也可以稍後邀請更多成員。" -#: src/app/main/ui/dashboard/team.cljs:292 +#: src/app/main/ui/dashboard/team.cljs:276 msgid "onboarding.choice.team-up.roles" msgstr "以此角色邀請:" -#: src/app/main/ui/onboarding/questions.cljs:104 +#: src/app/main/ui/onboarding/questions.cljs:106 msgid "onboarding.questions.lets-get-started" msgstr "讓我們開始吧!" -#: src/app/main/ui/onboarding/questions.cljs:275 +#: src/app/main/ui/onboarding/questions.cljs:277 msgid "onboarding.questions.start-with.code" msgstr "將設計轉換成可用的程式碼" -#: src/app/main/ui/onboarding/questions.cljs:273 +#: src/app/main/ui/onboarding/questions.cljs:275 msgid "onboarding.questions.start-with.ds" msgstr "建立設計系統" -#: src/app/main/ui/onboarding/questions.cljs:271 +#: src/app/main/ui/onboarding/questions.cljs:273 msgid "onboarding.questions.start-with.prototyping" msgstr "原型設計" -#: src/app/main/ui/onboarding/questions.cljs:267 +#: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.start-with.ui" msgstr "設計應用程式的 UI/UX" -#: src/app/main/ui/onboarding/questions.cljs:269 +#: src/app/main/ui/onboarding/questions.cljs:271 msgid "onboarding.questions.start-with.wireframing" msgstr "繪製線框圖" -#: src/app/main/ui/onboarding/questions.cljs:114 +#: src/app/main/ui/onboarding/questions.cljs:116 msgid "onboarding.questions.step1.question1" msgstr "您打算用 Penpot 做什麼?" -#: src/app/main/ui/onboarding/questions.cljs:110 +#: src/app/main/ui/onboarding/questions.cljs:112 msgid "onboarding.questions.step1.subtitle" msgstr "讓我們多了解您一些,讓 Penpot 更符合您的需求。您的回答將幫助我們安排新功能的開發優先順序,並引導您順利入門。" -#: src/app/main/ui/onboarding/questions.cljs:107 +#: src/app/main/ui/onboarding/questions.cljs:109 msgid "onboarding.questions.step1.title" msgstr "讓我們更了解您" -#: src/app/main/ui/onboarding/questions.cljs:188 +#: src/app/main/ui/onboarding/questions.cljs:190 msgid "onboarding.questions.step2.title" msgstr "您最常使用下列哪一項工具?" -#: src/app/main/ui/onboarding/questions.cljs:121 +#: src/app/main/ui/onboarding/questions.cljs:123 msgid "onboarding.questions.step3.question1" msgstr "您從事哪一類工作?" -#: src/app/main/ui/onboarding/questions.cljs:242 +#: src/app/main/ui/onboarding/questions.cljs:244 msgid "onboarding.questions.step3.title" msgstr "您的公司規模有多大?" -#: src/app/main/ui/onboarding/questions.cljs:300 +#: src/app/main/ui/onboarding/questions.cljs:302 msgid "onboarding.questions.step4.title" msgstr "您想從哪裡開始?" -#: src/app/main/ui/onboarding/questions.cljs:226 +#: src/app/main/ui/onboarding/questions.cljs:228 msgid "onboarding.questions.team-size.1001-5000" msgstr "1,001 - 5,000" -#: src/app/main/ui/onboarding/questions.cljs:222 +#: src/app/main/ui/onboarding/questions.cljs:224 msgid "onboarding.questions.team-size.101-500" msgstr "101 - 500" -#: src/app/main/ui/onboarding/questions.cljs:220 +#: src/app/main/ui/onboarding/questions.cljs:222 msgid "onboarding.questions.team-size.2-100" msgstr "2 - 100" -#: src/app/main/ui/onboarding/questions.cljs:224 +#: src/app/main/ui/onboarding/questions.cljs:226 msgid "onboarding.questions.team-size.501-1000" msgstr "501 - 1,000" -#: src/app/main/ui/onboarding/questions.cljs:218 +#: src/app/main/ui/onboarding/questions.cljs:220 msgid "onboarding.questions.team-size.just-me" msgstr "只有我一個人" -#: src/app/main/ui/onboarding/questions.cljs:228 +#: src/app/main/ui/onboarding/questions.cljs:230 msgid "onboarding.questions.team-size.more-than-5001" msgstr "5,001+" -#: src/app/main/ui/onboarding/questions.cljs:76 +#: src/app/main/ui/onboarding/questions.cljs:78 msgid "onboarding.questions.use.education" msgstr "教育" -#: src/app/main/ui/onboarding/questions.cljs:77 +#: src/app/main/ui/onboarding/questions.cljs:79 msgid "onboarding.questions.use.personal" msgstr "個人" -#: src/app/main/ui/onboarding/questions.cljs:75 +#: src/app/main/ui/onboarding/questions.cljs:77 msgid "onboarding.questions.use.work" msgstr "工作" @@ -4423,11 +4367,11 @@ msgstr "還原為預設設定" msgid "restore-shortcuts.restore" msgstr "還原" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:990, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1034, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:425 msgid "settings.detach" msgstr "解除連結" -#: src/app/main/ui/ds/controls/numeric_input.cljs:742, src/app/main/ui/inspect/exports.cljs:170, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:345, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:502, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1057, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:244, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:755, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:793, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:86, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:189, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:572, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:595, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:634, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:647, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:656, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:667, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:688, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:699, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:622, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:679, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:698, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:410, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:451 +#: src/app/main/ui/ds/controls/numeric_input.cljs:744, src/app/main/ui/inspect/exports.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:192, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:222, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:243, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:261, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:297, src/app/main/ui/workspace/sidebar/options/menus/border_radius.cljs:299, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:352, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:509, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1071, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1315, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:163, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:174, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:225, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/input_wrapper_tokens.cljs:27, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:245, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:388, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:399, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:425, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:435, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:519, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:586, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:620, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:754, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:792, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:81, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:87, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:570, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:593, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:604, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:645, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:654, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:665, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:686, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:697, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:165, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:280, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:666, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:723, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:742, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:474, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:435, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:474 msgid "settings.multiple" msgstr "混合" @@ -4439,35 +4383,35 @@ msgstr "移除色彩" msgid "settings.select-this-color" msgstr "選取使用此樣式的項目" -#: src/app/main/data/workspace/shortcuts.cljs:431 +#: src/app/main/data/workspace/shortcuts.cljs:434 msgid "shortcuts.add-comment" msgstr "留言" -#: src/app/main/data/workspace/shortcuts.cljs:532 +#: src/app/main/data/workspace/shortcuts.cljs:535 msgid "shortcuts.align-bottom" msgstr "靠下對齊" -#: src/app/main/data/workspace/shortcuts.cljs:518 +#: src/app/main/data/workspace/shortcuts.cljs:521 msgid "shortcuts.align-hcenter" msgstr "水平置中對齊" -#: src/app/main/data/workspace/shortcuts.cljs:497 +#: src/app/main/data/workspace/shortcuts.cljs:500 msgid "shortcuts.align-left" msgstr "靠左對齊" -#: src/app/main/data/workspace/shortcuts.cljs:504 +#: src/app/main/data/workspace/shortcuts.cljs:507 msgid "shortcuts.align-right" msgstr "靠右對齊" -#: src/app/main/data/workspace/shortcuts.cljs:511 +#: src/app/main/data/workspace/shortcuts.cljs:514 msgid "shortcuts.align-top" msgstr "靠上對齊" -#: src/app/main/data/workspace/shortcuts.cljs:525 +#: src/app/main/data/workspace/shortcuts.cljs:528 msgid "shortcuts.align-vcenter" msgstr "垂直置中對齊" -#: src/app/main/data/workspace/shortcuts.cljs:341 +#: src/app/main/data/workspace/shortcuts.cljs:344 msgid "shortcuts.artboard-selection" msgstr "以選取項目建立畫板" @@ -4475,55 +4419,55 @@ msgstr "以選取項目建立畫板" msgid "shortcuts.bold" msgstr "切換粗體" -#: src/app/main/data/workspace/shortcuts.cljs:800 +#: src/app/main/data/workspace/shortcuts.cljs:802 msgid "shortcuts.bool-difference" msgstr "布林差集" -#: src/app/main/data/workspace/shortcuts.cljs:814 +#: src/app/main/data/workspace/shortcuts.cljs:816 msgid "shortcuts.bool-exclude" msgstr "布林排除" -#: src/app/main/data/workspace/shortcuts.cljs:807 +#: src/app/main/data/workspace/shortcuts.cljs:809 msgid "shortcuts.bool-intersection" msgstr "布林交集" -#: src/app/main/data/workspace/shortcuts.cljs:793 +#: src/app/main/data/workspace/shortcuts.cljs:795 msgid "shortcuts.bool-union" msgstr "布林聯集" -#: src/app/main/data/workspace/shortcuts.cljs:278 +#: src/app/main/data/workspace/shortcuts.cljs:281 msgid "shortcuts.bring-back" msgstr "移至最底層" -#: src/app/main/data/workspace/shortcuts.cljs:264 +#: src/app/main/data/workspace/shortcuts.cljs:267 msgid "shortcuts.bring-backward" msgstr "下移一層" -#: src/app/main/data/workspace/shortcuts.cljs:257 +#: src/app/main/data/workspace/shortcuts.cljs:260 msgid "shortcuts.bring-forward" msgstr "上移一層" -#: src/app/main/data/workspace/shortcuts.cljs:271 +#: src/app/main/data/workspace/shortcuts.cljs:274 msgid "shortcuts.bring-front" msgstr "移至最上層" -#: src/app/main/data/workspace/shortcuts.cljs:86 +#: src/app/main/data/workspace/shortcuts.cljs:87 msgid "shortcuts.clear-undo" msgstr "清除復原紀錄" -#: src/app/main/data/workspace/path/shortcuts.cljs:127, src/app/main/data/workspace/shortcuts.cljs:93 +#: src/app/main/data/workspace/path/shortcuts.cljs:119, src/app/main/data/workspace/shortcuts.cljs:94 msgid "shortcuts.copy" msgstr "複製" -#: src/app/main/data/workspace/shortcuts.cljs:100 +#: src/app/main/data/workspace/shortcuts.cljs:102 msgid "shortcuts.copy-link" msgstr "複製連結" -#: src/app/main/data/workspace/shortcuts.cljs:132 +#: src/app/main/data/workspace/shortcuts.cljs:134 msgid "shortcuts.copy-props" msgstr "複製屬性" -#: src/app/main/data/workspace/shortcuts.cljs:230 +#: src/app/main/data/workspace/shortcuts.cljs:233 msgid "shortcuts.create-component-variant" msgstr "建立元件 / 變體" @@ -4531,63 +4475,63 @@ msgstr "建立元件 / 變體" msgid "shortcuts.create-new-project" msgstr "新增專案" -#: src/app/main/data/workspace/path/shortcuts.cljs:133, src/app/main/data/workspace/shortcuts.cljs:107 +#: src/app/main/data/workspace/path/shortcuts.cljs:126, src/app/main/data/workspace/shortcuts.cljs:109 msgid "shortcuts.cut" msgstr "剪下" -#: src/app/main/data/viewer/shortcuts.cljs:34, src/app/main/data/workspace/grid_layout/shortcuts.cljs:63, src/app/main/data/workspace/path/shortcuts.cljs:214, src/app/main/data/workspace/shortcuts.cljs:697, src/app/main/ui/workspace/right_header.cljs:86 +#: src/app/main/data/viewer/shortcuts.cljs:35, src/app/main/data/workspace/grid_layout/shortcuts.cljs:64, src/app/main/data/workspace/path/shortcuts.cljs:211, src/app/main/data/workspace/shortcuts.cljs:699, src/app/main/ui/viewer/header.cljs:96, src/app/main/ui/workspace/right_header.cljs:88 msgid "shortcuts.decrease-zoom" msgstr "縮小" -#: src/app/main/data/workspace/shortcuts.cljs:146 +#: src/app/main/data/workspace/shortcuts.cljs:148 msgid "shortcuts.delete" msgstr "刪除" -#: src/app/main/data/workspace/path/shortcuts.cljs:68 +#: src/app/main/data/workspace/path/shortcuts.cljs:61 msgid "shortcuts.delete-node" msgstr "刪除節點" -#: src/app/main/data/workspace/path/shortcuts.cljs:76 +#: src/app/main/data/workspace/path/shortcuts.cljs:69 msgid "shortcuts.delete-node-and-segments" msgstr "刪除節點與線段" -#: src/app/main/data/workspace/path/shortcuts.cljs:157 +#: src/app/main/data/workspace/path/shortcuts.cljs:151 msgid "shortcuts.deselect-all" msgstr "取消全選" -#: src/app/main/data/workspace/shortcuts.cljs:237 +#: src/app/main/data/workspace/shortcuts.cljs:240 msgid "shortcuts.detach-component" msgstr "分離元件" -#: src/app/main/ui/shortcuts.cljs:662 +#: src/app/main/ui/shortcuts.cljs:795 msgid "shortcuts.disable" msgstr "停用快捷鍵" -#: src/app/main/ui/settings/shortcuts.cljs:299, src/app/main/ui/settings/shortcuts.cljs:529 +#: src/app/main/ui/settings/shortcuts.cljs:346, src/app/main/ui/settings/shortcuts.cljs:580 msgid "shortcuts.disabled" msgstr "已停用" -#: src/app/main/ui/shortcuts.cljs:735 +#: src/app/main/ui/shortcuts.cljs:868 msgid "shortcuts.disabled-shortcut" msgstr "已停用的快捷鍵" -#: src/app/main/data/workspace/shortcuts.cljs:417 +#: src/app/main/data/workspace/shortcuts.cljs:420 msgid "shortcuts.draw-arrow" msgstr "箭頭" -#: src/app/main/data/workspace/shortcuts.cljs:424 +#: src/app/main/data/workspace/shortcuts.cljs:427 msgid "shortcuts.draw-curve" msgstr "曲線" -#: src/app/main/data/workspace/shortcuts.cljs:388 +#: src/app/main/data/workspace/shortcuts.cljs:391 msgid "shortcuts.draw-ellipse" msgstr "橢圓" -#: src/app/main/data/workspace/shortcuts.cljs:367 +#: src/app/main/data/workspace/shortcuts.cljs:370 msgid "shortcuts.draw-frame" msgstr "畫板" -#: src/app/main/data/workspace/shortcuts.cljs:410 +#: src/app/main/data/workspace/shortcuts.cljs:413 msgid "shortcuts.draw-line" msgstr "直線" @@ -4595,63 +4539,63 @@ msgstr "直線" msgid "shortcuts.draw-nodes" msgstr "繪製路徑" -#: src/app/main/data/workspace/shortcuts.cljs:403 +#: src/app/main/data/workspace/shortcuts.cljs:406 msgid "shortcuts.draw-path" msgstr "路徑" -#: src/app/main/data/workspace/shortcuts.cljs:381 +#: src/app/main/data/workspace/shortcuts.cljs:384 msgid "shortcuts.draw-rect" msgstr "矩形" -#: src/app/main/data/workspace/shortcuts.cljs:395 +#: src/app/main/data/workspace/shortcuts.cljs:398 msgid "shortcuts.draw-text" msgstr "文字" -#: src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:153 +#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:155 msgid "shortcuts.duplicate" msgstr "建立複本" -#: src/app/main/ui/shortcuts.cljs:645 +#: src/app/main/ui/shortcuts.cljs:775 msgid "shortcuts.edit-modal.conflict" msgstr "此按鍵組合已指派給「%s」。儲存後將移除該功能的此組快捷鍵。" -#: src/app/main/ui/shortcuts.cljs:636 +#: src/app/main/ui/shortcuts.cljs:761 msgid "shortcuts.edit-modal.reset-pending" msgstr "儲存後,此快捷鍵將還原為原始設定:" -#: src/app/main/ui/shortcuts.cljs:647 +#: src/app/main/ui/shortcuts.cljs:779 msgid "shortcuts.edit-modal.success" msgstr "此按鍵組合可用,指派後不會產生衝突。" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:157 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:161 msgid "shortcuts.edit-on-settings" msgstr "在設定中自訂" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:175, src/app/main/data/workspace/shortcuts.cljs:177 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:37, src/app/main/data/workspace/path/shortcuts.cljs:170, src/app/main/data/workspace/shortcuts.cljs:179 msgid "shortcuts.escape" msgstr "取消" -#: src/app/main/data/workspace/shortcuts.cljs:606 +#: src/app/main/data/workspace/shortcuts.cljs:608 msgid "shortcuts.export-shapes" msgstr "匯出形狀" -#: src/app/main/data/workspace/shortcuts.cljs:184 +#: src/app/main/data/workspace/shortcuts.cljs:187 msgid "shortcuts.find" msgstr "尋找" -#: src/app/main/data/workspace/shortcuts.cljs:188 +#: src/app/main/data/workspace/shortcuts.cljs:191 msgid "shortcuts.find-and-replace" msgstr "尋找與取代" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:75, src/app/main/data/workspace/path/shortcuts.cljs:226, src/app/main/data/workspace/shortcuts.cljs:711 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:76, src/app/main/data/workspace/path/shortcuts.cljs:223, src/app/main/data/workspace/shortcuts.cljs:713 msgid "shortcuts.fit-all" msgstr "縮放至顯示全部內容" -#: src/app/main/data/workspace/path/shortcuts.cljs:163, src/app/main/data/workspace/shortcuts.cljs:251 +#: src/app/main/data/workspace/path/shortcuts.cljs:158, src/app/main/data/workspace/shortcuts.cljs:254 msgid "shortcuts.flip-horizontal" msgstr "水平翻轉" -#: src/app/main/data/workspace/path/shortcuts.cljs:169, src/app/main/data/workspace/shortcuts.cljs:244 +#: src/app/main/data/workspace/path/shortcuts.cljs:164, src/app/main/data/workspace/shortcuts.cljs:247 msgid "shortcuts.flip-vertical" msgstr "垂直翻轉" @@ -4675,27 +4619,27 @@ msgstr "前往共享資料庫" msgid "shortcuts.go-to-search" msgstr "搜尋" -#: src/app/main/data/workspace/shortcuts.cljs:202 +#: src/app/main/data/workspace/shortcuts.cljs:205 msgid "shortcuts.group" msgstr "建立群組" -#: src/app/main/data/workspace/shortcuts.cljs:539 +#: src/app/main/data/workspace/shortcuts.cljs:542 msgid "shortcuts.h-distribute" msgstr "水平平均分布" -#: src/app/main/data/workspace/shortcuts.cljs:681 +#: src/app/main/data/workspace/shortcuts.cljs:683 msgid "shortcuts.hide-ui" msgstr "顯示 / 隱藏介面" -#: src/app/main/ui/settings/shortcuts.cljs:536 +#: src/app/main/ui/settings/shortcuts.cljs:587 msgid "shortcuts.import-export" msgstr "匯入 / 匯出" -#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:208, src/app/main/data/workspace/shortcuts.cljs:690, src/app/main/ui/workspace/right_header.cljs:91 +#: src/app/main/data/viewer/shortcuts.cljs:19, src/app/main/data/workspace/grid_layout/shortcuts.cljs:57, src/app/main/data/workspace/path/shortcuts.cljs:204, src/app/main/data/workspace/shortcuts.cljs:692, src/app/main/ui/viewer/header.cljs:102, src/app/main/ui/workspace/right_header.cljs:93 msgid "shortcuts.increase-zoom" msgstr "放大" -#: src/app/main/data/workspace/shortcuts.cljs:446 +#: src/app/main/data/workspace/shortcuts.cljs:449 msgid "shortcuts.insert-image" msgstr "插入圖片" @@ -4703,11 +4647,11 @@ msgstr "插入圖片" msgid "shortcuts.italic" msgstr "切換斜體" -#: src/app/main/data/workspace/path/shortcuts.cljs:89 +#: src/app/main/data/workspace/path/shortcuts.cljs:82 msgid "shortcuts.join-nodes" msgstr "連接節點" -#: src/app/main/ui/shortcuts.cljs:624 +#: src/app/main/ui/shortcuts.cljs:749 msgid "shortcuts.key-combo" msgstr "按下組合鍵" @@ -4715,39 +4659,39 @@ msgstr "按下組合鍵" msgid "shortcuts.line-through" msgstr "切換刪除線" -#: src/app/main/data/workspace/path/shortcuts.cljs:104 +#: src/app/main/data/workspace/path/shortcuts.cljs:97 msgid "shortcuts.make-corner" msgstr "轉為角點" -#: src/app/main/data/workspace/path/shortcuts.cljs:111 +#: src/app/main/data/workspace/path/shortcuts.cljs:104 msgid "shortcuts.make-curve" msgstr "轉為曲線" -#: src/app/main/data/workspace/shortcuts.cljs:216 +#: src/app/main/data/workspace/shortcuts.cljs:219 msgid "shortcuts.mask" msgstr "遮罩" -#: src/app/main/data/workspace/path/shortcuts.cljs:82 +#: src/app/main/data/workspace/path/shortcuts.cljs:75 msgid "shortcuts.merge-nodes" msgstr "合併節點" -#: src/app/main/data/workspace/shortcuts.cljs:374 +#: src/app/main/data/workspace/shortcuts.cljs:377 msgid "shortcuts.move" msgstr "移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:246, src/app/main/data/workspace/shortcuts.cljs:292 +#: src/app/main/data/workspace/path/shortcuts.cljs:243, src/app/main/data/workspace/shortcuts.cljs:295 msgid "shortcuts.move-fast-down" msgstr "快速向下移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:258, src/app/main/data/workspace/shortcuts.cljs:306 +#: src/app/main/data/workspace/path/shortcuts.cljs:255, src/app/main/data/workspace/shortcuts.cljs:309 msgid "shortcuts.move-fast-left" msgstr "快速向左移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:252, src/app/main/data/workspace/shortcuts.cljs:299 +#: src/app/main/data/workspace/path/shortcuts.cljs:249, src/app/main/data/workspace/shortcuts.cljs:302 msgid "shortcuts.move-fast-right" msgstr "快速向右移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:240, src/app/main/data/workspace/shortcuts.cljs:285 +#: src/app/main/data/workspace/path/shortcuts.cljs:237, src/app/main/data/workspace/shortcuts.cljs:288 msgid "shortcuts.move-fast-up" msgstr "快速向上移動" @@ -4755,377 +4699,377 @@ msgstr "快速向上移動" msgid "shortcuts.move-nodes" msgstr "移動節點" -#: src/app/main/data/workspace/path/shortcuts.cljs:270, src/app/main/data/workspace/shortcuts.cljs:320 +#: src/app/main/data/workspace/path/shortcuts.cljs:267, src/app/main/data/workspace/shortcuts.cljs:323 msgid "shortcuts.move-unit-down" msgstr "向下移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:276, src/app/main/data/workspace/shortcuts.cljs:327 +#: src/app/main/data/workspace/path/shortcuts.cljs:273, src/app/main/data/workspace/shortcuts.cljs:330 msgid "shortcuts.move-unit-left" msgstr "向左移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:282, src/app/main/data/workspace/shortcuts.cljs:334 +#: src/app/main/data/workspace/path/shortcuts.cljs:279, src/app/main/data/workspace/shortcuts.cljs:337 msgid "shortcuts.move-unit-right" msgstr "向右移動" -#: src/app/main/data/workspace/path/shortcuts.cljs:264, src/app/main/data/workspace/shortcuts.cljs:313 +#: src/app/main/data/workspace/path/shortcuts.cljs:261, src/app/main/data/workspace/shortcuts.cljs:316 msgid "shortcuts.move-unit-up" msgstr "向上移動" -#: src/app/main/data/viewer/shortcuts.cljs:76 +#: src/app/main/data/viewer/shortcuts.cljs:77 msgid "shortcuts.next-frame" msgstr "下一個畫板" -#: src/app/main/ui/settings/shortcuts.cljs:510 +#: src/app/main/ui/settings/shortcuts.cljs:561 msgid "shortcuts.no-disabled" msgstr "沒有已停用的快捷鍵" -#: src/app/main/ui/settings/shortcuts.cljs:500 +#: src/app/main/ui/settings/shortcuts.cljs:551 msgid "shortcuts.no-personalized" msgstr "前往「全部」開始自訂快捷鍵。" -#: src/app/main/ui/settings/shortcuts.cljs:242, src/app/main/ui/settings/shortcuts.cljs:491 +#: src/app/main/ui/settings/shortcuts.cljs:289, src/app/main/ui/settings/shortcuts.cljs:542 msgid "shortcuts.no-shortcuts" msgstr "沒有符合搜尋條件的快捷鍵" -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:154 +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:158 msgid "shortcuts.not-found" msgstr "找不到快捷鍵" -#: src/app/main/data/workspace/shortcuts.cljs:848 +#: src/app/main/data/workspace/shortcuts.cljs:850 msgid "shortcuts.opacity-0" msgstr "將不透明度設為 100%" -#: src/app/main/data/workspace/shortcuts.cljs:854 +#: src/app/main/data/workspace/shortcuts.cljs:856 msgid "shortcuts.opacity-1" msgstr "將不透明度設為 10%" -#: src/app/main/data/workspace/shortcuts.cljs:860 +#: src/app/main/data/workspace/shortcuts.cljs:862 msgid "shortcuts.opacity-2" msgstr "將不透明度設為 20%" -#: src/app/main/data/workspace/shortcuts.cljs:866 +#: src/app/main/data/workspace/shortcuts.cljs:868 msgid "shortcuts.opacity-3" msgstr "將不透明度設為 30%" -#: src/app/main/data/workspace/shortcuts.cljs:872 +#: src/app/main/data/workspace/shortcuts.cljs:874 msgid "shortcuts.opacity-4" msgstr "將不透明度設為 40%" -#: src/app/main/data/workspace/shortcuts.cljs:878 +#: src/app/main/data/workspace/shortcuts.cljs:880 msgid "shortcuts.opacity-5" msgstr "將不透明度設為 50%" -#: src/app/main/data/workspace/shortcuts.cljs:884 +#: src/app/main/data/workspace/shortcuts.cljs:886 msgid "shortcuts.opacity-6" msgstr "將不透明度設為 60%" -#: src/app/main/data/workspace/shortcuts.cljs:890 +#: src/app/main/data/workspace/shortcuts.cljs:892 msgid "shortcuts.opacity-7" msgstr "將不透明度設為 70%" -#: src/app/main/data/workspace/shortcuts.cljs:896 +#: src/app/main/data/workspace/shortcuts.cljs:898 msgid "shortcuts.opacity-8" msgstr "將不透明度設為 80%" -#: src/app/main/data/workspace/shortcuts.cljs:902 +#: src/app/main/data/workspace/shortcuts.cljs:904 msgid "shortcuts.opacity-9" msgstr "將不透明度設為 90%" -#: src/app/main/data/workspace/shortcuts.cljs:481 +#: src/app/main/data/workspace/shortcuts.cljs:484 msgid "shortcuts.open-color-picker" msgstr "色彩選擇器" -#: src/app/main/data/viewer/shortcuts.cljs:90, src/app/main/data/workspace/shortcuts.cljs:756 +#: src/app/main/data/viewer/shortcuts.cljs:91, src/app/main/data/workspace/shortcuts.cljs:758 msgid "shortcuts.open-comments" msgstr "前往檢視器的留言區" -#: src/app/main/data/workspace/shortcuts.cljs:763 +#: src/app/main/data/workspace/shortcuts.cljs:765 msgid "shortcuts.open-dashboard" msgstr "前往控制台" -#: src/app/main/data/viewer/shortcuts.cljs:83, src/app/main/data/workspace/shortcuts.cljs:749 +#: src/app/main/data/viewer/shortcuts.cljs:84, src/app/main/data/workspace/shortcuts.cljs:751 msgid "shortcuts.open-inspect" msgstr "前往檢視器的檢查區" -#: src/app/main/data/viewer/shortcuts.cljs:97 +#: src/app/main/data/viewer/shortcuts.cljs:98 msgid "shortcuts.open-interactions" msgstr "前往檢視器的互動區" -#: src/app/main/data/workspace/shortcuts.cljs:742 +#: src/app/main/data/workspace/shortcuts.cljs:744 msgid "shortcuts.open-viewer" msgstr "前往檢視器的互動區" -#: src/app/main/data/viewer/shortcuts.cljs:104 +#: src/app/main/data/viewer/shortcuts.cljs:105 msgid "shortcuts.open-workspace" msgstr "前往工作區" -#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:437 +#: src/app/main/ui/settings/restore_shortcuts_modal.cljs:134, src/app/main/ui/settings/restore_shortcuts_modal.cljs:152, src/app/main/ui/shortcuts.cljs:559 msgid "shortcuts.or" msgstr " 或 " -#: src/app/main/ui/shortcuts.cljs:605, src/app/main/ui/shortcuts.cljs:747 +#: src/app/main/ui/shortcuts.cljs:730, src/app/main/ui/shortcuts.cljs:880 msgid "shortcuts.overwritten-by" msgstr "此快捷鍵已重新指派給 %s" -#: src/app/main/ui/settings/shortcuts.cljs:468 +#: src/app/main/ui/settings/shortcuts.cljs:519 msgid "shortcuts.page" msgstr "快捷鍵頁面" -#: src/app/main/data/workspace/path/shortcuts.cljs:139, src/app/main/data/workspace/shortcuts.cljs:116 +#: src/app/main/data/workspace/path/shortcuts.cljs:132, src/app/main/data/workspace/shortcuts.cljs:118 msgid "shortcuts.paste" msgstr "貼上" -#: src/app/main/data/workspace/shortcuts.cljs:139 +#: src/app/main/data/workspace/shortcuts.cljs:141 msgid "shortcuts.paste-props" msgstr "貼上屬性" -#: src/app/main/data/workspace/shortcuts.cljs:125 +#: src/app/main/data/workspace/shortcuts.cljs:127 msgid "shortcuts.paste-replace" msgstr "貼上並取代" -#: src/app/main/ui/settings/shortcuts.cljs:296, src/app/main/ui/settings/shortcuts.cljs:523 +#: src/app/main/ui/settings/shortcuts.cljs:343, src/app/main/ui/settings/shortcuts.cljs:574 msgid "shortcuts.personalized" msgstr "個人化" -#: src/app/main/data/workspace/shortcuts.cljs:832 +#: src/app/main/data/workspace/shortcuts.cljs:834 msgid "shortcuts.plugins" msgstr "外掛管理員" -#: src/app/main/data/viewer/shortcuts.cljs:69 +#: src/app/main/data/viewer/shortcuts.cljs:70 msgid "shortcuts.prev-frame" msgstr "上一個畫板" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:200, src/app/main/data/workspace/shortcuts.cljs:79 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:49, src/app/main/data/workspace/path/shortcuts.cljs:196, src/app/main/data/workspace/shortcuts.cljs:80 msgid "shortcuts.redo" msgstr "重做" -#: src/app/main/ui/settings/shortcuts.cljs:479 +#: src/app/main/ui/settings/shortcuts.cljs:530 msgid "shortcuts.reload-hint" msgstr "編輯快捷鍵後,請重新載入其他已開啟的 Penpot 分頁,以套用變更。" -#: src/app/main/data/workspace/shortcuts.cljs:195 +#: src/app/main/data/workspace/shortcuts.cljs:198 msgid "shortcuts.rename" msgstr "重新命名" -#: src/app/main/ui/shortcuts.cljs:670 +#: src/app/main/ui/shortcuts.cljs:803 msgid "shortcuts.reset" msgstr "還原為預設值" -#: src/app/main/data/viewer/shortcuts.cljs:48, src/app/main/data/workspace/grid_layout/shortcuts.cljs:69, src/app/main/data/workspace/path/shortcuts.cljs:220, src/app/main/data/workspace/shortcuts.cljs:704 +#: src/app/main/data/viewer/shortcuts.cljs:49, src/app/main/data/workspace/grid_layout/shortcuts.cljs:70, src/app/main/data/workspace/path/shortcuts.cljs:217, src/app/main/data/workspace/shortcuts.cljs:706 msgid "shortcuts.reset-zoom" msgstr "重設縮放比例" -#: src/app/main/data/workspace/shortcuts.cljs:474 +#: src/app/main/data/workspace/shortcuts.cljs:477 msgid "shortcuts.scale" msgstr "縮放" # SECTIONS -#: src/app/main/ui/shortcuts.cljs:126, src/app/main/ui/shortcuts.cljs:298 +#: src/app/main/ui/shortcuts.cljs:255, src/app/main/ui/shortcuts.cljs:427 msgid "shortcuts.section.basics" msgstr "基本" -#: src/app/main/ui/shortcuts.cljs:128, src/app/main/ui/shortcuts.cljs:302, src/app/main/ui/shortcuts.cljs:369 +#: src/app/main/ui/shortcuts.cljs:257, src/app/main/ui/shortcuts.cljs:431, src/app/main/ui/shortcuts.cljs:498 msgid "shortcuts.section.dashboard" msgstr "儀表板" -#: src/app/main/ui/shortcuts.cljs:129, src/app/main/ui/shortcuts.cljs:304, src/app/main/ui/shortcuts.cljs:371 +#: src/app/main/ui/shortcuts.cljs:258, src/app/main/ui/shortcuts.cljs:433, src/app/main/ui/shortcuts.cljs:500 msgid "shortcuts.section.viewer" msgstr "檢視者" -#: src/app/main/ui/shortcuts.cljs:127, src/app/main/ui/shortcuts.cljs:300, src/app/main/ui/shortcuts.cljs:367 +#: src/app/main/ui/shortcuts.cljs:256, src/app/main/ui/shortcuts.cljs:429, src/app/main/ui/shortcuts.cljs:496 msgid "shortcuts.section.workspace" msgstr "工作區" -#: src/app/main/data/viewer/shortcuts.cljs:41, src/app/main/data/workspace/path/shortcuts.cljs:151, src/app/main/data/workspace/shortcuts.cljs:562 +#: src/app/main/data/viewer/shortcuts.cljs:42, src/app/main/data/workspace/path/shortcuts.cljs:145, src/app/main/data/workspace/shortcuts.cljs:565 msgid "shortcuts.select-all" msgstr "全選" -#: src/app/main/data/workspace/shortcuts.cljs:777 +#: src/app/main/data/workspace/shortcuts.cljs:779 msgid "shortcuts.select-next" msgstr "選取下一個圖層" -#: src/app/main/data/workspace/shortcuts.cljs:784 +#: src/app/main/data/workspace/shortcuts.cljs:786 msgid "shortcuts.select-parent-layer" msgstr "選取父圖層" -#: src/app/main/data/workspace/shortcuts.cljs:770 +#: src/app/main/data/workspace/shortcuts.cljs:772 msgid "shortcuts.select-prev" msgstr "選取上一個圖層" -#: src/app/main/data/workspace/path/shortcuts.cljs:96 +#: src/app/main/data/workspace/path/shortcuts.cljs:89 msgid "shortcuts.separate-nodes" msgstr "分離節點" -#: src/app/main/data/workspace/shortcuts.cljs:167 +#: src/app/main/data/workspace/shortcuts.cljs:169 msgid "shortcuts.show-measure" msgstr "顯示測量數值" -#: src/app/main/data/workspace/shortcuts.cljs:592 +#: src/app/main/data/workspace/shortcuts.cljs:594 msgid "shortcuts.show-pixel-grid" msgstr "顯示 / 隱藏像素網格" -#: src/app/main/data/workspace/shortcuts.cljs:630 +#: src/app/main/data/workspace/shortcuts.cljs:631 msgid "shortcuts.show-shortcuts" msgstr "顯示 / 隱藏快捷鍵" -#: src/app/main/data/workspace/path/shortcuts.cljs:119 +#: src/app/main/data/workspace/path/shortcuts.cljs:112 msgid "shortcuts.snap-nodes" msgstr "貼齊至節點" -#: src/app/main/data/workspace/shortcuts.cljs:599 +#: src/app/main/data/workspace/shortcuts.cljs:601 msgid "shortcuts.snap-pixel-grid" msgstr "貼齊至像素網格" -#: src/app/main/data/workspace/path/shortcuts.cljs:187, src/app/main/data/workspace/shortcuts.cljs:160 +#: src/app/main/data/workspace/path/shortcuts.cljs:183, src/app/main/data/workspace/shortcuts.cljs:162 msgid "shortcuts.start-editing" msgstr "開始編輯" # SUBSECTIONS -#: src/app/main/ui/shortcuts.cljs:132 +#: src/app/main/ui/shortcuts.cljs:261 msgid "shortcuts.subsection.alignment" msgstr "對齊" -#: src/app/main/ui/shortcuts.cljs:133 +#: src/app/main/ui/shortcuts.cljs:262 msgid "shortcuts.subsection.edit" msgstr "編輯" -#: src/app/main/ui/shortcuts.cljs:134 +#: src/app/main/ui/shortcuts.cljs:263 msgid "shortcuts.subsection.generic" msgstr "一般" -#: src/app/main/ui/shortcuts.cljs:135, src/app/main/ui/workspace/main_menu.cljs:1008 +#: src/app/main/ui/shortcuts.cljs:264, src/app/main/ui/workspace/main_menu.cljs:1009 msgid "shortcuts.subsection.main-menu" msgstr "主功能表" -#: src/app/main/ui/shortcuts.cljs:136 +#: src/app/main/ui/shortcuts.cljs:265 msgid "shortcuts.subsection.modify-layers" msgstr "修改圖層" -#: src/app/main/ui/shortcuts.cljs:137 +#: src/app/main/ui/shortcuts.cljs:266 msgid "shortcuts.subsection.navigation-dashboard" msgstr "導航" -#: src/app/main/ui/shortcuts.cljs:138 +#: src/app/main/ui/shortcuts.cljs:267 msgid "shortcuts.subsection.navigation-viewer" msgstr "導航" -#: src/app/main/ui/shortcuts.cljs:139 +#: src/app/main/ui/shortcuts.cljs:268 msgid "shortcuts.subsection.navigation-workspace" msgstr "導航" -#: src/app/main/ui/shortcuts.cljs:140 +#: src/app/main/ui/shortcuts.cljs:269 msgid "shortcuts.subsection.panels" msgstr "面板" -#: src/app/main/ui/shortcuts.cljs:141 +#: src/app/main/ui/shortcuts.cljs:270 msgid "shortcuts.subsection.path-editor" msgstr "路徑" -#: src/app/main/ui/shortcuts.cljs:142 +#: src/app/main/ui/shortcuts.cljs:271 msgid "shortcuts.subsection.shape" msgstr "形狀" -#: src/app/main/ui/shortcuts.cljs:143 +#: src/app/main/ui/shortcuts.cljs:272 msgid "shortcuts.subsection.text-editor" msgstr "文字" -#: src/app/main/ui/shortcuts.cljs:144 +#: src/app/main/ui/shortcuts.cljs:273 msgid "shortcuts.subsection.tools" msgstr "工具" -#: src/app/main/ui/shortcuts.cljs:145 +#: src/app/main/ui/shortcuts.cljs:274 msgid "shortcuts.subsection.zoom-viewer" msgstr "縮放" -#: src/app/main/ui/shortcuts.cljs:146 +#: src/app/main/ui/shortcuts.cljs:275 msgid "shortcuts.subsection.zoom-workspace" msgstr "縮放" -#: src/app/main/data/workspace/shortcuts.cljs:585 +#: src/app/main/data/workspace/shortcuts.cljs:587 msgid "shortcuts.thumbnail-set" msgstr "設定縮圖" -#: src/app/main/ui/settings/shortcuts.cljs:170, src/app/main/ui/workspace/sidebar/shortcuts.cljs:132 +#: src/app/main/ui/settings/shortcuts.cljs:217, src/app/main/ui/workspace/sidebar/shortcuts.cljs:136 msgid "shortcuts.title" msgstr "鍵盤快速鍵" -#: src/app/main/data/workspace/shortcuts.cljs:578 +#: src/app/main/data/workspace/shortcuts.cljs:580 msgid "shortcuts.toggle-alignment" msgstr "切換動態對齊" -#: src/app/main/data/workspace/shortcuts.cljs:646 +#: src/app/main/data/workspace/shortcuts.cljs:647 msgid "shortcuts.toggle-assets" msgstr "切換資產面板" -#: src/app/main/data/workspace/shortcuts.cljs:661 +#: src/app/main/data/workspace/shortcuts.cljs:663 msgid "shortcuts.toggle-colorpalette" msgstr "顯示 / 隱藏調色盤" -#: src/app/main/data/workspace/shortcuts.cljs:439 +#: src/app/main/data/workspace/shortcuts.cljs:442 msgid "shortcuts.toggle-comments-visibility" msgstr "顯示 / 隱藏留言" -#: src/app/main/data/workspace/shortcuts.cljs:488 +#: src/app/main/data/workspace/shortcuts.cljs:491 msgid "shortcuts.toggle-focus-mode" msgstr "切換專注模式" -#: src/app/main/data/viewer/shortcuts.cljs:62 +#: src/app/main/data/viewer/shortcuts.cljs:63 msgid "shortcuts.toggle-fullscreen" msgstr "切換全螢幕" -#: src/app/main/data/workspace/shortcuts.cljs:569 +#: src/app/main/data/workspace/shortcuts.cljs:572 msgid "shortcuts.toggle-guides" msgstr "顯示 / 隱藏參考線" -#: src/app/main/data/workspace/shortcuts.cljs:653 +#: src/app/main/data/workspace/shortcuts.cljs:654 msgid "shortcuts.toggle-history" msgstr "顯示 / 隱藏歷史記錄" -#: src/app/main/data/workspace/shortcuts.cljs:639 +#: src/app/main/data/workspace/shortcuts.cljs:640 msgid "shortcuts.toggle-layers" msgstr "顯示 / 隱藏圖層面板" -#: src/app/main/data/workspace/shortcuts.cljs:348 +#: src/app/main/data/workspace/shortcuts.cljs:351 msgid "shortcuts.toggle-layout-flex" msgstr "新增 / 移除彈性佈局" -#: src/app/main/data/workspace/shortcuts.cljs:357 +#: src/app/main/data/workspace/shortcuts.cljs:360 msgid "shortcuts.toggle-layout-grid" msgstr "新增/移除網格佈局" -#: src/app/main/data/workspace/shortcuts.cljs:460 +#: src/app/main/data/workspace/shortcuts.cljs:463 msgid "shortcuts.toggle-lock" msgstr "鎖定 / 解除鎖定" -#: src/app/main/data/workspace/shortcuts.cljs:467 +#: src/app/main/data/workspace/shortcuts.cljs:470 msgid "shortcuts.toggle-lock-size" msgstr "鎖定比例" -#: src/app/main/data/workspace/shortcuts.cljs:555 +#: src/app/main/data/workspace/shortcuts.cljs:558 msgid "shortcuts.toggle-rulers" msgstr "顯示 / 隱藏尺規" -#: src/app/main/data/workspace/shortcuts.cljs:621 +#: src/app/main/data/workspace/shortcuts.cljs:623 msgid "shortcuts.toggle-snap-guides" msgstr "貼齊輔助線" -#: src/app/main/data/workspace/shortcuts.cljs:614 +#: src/app/main/data/workspace/shortcuts.cljs:616 msgid "shortcuts.toggle-snap-ruler-guide" msgstr "貼齊尺規輔助線" -#: src/app/main/data/workspace/shortcuts.cljs:671 +#: src/app/main/data/workspace/shortcuts.cljs:673 msgid "shortcuts.toggle-textpalette" msgstr "顯示 / 隱藏文字樣式面板" -#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:26, src/app/main/data/workspace/shortcuts.cljs:822 +#: src/app/main/data/dashboard/shortcuts.cljs:20, src/app/main/data/viewer/shortcuts.cljs:27, src/app/main/data/workspace/shortcuts.cljs:824 msgid "shortcuts.toggle-theme" msgstr "切換主題" -#: src/app/main/data/workspace/shortcuts.cljs:453 +#: src/app/main/data/workspace/shortcuts.cljs:456 msgid "shortcuts.toggle-visibility" msgstr "顯示 / 隱藏" -#: src/app/main/data/viewer/shortcuts.cljs:55 +#: src/app/main/data/viewer/shortcuts.cljs:56 msgid "shortcuts.toggle-zoom-style" msgstr "切換縮放模式" @@ -5133,31 +5077,31 @@ msgstr "切換縮放模式" msgid "shortcuts.underline" msgstr "顯示/隱藏底線" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:194, src/app/main/data/workspace/shortcuts.cljs:72 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:43, src/app/main/data/workspace/path/shortcuts.cljs:190, src/app/main/data/workspace/shortcuts.cljs:73 msgid "shortcuts.undo" msgstr "復原" -#: src/app/main/data/workspace/shortcuts.cljs:209 +#: src/app/main/data/workspace/shortcuts.cljs:212 msgid "shortcuts.ungroup" msgstr "取消群組" -#: src/app/main/data/workspace/shortcuts.cljs:223 +#: src/app/main/data/workspace/shortcuts.cljs:226 msgid "shortcuts.unmask" msgstr "解除遮罩" -#: src/app/main/data/workspace/shortcuts.cljs:546 +#: src/app/main/data/workspace/shortcuts.cljs:549 msgid "shortcuts.v-distribute" msgstr "垂直平均分布" -#: src/app/main/data/workspace/shortcuts.cljs:732 +#: src/app/main/data/workspace/shortcuts.cljs:734 msgid "shortcuts.zoom-lense-decrease" msgstr "降低放大鏡倍率" -#: src/app/main/data/workspace/shortcuts.cljs:725 +#: src/app/main/data/workspace/shortcuts.cljs:727 msgid "shortcuts.zoom-lense-increase" msgstr "提高放大鏡倍率" -#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:81, src/app/main/data/workspace/path/shortcuts.cljs:232, src/app/main/data/workspace/shortcuts.cljs:718 +#: src/app/main/data/workspace/grid_layout/shortcuts.cljs:82, src/app/main/data/workspace/path/shortcuts.cljs:229, src/app/main/data/workspace/shortcuts.cljs:720 msgid "shortcuts.zoom-selected" msgstr "縮放至選取範圍" @@ -5173,7 +5117,7 @@ msgstr "體驗企業版" msgid "subscription.current-plan.enterprise" msgstr "企業版" -#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:785 +#: src/app/main/ui/dashboard/subscription.cljs:268, src/app/main/ui/settings/subscription.cljs:808 msgid "subscription.current-plan.nitrate" msgstr "企業版" @@ -5185,7 +5129,7 @@ msgstr "企業版試用" msgid "subscription.current-plan.professional" msgstr "專業版" -#: src/app/main/ui/dashboard/subscription.cljs:257 +#: src/app/main/ui/dashboard/subscription.cljs:259 msgid "subscription.current-plan.title" msgstr "你的訂閱" @@ -5299,257 +5243,257 @@ msgstr "未來的帳單只會計入您擁有的所有團隊中新增的編輯者 msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" msgstr "使用 Unlimited 方案時邀請成員" -#: src/app/main/ui/settings/subscription.cljs:432 +#: src/app/main/ui/settings/subscription.cljs:439 msgid "subscription.error.nitrate.checkout-cancelled" msgstr "付款尚未完成。準備好後,隨時可以重試。" -#: src/app/main/ui/settings/subscription.cljs:431, src/app/main/ui/settings/subscription.cljs:451 +#: src/app/main/ui/settings/subscription.cljs:438, src/app/main/ui/settings/subscription.cljs:458 msgid "subscription.error.nitrate.checkout-failed" msgstr "付款尚未完成。請再試一次。如果問題持續發生,請聯絡我們:support@penpot.app。" -#: src/app/main/ui/settings/sidebar.cljs:126, src/app/main/ui/settings/subscription.cljs:550, src/app/main/ui/settings/subscription.cljs:611 +#: src/app/main/ui/settings/sidebar.cljs:122, src/app/main/ui/settings/subscription.cljs:564, src/app/main/ui/settings/subscription.cljs:625 msgid "subscription.labels" msgstr "訂閱" -#: src/app/main/ui/settings/subscription.cljs:105 +#: src/app/main/ui/settings/subscription.cljs:107 msgid "subscription.settings.activate-by-code" msgstr "輸入啟用碼" -#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:686 +#: src/app/main/ui/settings/subscription.cljs:676, src/app/main/ui/settings/subscription.cljs:702 msgid "subscription.settings.add-payment-to-continue" msgstr "新增付款方式,即可在試用期結束後繼續使用" -#: src/app/main/ui/settings/subscription.cljs:626 +#: src/app/main/ui/settings/subscription.cljs:642 msgid "subscription.settings.admin-console" msgstr "管理控制台" -#: src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:745 +#: src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:768 msgid "subscription.settings.benefits.all-professional-benefits" msgstr "包含 Professional 方案的所有權益,以及:" -#: src/app/main/ui/settings/subscription.cljs:667, src/app/main/ui/settings/subscription.cljs:680, src/app/main/ui/settings/subscription.cljs:691, src/app/main/ui/settings/subscription.cljs:761 +#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:696, src/app/main/ui/settings/subscription.cljs:707, src/app/main/ui/settings/subscription.cljs:784 msgid "subscription.settings.benefits.all-unlimited-benefits" msgstr "包含 Unlimited 方案的所有權益,以及:" -#: src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:776 +#: src/app/main/ui/settings/subscription.cljs:635, src/app/main/ui/settings/subscription.cljs:799 msgid "subscription.settings.benefits.nitrate-unlimited-benefits" msgstr "包含 Unlimited 方案的所有權益,另享:" -#: src/app/main/ui/settings/subscription.cljs:64 +#: src/app/main/ui/settings/subscription.cljs:65 msgid "subscription.settings.editors" msgstr "(x %s 位編輯者)" -#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:467, src/app/main/ui/settings/subscription.cljs:469, src/app/main/ui/settings/subscription.cljs:605, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:689, src/app/main/ui/settings/subscription.cljs:757, src/app/main/ui/settings/subscription.cljs:773 +#: src/app/main/ui/dashboard/subscription.cljs:299, src/app/main/ui/settings/subscription.cljs:126, src/app/main/ui/settings/subscription.cljs:474, src/app/main/ui/settings/subscription.cljs:476, src/app/main/ui/settings/subscription.cljs:619, src/app/main/ui/settings/subscription.cljs:632, src/app/main/ui/settings/subscription.cljs:705, src/app/main/ui/settings/subscription.cljs:780, src/app/main/ui/settings/subscription.cljs:796 msgid "subscription.settings.enterprise" msgstr "Enterprise" -#: src/app/main/ui/settings/subscription.cljs:120, src/app/main/ui/settings/subscription.cljs:604, src/app/main/ui/settings/subscription.cljs:678 +#: src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:618, src/app/main/ui/settings/subscription.cljs:631, src/app/main/ui/settings/subscription.cljs:694 msgid "subscription.settings.enterprise-trial" msgstr "Enterprise(試用版)" -#: src/app/main/ui/settings/subscription.cljs:682, src/app/main/ui/settings/subscription.cljs:693, src/app/main/ui/settings/subscription.cljs:763 +#: src/app/main/ui/settings/subscription.cljs:698, src/app/main/ui/settings/subscription.cljs:709, src/app/main/ui/settings/subscription.cljs:786 msgid "subscription.settings.enterprise.autosave" msgstr "保留 90 天的自動儲存版本,並提供檔案復原功能" -#: src/app/main/ui/settings/subscription.cljs:683, src/app/main/ui/settings/subscription.cljs:694, src/app/main/ui/settings/subscription.cljs:764 +#: src/app/main/ui/settings/subscription.cljs:699, src/app/main/ui/settings/subscription.cljs:710, src/app/main/ui/settings/subscription.cljs:787 msgid "subscription.settings.enterprise.capped-bill" msgstr "每月固定費用" -#: src/app/main/ui/settings/subscription.cljs:624, src/app/main/ui/settings/subscription.cljs:781 +#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:804 msgid "subscription.settings.enterprise.nitrate.advanced-control" msgstr "進階控管:" -#: src/app/main/ui/settings/subscription.cljs:622, src/app/main/ui/settings/subscription.cljs:779 +#: src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:802 msgid "subscription.settings.enterprise.nitrate.enterprise-security" msgstr "企業級安全性:" -#: src/app/main/ui/settings/subscription.cljs:620, src/app/main/ui/settings/subscription.cljs:777 +#: src/app/main/ui/settings/subscription.cljs:636, src/app/main/ui/settings/subscription.cljs:800 msgid "subscription.settings.enterprise.nitrate.multi-organization-management" msgstr "多組織管理:" -#: src/app/main/ui/settings/subscription.cljs:623, src/app/main/ui/settings/subscription.cljs:780 +#: src/app/main/ui/settings/subscription.cljs:639, src/app/main/ui/settings/subscription.cljs:803 msgid "subscription.settings.enterprise.nitrate.native-sso" msgstr "原生支援 SAML/SSO,並提供完整的稽核軌跡紀錄,以供製作法規遵循報告。" -#: src/app/main/ui/settings/subscription.cljs:625, src/app/main/ui/settings/subscription.cljs:782 +#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:805 msgid "subscription.settings.enterprise.nitrate.plugin-whitelisting" msgstr "精細的外掛允許清單控管,涵蓋我們用於連接 AI 的安全、隔離式 MCP 伺服器。" -#: src/app/main/ui/settings/subscription.cljs:621, src/app/main/ui/settings/subscription.cljs:778 +#: src/app/main/ui/settings/subscription.cljs:637, src/app/main/ui/settings/subscription.cljs:801 msgid "subscription.settings.enterprise.nitrate.support-team" msgstr "支援複雜的團隊階層架構與獨立的子組織。" -#: src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:692, src/app/main/ui/settings/subscription.cljs:762 +#: src/app/main/ui/settings/subscription.cljs:697, src/app/main/ui/settings/subscription.cljs:708, src/app/main/ui/settings/subscription.cljs:785 msgid "subscription.settings.enterprise.unlimited-storage-benefit" msgstr "無限儲存空間" -#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:629, src/app/main/ui/settings/subscription.cljs:658, src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:695 +#: src/app/main/ui/dashboard/subscription.cljs:304, src/app/main/ui/settings/subscription.cljs:645, src/app/main/ui/settings/subscription.cljs:674, src/app/main/ui/settings/subscription.cljs:687, src/app/main/ui/settings/subscription.cljs:700, src/app/main/ui/settings/subscription.cljs:711 msgid "subscription.settings.manage-your-subscription" msgstr "管理你的訂閱" -#: src/app/main/ui/settings/subscription.cljs:352 +#: src/app/main/ui/settings/subscription.cljs:356 msgid "subscription.settings.management-dialog.step-2-add-payment-button" msgstr "新增付款資訊" -#: src/app/main/ui/settings/subscription.cljs:339 +#: src/app/main/ui/settings/subscription.cljs:343 msgid "subscription.settings.management-dialog.step-2-description" msgstr "立即新增付款資訊,讓訂閱在試用結束後順利延續,並持續支持我們的開源專案。目前不會向你收費。" -#: src/app/main/ui/settings/subscription.cljs:347 +#: src/app/main/ui/settings/subscription.cljs:351 msgid "subscription.settings.management-dialog.step-2-skip-button" msgstr "暫時略過並開始試用" -#: src/app/main/ui/settings/subscription.cljs:270 +#: src/app/main/ui/settings/subscription.cljs:274 msgid "subscription.settings.management-dialog.step-2-title" msgstr "支持我們成長,讓試用更順暢" -#: src/app/main/ui/settings/subscription.cljs:136 +#: src/app/main/ui/settings/subscription.cljs:138 msgid "subscription.settings.management.dialog.currently-editors-title" msgid_plural "subscription.settings.management.dialog.currently-editors-title" msgstr[0] "目前,你的所有團隊共有 %s 人具備編輯權限。" -#: src/app/main/ui/settings/subscription.cljs:284 +#: src/app/main/ui/settings/subscription.cljs:288 msgid "subscription.settings.management.dialog.downgrade" msgstr "提醒你:降級方案會減少儲存空間,並縮短備份與版本紀錄的保留時間。" -#: src/app/main/ui/settings/subscription.cljs:138 +#: src/app/main/ui/settings/subscription.cljs:140 msgid "subscription.settings.management.dialog.editors" msgstr "編輯者" -#: src/app/main/ui/settings/subscription.cljs:145 +#: src/app/main/ui/settings/subscription.cljs:147 msgid "subscription.settings.management.dialog.editors-explanation" msgstr "(包含擁有者、管理員與編輯者。檢視者不計入編輯者人數)" -#: src/app/main/ui/settings/subscription.cljs:317 +#: src/app/main/ui/settings/subscription.cljs:321 msgid "subscription.settings.management.dialog.input-error" msgstr "編輯者人數不能少於目前人數。請在團隊設定中,將實際上不編輯檔案的成員從編輯者或管理員改為檢視者。" -#: src/app/main/ui/settings/subscription.cljs:313 +#: src/app/main/ui/settings/subscription.cljs:317 msgid "subscription.settings.management.dialog.payment-explanation" msgstr "試用結束後才會收費,目前無須提供信用卡。" -#: src/app/main/ui/settings/subscription.cljs:306, src/app/main/ui/settings/subscription.cljs:310 +#: src/app/main/ui/settings/subscription.cljs:310, src/app/main/ui/settings/subscription.cljs:314 #, markdown msgid "subscription.settings.management.dialog.price-month" msgstr "**$%s**/月" -#: src/app/main/ui/settings/subscription.cljs:271 +#: src/app/main/ui/settings/subscription.cljs:275 msgid "subscription.settings.management.dialog.title" msgstr "將 %s 套用至你的團隊" -#: src/app/main/ui/settings/subscription.cljs:320 +#: src/app/main/ui/settings/subscription.cljs:324 msgid "subscription.settings.management.dialog.unlimited-capped-warning" msgstr "提示:你可以現在增加席次,為後續邀請預留名額。所有團隊的編輯者合計達 25 人以上時,即可享有每月 $175 的固定費用。" -#: src/app/main/ui/settings/subscription.cljs:714 +#: src/app/main/ui/settings/subscription.cljs:737 msgid "subscription.settings.member-since" msgstr "加入 Penpot 的日期:%s" -#: src/app/main/ui/settings/subscription.cljs:736, src/app/main/ui/settings/subscription.cljs:751, src/app/main/ui/settings/subscription.cljs:767, src/app/main/ui/settings/subscription.cljs:787 +#: src/app/main/ui/settings/subscription.cljs:759, src/app/main/ui/settings/subscription.cljs:774, src/app/main/ui/settings/subscription.cljs:790, src/app/main/ui/settings/subscription.cljs:810 msgid "subscription.settings.more-information" msgstr "更多資訊" -#: src/app/main/ui/settings/subscription.cljs:775, src/app/main/ui/settings/subscription.cljs:835 +#: src/app/main/ui/settings/subscription.cljs:798, src/app/main/ui/settings/subscription.cljs:860 msgid "subscription.settings.organization-member-month" msgstr "組織成員 / 月" -#: src/app/main/ui/settings/subscription.cljs:717 +#: src/app/main/ui/settings/subscription.cljs:740 msgid "subscription.settings.other-plans" msgstr "其他 Penpot 方案" -#: src/app/main/ui/settings/subscription.cljs:760 +#: src/app/main/ui/settings/subscription.cljs:783 msgid "subscription.settings.price-organization-month" msgstr "每個組織每月" -#: src/app/main/ui/settings/subscription.cljs:721, src/app/main/ui/settings/subscription.cljs:744 +#: src/app/main/ui/settings/subscription.cljs:744, src/app/main/ui/settings/subscription.cljs:767 msgid "subscription.settings.price-user-month" msgstr "使用者 / 月" -#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:122, src/app/main/ui/settings/subscription.cljs:638, src/app/main/ui/settings/subscription.cljs:719 +#: src/app/main/ui/dashboard/subscription.cljs:294, src/app/main/ui/settings/subscription.cljs:124, src/app/main/ui/settings/subscription.cljs:654, src/app/main/ui/settings/subscription.cljs:742 msgid "subscription.settings.professional" msgstr "專業版" -#: src/app/main/ui/settings/subscription.cljs:643, src/app/main/ui/settings/subscription.cljs:726 +#: src/app/main/ui/settings/subscription.cljs:659, src/app/main/ui/settings/subscription.cljs:749 msgid "subscription.settings.professional.autosave-benefit" msgstr "保留 7 天的自動儲存版本,並可復原檔案" -#: src/app/main/ui/settings/subscription.cljs:647, src/app/main/ui/settings/subscription.cljs:730 +#: src/app/main/ui/settings/subscription.cljs:663, src/app/main/ui/settings/subscription.cljs:753 msgid "subscription.settings.professional.selfhost.community-support" msgstr "所有設計與原型設計功能,以及社群支援。" -#: src/app/main/ui/settings/subscription.cljs:641, src/app/main/ui/settings/subscription.cljs:724 +#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:747 msgid "subscription.settings.professional.selfhost.control-over-data" msgstr "完全掌控資料與基礎架構。" -#: src/app/main/ui/settings/subscription.cljs:644, src/app/main/ui/settings/subscription.cljs:727 +#: src/app/main/ui/settings/subscription.cljs:660, src/app/main/ui/settings/subscription.cljs:750 msgid "subscription.settings.professional.selfhost.unlimited-users" msgstr "使用者、團隊與檔案數量無上限。" -#: src/app/main/ui/settings/subscription.cljs:640, src/app/main/ui/settings/subscription.cljs:723 +#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:746 msgid "subscription.settings.professional.storage-benefit" msgstr "10 GB 儲存空間" -#: src/app/main/ui/settings/subscription.cljs:646, src/app/main/ui/settings/subscription.cljs:729 +#: src/app/main/ui/settings/subscription.cljs:662, src/app/main/ui/settings/subscription.cljs:752 msgid "subscription.settings.professional.teams-editors-benefit" msgstr "團隊數量無上限。您擁有的所有團隊合計最多可有 8 位編輯者。" -#: src/app/main/ui/settings/subscription.cljs:60 +#: src/app/main/ui/settings/subscription.cljs:61 msgid "subscription.settings.recommended" msgstr "推薦" -#: src/app/main/ui/settings/subscription.cljs:614 +#: src/app/main/ui/settings/subscription.cljs:628 msgid "subscription.settings.section-plan" msgstr "您的訂閱" -#: src/app/main/ui/settings/subscription.cljs:367 +#: src/app/main/ui/settings/subscription.cljs:371 msgid "subscription.settings.start-trial" msgstr "開始免費試用" -#: src/app/main/ui/settings/subscription.cljs:332, src/app/main/ui/settings/subscription.cljs:731, src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765, src/app/main/ui/settings/subscription.cljs:783, src/app/main/ui/settings/subscription.cljs:850 +#: src/app/main/ui/settings/subscription.cljs:336, src/app/main/ui/settings/subscription.cljs:754, src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788, src/app/main/ui/settings/subscription.cljs:806, src/app/main/ui/settings/subscription.cljs:875 msgid "subscription.settings.subscribe" msgstr "訂閱" -#: src/app/main/ui/settings/subscription.cljs:707 +#: src/app/main/ui/settings/subscription.cljs:730 msgid "subscription.settings.subscribed-since" msgstr "您自 %s 方案於 %s 啟用後便持續使用至今。" -#: src/app/main/ui/settings/subscription.cljs:399 +#: src/app/main/ui/settings/subscription.cljs:406 msgid "subscription.settings.success.dialog.description" msgstr "您隨時都可以前往帳號詳細資料中的「訂閱」頁面,變更訂閱內容。" -#: src/app/main/ui/settings/subscription.cljs:401 +#: src/app/main/ui/settings/subscription.cljs:408 msgid "subscription.settings.success.dialog.footer" msgstr "盡情享用您的方案!" -#: src/app/main/ui/settings/subscription.cljs:397 +#: src/app/main/ui/settings/subscription.cljs:404 msgid "subscription.settings.success.dialog.thanks" msgstr "感謝您選擇 Penpot %s 方案!" -#: src/app/main/ui/settings/subscription.cljs:394 +#: src/app/main/ui/settings/subscription.cljs:401 msgid "subscription.settings.success.dialog.title" msgstr "您現在是 %s 方案用戶!" -#: src/app/main/ui/settings/subscription.cljs:749, src/app/main/ui/settings/subscription.cljs:765 +#: src/app/main/ui/settings/subscription.cljs:772, src/app/main/ui/settings/subscription.cljs:788 msgid "subscription.settings.try-it-free" msgstr "免費試用 14 天" -#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:123, src/app/main/ui/settings/subscription.cljs:468, src/app/main/ui/settings/subscription.cljs:602, src/app/main/ui/settings/subscription.cljs:665, src/app/main/ui/settings/subscription.cljs:741 +#: src/app/main/ui/dashboard/subscription.cljs:297, src/app/main/ui/settings/subscription.cljs:125, src/app/main/ui/settings/subscription.cljs:475, src/app/main/ui/settings/subscription.cljs:616, src/app/main/ui/settings/subscription.cljs:681, src/app/main/ui/settings/subscription.cljs:764 msgid "subscription.settings.unlimited" msgstr "" -#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:119, src/app/main/ui/settings/subscription.cljs:601, src/app/main/ui/settings/subscription.cljs:652 +#: src/app/main/ui/dashboard/subscription.cljs:296, src/app/main/ui/settings/subscription.cljs:121, src/app/main/ui/settings/subscription.cljs:615, src/app/main/ui/settings/subscription.cljs:668 msgid "subscription.settings.unlimited-trial" msgstr "Unlimited(試用版)" -#: src/app/main/ui/settings/subscription.cljs:656, src/app/main/ui/settings/subscription.cljs:669, src/app/main/ui/settings/subscription.cljs:747 +#: src/app/main/ui/settings/subscription.cljs:672, src/app/main/ui/settings/subscription.cljs:685, src/app/main/ui/settings/subscription.cljs:770 msgid "subscription.settings.unlimited.autosave-benefit" msgstr "保留 30 天的自動儲存版本,並提供檔案復原功能" -#: src/app/main/ui/settings/subscription.cljs:657, src/app/main/ui/settings/subscription.cljs:670, src/app/main/ui/settings/subscription.cljs:748 +#: src/app/main/ui/settings/subscription.cljs:673, src/app/main/ui/settings/subscription.cljs:686, src/app/main/ui/settings/subscription.cljs:771 msgid "subscription.settings.unlimited.bill" msgstr "每月帳單金額上限為 $175" -#: src/app/main/ui/settings/subscription.cljs:655, src/app/main/ui/settings/subscription.cljs:668, src/app/main/ui/settings/subscription.cljs:746 +#: src/app/main/ui/settings/subscription.cljs:671, src/app/main/ui/settings/subscription.cljs:684, src/app/main/ui/settings/subscription.cljs:769 msgid "subscription.settings.unlimited.storage-benefit" msgstr "25GB 儲存空間" @@ -5572,44 +5516,44 @@ msgstr "如需提高此上限,請聯絡團隊擁有者:[%s](mailto)" msgid "subscription.workspace.versions.warning.subtext-owner" msgstr "如需提高此上限,請[升級你的方案|target:self](%s)" -#: src/app/main/ui/dashboard/team.cljs:1208 +#: src/app/main/ui/dashboard/team.cljs:1209 msgid "team.invitations-selected" msgid_plural "team.invitations-selected" msgstr[0] "'已選取 %s 個邀請'" -#: src/app/main/ui/dashboard/files.cljs:209 +#: src/app/main/ui/dashboard/files.cljs:219 msgid "title.dashboard.files" msgstr "%s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:50 +#: src/app/main/ui/dashboard/fonts.cljs:55 msgid "title.dashboard.font-providers" msgstr "字型提供者 - %s - Penpot" -#: src/app/main/ui/dashboard/fonts.cljs:49 +#: src/app/main/ui/dashboard/fonts.cljs:54 msgid "title.dashboard.fonts" msgstr "字型 - %s - Penpot" -#: src/app/main/ui/dashboard/projects.cljs:366 +#: src/app/main/ui/dashboard/projects.cljs:375 msgid "title.dashboard.projects" msgstr "專案 - %s - Penpot" -#: src/app/main/ui/dashboard/search.cljs:49 +#: src/app/main/ui/dashboard/search.cljs:51 msgid "title.dashboard.search" msgstr "搜尋 - %s - Penpot" -#: src/app/main/ui/dashboard/libraries.cljs:60 +#: src/app/main/ui/dashboard/libraries.cljs:62 msgid "title.dashboard.shared-libraries" msgstr "共享資料庫 - %s - Penpot" -#: src/app/main/ui/auth/verify_token.cljs:110, src/app/main/ui/auth.cljs:40 +#: src/app/main/ui/auth/verify_token.cljs:83, src/app/main/ui/auth.cljs:40 msgid "title.default" msgstr "Penpot | 全端設計" -#: src/app/main/ui/settings/feedback.cljs:162 +#: src/app/main/ui/settings/feedback.cljs:164 msgid "title.settings.feedback" msgstr "提供回饋 - Penpot" -#: src/app/main/ui/settings/integrations.cljs:646 +#: src/app/main/ui/settings/integrations.cljs:642 msgid "title.settings.integrations" msgstr "整合 - Penpot" @@ -5617,7 +5561,7 @@ msgstr "整合 - Penpot" msgid "title.settings.notifications" msgstr "通知 - Penpot" -#: src/app/main/ui/settings/options.cljs:123 +#: src/app/main/ui/settings/options.cljs:129 msgid "title.settings.options" msgstr "設定 - Penpot" @@ -5625,27 +5569,27 @@ msgstr "設定 - Penpot" msgid "title.settings.password" msgstr "密碼 - Penpot" -#: src/app/main/ui/settings/profile.cljs:144 +#: src/app/main/ui/settings/profile.cljs:153 msgid "title.settings.profile" msgstr "個人資料 - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1255 +#: src/app/main/ui/dashboard/team.cljs:1256 msgid "title.team-invitations" msgstr "邀請 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:645 +#: src/app/main/ui/dashboard/team.cljs:636 msgid "title.team-members" msgstr "成員 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1642 +#: src/app/main/ui/dashboard/team.cljs:1653 msgid "title.team-settings" msgstr "設定 - %s - Penpot" -#: src/app/main/ui/dashboard/team.cljs:1535 +#: src/app/main/ui/dashboard/team.cljs:1546 msgid "title.team-webhooks" msgstr "Webhooks - %s - Penpot" -#: src/app/main/ui/viewer.cljs:422 +#: src/app/main/ui/viewer.cljs:433 msgid "title.viewer" msgstr "%s - 檢視模式 - Penpot" @@ -5657,51 +5601,51 @@ msgstr "%s - Penpot" msgid "token-actions.detach-token" msgstr "解除 token 連結" -#: src/app/main/ui/viewer.cljs:572 +#: src/app/main/ui/viewer.cljs:583 msgid "viewer.empty-state" msgstr "此頁面沒有畫板。" -#: src/app/main/ui/viewer.cljs:577 +#: src/app/main/ui/viewer.cljs:588 msgid "viewer.frame-not-found" msgstr "找不到畫板。" -#: src/app/main/ui/viewer/header.cljs:341 +#: src/app/main/ui/viewer/header.cljs:367 msgid "viewer.header.comments-section" msgstr "留言 (%s)" -#: src/app/main/ui/viewer/interactions.cljs:268 +#: src/app/main/ui/viewer/interactions.cljs:280 msgid "viewer.header.dont-show-interactions" msgstr "隱藏互動" -#: src/app/main/ui/viewer/header.cljs:195 +#: src/app/main/ui/viewer/header.cljs:206 msgid "viewer.header.fullscreen" msgstr "全螢幕" -#: src/app/main/ui/viewer/header.cljs:350 +#: src/app/main/ui/viewer/header.cljs:376 msgid "viewer.header.inspect-section" msgstr "檢視 (%s)" -#: src/app/main/ui/viewer/interactions.cljs:258 +#: src/app/main/ui/viewer/interactions.cljs:268 msgid "viewer.header.interactions" msgstr "互動" -#: src/app/main/ui/viewer/header.cljs:332 +#: src/app/main/ui/viewer/header.cljs:358 msgid "viewer.header.interactions-section" msgstr "互動 (%s)" -#: src/app/main/ui/viewer/share_link.cljs:187 +#: src/app/main/ui/viewer/share_link.cljs:191 msgid "viewer.header.share.copy-link" msgstr "複製連結" -#: src/app/main/ui/viewer/interactions.cljs:276 +#: src/app/main/ui/viewer/interactions.cljs:288 msgid "viewer.header.show-interactions" msgstr "顯示互動" -#: src/app/main/ui/viewer/interactions.cljs:287 +#: src/app/main/ui/viewer/interactions.cljs:299 msgid "viewer.header.show-interactions-on-click" msgstr "點選時顯示互動" -#: src/app/main/ui/viewer/header.cljs:236 +#: src/app/main/ui/viewer/header.cljs:248 msgid "viewer.header.sitemap" msgstr "網站地圖" @@ -5729,67 +5673,67 @@ msgstr "請參閱我們的 " msgid "webgl.modals.webgl-unavailable.troubleshooting.link" msgstr "WebGL 疑難排解指南" -#: src/app/main/ui/settings/options.cljs:105 +#: src/app/main/ui/settings/options.cljs:111 msgid "webgl.toast.webgl-render-disabled" msgstr "已停用 WebGL 渲染" -#: src/app/main/ui/settings/options.cljs:104 +#: src/app/main/ui/settings/options.cljs:110 msgid "webgl.toast.webgl-render-enabled" msgstr "已啟用 WebGL 渲染" -#: src/app/render_wasm/api.cljs:2462 +#: src/app/render_wasm/api.cljs:2402 msgid "webgl.webgl-context-lost.toast" msgstr "WebGL 繪圖環境已遺失" -#: src/app/render_wasm/api.cljs:2485 +#: src/app/render_wasm/api.cljs:2425 msgid "webgl.webgl-context-recovered.toast" msgstr "WebGL 繪圖環境已恢復" -#: src/app/main/ui/dashboard/team.cljs:1489 +#: src/app/main/ui/dashboard/team.cljs:1498 msgid "webhooks.last-delivery.success" msgstr "上次傳送成功。" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:71 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:69 msgid "workspace.align.hcenter" msgstr "水平置中對齊 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:87 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:83 msgid "workspace.align.hdistribute" msgstr "平均分配水平間距 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:63 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:62 msgid "workspace.align.hleft" msgstr "靠左對齊 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:76 msgid "workspace.align.hright" msgstr "靠右對齊 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:105 msgid "workspace.align.vbottom" msgstr "靠下對齊 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:104 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:98 msgid "workspace.align.vcenter" msgstr "垂直置中對齊 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:117 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:112 msgid "workspace.align.vdistribute" msgstr "平均分配垂直間距 (%s)" -#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:96 +#: src/app/main/ui/workspace/sidebar/options/menus/align.cljs:91 msgid "workspace.align.vtop" msgstr "靠上對齊 (%s)" -#: src/app/main/ui/workspace/sidebar/assets.cljs:207 +#: src/app/main/ui/workspace/sidebar/assets.cljs:210 msgid "workspace.assets.add-library" msgstr "新增資料庫" -#: src/app/main/ui/workspace/sidebar/assets.cljs:179 +#: src/app/main/ui/workspace/sidebar/assets.cljs:180 msgid "workspace.assets.box-filter-all" msgstr "所有資產" -#: src/app/main/ui/dashboard/grid.cljs:156, src/app/main/ui/dashboard/grid.cljs:189, src/app/main/ui/workspace/colorpicker/libraries.cljs:364, src/app/main/ui/workspace/colorpicker/libraries.cljs:381, src/app/main/ui/workspace/colorpicker/libraries.cljs:395, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:185 +#: src/app/main/ui/dashboard/grid.cljs:157, src/app/main/ui/dashboard/grid.cljs:190, src/app/main/ui/workspace/colorpicker/libraries.cljs:360, src/app/main/ui/workspace/colorpicker/libraries.cljs:377, src/app/main/ui/workspace/colorpicker/libraries.cljs:391, src/app/main/ui/workspace/sidebar/assets/colors.cljs:512, src/app/main/ui/workspace/sidebar/assets.cljs:186 msgid "workspace.assets.colors" msgstr "色彩" @@ -5801,7 +5745,7 @@ msgstr "新增色彩" msgid "workspace.assets.component-group-options" msgstr "元件群組選項" -#: src/app/main/ui/dashboard/grid.cljs:152, src/app/main/ui/dashboard/grid.cljs:167, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:182 +#: src/app/main/ui/dashboard/grid.cljs:153, src/app/main/ui/dashboard/grid.cljs:168, src/app/main/ui/workspace/sidebar/assets/components.cljs:588, src/app/main/ui/workspace/sidebar/assets.cljs:183 msgid "workspace.assets.components" msgstr "元件" @@ -5809,7 +5753,7 @@ msgstr "元件" msgid "workspace.assets.components.add-component" msgstr "新增元件" -#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:613 +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:177, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:620 msgid "workspace.assets.components.num-variants" msgstr "%s 個變體" @@ -5817,11 +5761,11 @@ msgstr "%s 個變體" msgid "workspace.assets.create-group" msgstr "建立群組" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:171 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:174 msgid "workspace.assets.create-group-hint" msgstr "您的項目將自動命名為「群組名稱 / 項目名稱」" -#: src/app/main/ui/workspace/context_menu.cljs:828, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:821 +#: src/app/main/ui/workspace/context_menu.cljs:836, src/app/main/ui/workspace/sidebar/assets/colors.cljs:265, src/app/main/ui/workspace/sidebar/assets/components.cljs:670, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:529, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:865 msgid "workspace.assets.delete" msgstr "刪除" @@ -5829,11 +5773,11 @@ msgstr "刪除" msgid "workspace.assets.delete-group" msgstr "刪除群組" -#: src/app/main/ui/workspace/context_menu.cljs:824 +#: src/app/main/ui/workspace/context_menu.cljs:832 msgid "workspace.assets.delete-pages" msgstr "刪除頁面" -#: src/app/main/ui/workspace/context_menu.cljs:833, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:472, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:817 +#: src/app/main/ui/workspace/context_menu.cljs:841, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:525, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:861 msgid "workspace.assets.duplicate" msgstr "建立複本" @@ -5841,39 +5785,39 @@ msgstr "建立複本" msgid "workspace.assets.duplicate-main" msgstr "複製主元件" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:467 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:258, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:520 msgid "workspace.assets.edit" msgstr "編輯" -#: src/app/main/ui/workspace/sidebar/assets.cljs:221 +#: src/app/main/ui/workspace/sidebar/assets.cljs:226 msgid "workspace.assets.filter" msgstr "篩選" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:347, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:194, src/app/main/ui/workspace/colorpicker/libraries.cljs:343, src/app/main/ui/workspace/sidebar/assets/components.cljs:604 msgid "workspace.assets.grid-view" msgstr "方格檢視" -#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:481 +#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:269, src/app/main/ui/workspace/sidebar/assets/components.cljs:654, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:534 msgid "workspace.assets.group" msgstr "群組" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:170 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:173 msgid "workspace.assets.group-name" msgstr "群組名稱" -#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:346, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:195, src/app/main/ui/workspace/colorpicker/libraries.cljs:342, src/app/main/ui/workspace/sidebar/assets/components.cljs:600 msgid "workspace.assets.list-view" msgstr "清單檢視" -#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:697 +#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:116, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:705 msgid "workspace.assets.local-library" msgstr "本機資料庫" -#: src/app/main/ui/workspace/sidebar/assets.cljs:212 +#: src/app/main/ui/workspace/sidebar/assets.cljs:217 msgid "workspace.assets.manage-library" msgstr "管理資料庫" -#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 +#: src/app/main/ui/workspace/color_palette.cljs:205, src/app/main/ui/workspace/sidebar/assets/file_library.cljs:323 msgid "workspace.assets.not-found" msgstr "找不到資產" @@ -5881,7 +5825,7 @@ msgstr "找不到資產" msgid "workspace.assets.open-library" msgstr "開啟資料庫檔案" -#: src/app/main/ui/workspace/context_menu.cljs:831, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:462 +#: src/app/main/ui/workspace/context_menu.cljs:839, src/app/main/ui/workspace/sidebar/assets/colors.cljs:254, src/app/main/ui/workspace/sidebar/assets/components.cljs:645, src/app/main/ui/workspace/sidebar/assets/groups.cljs:66, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:515 msgid "workspace.assets.rename" msgstr "重新命名" @@ -5889,11 +5833,11 @@ msgstr "重新命名" msgid "workspace.assets.rename-group" msgstr "重新命名群組" -#: src/app/main/ui/workspace/color_palette.cljs:182, src/app/main/ui/workspace/color_palette.cljs:187, src/app/main/ui/workspace/sidebar/assets.cljs:218 +#: src/app/main/ui/workspace/color_palette.cljs:181, src/app/main/ui/workspace/color_palette.cljs:186, src/app/main/ui/workspace/sidebar/assets.cljs:223 msgid "workspace.assets.search" msgstr "搜尋資產" -#: src/app/main/ui/dashboard/grid.cljs:463, src/app/main/ui/dashboard/grid.cljs:464 +#: src/app/main/ui/dashboard/grid.cljs:477, src/app/main/ui/dashboard/grid.cljs:478 msgid "workspace.assets.shared-library" msgstr "共享資料庫" @@ -5902,43 +5846,43 @@ msgid "workspace.assets.sidebar.components" msgid_plural "workspace.assets.sidebar.components" msgstr[0] "%s 個元件" -#: src/app/main/ui/workspace/sidebar/assets.cljs:239 +#: src/app/main/ui/workspace/sidebar/assets.cljs:245 msgid "workspace.assets.sort" msgstr "排序" -#: src/app/main/ui/dashboard/grid.cljs:160, src/app/main/ui/dashboard/grid.cljs:216, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:423, src/app/main/ui/workspace/sidebar/assets.cljs:188 +#: src/app/main/ui/dashboard/grid.cljs:161, src/app/main/ui/dashboard/grid.cljs:217, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:479, src/app/main/ui/workspace/sidebar/assets.cljs:189 msgid "workspace.assets.typography" msgstr "文字樣式" -#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:431 +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:87, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:304 msgid "workspace.assets.typography.add-typography" msgstr "新增文字樣式" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:855 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:899 msgid "workspace.assets.typography.font-size" msgstr "字級" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:851 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:895 msgid "workspace.assets.typography.font-style" msgstr "字型樣式" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:873 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:917 msgid "workspace.assets.typography.go-to-edit" msgstr "前往樣式資料庫檔案進行編輯" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:863 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:907 msgid "workspace.assets.typography.letter-spacing" msgstr "字距" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:859 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:903 msgid "workspace.assets.typography.line-height" msgstr "行高" -#: src/app/main/ui/dashboard/grid.cljs:226, src/app/main/ui/workspace/libraries.cljs:1070, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:802, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:838, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:957, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:976 +#: src/app/main/ui/dashboard/grid.cljs:227, src/app/main/ui/workspace/libraries.cljs:1094, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:846, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:882, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1001, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:1020 msgid "workspace.assets.typography.sample" msgstr "Ag" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:867 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:911 msgid "workspace.assets.typography.text-transform" msgstr "文字轉換" @@ -5946,15 +5890,15 @@ msgstr "文字轉換" msgid "workspace.assets.ungroup" msgstr "取消群組" -#: src/app/main/data/workspace/clipboard.cljs:1170 +#: src/app/main/data/workspace/clipboard.cljs:1242 msgid "workspace.clipboard.copying" msgstr "正在複製圖片…" -#: src/app/main/data/workspace/clipboard.cljs:1192 +#: src/app/main/data/workspace/clipboard.cljs:1264 msgid "workspace.clipboard.image-copied" msgstr "已將圖片複製到剪貼簿" -#: src/app/main/data/workspace/clipboard.cljs:1198 +#: src/app/main/data/workspace/clipboard.cljs:1270 msgid "workspace.clipboard.image-copy-failed" msgstr "複製圖片時發生錯誤" @@ -5962,83 +5906,83 @@ msgstr "複製圖片時發生錯誤" msgid "workspace.colorpicker.color-tokens" msgstr "色彩 tokens" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:519 msgid "workspace.component.swap.loop-error" msgstr "元件不能巢狀包含自身。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:511 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:518 msgid "workspace.component.switch.loop-error-multi" msgstr "部分副本無法切換。元件不能巢狀包含自身。" -#: src/app/main/ui/workspace/context_menu.cljs:980 +#: src/app/main/ui/workspace/context_menu.cljs:988 msgid "workspace.context-menu.grid-cells.area" msgstr "建立區域" -#: src/app/main/ui/workspace/context_menu.cljs:991 +#: src/app/main/ui/workspace/context_menu.cljs:999 msgid "workspace.context-menu.grid-cells.copy-columns" msgstr "複製欄" -#: src/app/main/ui/workspace/context_menu.cljs:987 +#: src/app/main/ui/workspace/context_menu.cljs:995 msgid "workspace.context-menu.grid-cells.copy-rows" msgstr "複製列" -#: src/app/main/ui/workspace/context_menu.cljs:983 +#: src/app/main/ui/workspace/context_menu.cljs:991 msgid "workspace.context-menu.grid-cells.create-board" msgstr "建立畫板" -#: src/app/main/ui/workspace/context_menu.cljs:975 +#: src/app/main/ui/workspace/context_menu.cljs:983 msgid "workspace.context-menu.grid-cells.merge" msgstr "合併儲存格" -#: src/app/main/ui/workspace/context_menu.cljs:995 +#: src/app/main/ui/workspace/context_menu.cljs:1003 msgid "workspace.context-menu.grid-cells.paste-tracks" msgstr "貼上" -#: src/app/main/ui/workspace/context_menu.cljs:902 +#: src/app/main/ui/workspace/context_menu.cljs:910 msgid "workspace.context-menu.grid-track.column.add-after" msgstr "在右側新增 1 欄" -#: src/app/main/ui/workspace/context_menu.cljs:901 +#: src/app/main/ui/workspace/context_menu.cljs:909 msgid "workspace.context-menu.grid-track.column.add-before" msgstr "在左側新增 1 欄" -#: src/app/main/ui/workspace/context_menu.cljs:903 +#: src/app/main/ui/workspace/context_menu.cljs:911 msgid "workspace.context-menu.grid-track.column.delete" msgstr "刪除欄" -#: src/app/main/ui/workspace/context_menu.cljs:904 +#: src/app/main/ui/workspace/context_menu.cljs:912 msgid "workspace.context-menu.grid-track.column.delete-shapes" msgstr "刪除欄及其中的圖形" -#: src/app/main/ui/workspace/context_menu.cljs:900 +#: src/app/main/ui/workspace/context_menu.cljs:908 msgid "workspace.context-menu.grid-track.column.duplicate" msgstr "複製欄" -#: src/app/main/ui/workspace/context_menu.cljs:909 +#: src/app/main/ui/workspace/context_menu.cljs:917 msgid "workspace.context-menu.grid-track.row.add-after" msgstr "在下方新增 1 列" -#: src/app/main/ui/workspace/context_menu.cljs:908 +#: src/app/main/ui/workspace/context_menu.cljs:916 msgid "workspace.context-menu.grid-track.row.add-before" msgstr "在上方新增 1 列" -#: src/app/main/ui/workspace/context_menu.cljs:910 +#: src/app/main/ui/workspace/context_menu.cljs:918 msgid "workspace.context-menu.grid-track.row.delete" msgstr "刪除列" -#: src/app/main/ui/workspace/context_menu.cljs:911 +#: src/app/main/ui/workspace/context_menu.cljs:919 msgid "workspace.context-menu.grid-track.row.delete-shapes" msgstr "刪除列及其中的圖形" -#: src/app/main/ui/workspace/context_menu.cljs:907 +#: src/app/main/ui/workspace/context_menu.cljs:915 msgid "workspace.context-menu.grid-track.row.duplicate" msgstr "複製列" -#: src/app/main/ui/workspace/context_menu.cljs:1029 +#: src/app/main/ui/workspace/context_menu.cljs:1037 msgid "workspace.context-menu.guides.change-color" msgstr "參考線色彩" -#: src/app/main/ui/workspace/context_menu.cljs:1041 +#: src/app/main/ui/workspace/context_menu.cljs:1049 msgid "workspace.context-menu.guides.remove" msgstr "移除參考線" @@ -6046,15 +5990,15 @@ msgstr "移除參考線" msgid "workspace.debug.title" msgstr "除錯工具" -#: src/app/main/ui/workspace/sidebar/layers.cljs:885 +#: src/app/main/ui/workspace/sidebar/layers.cljs:924 msgid "workspace.focus.focus-mode" msgstr "專注模式" -#: src/app/main/ui/workspace/context_menu.cljs:461, src/app/main/ui/workspace/context_menu.cljs:859 +#: src/app/main/ui/workspace/context_menu.cljs:463, src/app/main/ui/workspace/context_menu.cljs:867 msgid "workspace.focus.focus-off" msgstr "關閉專注模式" -#: src/app/main/ui/workspace/context_menu.cljs:460 +#: src/app/main/ui/workspace/context_menu.cljs:462 msgid "workspace.focus.focus-on" msgstr "開啟專注模式" @@ -6066,243 +6010,243 @@ msgstr "線性漸層" msgid "workspace.gradients.radial" msgstr "放射狀漸層" -#: src/app/main/ui/workspace/main_menu.cljs:293 +#: src/app/main/ui/workspace/main_menu.cljs:294 msgid "workspace.header.menu.disable-dynamic-alignment" msgstr "關閉動態對齊" -#: src/app/main/ui/workspace/main_menu.cljs:254 +#: src/app/main/ui/workspace/main_menu.cljs:255 msgid "workspace.header.menu.disable-scale-content" msgstr "關閉等比縮放" -#: src/app/main/ui/workspace/main_menu.cljs:280 +#: src/app/main/ui/workspace/main_menu.cljs:281 msgid "workspace.header.menu.disable-snap-guides" msgstr "停用貼齊參考線" -#: src/app/main/ui/workspace/main_menu.cljs:306 +#: src/app/main/ui/workspace/main_menu.cljs:307 msgid "workspace.header.menu.disable-snap-pixel-grid" msgstr "停用貼齊像素" -#: src/app/main/ui/workspace/main_menu.cljs:267 +#: src/app/main/ui/workspace/main_menu.cljs:268 msgid "workspace.header.menu.disable-snap-ruler-guides" msgstr "停用貼齊尺規參考線" -#: src/app/main/ui/workspace/main_menu.cljs:341 +#: src/app/main/ui/workspace/main_menu.cljs:342 msgid "workspace.header.menu.disable-webgl" msgstr "停用 WebGL 渲染(測試版)" -#: src/app/main/ui/workspace/main_menu.cljs:294 +#: src/app/main/ui/workspace/main_menu.cljs:295 msgid "workspace.header.menu.enable-dynamic-alignment" msgstr "啟用動態對齊" -#: src/app/main/ui/workspace/main_menu.cljs:255 +#: src/app/main/ui/workspace/main_menu.cljs:256 msgid "workspace.header.menu.enable-scale-content" msgstr "啟用等比例縮放" -#: src/app/main/ui/workspace/main_menu.cljs:281 +#: src/app/main/ui/workspace/main_menu.cljs:282 msgid "workspace.header.menu.enable-snap-guides" msgstr "貼齊參考線" -#: src/app/main/ui/workspace/main_menu.cljs:307 +#: src/app/main/ui/workspace/main_menu.cljs:308 msgid "workspace.header.menu.enable-snap-pixel-grid" msgstr "啟用貼齊像素" -#: src/app/main/ui/workspace/main_menu.cljs:268 +#: src/app/main/ui/workspace/main_menu.cljs:269 msgid "workspace.header.menu.enable-snap-ruler-guides" msgstr "貼齊尺規參考線" -#: src/app/main/ui/workspace/main_menu.cljs:342 +#: src/app/main/ui/workspace/main_menu.cljs:343 msgid "workspace.header.menu.enable-webgl" msgstr "啟用 WebGL 渲染(測試版)" -#: src/app/main/ui/workspace/main_menu.cljs:535 +#: src/app/main/ui/workspace/main_menu.cljs:536 msgid "workspace.header.menu.find" msgstr "尋找" -#: src/app/main/ui/workspace/main_menu.cljs:542 +#: src/app/main/ui/workspace/main_menu.cljs:543 msgid "workspace.header.menu.find-and-replace" msgstr "尋找與取代" -#: src/app/main/ui/workspace/main_menu.cljs:465 +#: src/app/main/ui/workspace/main_menu.cljs:466 msgid "workspace.header.menu.hide-artboard-names" msgstr "隱藏畫板名稱" -#: src/app/main/ui/workspace/main_menu.cljs:426 +#: src/app/main/ui/workspace/main_menu.cljs:427 msgid "workspace.header.menu.hide-comments" msgstr "隱藏留言" -#: src/app/main/ui/workspace/main_menu.cljs:401 +#: src/app/main/ui/workspace/main_menu.cljs:402 msgid "workspace.header.menu.hide-guides" msgstr "隱藏參考線" -#: src/app/main/ui/workspace/main_menu.cljs:440 +#: src/app/main/ui/workspace/main_menu.cljs:441 msgid "workspace.header.menu.hide-palette" msgstr "隱藏調色盤" -#: src/app/main/ui/workspace/main_menu.cljs:477 +#: src/app/main/ui/workspace/main_menu.cljs:478 msgid "workspace.header.menu.hide-pixel-grid" msgstr "隱藏像素網格" -#: src/app/main/ui/workspace/main_menu.cljs:387 +#: src/app/main/ui/workspace/main_menu.cljs:388 msgid "workspace.header.menu.hide-rules" msgstr "隱藏尺規" -#: src/app/main/ui/workspace/main_menu.cljs:452 +#: src/app/main/ui/workspace/main_menu.cljs:453 msgid "workspace.header.menu.hide-textpalette" msgstr "隱藏字型面板" -#: src/app/main/ui/workspace/main_menu.cljs:415 +#: src/app/main/ui/workspace/main_menu.cljs:416 msgid "workspace.header.menu.lock-guides" msgstr "鎖定參考線" -#: src/app/main/ui/workspace/main_menu.cljs:874 +#: src/app/main/ui/components/mcp_menu.cljs:141, src/app/main/ui/workspace/main_menu.cljs:875 msgid "workspace.header.menu.mcp.plugin.status.connect" msgstr "連線" -#: src/app/main/ui/workspace/main_menu.cljs:873 +#: src/app/main/ui/components/mcp_menu.cljs:140, src/app/main/ui/workspace/main_menu.cljs:874 msgid "workspace.header.menu.mcp.plugin.status.disconnect" msgstr "中斷連線" -#: src/app/main/ui/workspace/main_menu.cljs:883 +#: src/app/main/ui/workspace/main_menu.cljs:884 msgid "workspace.header.menu.mcp.server.status.disabled" msgstr "管理(狀態:已停用)" -#: src/app/main/ui/workspace/main_menu.cljs:882 +#: src/app/main/ui/workspace/main_menu.cljs:883 msgid "workspace.header.menu.mcp.server.status.enabled" msgstr "管理(狀態:已啟用)" -#: src/app/main/ui/workspace/main_menu.cljs:1038 +#: src/app/main/ui/workspace/main_menu.cljs:1039 msgid "workspace.header.menu.option.edit" msgstr "編輯" -#: src/app/main/ui/workspace/main_menu.cljs:1025 +#: src/app/main/ui/workspace/main_menu.cljs:1026 msgid "workspace.header.menu.option.file" msgstr "檔案" -#: src/app/main/ui/workspace/main_menu.cljs:1122 +#: src/app/main/ui/workspace/main_menu.cljs:1123 msgid "workspace.header.menu.option.help-info" msgstr "說明與資訊" -#: src/app/main/ui/workspace/main_menu.cljs:1104 +#: src/app/main/ui/workspace/main_menu.cljs:1105 msgid "workspace.header.menu.option.mcp" msgstr "MCP 伺服器" -#: src/app/main/ui/workspace/main_menu.cljs:1064 +#: src/app/main/ui/workspace/main_menu.cljs:1065 msgid "workspace.header.menu.option.preferences" msgstr "偏好設定" -#: src/app/main/ui/workspace/main_menu.cljs:1051 +#: src/app/main/ui/workspace/main_menu.cljs:1052 msgid "workspace.header.menu.option.view" msgstr "檢視" -#: src/app/main/ui/workspace/main_menu.cljs:564 +#: src/app/main/ui/workspace/main_menu.cljs:565 msgid "workspace.header.menu.redo" msgstr "重做" -#: src/app/main/ui/workspace/context_menu.cljs:1074, src/app/main/ui/workspace/main_menu.cljs:528 +#: src/app/main/ui/workspace/context_menu.cljs:1082, src/app/main/ui/workspace/main_menu.cljs:529 msgid "workspace.header.menu.select-all" msgstr "全選" -#: src/app/main/ui/workspace/main_menu.cljs:466 +#: src/app/main/ui/workspace/main_menu.cljs:467 msgid "workspace.header.menu.show-artboard-names" msgstr "顯示畫板名稱" -#: src/app/main/ui/workspace/main_menu.cljs:427 +#: src/app/main/ui/workspace/main_menu.cljs:428 msgid "workspace.header.menu.show-comments" msgstr "顯示留言" -#: src/app/main/ui/workspace/main_menu.cljs:402 +#: src/app/main/ui/workspace/main_menu.cljs:403 msgid "workspace.header.menu.show-guides" msgstr "顯示參考線" -#: src/app/main/ui/workspace/main_menu.cljs:441 +#: src/app/main/ui/workspace/main_menu.cljs:442 msgid "workspace.header.menu.show-palette" msgstr "顯示調色盤" -#: src/app/main/ui/workspace/main_menu.cljs:478 +#: src/app/main/ui/workspace/main_menu.cljs:479 msgid "workspace.header.menu.show-pixel-grid" msgstr "顯示像素網格" -#: src/app/main/ui/workspace/main_menu.cljs:388 +#: src/app/main/ui/workspace/main_menu.cljs:389 msgid "workspace.header.menu.show-rules" msgstr "顯示尺規" -#: src/app/main/ui/workspace/main_menu.cljs:453 +#: src/app/main/ui/workspace/main_menu.cljs:454 msgid "workspace.header.menu.show-textpalette" msgstr "顯示字型面板" -#: src/app/main/ui/workspace/main_menu.cljs:330 +#: src/app/main/ui/workspace/main_menu.cljs:331 msgid "workspace.header.menu.toggle-dark-theme" msgstr "切換至深色主題" -#: src/app/main/ui/workspace/main_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:331 +#: src/app/main/ui/workspace/main_menu.cljs:329, src/app/main/ui/workspace/main_menu.cljs:332 msgid "workspace.header.menu.toggle-light-theme" msgstr "切換至淺色主題" -#: src/app/main/ui/workspace/main_menu.cljs:329 +#: src/app/main/ui/workspace/main_menu.cljs:330 msgid "workspace.header.menu.toggle-system-theme" msgstr "切換至系統主題" -#: src/app/main/ui/workspace/main_menu.cljs:553 +#: src/app/main/ui/workspace/main_menu.cljs:554 msgid "workspace.header.menu.undo" msgstr "復原" -#: src/app/main/ui/workspace/main_menu.cljs:414 +#: src/app/main/ui/workspace/main_menu.cljs:415 msgid "workspace.header.menu.unlock-guides" msgstr "解除參考線鎖定" -#: src/app/main/ui/viewer/header.cljs:98, src/app/main/ui/workspace/right_header.cljs:96 +#: src/app/main/ui/viewer/header.cljs:107, src/app/main/ui/workspace/right_header.cljs:98 msgid "workspace.header.reset-zoom" msgstr "重設" -#: src/app/main/ui/workspace/left_header.cljs:126 +#: src/app/main/ui/workspace/left_header.cljs:134 msgid "workspace.header.save-error" msgstr "儲存時發生錯誤" -#: src/app/main/ui/workspace/left_header.cljs:124 +#: src/app/main/ui/workspace/left_header.cljs:132 msgid "workspace.header.saved" msgstr "已儲存" -#: src/app/main/ui/workspace/left_header.cljs:122, src/app/main/ui/workspace/left_header.cljs:123 +#: src/app/main/ui/workspace/left_header.cljs:130, src/app/main/ui/workspace/left_header.cljs:131 msgid "workspace.header.saving" msgstr "正在儲存" -#: src/app/main/ui/workspace/right_header.cljs:241 +#: src/app/main/ui/workspace/right_header.cljs:243 msgid "workspace.header.share" msgstr "分享" -#: src/app/main/ui/workspace/right_header.cljs:246 +#: src/app/main/ui/workspace/right_header.cljs:249 msgid "workspace.header.viewer" msgstr "檢視模式 (%s)" -#: src/app/main/ui/viewer/header.cljs:79, src/app/main/ui/workspace/right_header.cljs:79 +#: src/app/main/ui/viewer/header.cljs:87, src/app/main/ui/workspace/right_header.cljs:80 msgid "workspace.header.zoom" msgstr "縮放" -#: src/app/main/ui/viewer/header.cljs:109 +#: src/app/main/ui/viewer/header.cljs:118 msgid "workspace.header.zoom-fill" msgstr "填滿 - 等比例縮放至填滿" -#: src/app/main/ui/viewer/header.cljs:102 +#: src/app/main/ui/viewer/header.cljs:111 msgid "workspace.header.zoom-fit" msgstr "符合視窗 - 等比例縮小至完整顯示" -#: src/app/main/ui/workspace/right_header.cljs:99 +#: src/app/main/ui/workspace/right_header.cljs:101 msgid "workspace.header.zoom-fit-all" msgstr "縮放至顯示全部內容" -#: src/app/main/ui/viewer/header.cljs:116 +#: src/app/main/ui/viewer/header.cljs:125 msgid "workspace.header.zoom-full-screen" msgstr "全螢幕" -#: src/app/main/ui/workspace/right_header.cljs:106 +#: src/app/main/ui/workspace/right_header.cljs:108 msgid "workspace.header.zoom-selected" msgstr "縮放至選取範圍" -#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:294, src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:854 +#: src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs:296, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:852 msgid "workspace.layout-grid.editor.options.edit-grid" msgstr "編輯網格" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1575 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1590 msgid "workspace.layout-grid.editor.options.exit" msgstr "離開" @@ -6310,7 +6254,7 @@ msgstr "離開" msgid "workspace.layout-grid.editor.padding.bottom" msgstr "下內距" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:668 msgid "workspace.layout-grid.editor.padding.expand" msgstr "顯示四邊內距選項" @@ -6346,36 +6290,36 @@ msgstr "完成" msgid "workspace.layout-grid.editor.top-bar.locate" msgstr "定位" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1601 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1616 msgid "workspace.layout-grid.editor.top-bar.locate.tooltip" msgstr "定位網格佈局" -#: src/app/main/ui/workspace/libraries.cljs:126, src/app/main/ui/workspace/libraries.cljs:155, src/app/main/ui/workspace/libraries.cljs:199 +#: src/app/main/ui/workspace/libraries.cljs:178, src/app/main/ui/workspace/libraries.cljs:207, src/app/main/ui/workspace/libraries.cljs:251 msgid "workspace.libraries.colors" msgid_plural "workspace.libraries.colors" msgstr[0] "%s 種色彩" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:356 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:352 msgid "workspace.libraries.colors.add-library-color" msgstr "將色彩新增至資料庫" -#: src/app/main/ui/workspace/color_palette.cljs:203, src/app/main/ui/workspace/colorpicker/libraries.cljs:374, src/app/main/ui/workspace/colorpicker/libraries.cljs:405 +#: src/app/main/ui/workspace/color_palette.cljs:204, src/app/main/ui/workspace/colorpicker/libraries.cljs:370, src/app/main/ui/workspace/colorpicker/libraries.cljs:401 msgid "workspace.libraries.colors.empty-palette" msgstr "您的資料庫中尚無色彩樣式" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:373, src/app/main/ui/workspace/colorpicker/libraries.cljs:389 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:369, src/app/main/ui/workspace/colorpicker/libraries.cljs:385 msgid "workspace.libraries.colors.empty-recent-colors" msgstr "尚無最近使用的色彩" -#: src/app/main/ui/workspace/text_palette.cljs:158 +#: src/app/main/ui/workspace/text_palette.cljs:159 msgid "workspace.libraries.colors.empty-typography-palette" msgstr "您的資料庫中尚無文字樣式" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:222, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:97, src/app/main/ui/workspace/colorpicker/libraries.cljs:216, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:49 msgid "workspace.libraries.colors.file-library" msgstr "檔案資料庫" -#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:221 +#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:120, src/app/main/ui/workspace/colorpicker/libraries.cljs:215 msgid "workspace.libraries.colors.recent-colors" msgstr "最近使用的色彩" @@ -6387,73 +6331,73 @@ msgstr "RGBA" msgid "workspace.libraries.colors.save-color" msgstr "儲存色彩樣式" -#: src/app/main/ui/workspace/colorpicker/libraries.cljs:338 +#: src/app/main/ui/workspace/colorpicker/libraries.cljs:334 msgid "workspace.libraries.colors.toggle-color-palette" msgstr "顯示或隱藏調色盤" -#: src/app/main/ui/workspace/libraries.cljs:120, src/app/main/ui/workspace/libraries.cljs:149, src/app/main/ui/workspace/libraries.cljs:191 +#: src/app/main/ui/workspace/libraries.cljs:172, src/app/main/ui/workspace/libraries.cljs:201, src/app/main/ui/workspace/libraries.cljs:243 msgid "workspace.libraries.components" msgid_plural "workspace.libraries.components" msgstr[0] "%s 個元件" -#: src/app/main/ui/workspace/libraries.cljs:454 +#: src/app/main/ui/workspace/libraries.cljs:507 msgid "workspace.libraries.connected-to" msgstr "已連結至" -#: src/app/main/ui/workspace/libraries.cljs:520, src/app/main/ui/workspace/libraries.cljs:644 +#: src/app/main/ui/workspace/libraries.cljs:573, src/app/main/ui/workspace/libraries.cljs:697 msgid "workspace.libraries.empty.add-some" msgstr "或新增下列項目來試試:" -#: src/app/main/ui/workspace/libraries.cljs:514, src/app/main/ui/workspace/libraries.cljs:638 +#: src/app/main/ui/workspace/libraries.cljs:567, src/app/main/ui/workspace/libraries.cljs:691 msgid "workspace.libraries.empty.no-libraries" msgstr "您的團隊尚無共享資料庫,您可以尋找" -#: src/app/main/ui/workspace/libraries.cljs:518, src/app/main/ui/workspace/libraries.cljs:642 +#: src/app/main/ui/workspace/libraries.cljs:571, src/app/main/ui/workspace/libraries.cljs:695 msgid "workspace.libraries.empty.some-templates" msgstr "這裡的一些範本" -#: src/app/main/ui/workspace/libraries.cljs:424, src/app/main/ui/workspace/libraries.cljs:806 +#: src/app/main/ui/workspace/libraries.cljs:476, src/app/main/ui/workspace/libraries.cljs:828 msgid "workspace.libraries.file-library" msgstr "檔案資料庫" -#: src/app/main/ui/workspace/libraries.cljs:123, src/app/main/ui/workspace/libraries.cljs:152, src/app/main/ui/workspace/libraries.cljs:195 +#: src/app/main/ui/workspace/libraries.cljs:175, src/app/main/ui/workspace/libraries.cljs:204, src/app/main/ui/workspace/libraries.cljs:247 msgid "workspace.libraries.graphics" msgid_plural "workspace.libraries.graphics" msgstr[0] "%s 個圖形" -#: src/app/main/ui/workspace/libraries.cljs:418 +#: src/app/main/ui/workspace/libraries.cljs:470 msgid "workspace.libraries.in-this-file" msgstr "此檔案中的資料庫" -#: src/app/main/ui/workspace/libraries.cljs:1137, src/app/main/ui/workspace/libraries.cljs:1141, src/app/main/ui/workspace/libraries.cljs:1159 +#: src/app/main/ui/workspace/libraries.cljs:1161, src/app/main/ui/workspace/libraries.cljs:1165, src/app/main/ui/workspace/libraries.cljs:1183 msgid "workspace.libraries.libraries" msgstr "資料庫" -#: src/app/main/ui/workspace/libraries.cljs:989 +#: src/app/main/ui/workspace/libraries.cljs:1012 msgid "workspace.libraries.library-updates" msgstr "資料庫更新" -#: src/app/main/ui/workspace/libraries.cljs:509, src/app/main/ui/workspace/libraries.cljs:633 +#: src/app/main/ui/workspace/libraries.cljs:562, src/app/main/ui/workspace/libraries.cljs:686 msgid "workspace.libraries.loading" msgstr "載入中…" -#: src/app/main/ui/workspace/libraries.cljs:986 +#: src/app/main/ui/workspace/libraries.cljs:1009 msgid "workspace.libraries.no-libraries-need-sync" msgstr "沒有需要更新的共享資料庫" -#: src/app/main/ui/workspace/libraries.cljs:528, src/app/main/ui/workspace/libraries.cljs:652 +#: src/app/main/ui/workspace/libraries.cljs:581, src/app/main/ui/workspace/libraries.cljs:705 msgid "workspace.libraries.no-matches-for" msgstr "找不到符合「%s」的結果" -#: src/app/main/ui/workspace/libraries.cljs:482, src/app/main/ui/workspace/libraries.cljs:599 +#: src/app/main/ui/workspace/libraries.cljs:535, src/app/main/ui/workspace/libraries.cljs:652 msgid "workspace.libraries.search-shared-libraries" msgstr "搜尋共享資料庫" -#: src/app/main/ui/workspace/libraries.cljs:478, src/app/main/ui/workspace/libraries.cljs:595 +#: src/app/main/ui/workspace/libraries.cljs:531, src/app/main/ui/workspace/libraries.cljs:648 msgid "workspace.libraries.shared-libraries" msgstr "共享資料庫" -#: src/app/main/ui/workspace/libraries.cljs:502, src/app/main/ui/workspace/libraries.cljs:626 +#: src/app/main/ui/workspace/libraries.cljs:555, src/app/main/ui/workspace/libraries.cljs:679 msgid "workspace.libraries.shared-library-btn" msgstr "連接資料庫" @@ -6477,24 +6421,24 @@ msgstr "解除所有資產的連結" msgid "workspace.libraries.text.multiple-token-tooltip" msgstr "解除所有 tokens 的套用" -#: src/app/main/ui/workspace/libraries.cljs:129, src/app/main/ui/workspace/libraries.cljs:158, src/app/main/ui/workspace/libraries.cljs:203 +#: src/app/main/ui/workspace/libraries.cljs:181, src/app/main/ui/workspace/libraries.cljs:210, src/app/main/ui/workspace/libraries.cljs:255 msgid "workspace.libraries.typography" msgid_plural "workspace.libraries.typography" msgstr[0] "%s 種文字樣式" -#: src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/libraries.cljs:885 +#: src/app/main/ui/workspace/libraries.cljs:522, src/app/main/ui/workspace/libraries.cljs:908 msgid "workspace.libraries.unlink-library-btn" msgstr "中斷資料庫連線" -#: src/app/main/ui/workspace/libraries.cljs:1011 +#: src/app/main/ui/workspace/libraries.cljs:1034 msgid "workspace.libraries.update" msgstr "更新" -#: src/app/main/ui/workspace/libraries.cljs:1087 +#: src/app/main/ui/workspace/libraries.cljs:1111 msgid "workspace.libraries.update.see-all-changes" msgstr "查看所有變更" -#: src/app/main/ui/workspace/libraries.cljs:1139, src/app/main/ui/workspace/libraries.cljs:1143 +#: src/app/main/ui/workspace/libraries.cljs:1163, src/app/main/ui/workspace/libraries.cljs:1167 msgid "workspace.libraries.updates" msgstr "更新" @@ -6554,7 +6498,7 @@ msgstr "開啟 / 關閉模糊效果" msgid "workspace.options.blur-options.toggle-more-options" msgstr "顯示 / 隱藏更多選項" -#: src/app/main/ui/exports/assets.cljs:332 +#: src/app/main/ui/exports/assets.cljs:327 msgid "workspace.options.cancel-export" msgstr "取消" @@ -6562,107 +6506,107 @@ msgstr "取消" msgid "workspace.options.canvas-background" msgstr "畫布背景" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:722 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:720 msgid "workspace.options.clip-content" msgstr "裁切內容" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1022, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1277 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1293 msgid "workspace.options.component" msgstr "元件" -#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:193 +#: src/app/main/ui/inspect/annotation.cljs:19, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:196 msgid "workspace.options.component.annotation" msgstr "註解" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1036 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1048 msgid "workspace.options.component.copy" msgstr "複製" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:186 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:184 msgid "workspace.options.component.create-annotation" msgstr "建立註解" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:185 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:183 msgid "workspace.options.component.edit-annotation" msgstr "編輯註解" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1035, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1281 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1047, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1297 msgid "workspace.options.component.main" msgstr "主要" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:781 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:789 msgid "workspace.options.component.swap" msgstr "替換元件" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:818 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:826 msgid "workspace.options.component.swap.empty" msgstr "此資料庫尚無資產" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1061 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1075 msgid "workspace.options.component.unlinked" msgstr "未連結" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:581 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:588 msgid "workspace.options.component.variant.duplicated.copy.locate" msgstr "找出衝突的變體" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:585 msgid "workspace.options.component.variant.duplicated.copy.title" msgstr "此元件有衝突的變體。請確認每個變體的屬性值組合皆不重複。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1336 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1355 msgid "workspace.options.component.variant.duplicated.group.locate" msgstr "定位重複的變體" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1333 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1352 msgid "workspace.options.component.variant.duplicated.group.title" msgstr "部分變體的屬性與值完全相同" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:273 msgid "workspace.options.component.variant.duplicated.single.all" msgstr "這些變體的屬性與值完全相同。請調整屬性值,以便正確找到各個變體。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:263 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:270 msgid "workspace.options.component.variant.duplicated.single.one" msgstr "此變體的屬性和值與另一個變體完全相同。請調整值,以便正確擷取變體。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:269 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:276 msgid "workspace.options.component.variant.duplicated.single.some" msgstr "其中部分變體的屬性和值完全相同。請調整值,以便正確擷取變體。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:568 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:575 msgid "workspace.options.component.variant.malformed.copy" msgstr "此元件中有變體的名稱無效。請確認每個變體的名稱都符合正確格式。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1326 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1345 msgid "workspace.options.component.variant.malformed.group.locate" msgstr "找出名稱無效的變體" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1323 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1342 msgid "workspace.options.component.variant.malformed.group.title" msgstr "部分變體的名稱無效" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:571 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:578 msgid "workspace.options.component.variant.malformed.locate" msgstr "定位名稱無效的變體" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:250 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:257 msgid "workspace.options.component.variant.malformed.single.all" msgstr "這些變體的名稱皆無效。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:247 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:254 msgid "workspace.options.component.variant.malformed.single.one" msgstr "此變體的名稱無效。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:253 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:260 msgid "workspace.options.component.variant.malformed.single.some" msgstr "其中部分變體的名稱無效。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:436 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:443 msgid "workspace.options.component.variant.malformed.structure.example" msgstr "[屬性]=[值], [屬性]=[值]" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:434 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:441 msgid "workspace.options.component.variant.malformed.structure.title" msgstr "請嘗試使用以下結構:" @@ -6694,7 +6638,7 @@ msgstr "位於相同的階層。" msgid "workspace.options.component.variants-help-modal.rule3.detail" msgstr "群組、畫板和版面佈局視為等同。" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1040, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1284, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1052, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1300, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 msgid "workspace.options.component.variants-help-modal.title" msgstr "變體如何維持關聯" @@ -6742,11 +6686,11 @@ msgstr "上與下" msgid "workspace.options.convert-to-typography" msgstr "建立文字樣式" -#: src/app/main/ui/workspace/sidebar/options.cljs:238 +#: src/app/main/ui/workspace/sidebar/options.cljs:240 msgid "workspace.options.design" msgstr "設計" -#: src/app/main/ui/inspect/exports.cljs:162, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 +#: src/app/main/ui/inspect/exports.cljs:164, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:213 msgid "workspace.options.export" msgstr "匯出" @@ -6754,52 +6698,52 @@ msgstr "匯出" msgid "workspace.options.export-multiple" msgstr "匯出選取項目" -#: src/app/main/ui/inspect/exports.cljs:218, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 +#: src/app/main/ui/inspect/exports.cljs:221, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:277 msgid "workspace.options.export-object" msgid_plural "workspace.options.export-object" msgstr[0] "匯出 %s 個元素" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 +#: src/app/main/ui/inspect/exports.cljs:167, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:216 msgid "workspace.options.export.add-export" msgstr "新增匯出" -#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 +#: src/app/main/ui/inspect/exports.cljs:177, src/app/main/ui/inspect/exports.cljs:211, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:263 msgid "workspace.options.export.remove-export" msgstr "移除匯出" -#: src/app/main/ui/inspect/exports.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 +#: src/app/main/ui/inspect/exports.cljs:205, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:257 msgid "workspace.options.export.suffix" msgstr "後綴" -#: src/app/main/ui/exports/assets.cljs:267 +#: src/app/main/ui/exports/assets.cljs:260 msgid "workspace.options.exporting-busy" msgstr "匯出服務忙碌中,請稍後再試" -#: src/app/main/ui/exports/assets.cljs:270 +#: src/app/main/ui/exports/assets.cljs:263 msgid "workspace.options.exporting-cancelled" msgstr "已取消匯出" -#: src/app/main/ui/exports/assets.cljs:269 +#: src/app/main/ui/exports/assets.cljs:262 msgid "workspace.options.exporting-cancelling" msgstr "正在取消..." -#: src/app/main/ui/exports/assets.cljs:272 +#: src/app/main/ui/exports/assets.cljs:265 msgid "workspace.options.exporting-complete" msgstr "匯出完成" -#: src/app/main/ui/exports/assets.cljs:171, src/app/main/ui/exports/assets.cljs:273, src/app/main/ui/inspect/exports.cljs:217, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 +#: src/app/main/ui/exports/assets.cljs:164, src/app/main/ui/exports/assets.cljs:266, src/app/main/ui/inspect/exports.cljs:220, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:276 msgid "workspace.options.exporting-object" msgstr "正在匯出…" -#: src/app/main/ui/exports/assets.cljs:268 +#: src/app/main/ui/exports/assets.cljs:261 msgid "workspace.options.exporting-object-error" msgstr "匯出失敗" -#: src/app/main/ui/exports/assets.cljs:274 +#: src/app/main/ui/exports/assets.cljs:267 msgid "workspace.options.exporting-object-slow" msgstr "匯出速度異常緩慢" -#: src/app/main/ui/exports/assets.cljs:271 +#: src/app/main/ui/exports/assets.cljs:264 msgid "workspace.options.exporting-queued" msgstr "等待中..." @@ -6819,7 +6763,7 @@ msgstr "移除填充" msgid "workspace.options.fill.section" msgstr "填充區塊" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:553 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:551 msgid "workspace.options.fit-content" msgstr "調整畫板大小以符合內容" @@ -6843,67 +6787,67 @@ msgstr "流程起點" msgid "workspace.options.grid.auto" msgstr "自動" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 msgid "workspace.options.grid.column" msgstr "欄" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:248, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:282 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:249, src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:283 msgid "workspace.options.grid.params.color" msgstr "色彩" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:302 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:303 msgid "workspace.options.grid.params.gutter" msgstr "網格間距" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:292 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 msgid "workspace.options.grid.params.height" msgstr "高度" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:311 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:312 msgid "workspace.options.grid.params.margin" msgstr "邊距" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:79 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:78 msgid "workspace.options.grid.params.set-default" msgstr "設為預設值" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:276 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 msgid "workspace.options.grid.params.type.bottom" msgstr "靠下" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:275 msgid "workspace.options.grid.params.type.center" msgstr "置中" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:274 msgid "workspace.options.grid.params.type.left" msgstr "靠左" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:277 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:278 msgid "workspace.options.grid.params.type.right" msgstr "右" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:270 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:271 msgid "workspace.options.grid.params.type.stretch" msgstr "延展" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:272 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:273 msgid "workspace.options.grid.params.type.top" msgstr "上" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:73 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:72 msgid "workspace.options.grid.params.use-default" msgstr "使用預設值" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:293 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:294 msgid "workspace.options.grid.params.width" msgstr "寬度" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:209 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:208 msgid "workspace.options.grid.row" msgstr "列" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:207 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:206 msgid "workspace.options.grid.square" msgstr "正方形" @@ -6915,7 +6859,7 @@ msgstr "群組填充" msgid "workspace.options.group-stroke" msgstr "群組筆畫" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:366 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:367 msgid "workspace.options.guides.add-guide" msgstr "新增輔助線" @@ -6923,19 +6867,19 @@ msgstr "新增輔助線" msgid "workspace.options.guides.remove-guide" msgstr "移除輔助線" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:363 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:364 msgid "workspace.options.guides.title" msgstr "輔助線" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:231 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:230 msgid "workspace.options.guides.toggle-guide" msgstr "切換輔助線顯示" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:583, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:602 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:581, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:600 msgid "workspace.options.height" msgstr "高度" -#: src/app/main/ui/workspace/sidebar/options.cljs:242 +#: src/app/main/ui/workspace/sidebar/options.cljs:244 msgid "workspace.options.inspect" msgstr "檢視" @@ -7151,115 +7095,115 @@ msgstr "互動" msgid "workspace.options.interactions.add-interaction" msgstr "新增互動" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 msgid "workspace.options.layer-options.blend-mode.color" msgstr "色彩" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 msgid "workspace.options.layer-options.blend-mode.color-burn" msgstr "加深色彩" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 msgid "workspace.options.layer-options.blend-mode.color-dodge" msgstr "加亮色彩" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 msgid "workspace.options.layer-options.blend-mode.darken" msgstr "變暗" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 msgid "workspace.options.layer-options.blend-mode.difference" msgstr "差異化" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 msgid "workspace.options.layer-options.blend-mode.exclusion" msgstr "排除" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:199 msgid "workspace.options.layer-options.blend-mode.hard-light" msgstr "強光" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:201 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 msgid "workspace.options.layer-options.blend-mode.hue" msgstr "色相" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:193 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 msgid "workspace.options.layer-options.blend-mode.lighten" msgstr "變亮" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:204 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:205 msgid "workspace.options.layer-options.blend-mode.luminosity" msgstr "明度" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:191 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:192 msgid "workspace.options.layer-options.blend-mode.multiply" msgstr "色彩增值" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:190 msgid "workspace.options.layer-options.blend-mode.normal" msgstr "正常" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:196 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 msgid "workspace.options.layer-options.blend-mode.overlay" msgstr "覆蓋" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:202 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:203 msgid "workspace.options.layer-options.blend-mode.saturation" msgstr "飽和度" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:194 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195 msgid "workspace.options.layer-options.blend-mode.screen" msgstr "濾色" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:197 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:198 msgid "workspace.options.layer-options.blend-mode.soft-light" msgstr "柔光" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:223, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:285 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:224, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:286 msgid "workspace.options.layer-options.layer-section" msgstr "圖層選單區塊" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:256, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:263, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:311, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:317 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:257, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:264, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:318 msgid "workspace.options.layer-options.toggle-layer" msgstr "切換圖層顯示狀態" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:695, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:702 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:701, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:707 msgid "workspace.options.layout-item.layout-item-max-h" msgstr "最大高度" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:633, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:640 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:647 msgid "workspace.options.layout-item.layout-item-max-w" msgstr "最大寬度" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:664, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:672 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:671, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:678 msgid "workspace.options.layout-item.layout-item-min-h" msgstr "最小高度" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:600, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:609, src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs:617 msgid "workspace.options.layout-item.layout-item-min-w" msgstr "最小寬度" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:245 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:273 msgid "workspace.options.more-colors" msgstr "更多色彩" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:227 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:254 msgid "workspace.options.more-lib-colors" msgstr "更多資料庫色彩" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:268 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:297 msgid "workspace.options.more-token-colors" msgstr "更多色彩 tokens" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:117, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:516 msgid "workspace.options.no-size-preset-results" msgstr "沒有符合的尺寸預設值" -#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:240, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:297 +#: src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:241, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:298 msgid "workspace.options.opacity" msgstr "不透明度" -#: src/app/main/ui/workspace/sidebar/options.cljs:240 +#: src/app/main/ui/workspace/sidebar/options.cljs:242 msgid "workspace.options.prototype" msgstr "原型" @@ -7295,23 +7239,23 @@ msgstr "圓角半徑區塊" msgid "workspace.options.radius.show-single-corners" msgstr "顯示各角半徑設定" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:464 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:501 msgid "workspace.options.recent-fonts" msgstr "最近使用" -#: src/app/main/ui/exports/assets.cljs:326 +#: src/app/main/ui/exports/assets.cljs:321 msgid "workspace.options.retry" msgstr "重試" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:684, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:692 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:682, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:690 msgid "workspace.options.rotation" msgstr "旋轉" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:461 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:498 msgid "workspace.options.search-font" msgstr "搜尋字型" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:512 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:111, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:510 msgid "workspace.options.search-size-preset" msgstr "搜尋預設尺寸" @@ -7319,11 +7263,11 @@ msgstr "搜尋預設尺寸" msgid "workspace.options.select-a-shape" msgstr "選取形狀、畫板或群組,再拖曳連接線至其他畫板。" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:205 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:231 msgid "workspace.options.selection-color" msgstr "已選取的色彩" -#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:200 +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:226 msgid "workspace.options.selection-color.section" msgstr "色彩選取區" @@ -7391,55 +7335,55 @@ msgstr "切換陰影" msgid "workspace.options.show-fill-on-export" msgstr "在匯出中顯示" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:737 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:735 msgid "workspace.options.show-in-viewer" msgstr "在檢視模式中顯示" -#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:213 +#: src/app/main/ui/workspace/sidebar/options/menus/frame_grid.cljs:212 msgid "workspace.options.size" msgstr "尺寸" -#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500 +#: src/app/main/ui/workspace/sidebar/options/drawing/frame.cljs:100, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:498 msgid "workspace.options.size-presets" msgstr "預設尺寸" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.lock" msgstr "鎖定長寬比" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:617 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:615 msgid "workspace.options.size.unlock" msgstr "解除長寬比鎖定" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:334 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:359 msgid "workspace.options.stroke-cap.circle-marker-short" msgstr "圓形" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:335 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:360 msgid "workspace.options.stroke-cap.diamond-marker-short" msgstr "菱形" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:331 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:356 msgid "workspace.options.stroke-cap.line-arrow-short" msgstr "箭頭" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:329 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:354 msgid "workspace.options.stroke-cap.none" msgstr "無" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:337 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:362 msgid "workspace.options.stroke-cap.round" msgstr "圓頭" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:338 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:363 msgid "workspace.options.stroke-cap.square" msgstr "方頭" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:333 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:358 msgid "workspace.options.stroke-cap.square-marker-short" msgstr "矩形" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:332 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:357 msgid "workspace.options.stroke-cap.triangle-arrow-short" msgstr "三角形" @@ -7447,31 +7391,31 @@ msgstr "三角形" msgid "workspace.options.stroke-color" msgstr "筆畫色彩" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:548 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:571 msgid "workspace.options.stroke-dash" msgstr "虛線" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:558 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:581 msgid "workspace.options.stroke-gap" msgstr "間距" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:405, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:443 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:430, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:466 msgid "workspace.options.stroke-width" msgstr "筆畫寬度" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:518 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:541 msgid "workspace.options.stroke-width-bottom" msgstr "下側(區塊結束端)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:531 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:554 msgid "workspace.options.stroke-width-left" msgstr "左側(行內起始端)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:505 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:528 msgid "workspace.options.stroke-width-right" msgstr "右側(行內結束端)" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:492 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:515 msgid "workspace.options.stroke-width-top" msgstr "上側(區塊起始端)" @@ -7479,47 +7423,47 @@ msgstr "上側(區塊起始端)" msgid "workspace.options.stroke.add-stroke" msgstr "新增筆畫色彩" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:238 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:263 msgid "workspace.options.stroke.center" msgstr "置中" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:266 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:291 msgid "workspace.options.stroke.dashed" msgstr "虛線" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:290 msgid "workspace.options.stroke.dotted" msgstr "點線" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:239 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 msgid "workspace.options.stroke.inner" msgstr "內側" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:267 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:292 msgid "workspace.options.stroke.mixed" msgstr "混合" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:240 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:265 msgid "workspace.options.stroke.outer" msgstr "外側" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:150 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:175 msgid "workspace.options.stroke.per-side" msgstr "各邊筆畫" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:149 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:174 msgid "workspace.options.stroke.per-side-disabled" msgstr "各邊筆畫功能僅適用於新版渲染引擎。請切換渲染引擎以使用此功能。" -#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:389 +#: src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:282, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:414 msgid "workspace.options.stroke.remove-stroke" msgstr "移除筆畫" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:264 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:289 msgid "workspace.options.stroke.solid" msgstr "實線" -#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:384 +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:409 msgid "workspace.options.stroke.toggle-stroke" msgstr "開啟或關閉筆畫" @@ -7555,11 +7499,11 @@ msgstr "自動寬度" msgid "workspace.options.text-options.grow-fixed" msgstr "固定" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:688 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:732 msgid "workspace.options.text-options.letter-spacing" msgstr "字距" -#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:669 +#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:713 msgid "workspace.options.text-options.line-height" msgstr "行高" @@ -7607,216 +7551,216 @@ msgstr "底線(%s)" msgid "workspace.options.use-play-button" msgstr "使用頂端列的播放按鈕預覽原型。" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:568, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:590 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:566, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:588 msgid "workspace.options.width" msgstr "寬度" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:630, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:653 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:191, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:628, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:651 msgid "workspace.options.x" msgstr "X 軸" -#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:643, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:664 +#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:641, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:662 msgid "workspace.options.y" msgstr "Y 軸" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:270 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:237 msgid "workspace.path.actions.draw-nodes" msgstr "繪製節點(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:165 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:132 msgid "workspace.path.actions.handler-aligned" msgstr "對齊" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:169 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:136 msgid "workspace.path.actions.handler-independent" msgstr "獨立" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:161 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:128 msgid "workspace.path.actions.handler-mirror" msgstr "等長" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:148 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:121 msgid "workspace.path.actions.handler-type" msgstr "控制柄行為" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:288 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:256 msgid "workspace.path.actions.join-nodes" msgstr "連接節點(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:303 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:271 msgid "workspace.path.actions.make-corner" msgstr "轉為角點(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:307 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:275 msgid "workspace.path.actions.make-curve" msgstr "轉為曲線(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:284 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:252 msgid "workspace.path.actions.merge-nodes" msgstr "合併節點(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:274 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:241 msgid "workspace.path.actions.move-nodes" msgstr "移動節點(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:292 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:260 msgid "workspace.path.actions.separate-nodes" msgstr "分離節點(%s)" -#: src/app/main/ui/workspace/viewport/path_actions.cljs:322 +#: src/app/main/ui/workspace/viewport/path_actions.cljs:289 msgid "workspace.path.actions.snap-nodes" msgstr "貼齊節點(%s)" -#: src/app/main/ui/workspace/plugins.cljs:98 +#: src/app/main/ui/workspace/plugins.cljs:95 msgid "workspace.plugins.button-open" msgstr "開啟" -#: src/app/main/ui/workspace/plugins.cljs:215 +#: src/app/main/ui/workspace/plugins.cljs:243 #, markdown msgid "workspace.plugins.discover" msgstr "探索[更多外掛](%s)" -#: src/app/main/ui/workspace/plugins.cljs:222 +#: src/app/main/ui/workspace/plugins.cljs:251 msgid "workspace.plugins.empty-plugins" msgstr "尚未安裝任何外掛" -#: src/app/main/ui/workspace/plugins.cljs:209 +#: src/app/main/ui/workspace/plugins.cljs:237 msgid "workspace.plugins.error.manifest" msgstr "外掛的資訊清單有誤。" -#: src/app/main/data/plugins.cljs:129, src/app/main/ui/workspace/main_menu.cljs:811, src/app/main/ui/workspace/plugins.cljs:97 +#: src/app/main/data/plugins.cljs:131, src/app/main/ui/workspace/main_menu.cljs:812, src/app/main/ui/workspace/plugins.cljs:94 msgid "workspace.plugins.error.need-editor" msgstr "您需要具備編輯者權限才能使用此外掛" -#: src/app/main/ui/workspace/plugins.cljs:205 +#: src/app/main/ui/workspace/plugins.cljs:233 msgid "workspace.plugins.error.url" msgstr "此外掛不存在,或網址不正確。" -#: src/app/main/ui/workspace/plugins.cljs:201 +#: src/app/main/ui/workspace/plugins.cljs:229 msgid "workspace.plugins.install" msgstr "安裝" -#: src/app/main/ui/workspace/plugins.cljs:231 +#: src/app/main/ui/workspace/plugins.cljs:263 msgid "workspace.plugins.installed-plugins" msgstr "已安裝的外掛" -#: src/app/main/ui/workspace/main_menu.cljs:766 +#: src/app/main/ui/workspace/main_menu.cljs:767 msgid "workspace.plugins.menu.plugins-manager" msgstr "外掛管理員" -#: src/app/main/ui/workspace/main_menu.cljs:1078 +#: src/app/main/ui/workspace/main_menu.cljs:1079 msgid "workspace.plugins.menu.title" msgstr "外掛" -#: src/app/main/ui/workspace/plugins.cljs:424 +#: src/app/main/ui/workspace/plugins.cljs:491 msgid "workspace.plugins.permissions-update.title" msgstr "更新此外掛" -#: src/app/main/ui/workspace/plugins.cljs:428 +#: src/app/main/ui/workspace/plugins.cljs:495 msgid "workspace.plugins.permissions-update.warning" msgstr "此外掛自您上次開啟後已有變更,現在它還需要以下存取權限:" -#: src/app/main/ui/workspace/plugins.cljs:297 +#: src/app/main/ui/workspace/plugins.cljs:330 msgid "workspace.plugins.permissions.allow-download" msgstr "啟動檔案下載。" -#: src/app/main/ui/workspace/plugins.cljs:304 +#: src/app/main/ui/workspace/plugins.cljs:337 msgid "workspace.plugins.permissions.allow-localstorage" msgstr "在瀏覽器中儲存資料。" -#: src/app/main/ui/workspace/plugins.cljs:317 +#: src/app/main/ui/workspace/plugins.cljs:378 msgid "workspace.plugins.permissions.clipboard-read" msgstr "讀取您的剪貼簿內容。" -#: src/app/main/ui/workspace/plugins.cljs:311 +#: src/app/main/ui/workspace/plugins.cljs:372 msgid "workspace.plugins.permissions.clipboard-write" msgstr "讀取及寫入您的剪貼簿內容。" -#: src/app/main/ui/workspace/plugins.cljs:290 +#: src/app/main/ui/workspace/plugins.cljs:323 msgid "workspace.plugins.permissions.comment-read" msgstr "讀取您的留言與回覆。" -#: src/app/main/ui/workspace/plugins.cljs:284 +#: src/app/main/ui/workspace/plugins.cljs:317 msgid "workspace.plugins.permissions.comment-write" msgstr "讀取及修改您的留言,並以您的名義回覆。" -#: src/app/main/ui/workspace/plugins.cljs:257 +#: src/app/main/ui/workspace/plugins.cljs:290 msgid "workspace.plugins.permissions.content-read" msgstr "讀取使用者有權存取的檔案內容。" -#: src/app/main/ui/workspace/plugins.cljs:251 +#: src/app/main/ui/workspace/plugins.cljs:284 msgid "workspace.plugins.permissions.content-write" msgstr "讀取及修改使用者有權存取的檔案內容。" -#: src/app/main/ui/workspace/plugins.cljs:366 +#: src/app/main/ui/workspace/plugins.cljs:431 msgid "workspace.plugins.permissions.disclaimer" msgstr "請注意,此外掛由第三方開發,授予存取權限前,請確認您信任此外掛。我們重視您的資料隱私與安全。如有任何疑慮,請聯絡客服。" -#: src/app/main/ui/workspace/plugins.cljs:277 +#: src/app/main/ui/workspace/plugins.cljs:310 msgid "workspace.plugins.permissions.library-read" msgstr "讀取您的資料庫與資產。" -#: src/app/main/ui/workspace/plugins.cljs:271 +#: src/app/main/ui/workspace/plugins.cljs:304 msgid "workspace.plugins.permissions.library-write" msgstr "讀取及修改您的資料庫與資產。" -#: src/app/main/ui/workspace/plugins.cljs:359 +#: src/app/main/ui/workspace/plugins.cljs:424 msgid "workspace.plugins.permissions.title" msgstr "「%s」外掛要求存取以下內容:" -#: src/app/main/ui/workspace/plugins.cljs:264 +#: src/app/main/ui/workspace/plugins.cljs:297 msgid "workspace.plugins.permissions.user-read" msgstr "讀取目前使用者的個人資料。" -#: src/app/main/ui/workspace/plugins.cljs:227 +#: src/app/main/ui/workspace/plugins.cljs:256 msgid "workspace.plugins.plugin-list-link" msgstr "外掛清單" -#: src/app/main/ui/workspace/plugins.cljs:101 +#: src/app/main/ui/workspace/plugins.cljs:98, src/app/main/ui/workspace/plugins.cljs:189 msgid "workspace.plugins.remove-plugin" msgstr "移除外掛" -#: src/app/main/ui/workspace/plugins.cljs:195 +#: src/app/main/ui/workspace/plugins.cljs:223 msgid "workspace.plugins.search-placeholder" msgstr "輸入外掛網址" -#: src/app/main/ui/workspace/plugins.cljs:188 +#: src/app/main/ui/workspace/plugins.cljs:216 msgid "workspace.plugins.title" msgstr "外掛" -#: src/app/main/ui/workspace/plugins.cljs:494 +#: src/app/main/ui/workspace/plugins.cljs:563 msgid "workspace.plugins.try-out.cancel" msgstr "暫時不要" -#: src/app/main/ui/workspace/plugins.cljs:487 +#: src/app/main/ui/workspace/plugins.cljs:556 msgid "workspace.plugins.try-out.message" msgstr "想看看嗎?它將在您當前的團隊中以新草稿開啟。(如果不想,您隨時可以在任何檔案的已安裝外掛中找到它。)" -#: src/app/main/ui/workspace/plugins.cljs:483 +#: src/app/main/ui/workspace/plugins.cljs:552 msgid "workspace.plugins.try-out.title" msgstr "已為您的帳號安裝「%s」外掛!" -#: src/app/main/ui/workspace/plugins.cljs:500 +#: src/app/main/ui/workspace/plugins.cljs:568 msgid "workspace.plugins.try-out.try" msgstr "試用外掛" -#: src/app/main/ui/workspace/context_menu.cljs:659 +#: src/app/main/ui/workspace/context_menu.cljs:667 msgid "workspace.shape.menu.add-flex" msgstr "新增彈性佈局" -#: src/app/main/ui/workspace/context_menu.cljs:664 +#: src/app/main/ui/workspace/context_menu.cljs:672 msgid "workspace.shape.menu.add-grid" msgstr "新增網格佈局" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1304, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1318, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1332 msgid "workspace.shape.menu.add-layout" msgstr "新增佈局" -#: src/app/main/ui/workspace/context_menu.cljs:719, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1046, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1214, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1288 +#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/common.cljs:585, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1059, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1230, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1305 msgid "workspace.shape.menu.add-variant" msgstr "建立變體" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1068, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1216, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1302 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:589, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1082, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1232, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1320 msgid "workspace.shape.menu.add-variant-property" msgstr "新增屬性" @@ -7828,11 +7772,11 @@ msgstr "移至最底層" msgid "workspace.shape.menu.backward" msgstr "下移一層" -#: src/app/main/ui/workspace/context_menu.cljs:746 +#: src/app/main/ui/workspace/context_menu.cljs:754 msgid "workspace.shape.menu.clear-guides" msgstr "清除畫板參考線" -#: src/app/main/ui/workspace/context_menu.cljs:727, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1101 +#: src/app/main/ui/workspace/context_menu.cljs:735, src/app/main/ui/workspace/sidebar/assets/components.cljs:663, src/app/main/ui/workspace/sidebar/assets/groups.cljs:80, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1116 msgid "workspace.shape.menu.combine-as-variants" msgstr "合併為變體" @@ -7840,7 +7784,7 @@ msgstr "合併為變體" msgid "workspace.shape.menu.combine-as-variants-error" msgstr "元件必須位於同一頁面" -#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1064 +#: src/app/main/ui/workspace/context_menu.cljs:228, src/app/main/ui/workspace/context_menu.cljs:1072 msgid "workspace.shape.menu.copy" msgstr "複製" @@ -7884,27 +7828,27 @@ msgstr "複製為文字" msgid "workspace.shape.menu.create-annotation" msgstr "建立註解" -#: src/app/main/ui/workspace/context_menu.cljs:444 +#: src/app/main/ui/workspace/context_menu.cljs:446 msgid "workspace.shape.menu.create-artboard-from-selection" msgstr "將選取項目轉為畫板" -#: src/app/main/ui/workspace/context_menu.cljs:697 +#: src/app/main/ui/workspace/context_menu.cljs:705 msgid "workspace.shape.menu.create-component" msgstr "建立元件" -#: src/app/main/ui/workspace/context_menu.cljs:702 +#: src/app/main/ui/workspace/context_menu.cljs:710 msgid "workspace.shape.menu.create-multiple-components" msgstr "建立多個元件" -#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1059 +#: src/app/main/ui/workspace/context_menu.cljs:236, src/app/main/ui/workspace/context_menu.cljs:1067 msgid "workspace.shape.menu.cut" msgstr "剪下" -#: src/app/main/ui/workspace/context_menu.cljs:758, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1013, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 +#: src/app/main/ui/workspace/context_menu.cljs:766, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1018, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1340 msgid "workspace.shape.menu.delete" msgstr "刪除" -#: src/app/main/ui/workspace/context_menu.cljs:602 +#: src/app/main/ui/workspace/context_menu.cljs:604 msgid "workspace.shape.menu.delete-flow-start" msgstr "刪除流程起點" @@ -7916,7 +7860,7 @@ msgstr "分離實例" msgid "workspace.shape.menu.detach-instances-in-bulk" msgstr "分離實例" -#: src/app/main/ui/workspace/context_menu.cljs:535, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:90 +#: src/app/main/ui/workspace/context_menu.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:87 msgid "workspace.shape.menu.difference" msgstr "差集" @@ -7924,15 +7868,15 @@ msgstr "差集" msgid "workspace.shape.menu.duplicate" msgstr "建立複本" -#: src/app/main/ui/workspace/context_menu.cljs:509 +#: src/app/main/ui/workspace/context_menu.cljs:511 msgid "workspace.shape.menu.edit" msgstr "編輯" -#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:100 +#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:97 msgid "workspace.shape.menu.exclude" msgstr "排除" -#: src/app/main/ui/workspace/context_menu.cljs:518, src/app/main/ui/workspace/context_menu.cljs:551, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:104 +#: src/app/main/ui/workspace/context_menu.cljs:520, src/app/main/ui/workspace/context_menu.cljs:553, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:102 msgid "workspace.shape.menu.flatten" msgstr "扁平化" @@ -7944,7 +7888,7 @@ msgstr "水平翻轉" msgid "workspace.shape.menu.flip-vertical" msgstr "垂直翻轉" -#: src/app/main/ui/workspace/context_menu.cljs:604 +#: src/app/main/ui/workspace/context_menu.cljs:606 msgid "workspace.shape.menu.flow-start" msgstr "流程起點" @@ -7956,31 +7900,31 @@ msgstr "上移一層" msgid "workspace.shape.menu.front" msgstr "移至最上層" -#: src/app/main/ui/workspace/context_menu.cljs:427 +#: src/app/main/ui/workspace/context_menu.cljs:429 msgid "workspace.shape.menu.group" msgstr "群組" -#: src/app/main/ui/workspace/context_menu.cljs:571, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 +#: src/app/main/ui/workspace/context_menu.cljs:573, src/app/main/ui/workspace/sidebar/layer_item.cljs:191 msgid "workspace.shape.menu.hide" msgstr "隱藏" -#: src/app/main/ui/workspace/context_menu.cljs:853, src/app/main/ui/workspace/main_menu.cljs:489 +#: src/app/main/ui/workspace/context_menu.cljs:861, src/app/main/ui/workspace/main_menu.cljs:490 msgid "workspace.shape.menu.hide-ui" msgstr "顯示 / 隱藏使用者介面" -#: src/app/main/ui/workspace/context_menu.cljs:539, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:95 +#: src/app/main/ui/workspace/context_menu.cljs:541, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:92 msgid "workspace.shape.menu.intersection" msgstr "交集" -#: src/app/main/ui/workspace/context_menu.cljs:581, src/app/main/ui/workspace/sidebar/layer_item.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:271, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:324 +#: src/app/main/ui/workspace/context_menu.cljs:583, src/app/main/ui/workspace/sidebar/layer_item.cljs:201, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:272, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:325 msgid "workspace.shape.menu.lock" msgstr "鎖定" -#: src/app/main/ui/workspace/context_menu.cljs:433 +#: src/app/main/ui/workspace/context_menu.cljs:435 msgid "workspace.shape.menu.mask" msgstr "遮罩" -#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:849, src/app/main/ui/workspace/context_menu.cljs:1069 +#: src/app/main/ui/workspace/context_menu.cljs:240, src/app/main/ui/workspace/context_menu.cljs:857, src/app/main/ui/workspace/context_menu.cljs:1077 msgid "workspace.shape.menu.paste" msgstr "貼上" @@ -7988,27 +7932,27 @@ msgstr "貼上" msgid "workspace.shape.menu.paste-props" msgstr "貼上屬性" -#: src/app/main/ui/workspace/context_menu.cljs:530 +#: src/app/main/ui/workspace/context_menu.cljs:532 msgid "workspace.shape.menu.path" msgstr "路徑" -#: src/app/main/ui/workspace/context_menu.cljs:647 +#: src/app/main/ui/workspace/context_menu.cljs:653 msgid "workspace.shape.menu.remove-flex" msgstr "移除彈性佈局" -#: src/app/main/ui/workspace/context_menu.cljs:651 +#: src/app/main/ui/workspace/context_menu.cljs:657 msgid "workspace.shape.menu.remove-grid" msgstr "移除網格佈局" -#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1312 +#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1326 msgid "workspace.shape.menu.remove-layout" msgstr "移除版面佈局" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1152 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1167 msgid "workspace.shape.menu.remove-variant-property" msgstr "移除屬性" -#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1151 +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1166 msgid "workspace.shape.menu.remove-variant-property.last-property" msgstr "變體至少須有一個屬性" @@ -8032,11 +7976,11 @@ msgstr "還原變體" msgid "workspace.shape.menu.select-layer" msgstr "選取圖層" -#: src/app/main/ui/workspace/context_menu.cljs:567, src/app/main/ui/workspace/sidebar/layer_item.cljs:189 +#: src/app/main/ui/workspace/context_menu.cljs:569, src/app/main/ui/workspace/sidebar/layer_item.cljs:190 msgid "workspace.shape.menu.show" msgstr "顯示" -#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1212 +#: src/app/main/ui/workspace/sidebar/assets/common.cljs:558, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1228 msgid "workspace.shape.menu.show-in-assets" msgstr "在資產面板中顯示" @@ -8044,7 +7988,7 @@ msgstr "在資產面板中顯示" msgid "workspace.shape.menu.show-main" msgstr "顯示主要元件" -#: src/app/main/ui/workspace/context_menu.cljs:524 +#: src/app/main/ui/workspace/context_menu.cljs:526 msgid "workspace.shape.menu.stroke-to-path" msgstr "將筆畫轉換為路徑" @@ -8056,19 +8000,19 @@ msgstr "移除縮圖" msgid "workspace.shape.menu.thumbnail-set" msgstr "設為縮圖" -#: src/app/main/ui/workspace/context_menu.cljs:422 +#: src/app/main/ui/workspace/context_menu.cljs:424 msgid "workspace.shape.menu.ungroup" msgstr "取消群組" -#: src/app/main/ui/workspace/context_menu.cljs:531, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85 +#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:82 msgid "workspace.shape.menu.union" msgstr "聯集" -#: src/app/main/ui/workspace/context_menu.cljs:577, src/app/main/ui/workspace/sidebar/layer_item.cljs:197, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:278, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:330 +#: src/app/main/ui/workspace/context_menu.cljs:579, src/app/main/ui/workspace/sidebar/layer_item.cljs:200, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:279, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:331 msgid "workspace.shape.menu.unlock" msgstr "解除鎖定" -#: src/app/main/ui/workspace/context_menu.cljs:439 +#: src/app/main/ui/workspace/context_menu.cljs:441 msgid "workspace.shape.menu.unmask" msgstr "取消遮罩" @@ -8084,79 +8028,79 @@ msgstr "收合側邊欄" msgid "workspace.sidebar.expand" msgstr "展開側邊欄" -#: src/app/main/ui/workspace/right_header.cljs:232, src/app/main/ui/workspace/right_header.cljs:233 +#: src/app/main/ui/workspace/right_header.cljs:236 msgid "workspace.sidebar.history" msgstr "歷史紀錄" -#: src/app/main/ui/workspace/sidebar/layers.cljs:882, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 +#: src/app/main/ui/workspace/sidebar/layers.cljs:921, src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:157, src/app/main/ui/workspace/sidebar.cljs:164 msgid "workspace.sidebar.layers" msgstr "圖層" -#: src/app/main/ui/workspace/sidebar/layers.cljs:652, src/app/main/ui/workspace/sidebar/layers.cljs:716 +#: src/app/main/ui/workspace/sidebar/layers.cljs:685, src/app/main/ui/workspace/sidebar/layers.cljs:749 msgid "workspace.sidebar.layers.components" msgstr "元件" -#: src/app/main/ui/workspace/sidebar/layers.cljs:585 +#: src/app/main/ui/workspace/sidebar/layers.cljs:617 msgid "workspace.sidebar.layers.filter" msgstr "篩選" -#: src/app/main/ui/workspace/sidebar/layers.cljs:649, src/app/main/ui/workspace/sidebar/layers.cljs:680 +#: src/app/main/ui/workspace/sidebar/layers.cljs:682, src/app/main/ui/workspace/sidebar/layers.cljs:713 msgid "workspace.sidebar.layers.frames" msgstr "畫板" -#: src/app/main/ui/workspace/sidebar/layers.cljs:650, src/app/main/ui/workspace/sidebar/layers.cljs:692 +#: src/app/main/ui/workspace/sidebar/layers.cljs:683, src/app/main/ui/workspace/sidebar/layers.cljs:725 msgid "workspace.sidebar.layers.groups" msgstr "群組" -#: src/app/main/ui/workspace/sidebar/layers.cljs:654, src/app/main/ui/workspace/sidebar/layers.cljs:740 +#: src/app/main/ui/workspace/sidebar/layers.cljs:687, src/app/main/ui/workspace/sidebar/layers.cljs:773 msgid "workspace.sidebar.layers.images" msgstr "圖片" -#: src/app/main/ui/workspace/sidebar/layers.cljs:651, src/app/main/ui/workspace/sidebar/layers.cljs:704 +#: src/app/main/ui/workspace/sidebar/layers.cljs:684, src/app/main/ui/workspace/sidebar/layers.cljs:737 msgid "workspace.sidebar.layers.masks" msgstr "遮罩" -#: src/app/main/ui/workspace/sidebar/layers.cljs:617 +#: src/app/main/ui/workspace/sidebar/layers.cljs:650 msgid "workspace.sidebar.layers.no-matches" msgstr "沒有符合的結果" -#: src/app/main/ui/workspace/sidebar/layers.cljs:636 +#: src/app/main/ui/workspace/sidebar/layers.cljs:669 msgid "workspace.sidebar.layers.replace" msgstr "取代" -#: src/app/main/ui/workspace/sidebar/layers.cljs:642 +#: src/app/main/ui/workspace/sidebar/layers.cljs:675 msgid "workspace.sidebar.layers.replace-all" msgstr "全部取代" -#: src/app/main/ui/workspace/sidebar/layers.cljs:603 +#: src/app/main/ui/workspace/sidebar/layers.cljs:636 msgid "workspace.sidebar.layers.replace-placeholder" msgstr "取代為…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:581 +#: src/app/main/ui/workspace/sidebar/layers.cljs:613 msgid "workspace.sidebar.layers.search" msgstr "尋找…" -#: src/app/main/ui/workspace/sidebar/layers.cljs:591 +#: src/app/main/ui/workspace/sidebar/layers.cljs:623 msgid "workspace.sidebar.layers.search-and-replace" msgstr "尋找與取代" -#: src/app/main/ui/workspace/sidebar/layers.cljs:622 +#: src/app/main/ui/workspace/sidebar/layers.cljs:655 msgid "workspace.sidebar.layers.search-scope-canvas" msgstr "文字內容" -#: src/app/main/ui/workspace/sidebar/layers.cljs:626 +#: src/app/main/ui/workspace/sidebar/layers.cljs:659 msgid "workspace.sidebar.layers.search-scope-layers" msgstr "圖層名稱" -#: src/app/main/ui/workspace/sidebar/layers.cljs:655, src/app/main/ui/workspace/sidebar/layers.cljs:752 +#: src/app/main/ui/workspace/sidebar/layers.cljs:688, src/app/main/ui/workspace/sidebar/layers.cljs:785 msgid "workspace.sidebar.layers.shapes" msgstr "形狀" -#: src/app/main/ui/workspace/sidebar/layers.cljs:653, src/app/main/ui/workspace/sidebar/layers.cljs:728 +#: src/app/main/ui/workspace/sidebar/layers.cljs:686, src/app/main/ui/workspace/sidebar/layers.cljs:761 msgid "workspace.sidebar.layers.texts" msgstr "文字" -#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:113 +#: src/app/main/ui/inspect/attributes/svg.cljs:56, src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs:114 msgid "workspace.sidebar.options.svg-attrs.title" msgstr "匯入的 SVG 屬性" @@ -8168,7 +8112,7 @@ msgstr "頁面" msgid "workspace.sidebar.sitemap.add-page" msgstr "新增頁面" -#: src/app/main/ui/workspace/left_header.cljs:94 +#: src/app/main/ui/workspace/left_header.cljs:100 msgid "workspace.sitemap" msgstr "網站地圖" @@ -8196,7 +8140,7 @@ msgstr "在此群組中新增集合" msgid "workspace.tokens.add-token" msgstr "新增 token:%s" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:156 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:163 msgid "workspace.tokens.applied-to" msgstr "套用至" @@ -8216,7 +8160,7 @@ msgstr "基準字級" msgid "workspace.tokens.base-font-size.error" msgstr "基準字級必須是以像素為單位或不含單位的數值。" -#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:132 +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:369, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152 msgid "workspace.tokens.color" msgstr "色彩" @@ -8228,7 +8172,7 @@ msgstr "複製 token 路徑" msgid "workspace.tokens.create-new-theme" msgstr "立即建立您的第一個主題。" -#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:44 +#: src/app/main/ui/workspace/tokens/sets/lists.cljs:96, src/app/main/ui/workspace/tokens/themes.cljs:47 msgid "workspace.tokens.create-one" msgstr "建立一個。" @@ -8252,7 +8196,7 @@ msgstr "複製 token" msgid "workspace.tokens.duplicate-group" msgstr "複製 Tokens 群組" -#: src/app/main/data/workspace/tokens/library_edit.cljs:395 +#: src/app/main/data/workspace/tokens/library_edit.cljs:425 msgid "workspace.tokens.duplicate-suffix" msgstr "複本" @@ -8276,7 +8220,7 @@ msgstr "編輯 %s token" msgid "workspace.tokens.enter-token-name" msgstr "輸入 %s token 名稱" -#: src/app/main/data/workspace/tokens/application.cljs:767, src/app/main/ui/workspace/tokens/management/group.cljs:164 +#: src/app/main/data/workspace/tokens/application.cljs:764, src/app/main/ui/workspace/tokens/management/group.cljs:164 msgid "workspace.tokens.error-text-edition" msgstr "編輯文字時無法套用 Tokens。請改為選取文字圖層。" @@ -8304,7 +8248,7 @@ msgstr "預覽:" msgid "workspace.tokens.export.single-file" msgstr "單一檔案" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:129 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:152 msgid "workspace.tokens.font-size-value-enter" msgstr "字型大小或 {alias}" @@ -8312,7 +8256,7 @@ msgstr "字型大小或 {alias}" msgid "workspace.tokens.font-variant-not-found" msgstr "設定字重或樣式時發生錯誤。目前的字型不支援此樣式" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:137 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:60, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:160 msgid "workspace.tokens.font-weight-value-enter" msgstr "字重(300、粗斜體…)或 {alias}" @@ -8348,7 +8292,7 @@ msgstr "匯入多個檔案時,檔案名稱或路徑會作為集合名稱。" msgid "workspace.tokens.import-single-file" msgstr "匯入單一 JSON 檔案時,第一層的鍵應為 token 集合名稱。" -#: src/app/main/ui/workspace/libraries.cljs:462, src/app/main/ui/workspace/libraries.cljs:878, src/app/main/ui/workspace/tokens/import/modal.cljs:236 +#: src/app/main/ui/workspace/libraries.cljs:515, src/app/main/ui/workspace/libraries.cljs:901, src/app/main/ui/workspace/tokens/import/modal.cljs:236 msgid "workspace.tokens.import-tokens" msgstr "匯入 tokens" @@ -8356,15 +8300,15 @@ msgstr "匯入 tokens" msgid "workspace.tokens.import-warning" msgstr "匯入 tokens 將覆寫目前所有的 tokens、集合與主題。" -#: src/app/main/ui/workspace/tokens/management.cljs:81 +#: src/app/main/ui/workspace/tokens/management.cljs:88 msgid "workspace.tokens.inactive-set" msgstr "未啟用" -#: src/app/main/ui/workspace/tokens/management.cljs:73 +#: src/app/main/ui/workspace/tokens/management.cljs:80 msgid "workspace.tokens.inactive-set-description" msgstr "此集合尚未啟用。請切換主題或啟用此集合,即可在檢視區域中查看變更" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:245, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:196 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:312, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:266 msgid "workspace.tokens.individual-tokens" msgstr "使用個別 tokens" @@ -8384,11 +8328,11 @@ msgstr "主題" msgid "workspace.tokens.label.theme-placeholder" msgstr "新增主題(例如:淺色)" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:153 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176 msgid "workspace.tokens.letter-spacing-value-enter-composite" msgstr "字距或 {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:145 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:168 msgid "workspace.tokens.line-height-value-enter" msgstr "行高(倍數、px、%)或 {alias}" @@ -8404,11 +8348,11 @@ msgstr "最大尺寸" msgid "workspace.tokens.min-size" msgstr "最小尺寸" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:351 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:349 msgid "workspace.tokens.missing-reference" msgstr "缺少參照" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:137 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:160 msgid "workspace.tokens.more-options" msgstr "按右鍵查看選項" @@ -8424,7 +8368,7 @@ msgstr "目前沒有啟用的主題" msgid "workspace.tokens.no-permisions-set" msgstr "您必須具備編輯者權限,才能啟用或停用集合" -#: src/app/main/ui/workspace/tokens/themes.cljs:54 +#: src/app/main/ui/workspace/tokens/themes.cljs:57 msgid "workspace.tokens.no-permission-themes" msgstr "您必須具備編輯者權限,才能使用主題" @@ -8436,7 +8380,7 @@ msgstr "尚未定義任何集合,請先建立一個。" msgid "workspace.tokens.no-sets-yet" msgstr "目前尚無集合。" -#: src/app/main/ui/workspace/tokens/themes.cljs:40 +#: src/app/main/ui/workspace/tokens/themes.cljs:43 msgid "workspace.tokens.no-themes" msgstr "沒有任何主題。" @@ -8452,7 +8396,7 @@ msgstr "不要重新對應" msgid "workspace.tokens.num-active-sets" msgstr "%s 個已啟用的集合" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:157 msgid "workspace.tokens.original-value" msgstr "原始值:%s" @@ -8464,15 +8408,15 @@ msgstr "內距" msgid "workspace.tokens.radius" msgstr "半徑" -#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:142 +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:58, src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:60, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:124, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:131, src/app/main/ui/workspace/tokens/management/token_pill.cljs:128 msgid "workspace.tokens.ref-not-valid" msgstr "{%s} 中的參照無效,或不在任何已啟用的集合中。" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:178 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:215 msgid "workspace.tokens.reference-composite" msgstr "輸入文字樣式 token 的別名" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:208 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:242 msgid "workspace.tokens.reference-composite-shadow" msgstr "輸入陰影 token 的別名" @@ -8508,7 +8452,7 @@ msgstr "重新命名 Tokens 群組" msgid "workspace.tokens.rename-group-name-hint" msgstr "你的 tokens 將自動重新命名為 %s.(suffix).(tokenName)" -#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:135 +#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:61, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:89, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:127, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:293, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:486, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:295, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:186, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:321, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:265, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:387, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:511, src/app/main/ui/workspace/tokens/management/token_pill.cljs:158 msgid "workspace.tokens.resolved-value" msgstr "解析後的值:%s" @@ -8544,27 +8488,27 @@ msgstr "您可以在這裡設定基準字型大小,決定 1rem 的值:" msgid "workspace.tokens.settings" msgstr "Tokens 設定" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:236 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:303 msgid "workspace.tokens.shadow-add-shadow" msgstr "新增陰影" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:164, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:183, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:184, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:188 msgid "workspace.tokens.shadow-blur" msgstr "模糊" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:116 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:136 msgid "workspace.tokens.shadow-inset" msgstr "內陰影" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:125 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:145 msgid "workspace.tokens.shadow-remove-shadow" msgstr "移除陰影" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:176, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:177, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:181 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:196, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:197, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:201 msgid "workspace.tokens.shadow-spread" msgstr "擴散" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:287, ../common/src/app/common/files/tokens.cljc:112 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:355, ../common/src/app/common/files/tokens.cljc:112 msgid "workspace.tokens.shadow-token-blur-value-error" msgstr "模糊值不可為負數" @@ -8572,11 +8516,11 @@ msgstr "模糊值不可為負數" msgid "workspace.tokens.shadow-token-spread-value-error" msgstr "擴散值不可為負數" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:141, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:143 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:161, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:163 msgid "workspace.tokens.shadow-x" msgstr "X" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:152, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:154 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:172, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:174 msgid "workspace.tokens.shadow-y" msgstr "Y" @@ -8584,11 +8528,11 @@ msgstr "Y" msgid "workspace.tokens.size" msgstr "尺寸" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:161 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:54, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:184 msgid "workspace.tokens.text-case-value-enter" msgstr "none | uppercase | lowercase | capitalize 或 {alias}" -#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:169 +#: src/app/main/ui/workspace/tokens/management/forms/form_container.cljs:57, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:192 msgid "workspace.tokens.text-decoration-value-enter" msgstr "none | underline | strike-through 或 {alias}" @@ -8604,31 +8548,31 @@ msgstr "主題清單" msgid "workspace.tokens.token-description" msgstr "描述" -#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:133, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:258 +#: src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:130, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:255 msgid "workspace.tokens.token-font-family-select" msgstr "選擇字型家族" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:121 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:144 msgid "workspace.tokens.token-font-family-value" msgstr "字型家族" -#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:120 +#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:143 msgid "workspace.tokens.token-font-family-value-enter" msgstr "字型系列,或以逗號 (,) 分隔的字型清單" -#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:131 +#: src/app/main/ui/ds/controls/shared/token_option.cljs:43, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:88, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:142, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:162, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:298, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:63, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:64, src/app/main/ui/workspace/tokens/management/token_pill.cljs:155 msgid "workspace.tokens.token-name" msgstr "名稱" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:273, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:341, ../common/src/app/common/files/tokens.cljc:156, ../common/src/app/common/files/tokens.cljc:169 msgid "workspace.tokens.token-name-duplication-validation-error" msgstr "路徑 %s 或其任一前綴已存在 token。" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:270, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:338, ../common/src/app/common/files/tokens.cljc:153, ../common/src/app/common/files/tokens.cljc:166, ../common/src/app/common/files/tokens.cljc:289, ../common/src/app/common/files/tokens.cljc:317, ../common/src/app/common/files/tokens.cljc:330 msgid "workspace.tokens.token-name-length-validation-error" msgstr "名稱至少須有 1 個字元" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:272, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:340, ../common/src/app/common/files/tokens.cljc:155, ../common/src/app/common/files/tokens.cljc:168 msgid "workspace.tokens.token-name-validation-error" msgstr "" " 不是有效的權杖(token)名稱。\n" @@ -8638,11 +8582,11 @@ msgstr "" msgid "workspace.tokens.token-value" msgstr "值" -#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:131 +#: src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:324, src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:151 msgid "workspace.tokens.token-value-enter" msgstr "輸入值,或以 {alias} 格式輸入別名" -#: src/app/main/ui/workspace/tokens/management.cljs:70 +#: src/app/main/ui/workspace/tokens/management.cljs:77 msgid "workspace.tokens.tokens-section-title" msgstr "權杖(TOKENS) - %s" @@ -8658,15 +8602,15 @@ msgstr "匯入成功,但部分 tokens 使用了不支援的 $type 值,因此 msgid "workspace.tokens.unknown-token-type-section" msgstr "不支援類型 '%s'(%s):" -#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:249, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:200 +#: src/app/main/ui/workspace/tokens/management/forms/shadow.cljs:316, src/app/main/ui/workspace/tokens/management/forms/typography.cljs:270 msgid "workspace.tokens.use-reference" msgstr "使用參照" -#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:148 +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:134 msgid "workspace.tokens.value-not-valid" msgstr "此值無效" -#: src/app/main/ui/workspace/top_toolbar.cljs:70 +#: src/app/main/ui/workspace/top_toolbar.cljs:68 msgid "workspace.toolbar.arrow" msgstr "箭頭(%s)" @@ -8674,75 +8618,71 @@ msgstr "箭頭(%s)" msgid "workspace.toolbar.assets" msgstr "資產" -#: src/app/main/ui/workspace/palette.cljs:185, src/app/main/ui/workspace/palette.cljs:186 +#: src/app/main/ui/workspace/palette.cljs:194 msgid "workspace.toolbar.color-palette" msgstr "調色盤(%s)" -#: src/app/main/ui/workspace/right_header.cljs:218, src/app/main/ui/workspace/right_header.cljs:219 +#: src/app/main/ui/workspace/right_header.cljs:224 msgid "workspace.toolbar.comments" msgstr "留言(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:74 +#: src/app/main/ui/workspace/top_toolbar.cljs:72 msgid "workspace.toolbar.curve" msgstr "曲線(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:68 +#: src/app/main/ui/workspace/top_toolbar.cljs:66 msgid "workspace.toolbar.ellipse" msgstr "橢圓(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:66 +#: src/app/main/ui/workspace/top_toolbar.cljs:64 msgid "workspace.toolbar.frame" msgstr "畫板(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:73 +#: src/app/main/ui/workspace/top_toolbar.cljs:71 msgid "workspace.toolbar.image" msgstr "圖片(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:69 +#: src/app/main/ui/workspace/top_toolbar.cljs:67 msgid "workspace.toolbar.line" msgstr "直線(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:333 +#: src/app/main/ui/components/mcp_menu.cljs:120 msgid "workspace.toolbar.mcp" msgstr "MCP" -#: src/app/main/ui/workspace/top_toolbar.cljs:345 -msgid "workspace.toolbar.mcp-connect-here" -msgstr "在此連線" - -#: src/app/main/ui/workspace/top_toolbar.cljs:342 +#: src/app/main/ui/components/mcp_menu.cljs:130 msgid "workspace.toolbar.mcp-connected" msgstr "MCP 已連線" -#: src/app/main/ui/workspace/top_toolbar.cljs:65, src/app/main/ui/workspace/top_toolbar.cljs:425 +#: src/app/main/ui/workspace/top_toolbar.cljs:63, src/app/main/ui/workspace/top_toolbar.cljs:374 msgid "workspace.toolbar.move" msgstr "移動(%s)" -#: src/app/main/ui/workspace/palette.cljs:169 +#: src/app/main/ui/workspace/palette.cljs:175 msgid "workspace.toolbar.palette-bar" msgstr "色票列" -#: src/app/main/ui/workspace/top_toolbar.cljs:72 +#: src/app/main/ui/workspace/top_toolbar.cljs:70 msgid "workspace.toolbar.path" msgstr "路徑(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:75 +#: src/app/main/ui/workspace/top_toolbar.cljs:73 msgid "workspace.toolbar.plugins" msgstr "外掛(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:67 +#: src/app/main/ui/workspace/top_toolbar.cljs:65 msgid "workspace.toolbar.rect" msgstr "矩形(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:71 +#: src/app/main/ui/workspace/top_toolbar.cljs:69 msgid "workspace.toolbar.text" msgstr "文字(%s)" -#: src/app/main/ui/workspace/palette.cljs:193, src/app/main/ui/workspace/palette.cljs:194 +#: src/app/main/ui/workspace/palette.cljs:202 msgid "workspace.toolbar.text-palette" msgstr "文字樣式(%s)" -#: src/app/main/ui/workspace/top_toolbar.cljs:485, src/app/main/ui/workspace/top_toolbar.cljs:486 +#: src/app/main/ui/workspace/top_toolbar.cljs:436, src/app/main/ui/workspace/top_toolbar.cljs:437 msgid "workspace.toolbar.toggle-toolbar" msgstr "顯示或隱藏工具列" @@ -8763,11 +8703,11 @@ msgstr "無法渲染。請重新整理以恢復編輯。" msgid "workspace.top-bar.webgl-context-lost.reload" msgstr "重新整理" -#: src/app/main/ui/workspace/sidebar/history.cljs:384 +#: src/app/main/ui/workspace/sidebar/history.cljs:391 msgid "workspace.undo.empty" msgstr "目前尚無變更記錄" -#: src/app/main/ui/workspace/sidebar/history.cljs:364 +#: src/app/main/ui/workspace/sidebar/history.cljs:365 msgid "workspace.undo.entry.by" msgstr "由 %s 操作" @@ -8907,19 +8847,19 @@ msgstr "文字樣式資產" msgid "workspace.undo.entry.unknown" msgstr "對 %s 進行操作" -#: src/app/main/data/workspace/libraries.cljs:1380, src/app/main/data/workspace/versions.cljs:241 +#: src/app/main/data/workspace/libraries.cljs:1442, src/app/main/data/workspace/versions.cljs:241 msgid "workspace.updates.dismiss" msgstr "關閉" -#: src/app/main/data/workspace/libraries.cljs:1378 +#: src/app/main/data/workspace/libraries.cljs:1440 msgid "workspace.updates.more-info" msgstr "更多資訊" -#: src/app/main/data/workspace/libraries.cljs:1376 +#: src/app/main/data/workspace/libraries.cljs:1438 msgid "workspace.updates.there-are-updates" msgstr "共享資料庫有更新" -#: src/app/main/data/workspace/libraries.cljs:1382 +#: src/app/main/data/workspace/libraries.cljs:1444 msgid "workspace.updates.update" msgstr "更新" diff --git a/library/package.json b/library/package.json index 0ddb8634a7..21ac2c9c1f 100644 --- a/library/package.json +++ b/library/package.json @@ -3,7 +3,7 @@ "version": "1.3.0", "license": "MPL-2.0", "author": "KALEIDOS SUBSIDIARY SL", - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "type": "module", "repository": { "type": "git", diff --git a/library/pnpm-lock.yaml b/library/pnpm-lock.yaml index 6a7c1982b3..10cbdf36b8 100644 --- a/library/pnpm-lock.yaml +++ b/library/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' diff --git a/manage.sh b/manage.sh index 408792d781..2696b9eefe 100755 --- a/manage.sh +++ b/manage.sh @@ -40,7 +40,18 @@ export PENPOT_SOURCE_PATH="${PENPOT_SOURCE_PATH:-$PWD}" # Base directory under which non-main workspace clones live (one subdir per # wsN, N>=1). Documented in defaults.env; default lives here so $HOME expands. -export PENPOT_WORKSPACES_DIR="${PENPOT_WORKSPACES_DIR:-$HOME/.penpot/penpot_workspaces}" +# The default follows the XDG base directory spec, in the data directory +# because a clone can hold commits that exist nowhere else. A directory that +# already exists at the former default, ~/.penpot/penpot_workspaces, keeps +# being used, so existing clones are neither orphaned nor seeded again. +if [[ -z "${PENPOT_WORKSPACES_DIR:-}" ]]; then + if [[ -d "$HOME/.penpot/penpot_workspaces" ]]; then + PENPOT_WORKSPACES_DIR="$HOME/.penpot/penpot_workspaces" + else + PENPOT_WORKSPACES_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/penpot-devenv/workspaces" + fi +fi +export PENPOT_WORKSPACES_DIR # Port allocation for parallel instances. Each wsN reserves a stride-wide port # block starting at N*stride; ws0 sits at offset 0, so a per-service base port @@ -69,7 +80,7 @@ PENPOT_PORT_BASE_STORYBOOK=${PENPOT_STORYBOOK_PORT:?missing in defaults.env} export CURRENT_USER_ID=$(id -u); export CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD); -export IMAGEMAGICK_VERSION=7.1.2-27 +export IMAGEMAGICK_VERSION=7.1.2-32 # Safe directory to avoid ownership errors with Git git config --global --add safe.directory /home/penpot/penpot || true @@ -236,6 +247,24 @@ function infra-compose { "$@" } +function check-devenv-gpu-support { + local runtimes + if ! runtimes=$(env -i HOME="$HOME" PATH="$PATH" PWD="$PWD" \ + docker info --format '{{range $name, $_ := .Runtimes}}{{$name}} {{end}}'); then + echo "run-devenv: cannot query Docker runtimes; check that Docker is running and accessible." >&2 + return 1 + fi + if [[ " $runtimes " != *" nvidia "* ]]; then + echo "run-devenv: --gpu requires Docker runtime 'nvidia'; install and configure NVIDIA Container Toolkit." >&2 + return 1 + fi + local icd=/usr/share/vulkan/icd.d/nvidia_icd.json + if [[ ! -f "$icd" || ! -r "$icd" ]]; then + echo "run-devenv: --gpu requires a readable $icd; install the host NVIDIA Vulkan driver." >&2 + return 1 + fi +} + function instance-compose { local instance="$1"; shift local source_path @@ -258,6 +287,10 @@ function instance-compose { compose_files+=(-f docker/devenv/docker-compose.opencode.yml) opencode_env=("PENPOT_OPENCODE_CONFIG_DIR=${PENPOT_OPENCODE_CONFIG_DIR}") fi + # Bash dynamic scope exposes run-devenv's local option here. + if [[ "${devenv_gpu:-false}" == "true" ]]; then + compose_files+=(-f docker/devenv/docker-compose.gpu.yml) + fi env -i HOME="$HOME" PATH="$PATH" PWD="$PWD" \ CURRENT_USER_ID="${CURRENT_USER_ID:-$(id -u)}" \ @@ -784,13 +817,16 @@ function start-instance { instance-compose "$instance" up -d main - # Wait briefly for main to be reachable; the tmux session lives inside. + # Running does not mean entrypoint setup has finished. Nginx starts only + # after ownership and Cargo setup, so wait for its local health endpoint + # before launching builds as penpot (especially on a fresh container). local container deadline container=$(devenv-main-container "$instance") - deadline=$(( SECONDS + 30 )) - while ! docker inspect -f '{{.State.Running}}' "$container" 2>/dev/null | grep -q true; do + deadline=$(( SECONDS + 120 )) + while ! docker exec "$container" curl --fail --silent --max-time 2 \ + --output /dev/null http://127.0.0.1:8082/stub_status 2>/dev/null; do [[ $SECONDS -ge $deadline ]] && { - echo "[${instance}] main container did not reach Running within 30s" >&2 + echo "[${instance}] main container initialization did not finish within 120s; check docker logs $container" >&2 return 1 } sleep 1 @@ -872,6 +908,7 @@ function run-devenv { local do_sync=false local do_attach=false local agentic=false + local devenv_gpu=false local serena_context="desktop-app" local git_user_name="" local git_user_email="" @@ -886,6 +923,8 @@ function run-devenv { do_sync=true; shift;; --agentic) agentic=true; shift;; + --gpu) + devenv_gpu=true; shift;; --attach) do_attach=true; shift;; --serena-context) @@ -901,12 +940,13 @@ function run-devenv { -e*) extra_env_args+=(-e "${1#-e}"); shift;; -h|--help) - echo "Usage: run-devenv [--ws N] [--sync] [--attach] [--agentic] [--serena-context CTX] [--opencode-config-dir DIR] [--git-user-name NAME] [--git-user-email EMAIL] [-e KEY=VAL]" + echo "Usage: run-devenv [--ws N] [--sync] [--attach] [--agentic] [--gpu] [--serena-context CTX] [--opencode-config-dir DIR] [--git-user-name NAME] [--git-user-email EMAIL] [-e KEY=VAL]" echo " Bring a single workspace up." echo " --ws N target workspace (default: 0)." echo " --sync re-seed the wsN clone from the live repo (forbidden on ws0)." echo " --attach attach to the tmux session after startup." echo " --agentic enable MCP + Serena (AI-agent mode)." + echo " --gpu grant NVIDIA GPU access at container creation." echo " --serena-context CTX context passed to Serena (default: desktop-app)." echo " --opencode-config-dir DIR bind-mount DIR at ~/.config/opencode inside the" echo " container (personal agents/prompts/skills kept in a" @@ -960,6 +1000,10 @@ function run-devenv { return 1 fi + if [[ "$devenv_gpu" == "true" ]]; then + check-devenv-gpu-support || return 1 + fi + pull-devenv-if-not-exists ensure-devenv-network ensure-infra-up @@ -1052,6 +1096,11 @@ function attach-devenv { # no flag → ws0 (the live repo at $PWD). # --ws N → wsN's workspace clone at ${PENPOT_WORKSPACES_DIR}/wsN # (N is an integer; non-integer values are rejected). +# --bin EXE → run EXE instead of the client's default executable (e.g. a +# wrapper such as 'claude-alt'); the client still picks +# how the MCP config is wired in. +# --ws and --bin may come in any order; everything after them is passed +# through to the client. # Before launching, the function cd's into the resolved workspace and refuses # to start unless the target instance's 'main' container is up — the Penpot # and Serena MCP servers only exist while the devenv is running. @@ -1066,14 +1115,14 @@ function attach-devenv { # committed templates). Nothing is written to .codex/config.toml, # so the developer's own Codex config is left untouched. # -# Usage: ./manage.sh start-coding-agent [--ws N] [...passthrough] +# Usage: ./manage.sh start-coding-agent [--ws N] [--bin EXE] [...passthrough] function start-coding-agent { local client="${1:-}" [[ $# -gt 0 ]] && shift case "$client" in ""|-h|--help) - echo "Usage: $0 start-coding-agent [--ws N] [...passthrough]" >&2 + echo "Usage: $0 start-coding-agent [--ws N] [--bin EXE] [...passthrough]" >&2 return 1 ;; claude|opencode|vscode|codex) @@ -1085,10 +1134,26 @@ function start-coding-agent { esac local instance="ws0" - if [[ $# -gt 0 && "$1" == "--ws" ]]; then - instance="$(parse-ws-integer "$2")" || return 1 - shift 2 - fi + local bin_override="" + while [[ $# -gt 0 ]]; do + case "$1" in + --ws) + instance="$(parse-ws-integer "$2")" || return 1 + shift 2 + ;; + --bin) + if [[ -z "${2:-}" ]]; then + echo "start-coding-agent: --bin requires an executable name or path." >&2 + return 1 + fi + bin_override="$2" + shift 2 + ;; + *) + break + ;; + esac + done # --ws is the default-elided flag: only emit it in suggestion strings for # ws1+; ws0 is the default target so 'run-devenv --agentic' is the right hint. @@ -1129,6 +1194,7 @@ function start-coding-agent { vscode) bin="code"; cfg_rel=".vscode/mcp.json" ;; codex) bin="codex"; cfg_rel=".devenv/templates/codex.toml" ;; esac + [[ -n "$bin_override" ]] && bin="$bin_override" if ! command -v "$bin" >/dev/null 2>&1; then echo "start-coding-agent: '$bin' is not on PATH." >&2 @@ -1146,13 +1212,13 @@ function start-coding-agent { cd "$workspace" || return 1 case "$client" in claude) - exec claude --mcp-config "$cfg_rel" "$@" + exec "$bin" --mcp-config "$cfg_rel" "$@" ;; opencode) - OPENCODE_CONFIG="$cfg_rel" exec opencode "$@" + OPENCODE_CONFIG="$cfg_rel" exec "$bin" "$@" ;; vscode) - exec code "$workspace" "$@" + exec "$bin" "$workspace" "$@" ;; codex) # Build our servers as `-c mcp_servers.....` overrides from the @@ -1174,7 +1240,7 @@ function start-coding-agent { echo " .devenv/{shared,templates}/codex.toml." >&2 return 1 fi - exec codex "${codex_args[@]}" "$@" + exec "$bin" "${codex_args[@]}" "$@" ;; esac } @@ -1482,6 +1548,7 @@ function usage { echo " start of a wsN with no on-disk workspace yet)." echo " --attach attach to the tmux session after startup." echo " --agentic enable MCP + Serena (AI-agent mode)." + echo " --gpu grant NVIDIA GPU access at container creation." echo " --serena-context CTX passed to Serena (default: desktop-app)." echo " --opencode-config-dir DIR" echo " bind-mount DIR over the container's" @@ -1513,7 +1580,8 @@ function usage { echo " config wired in. cd's into the workspace, refuses to launch if the" echo " instance is not running, and forwards extra args to the client." echo " client: claude | opencode | vscode | codex" - echo " Options: --ws N (default: 0). See agentic-devenv.md and" + echo " Options: --ws N (default: 0), --bin EXE (run EXE instead of" + echo " the client's default executable). See agentic-devenv.md and" echo " .devenv/README.md for per-client setup and override paths." echo "" echo "- build-bundle Build all bundles (frontend, backend, exporter, storybook and mcp)." diff --git a/mcp/.serena/.gitignore b/mcp/.serena/.gitignore deleted file mode 100644 index 14d86ad623..0000000000 --- a/mcp/.serena/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/cache diff --git a/mcp/.serena/memories/project_overview.md b/mcp/.serena/memories/project_overview.md deleted file mode 100644 index 64460c0289..0000000000 --- a/mcp/.serena/memories/project_overview.md +++ /dev/null @@ -1,87 +0,0 @@ -# Penpot MCP - -This subproject provides an MCP server for Penpot integration. -The MCP server communicates with a Penpot plugin via WebSockets, allowing -the MCP server to send tasks to the plugin and receive results, -enabling advanced AI-driven features in Penpot. - -## Tech Stack - -- Language: TypeScript -- Runtime: Node.js -- Framework: MCP SDK (@modelcontextprotocol/sdk) -- Build Tool: TypeScript Compiler (tsc) + esbuild -- Package Manager: pnpm - -## General Principles - -IMPORTANT: Use an idiomatic, object-oriented style. -In particular, this implies that, for any non-trivial interfaces, you use interfaces that expect explicitly typed abstractions -rather than mere functions (i.e. use the strategy pattern, for example). - -Comments: -When describing parameters, methods/functions and classes, you use a precise style, where the initial (elliptical) phrase -clearly defines *what* it is. Any details then follow in subsequent sentences. - -When describing what blocks of code do, you also use an elliptical style and start with a lower-case letter unless -the comment is a lengthy explanation with at least two sentences (in which case you start with a capital letter, as is -required for sentences). - -## Project Structure (Excerpt) - -``` -/ (project root) -├── packages/common/ # Shared type definitions -│ ├── src/ -│ │ ├── index.ts # exports for shared types -│ │ └── types.ts # PluginTaskResult, request/response interfaces -│ └── package.json # @penpot-mcp/common package -├── packages/server/ # MCP server subproject -│ ├── src/ -│ │ ├── index.ts # entry point -│ │ ├── PenpotMcpServer.ts # MCP server implementation (connection handling, tool registration, etc.) -│ │ ├── Tool.ts # base class for tools -│ │ ├── PluginTask.ts # base class for plugin tasks -│ │ ├── tasks/ # PluginTask implementations -│ │ └── tools/ # Tool implementations -| ├── data/ # contains resources, such as API info and prompts -│ └── package.json -├── packages/plugin/ # Penpot plugin subproject -│ ├── src/ -│ │ ├── main.ts # handles communication -│ │ └── plugin.ts # plugin implementation -│ └── package.json # Includes @penpot-mcp/common dependency -└── prepare-api-docs # Python project for the generation of API docs -``` - -## Key Development Tasks - -### Adjusting the Prompts - -The system prompt file (aka Penpot High-Level Overview) is located in -`packages/server/data/initial_instructions.md`. - -### Adding a new Tool - -1. Implement the tool class in `packages/server/src/tools/` following the `Tool` interface. - IMPORTANT: Do not catch any exceptions in the `executeCore` method. Let them propagate to be handled centrally. -2. Register the tool in `PenpotMcpServer`. - -Tools can be associated with a `PluginTask` that is executed in the plugin. -Many tools build on `ExecuteCodePluginTask`, as many operations can be reduced to code execution. - -### Adding a new PluginTask - -1. Implement the input data interface for the task in `packages/common/src/types.ts`. -2. Implement the `PluginTask` class in `packages/server/src/tasks/`. -3. Implement the corresponding task handler class in the plugin (`packages/plugin/src/task-handlers/`). - * In the success case, call `task.sendSuccess`. - * In the failure case, just throw an exception, which will be handled centrally! -4. Register the task handler in `packages/plugin/src/plugin.ts` in the `taskHandlers` list. - -## Dev Tooling - -From the project root directory, run - -* `pnpm run build` to test the build of all package -* `pnpm run fmt` to apply the auto-formatter diff --git a/mcp/.serena/project.yml b/mcp/.serena/project.yml deleted file mode 100644 index 3685e20b4d..0000000000 --- a/mcp/.serena/project.yml +++ /dev/null @@ -1,176 +0,0 @@ - -# whether to use project's .gitignore files to ignore files -ignore_all_files_in_gitignore: true - -# list of additional paths to ignore in this project. -# Same syntax as gitignore, so you can use * and **. -# Important: quote patterns that start with `*`, otherwise YAML treats them as aliases. -# Example: -# ignored_paths: -# - "examples/**" -# - ".worktrees/**" -# - "**/bin/**" -# - "**/obj/**" -# Note: global ignored_paths from serena_config.yml are also applied additively. -ignored_paths: [] - -# whether the project is in read-only mode -# If set to true, all editing tools will be disabled and attempts to use them will result in an error -# Added on 2025-04-18 -read_only: false - -# list of tool names to exclude. -# This extends the existing exclusions (e.g. from the global configuration) -# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html -excluded_tools: [] - -# initial prompt for the project. It will always be given to the LLM upon activating the project -# (contrary to the memories, which are loaded on demand). -initial_prompt: | - CRITICAL: Always read the "project_overview" memory. -# the name by which the project can be referenced within Serena/when chatting with the LLM. -project_name: "penpot-mcp" - -# list of mode names to that are always to be included in the set of active modes -# The full set of modes to be activated is base_modes + default_modes. -# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply. -# Otherwise, this setting overrides the global configuration. -# Set this to [] to disable base modes for this project. -# Set this to a list of mode names to always include the respective modes for this project. -base_modes: - -# list of mode names that are to be activated by default, overriding the setting in the global configuration. -# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes. -# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply. -# Otherwise, this overrides the setting from the global configuration (serena_config.yml). -# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply -# for this project. -# This setting can, in turn, be overridden by CLI parameters (--mode). -# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes -default_modes: - -# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default). -# This extends the existing inclusions (e.g. from the global configuration). -# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html -included_optional_tools: [] - -# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. -# This cannot be combined with non-empty excluded_tools or included_optional_tools. -# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html -fixed_tools: [] - -# the encoding used by text files in the project -# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings -encoding: utf-8 - -# list of languages for which language servers are started (LSP backend only); choose from: -# ada al angular ansible bash -# bsl clojure cpp cpp_ccls crystal -# csharp csharp_omnisharp cue dart elixir -# elm erlang fortran fsharp gdscript -# go groovy haskell haxe hlsl -# html java json julia kotlin -# latex lean4 lua luau markdown -# matlab msl nix ocaml pascal -# perl php php_phpactor php_phpantom powershell -# python python_jedi python_pyrefly python_ty r -# rego ruby ruby_solargraph rust scala -# scss solidity svelte swift systemverilog -# terraform toml typescript typescript_vts vue -# yaml zig -# (This list may be outdated; generated with scripts/print_language_list.py; -# For the current list, see values of Language enum here: -# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py) -# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) -# Note: -# - For C, use cpp -# - For JavaScript, use typescript -# - For Angular projects, use angular (subsumes typescript+html; requires `npm install` in the project root) -# - For Svelte projects, use svelte (subsumes typescript/javascript for .svelte projects; requires npm) -# - For SCSS / Sass / plain CSS, use scss (some-sass-language-server handles all three) -# - For Free Pascal/Lazarus, use pascal -# Special requirements: -# Some languages require additional setup/installations. -# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers -# When using multiple languages, the first language server that supports a given file will be used for that file. -# The first language is the default language and the respective language server will be used as a fallback. -# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. -languages: -- typescript - -# time budget (seconds) per tool call for the retrieval of additional symbol information -# such as docstrings or parameter information. -# This overrides the corresponding setting in the global configuration; see the documentation there. -# If null or missing, use the setting from the global configuration. -symbol_info_budget: - -# The language backend to use for this project. -# If not set, the global setting from serena_config.yml is used. -# Valid values: LSP, JetBrains -# Note: the backend is fixed at startup. If a project with a different backend -# is activated post-init, an error will be returned. -language_backend: - -# line ending convention to use when writing source files. -# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) -# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. -line_ending: - -# list of regex patterns which, when matched, mark a memory entry as read‑only. -# Extends the list from the global configuration, merging the two lists. -read_only_memory_patterns: [] - -# list of regex patterns for memories to completely ignore. -# Matching memories will not appear in list_memories or activate_project output -# and cannot be accessed via read_memory or write_memory. -# To access ignored memory files, use the read_file tool on the raw file path. -# Extends the list from the global configuration, merging the two lists. -# Example: ["_archive/.*", "_episodes/.*"] -ignored_memory_patterns: [] - -# advanced configuration option allowing to configure language server-specific options. -# Maps the language key to the options. -# The settings are considered only if the project is trusted (see global configuration to define trusted projects). -# See https://oraios.github.io/serena/02-usage/050_configuration.html#language-server-specific-settings -ls_specific_settings: {} - -# list of mode names to be activated additionally for this project, e.g. ["query-projects"] -# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes. -# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes -added_modes: - -# list of additional workspace folder paths for cross-package reference support. -# Paths can be absolute or relative to the project root. -# Each folder is registered as an LSP workspace folder, enabling language servers to discover -# symbols and references across package boundaries, but these folders are not indexed by Serena, -# i.e. the respective symbols will not be found using Serena's symbol search tools. -# Example: -# additional_workspace_folders: -# - ../sibling-package -# - ../shared-lib -ls_additional_workspace_folders: [] - -# list of workspace folder paths (LSP backend only). -# These folders will be used to build up Serena's symbol index. -# Paths must be within the project root and should thus be relative to the project root. -# Furthermore, the paths should not be filtered by ignore settings. -# Default setting: The entire project root folder (".") is considered. -# In (large) monorepos, this can be used to index only subfolders of the project root, e.g. -# ls_workspace_folders: -# - "./subproject1" -# - "./subproject2" -ls_workspace_folders: -- . - -# optional shell command to run before the language backend (LSP or JetBrains) is initialised. -# the command runs in the project root directory and is only executed if the project is trusted -# (see trusted_project_path_patterns in the global configuration). -# serena waits for the command to exit: a non-zero exit code is logged as an error but does not -# abort activation. a per-project timeout (activation_command_timeout, default 180s) is the safety -# backstop for non-terminating commands; on expiry the process is killed and activation continues. -# example: activation_command: "npx nx run-many -t build" -activation_command: - -# maximum time in seconds to wait for activation_command to complete before killing it (default 180s). -# must be a positive number. -activation_command_timeout: 180.0 diff --git a/mcp/README.md b/mcp/README.md index 32530bc869..2f1636d24a 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -40,14 +40,24 @@ but also the supporting Penpot MCP Plugin ## Usage -To use the Penpot MCP server, you must +> [!IMPORTANT] +> **These instructions are for local MCP server usage only!** +> +> The instructions below are for users who want to run the server locally, +> allowing the server to have access to local files, which can be +> relevant for development/coding tasks. +> +> If you need to work only on designs, the centrally hosted **remote MCP server** +> should be considered instead. +> Please refer to the [MCP usage instructions in the help center](https://help.penpot.app/mcp/). + +To run the Penpot MCP server locally, you must * run the MCP server and connect your AI client to it, * run the web server providing the Penpot MCP plugin, and * open the Penpot MCP plugin in Penpot and connect it to the MCP server. Follow the steps below to enable the integration. - ### Prerequisites The project requires [Node.js](https://nodejs.org/) 20 or later (tested with v22.x). @@ -133,7 +143,7 @@ This bootstrap command will: 4. Load the plugin using the development URL (`http://localhost:4400/manifest.json` by default) 5. Open the plugin UI 6. In the plugin UI, click "Connect to MCP server". - The connection status should change from "Not connected" to "Connected to MCP server". + The connection status should change from "Not connected" to "Connected". (Check the browser's developer console for WebSocket connection logs. Check the MCP server terminal for WebSocket connection messages.) @@ -161,15 +171,15 @@ This bootstrap command will: > (If you are using a standard commercial model, it almost certainly supports vision already.) By default, the server provides a Streamable HTTP endpoint at `http://localhost:4401/mcp`. +Simply configure the client to connect the MCP server by providing the respective URL. The legacy `/sse` and `/messages` endpoints are no longer supported. Clients using the legacy SSE transport must switch to Streamable HTTP at `/mcp`. You can change the port by setting the `PENPOT_MCP_SERVER_PORT` environment variable before starting the server. This endpoint can be used directly by MCP clients that support Streamable HTTP. -Simply configure the client to connect the MCP server by providing the respective URL. -#### Configuring your client +#### Automatically configuring your client You can configure your client with the [add-mcp](https://github.com/neon-solutions/add-mcp) helper. Simply call @@ -228,6 +238,24 @@ After updating the configuration file, restart Claude Desktop completely for the After the restart, you should see the MCP server listed when clicking on the "Search and tools" icon at the bottom of the prompt input area. +### Working with Multiple Files + +Connect the files you want to use: + +* Open each file in a separate Penpot tab, run the plugin, and connect it to the same MCP server. +* Each browser tab uses a short session ID derived only from `penpot.currentUser.sessionId`, shown in the plugin UI with a **Copy** button. The ID stays the same across workspace navigation. +* Multiple tabs can connect to the same file. Connecting or disconnecting one tab does not disconnect the others. + +Choose a session for your agent: + +* With exactly one connected session, the server selects it automatically when the agent omits the session ID. +* With several connected sessions, the server lists them and asks the agent to have you select one. +* You can also copy an ID from the plugin and include it in your prompt, for example: + "Use Penpot session `o37vgcqsvt` and list the pages in that file." + +For integrated remote MCP connection controls and session behavior, see the +[Help Center guide](../docs/mcp/index.md#working-with-multiple-files). + ## Repository Structure This repository is a monorepo containing four main components: @@ -262,7 +290,7 @@ The Penpot MCP server can be configured using environment variables. | Environment Variable | Description | Default | |--------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------| | `PENPOT_MCP_SERVER_HOST` | Address on which the MCP server listens (binds to) | `localhost` | -| `PENPOT_MCP_SERVER_PORT` | Port for the MCP server | `4401` | +| `PENPOT_MCP_SERVER_PORT` | Port for the MCP server | `4401` | | `PENPOT_MCP_WEBSOCKET_PORT` | Port for the WebSocket server (plugin connection) | `4402` | | `PENPOT_MCP_REPL_PORT` | Port for the REPL server (development/debugging) | `4403` | | `PENPOT_MCP_REPL_HOST` | Address on which the REPL server listens (binds to) | `localhost` | @@ -270,8 +298,21 @@ The Penpot MCP server can be configured using environment variables. | `PENPOT_MCP_REMOTE_MODE` | Enable remote mode (disables file system access). Set to `true` to enable. | `false` | | `PENPOT_MCP_DEVENV` | Enable Penpot development environment tools in local single-user mode. Set to `true` to enable. | `false` | | `PENPOT_MCP_TOOL_TIMEOUT_S` | Timeout, in seconds, for tool calls dispatched to the Penpot plugin | `120` | +| `PENPOT_MCP_MAX_BODY_SIZE` | Maximum size of a JSON request body accepted by the MCP endpoint, in any format supported by Express (e.g. `10mb`) | `10mb` | | `PENPOT_MCP_EXPORT_SHAPE_MAX_PARALLEL_REQUESTS` | Maximum number of parallel export shape requests (multi-user mode only). | `0` (no limit) | +| `PENPOT_MCP_AUTH_URI` | Public base URL of the MCP authorization service, e.g. `https://penpot.example/api/mcp/oauth`. Set to enable OAuth; requires `--multi-user`. Use HTTPS (HTTP is allowed on loopback for development), without credentials, query parameters, or a fragment. | (unset; OAuth disabled) | | `PENPOT_MCP_REDIS_URI` | Redis connection URI (e.g. `redis://host:6379`) enabling multi-instance horizontal scaling via Redis pub/sub task routing (multi-user mode only). When unset, the server runs in single-instance mode, requiring the plugin and MCP client to connect to the same instance. | (unset) | +| `PENPOT_MCP_BACKEND_URI` | Penpot instance base URI. Setting a nonblank value enables tools that communicate with the Penpot backend (currently only the `submit_feedback` tool). The MCP server appends suffixes such as `api/main/methods/send-mcp-feedback`. Backend interactions require MCP user tokens to be provided. | (unset) | + +### Backend Configuration + +If `PENPOT_MCP_BACKEND_URI` is set, the backend must itself be configured correspondingly, i.e. set +the following variables for the backend process: + +| Environment Variable | Description | Default | +|----------------------|-------------|---------| +| `PENPOT_MCP_FEEDBACK_EMAIL` | Selects the inbox for MCP feedback. Unset means the command rejects submissions. Requires the `enable-mcp` or `enable-access-tokens` flag and uses the backend's existing SMTP configuration. | (unset) | + ### Logging Configuration @@ -286,22 +327,33 @@ The Penpot MCP server can be configured using environment variables. |-------------------------------------------|-----------------------------------------------------------------------------------------|--------------| | `PENPOT_MCP_PLUGIN_SERVER_HOST` | Address on which the plugin web server listens (single address or comma-separated list) | (local only) | -## Beyond Local Execution +## The Server's Modes of Operation -The above instructions describe how to run the MCP server and plugin server locally. +The above instructions describe how to run the MCP server and plugin server locally, +for a single user – its simplest mode of operation. -The Penpot MCP server can also support multiple remote users simultaneously -in [multi-user mode](docs/multi-user-mode.md). +The server supports additional modes of operation, which are intended for +non-local usage (and which are relevant to hosted deployments only). +Some of the aforementioned configuration options control these modes. -To run the server remotely (even for a single user), -you may set the following environment variables to configure the two servers -(MCP server & plugin server) appropriately: - * `PENPOT_MCP_REMOTE_MODE=true`: This ensures that the MCP server is operating - in remote mode, with local file system access disabled. - * `PENPOT_MCP_SERVER_HOST` and `PENPOT_MCP_PLUGIN_SERVER_HOST`: - Set these according to your requirements for remote connectivity. - To bind all interfaces, use `0.0.0.0` (use caution in untrusted networks). +* **remote mode**: + In remote mode, the server is not assumed to be accessed by a local user on the same machine, + with corresponding limitations being enforced (tools offering local file system access are restricted/disabled). +* **multi-user mode**: + In multi-user mode, the server can be accessed by multiple users simultaneously. + This mode always implies *remote mode*. + User tokens are passed to the server both when connecting an MCP client and when establishing + plugin connections, associating each connection with the respective user and allowing tasks to be routed to the correct Penpot instance. + This mode is intended for hosted deployments using the integrated version of the Penpot MCP plugin, + which automatically sends the user token to the server when establishing WebSocket connections. + It is enabled by running the server with the `--multi-user` flag. + +* **multi-instance mode**: + In multi-instance mode, multiple instances of the MCP server can be run in parallel (load balancing). + This can result in connections from the same user being owned by different server instances, + making it necessary to dispatch tasks to the correct instance. This is handled through Redis, + and the mode is thus enabled by setting the environment variable providing the Redis connection URI. ## Development @@ -313,7 +365,8 @@ you may set the following environment variables to configure the two servers - The MCP plugin assumes that a mismatch between the major.minor components of the MCP version and the Penpot version (as returned by the API) indicates incompatibility, resulting in the display of a warning message in the plugin UI. Patch releases are treated as compatible. -* Packaging and publishing: - 1. Ensure release version is set correctly in package.json (call `bash scripts/set-version` to update it automatically) - 2. Create npm package: `bash scripts/pack` (creates `penpot-mcp-.tgz` for publishing) - 3. Publish to npm: `npm publish penpot-mcp-.tgz --access public` +* Packaging and publishing: + 1. Ensure that the API type data is up-to-date (see above). + 2. Ensure release version is set correctly in package.json (call `bash scripts/set-version` to update it automatically) + 3. Create npm package: `bash scripts/pack` (creates `penpot-mcp-.tgz` for publishing) + 4. Publish to npm: `npm publish penpot-mcp-.tgz --access public` diff --git a/mcp/docs/multi-user-mode.md b/mcp/docs/multi-user-mode.md deleted file mode 100644 index b4471d18b9..0000000000 --- a/mcp/docs/multi-user-mode.md +++ /dev/null @@ -1,41 +0,0 @@ -# Multi-User Mode - -> [!WARNING] -> Multi-user mode is under development and not yet fully integrated. -> This information is provided for testing purposes only. - -The Penpot MCP server supports a multi-user mode, allowing multiple Penpot users -to connect to the same MCP server instance simultaneously. -This supports remote deployments of the MCP server, without requiring each user -to run their own server instance. - -## Limitations - -Multi-user mode has the limitation that tools which read from or write to -the local file system are not supported, as the server cannot access -the client's file system. This affects the import and export tools. - -## Running Components in Multi-User Mode - -To run the MCP server and the Penpot MCP plugin in multi-user mode (for testing), -you can use the following command: - -```shell -npm run bootstrap:multi-user -``` - -This will: -* launch the MCP server in multi-user mode (adding the `--multi-user` flag), -* build and launch the Penpot MCP plugin server in multi-user mode. - -See the package.json scripts for both `mcp-server` and `penpot-plugin` for details. - -In multi-user mode, users are required to be authenticated via a token. - -* This token is provided in the URL used to connect to the MCP server, - e.g. `http://localhost:4401/mcp?userToken=USER_TOKEN`. -* The same token must be provided when connecting the Penpot MCP plugin - to the MCP server. - In the future, the token will, most likely be generated by Penpot and - provided to the plugin automatically. - :warning: For now, it is hard-coded in the plugin's source code for testing purposes. diff --git a/mcp/package.json b/mcp/package.json index d1d1fcc72a..3c2601a59e 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -23,7 +23,7 @@ "type": "git", "url": "https://github.com/penpot/penpot.git" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "devDependencies": { "concurrently": "^10.0.6", "prettier": "^3.9.10" diff --git a/mcp/packages/browser-extension/.gitignore b/mcp/packages/browser-extension/.gitignore new file mode 100644 index 0000000000..f06235c460 --- /dev/null +++ b/mcp/packages/browser-extension/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/mcp/packages/browser-extension/README.md b/mcp/packages/browser-extension/README.md new file mode 100644 index 0000000000..21597628ae --- /dev/null +++ b/mcp/packages/browser-extension/README.md @@ -0,0 +1,53 @@ +# Penpot MCP + +A Chrome and Firefox extension that keeps the browser from discarding Penpot tabs connected to +MCP. A discarded tab reloads when the user returns to it, and loses its MCP session. + +## How it works + +- The content script asks the Penpot page for its MCP session. The page answers with the + session ID and connection status. +- The background (a service worker in Chrome, an event page in Firefox) maps each session to + its tab and marks the tab as not discardable (`autoDiscardable: false`) until the session + ends. + +The popup lists the connected tabs. + +The icon is the Penpot logo; `public/icons/icon.svg` is its source. To redraw the PNGs after +changing it: + +```sh +for size in 16 32 48 128; do + rsvg-convert -w $size -h $size public/icons/icon.svg -o public/icons/icon-$size.png +done +``` + +## Frozen tabs + +Chrome can also freeze a background page, which then cannot run the MCP plugin. The extension +does not handle this: the Penpot page holds a shared Web Lock while its MCP connection is +requested, and Chrome does not freeze a page holding one. Firefox does not freeze background +pages. + +## Build and load + +From `mcp/`: + +```sh +pnpm install +pnpm --filter mcp-common run build +pnpm --filter mcp-browser-extension run build +``` + +The build writes one extension per browser, in `dist/chrome` and `dist/firefox`. + +- **Chrome:** open `chrome://extensions`, turn on developer mode, choose "Load unpacked" and + pick `mcp/packages/browser-extension/dist/chrome`. +- **Firefox** (140 or later): open `about:debugging#/runtime/this-firefox`, choose "Load + Temporary Add-on…" and pick `mcp/packages/browser-extension/dist/firefox/manifest.json`. + Firefox removes temporary add-ons when it closes. + +Reload Penpot tabs opened before the extension was loaded, so the content script runs in them. + +The content script runs on `https://design.penpot.app`, on the test environments under +`https://*.penpot.dev` and on `localhost` at any port. diff --git a/mcp/packages/browser-extension/package.json b/mcp/packages/browser-extension/package.json new file mode 100644 index 0000000000..43a5c4d8fd --- /dev/null +++ b/mcp/packages/browser-extension/package.json @@ -0,0 +1,21 @@ +{ + "name": "mcp-browser-extension", + "private": true, + "version": "0.1.0", + "description": "Browser extension that keeps Penpot tabs connected to MCP from being discarded", + "type": "module", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", + "scripts": { + "build": "tsc && node ./scripts/build.mjs", + "test": "tsx --test \"src/**/*.test.ts\"", + "types:check": "tsc", + "clean": "rm -rf dist/" + }, + "devDependencies": { + "@penpot/mcp-common": "workspace:../common", + "@types/chrome": "^0.3.6", + "esbuild": "^0.28.2", + "tsx": "^4.23.15", + "typescript": "^6.0.3" + } +} diff --git a/mcp/packages/browser-extension/public/icons/icon-128.png b/mcp/packages/browser-extension/public/icons/icon-128.png new file mode 100644 index 0000000000..a545f42dd5 Binary files /dev/null and b/mcp/packages/browser-extension/public/icons/icon-128.png differ diff --git a/mcp/packages/browser-extension/public/icons/icon-16.png b/mcp/packages/browser-extension/public/icons/icon-16.png new file mode 100644 index 0000000000..3e6eb3e20e Binary files /dev/null and b/mcp/packages/browser-extension/public/icons/icon-16.png differ diff --git a/mcp/packages/browser-extension/public/icons/icon-32.png b/mcp/packages/browser-extension/public/icons/icon-32.png new file mode 100644 index 0000000000..31f2fe30e5 Binary files /dev/null and b/mcp/packages/browser-extension/public/icons/icon-32.png differ diff --git a/mcp/packages/browser-extension/public/icons/icon-48.png b/mcp/packages/browser-extension/public/icons/icon-48.png new file mode 100644 index 0000000000..f92dd70100 Binary files /dev/null and b/mcp/packages/browser-extension/public/icons/icon-48.png differ diff --git a/mcp/packages/browser-extension/public/icons/icon.svg b/mcp/packages/browser-extension/public/icons/icon.svg new file mode 100644 index 0000000000..b12e54c10e --- /dev/null +++ b/mcp/packages/browser-extension/public/icons/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/mcp/packages/browser-extension/public/popup.css b/mcp/packages/browser-extension/public/popup.css new file mode 100644 index 0000000000..acbc7ba3dd --- /dev/null +++ b/mcp/packages/browser-extension/public/popup.css @@ -0,0 +1,54 @@ +:root { + color-scheme: light dark; + --text: #1f1f1f; + --muted: #6b6b6b; + --background: #ffffff; +} + +@media (prefers-color-scheme: dark) { + :root { + --text: #ededed; + --muted: #a0a0a0; + --background: #1f1f1f; + } +} + +body { + width: 320px; + margin: 0; + padding: 12px 16px; + font: + 13px/1.4 system-ui, + sans-serif; + color: var(--text); + background: var(--background); +} + +h1 { + margin: 0 0 12px; + font-size: 15px; +} + +h2 { + margin: 16px 0 4px; + font-size: 12px; + text-transform: uppercase; + color: var(--muted); +} + +.note { + margin: 0; + color: var(--muted); +} + +ul { + margin: 0; + padding-left: 18px; + overflow-wrap: anywhere; +} + +li.empty { + list-style: none; + margin-left: -18px; + color: var(--muted); +} diff --git a/mcp/packages/browser-extension/public/popup.html b/mcp/packages/browser-extension/public/popup.html new file mode 100644 index 0000000000..778829cfab --- /dev/null +++ b/mcp/packages/browser-extension/public/popup.html @@ -0,0 +1,15 @@ + + + + + Penpot MCP + + + +

Penpot MCP

+

The browser does not discard these tabs while they are connected to MCP.

+

Connected tabs

+
    + + + diff --git a/mcp/packages/browser-extension/scripts/build.mjs b/mcp/packages/browser-extension/scripts/build.mjs new file mode 100644 index 0000000000..6d7b6b3069 --- /dev/null +++ b/mcp/packages/browser-extension/scripts/build.mjs @@ -0,0 +1,18 @@ +import { build } from "esbuild"; +import { cp, rm, writeFile } from "node:fs/promises"; +import { BROWSERS, createManifest } from "./manifest.mjs"; + +const TARGETS = { chrome: "chrome116", firefox: "firefox140" }; + +await rm("dist", { recursive: true, force: true }); +for (const browser of BROWSERS) { + const outdir = `dist/${browser}`; + const options = { bundle: true, target: TARGETS[browser], outdir, logLevel: "info" }; + // Chrome loads the service worker as a module, Firefox the event page as a classic script + await build({ ...options, entryPoints: ["src/background.ts"], format: browser === "chrome" ? "esm" : "iife" }); + await build({ ...options, entryPoints: ["src/popup.ts"], format: "esm" }); + // content scripts load as classic scripts + await build({ ...options, entryPoints: ["src/content.ts"], format: "iife" }); + await cp("public", outdir, { recursive: true }); + await writeFile(`${outdir}/manifest.json`, JSON.stringify(createManifest(browser), null, 4) + "\n"); +} diff --git a/mcp/packages/browser-extension/scripts/manifest.mjs b/mcp/packages/browser-extension/scripts/manifest.mjs new file mode 100644 index 0000000000..5b11972c55 --- /dev/null +++ b/mcp/packages/browser-extension/scripts/manifest.mjs @@ -0,0 +1,61 @@ +/** Browsers the extension is built for, one directory of `dist/` each. */ +export const BROWSERS = ["chrome", "firefox"]; + +const ICONS = { + 16: "icons/icon-16.png", + 32: "icons/icon-32.png", + 48: "icons/icon-48.png", + 128: "icons/icon-128.png", +}; + +const CONTENT_SCRIPTS = [ + { + matches: ["https://design.penpot.app/*", "https://*.penpot.dev/*", "https://localhost/*", "http://localhost/*"], + js: ["content.js"], + run_at: "document_idle", + }, +]; + +/** + * Creates the extension manifest for a browser. + * + * Chrome runs the background as a module service worker. Firefox runs it as an event page and + * needs an add-on ID. + * + * @param {"chrome" | "firefox"} browser - The browser to build for + */ +export function createManifest(browser) { + const common = { + manifest_version: 3, + name: "Penpot MCP", + version: "0.1.0", + description: "Keeps the browser from discarding Penpot tabs connected to MCP.", + icons: ICONS, + permissions: ["storage"], + action: { + default_title: "Penpot MCP", + default_popup: "popup.html", + default_icon: { 16: ICONS[16], 32: ICONS[32] }, + }, + content_scripts: CONTENT_SCRIPTS, + }; + if (browser === "chrome") { + return { + ...common, + minimum_chrome_version: "116", + background: { service_worker: "background.js", type: "module" }, + }; + } + return { + ...common, + background: { scripts: ["background.js"] }, + browser_specific_settings: { + gecko: { + id: "mcp-extension@penpot.app", + // the first version that knows data_collection_permissions + strict_min_version: "140.0", + data_collection_permissions: { required: ["none"] }, + }, + }, + }; +} diff --git a/mcp/packages/browser-extension/src/DiscardGuard.ts b/mcp/packages/browser-extension/src/DiscardGuard.ts new file mode 100644 index 0000000000..a50b06d682 --- /dev/null +++ b/mcp/packages/browser-extension/src/DiscardGuard.ts @@ -0,0 +1,28 @@ +import type { SessionChange, SessionMapObserver } from "./SessionMap.ts"; + +/** Browser control over the automatic discarding of tabs. */ +export interface DiscardControl { + setAutoDiscardable(tabId: number, autoDiscardable: boolean): Promise; +} + +/** + * Keeps the browser from discarding tabs that hold an MCP session, since a discarded tab reloads + * and loses its MCP connection. Tabs regain automatic discarding when they stop holding a session. + */ +export class DiscardGuard implements SessionMapObserver { + constructor(private readonly control: DiscardControl) {} + + sessionsChanged(change: SessionChange): void { + for (const tabId of change.tabsAdded) { + this.set(tabId, false); + } + for (const tabId of change.tabsRemoved) { + this.set(tabId, true); + } + } + + private set(tabId: number, autoDiscardable: boolean): void { + // the tab may have been closed meanwhile + this.control.setAutoDiscardable(tabId, autoDiscardable).catch(() => {}); + } +} diff --git a/mcp/packages/browser-extension/src/PageBridge.test.ts b/mcp/packages/browser-extension/src/PageBridge.test.ts new file mode 100644 index 0000000000..afaeabd048 --- /dev/null +++ b/mcp/packages/browser-extension/src/PageBridge.test.ts @@ -0,0 +1,102 @@ +import assert from "node:assert/strict"; +import { beforeEach, test } from "node:test"; +import type { ContentMessage } from "./messages.ts"; +import { PageBridge, type PageWindow, type WorkerChannel } from "./PageBridge.ts"; + +const ORIGIN = "https://design.penpot.app"; + +/** Page recording what the bridge posts and dispatching events to it. */ +class FakePage implements PageWindow { + readonly location = { origin: ORIGIN }; + readonly posted: { message: unknown; targetOrigin: string }[] = []; + private readonly listeners = new Map void)[]>(); + + postMessage(message: unknown, targetOrigin: string): void { + this.posted.push({ message, targetOrigin }); + } + + addEventListener(type: string, listener: (event: any) => void): void { + this.listeners.set(type, [...(this.listeners.get(type) ?? []), listener]); + } + + dispatch(type: string, event: object = {}): void { + for (const listener of this.listeners.get(type) ?? []) listener(event); + } + + /** Posts a message on the page as the Penpot app does. */ + post(data: unknown, origin = ORIGIN, source: unknown = this): void { + this.dispatch("message", { data, origin, source }); + } +} + +/** Worker channel recording what the bridge sends. */ +class FakeWorker implements WorkerChannel { + readonly received: ContentMessage[] = []; + + async sendMessage(message: ContentMessage): Promise { + this.received.push(message); + return undefined; + } +} + +const announcement = { type: "penpot-mcp:session", sessionId: "abcdefghij", status: "connected" }; + +let page: FakePage; +let worker: FakeWorker; + +beforeEach(() => { + page = new FakePage(); + worker = new FakeWorker(); + new PageBridge(page, worker).start(); +}); + +test("asks the page for its session on start, restricted to the page's origin", () => { + assert.deepEqual(page.posted, [{ message: { type: "penpot-mcp-extension:hello" }, targetOrigin: ORIGIN }]); +}); + +test("says hello again when the page reports it has started listening", () => { + page.post({ type: "penpot-mcp:ready" }); + page.post({ type: "penpot-mcp:ready" }, "https://evil.example"); + assert.equal(page.posted.length, 2); + assert.deepEqual(page.posted[1], { message: { type: "penpot-mcp-extension:hello" }, targetOrigin: ORIGIN }); +}); + +test("forwards the page's session announcements to the worker", () => { + page.post(announcement); + assert.deepEqual(worker.received, [{ type: "session", announcement }]); +}); + +test("ignores announcements from other origins or other windows", () => { + page.post(announcement, "https://evil.example"); + page.post(announcement, ORIGIN, {}); + assert.deepEqual(worker.received, []); +}); + +test("ignores invalid announcements and its own hello", () => { + page.post({ ...announcement, status: "unknown" }); + page.post({ type: "penpot-mcp-extension:hello" }); + assert.deepEqual(worker.received, []); +}); + +test("reports a hidden page and asks again when it returns from the back/forward cache", () => { + page.dispatch("pagehide"); + page.dispatch("pageshow", { persisted: false }); + assert.equal(page.posted.length, 1); + page.dispatch("pageshow", { persisted: true }); + assert.deepEqual(worker.received, [{ type: "page-hidden" }]); + assert.equal(page.posted.length, 2); +}); + +test("keeps working when the worker cannot be reached", () => { + const failing = new FakePage(); + new PageBridge(failing, { + sendMessage: () => { + throw new Error("Extension context invalidated."); + }, + }).start(); + failing.post(announcement); + new PageBridge(failing, { + sendMessage: () => Promise.reject(new Error("No receiver")), + }).start(); + failing.post(announcement); +}); diff --git a/mcp/packages/browser-extension/src/PageBridge.ts b/mcp/packages/browser-extension/src/PageBridge.ts new file mode 100644 index 0000000000..42a0a54887 --- /dev/null +++ b/mcp/packages/browser-extension/src/PageBridge.ts @@ -0,0 +1,69 @@ +import type { ExtensionHello } from "@penpot/mcp-common"; +import { parseAnnouncement, type ContentMessage } from "./messages.ts"; + +/** Page operations the bridge uses; the content script passes its window. */ +export interface PageWindow { + readonly location: { readonly origin: string }; + postMessage(message: unknown, targetOrigin: string): void; + addEventListener(type: string, listener: (event: any) => void): void; +} + +/** Channel from the content script to the service worker. */ +export interface WorkerChannel { + sendMessage(message: ContentMessage): Promise; +} + +/** + * Relays the MCP session announced by a Penpot page to the extension's service worker. + * + * The bridge asks the page for its session on load, when the page reports it has started + * listening, and when the page returns from the back/forward cache. It accepts only messages the + * page posted on its own window from its own origin. + */ +export class PageBridge { + constructor( + private readonly page: PageWindow, + private readonly worker: WorkerChannel + ) {} + + /** Starts listening to the page and asks it for its session. */ + start(): void { + this.page.addEventListener("message", (event: MessageEvent) => this.handleMessage(event)); + this.page.addEventListener("pagehide", () => this.send({ type: "page-hidden" })); + this.page.addEventListener("pageshow", (event: PageTransitionEvent) => { + if (event.persisted) { + this.sayHello(); + } + }); + this.sayHello(); + } + + private sayHello(): void { + const hello: ExtensionHello = { type: "penpot-mcp-extension:hello" }; + this.page.postMessage(hello, this.page.location.origin); + } + + private handleMessage(event: MessageEvent): void { + if (event.source !== this.page || event.origin !== this.page.location.origin) { + return; + } + // the page started listening after the hello sent on load + if (event.data?.type === "penpot-mcp:ready") { + this.sayHello(); + return; + } + const announcement = parseAnnouncement(event.data); + if (announcement) { + this.send({ type: "session", announcement }); + } + } + + private send(message: ContentMessage): void { + try { + // the worker cannot be reached after the extension is reloaded or removed + this.worker.sendMessage(message).catch(() => {}); + } catch { + // ignored for the same reason + } + } +} diff --git a/mcp/packages/browser-extension/src/PopupState.test.ts b/mcp/packages/browser-extension/src/PopupState.test.ts new file mode 100644 index 0000000000..94a1b9d892 --- /dev/null +++ b/mcp/packages/browser-extension/src/PopupState.test.ts @@ -0,0 +1,13 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { getPopupState } from "./PopupState.ts"; +import { SessionMap } from "./SessionMap.ts"; + +test("lists the sessions and their tabs", async () => { + const sessions = new SessionMap({ load: async () => undefined, save: async () => {} }); + await sessions.announce( + { tabId: 7, windowId: 1 }, + { type: "penpot-mcp:session", sessionId: "tab", status: "connected" } + ); + assert.deepEqual(getPopupState(sessions), { sessions: [{ sessionId: "tab", tabId: 7 }] }); +}); diff --git a/mcp/packages/browser-extension/src/PopupState.ts b/mcp/packages/browser-extension/src/PopupState.ts new file mode 100644 index 0000000000..1bf6678a69 --- /dev/null +++ b/mcp/packages/browser-extension/src/PopupState.ts @@ -0,0 +1,22 @@ +import type { SessionMap } from "./SessionMap.ts"; + +/** Request of the popup for the extension's state. */ +export interface PopupStateRequest { + type: "popup:get-state"; +} + +/** State of the extension shown in the popup. */ +export interface PopupState { + sessions: { sessionId: string; tabId: number }[]; +} + +/** + * Collects the state shown in the popup. + * + * @param sessions - The map of sessions to tabs + */ +export function getPopupState(sessions: SessionMap): PopupState { + return { + sessions: sessions.all().map(([sessionId, entry]) => ({ sessionId, tabId: entry.tabId })), + }; +} diff --git a/mcp/packages/browser-extension/src/SessionMap.test.ts b/mcp/packages/browser-extension/src/SessionMap.test.ts new file mode 100644 index 0000000000..38e31fa874 --- /dev/null +++ b/mcp/packages/browser-extension/src/SessionMap.test.ts @@ -0,0 +1,125 @@ +import assert from "node:assert/strict"; +import { beforeEach, test } from "node:test"; +import type { SessionAnnouncement } from "@penpot/mcp-common"; +import { DiscardGuard } from "./DiscardGuard.ts"; +import { SessionMap, type SessionChange, type SessionEntry, type SessionStore } from "./SessionMap.ts"; + +/** In-memory store standing in for session storage. */ +class MemoryStore implements SessionStore { + constructor(public saved?: Record) {} + + async load() { + return this.saved; + } + + async save(entries: Record) { + this.saved = structuredClone(entries); + } +} + +const TAB = { tabId: 1, windowId: 10 }; +const OTHER_TAB = { tabId: 2, windowId: 10 }; + +function announcement( + status: SessionAnnouncement["status"], + sessionId: string | null = "session" +): SessionAnnouncement { + return { type: "penpot-mcp:session", sessionId, status }; +} + +let store: MemoryStore; +let changes: SessionChange[]; +let sessions: SessionMap; + +beforeEach(() => { + store = new MemoryStore(); + changes = []; + sessions = new SessionMap(store); + sessions.observe({ sessionsChanged: (change) => changes.push(change) }); +}); + +test("maps a connected session to the announcing tab and saves it", async () => { + await sessions.announce(TAB, announcement("connected")); + assert.deepEqual(sessions.get("session"), TAB); + assert.deepEqual(store.saved, { session: TAB }); + assert.deepEqual(changes, [{ tabsAdded: [1], tabsRemoved: [] }]); +}); + +test("ignores announcements without a connected session", async () => { + await sessions.announce(TAB, announcement("connecting", null)); + await sessions.announce(TAB, announcement("connected", null)); + await sessions.announce(TAB, announcement("error")); + assert.deepEqual(sessions.all(), []); + assert.deepEqual(changes, []); +}); + +test("keeps the session while the plugin reconnects", async () => { + await sessions.announce(TAB, announcement("connected")); + await sessions.announce(TAB, announcement("connecting")); + await sessions.announce(TAB, announcement("connected")); + assert.equal(sessions.get("session")?.tabId, 1); + assert.equal(changes.length, 1); +}); + +test("forgets the tab's session when its page announces a disconnect", async () => { + await sessions.announce(TAB, announcement("connected")); + await sessions.announce(TAB, announcement("disconnected", null)); + assert.equal(sessions.get("session"), undefined); + assert.deepEqual(changes.at(-1), { tabsAdded: [], tabsRemoved: [1] }); +}); + +test("a tab holds only the session it announced last", async () => { + await sessions.announce(TAB, announcement("connected", "old")); + await sessions.announce(TAB, announcement("connected", "new")); + assert.equal(sessions.get("old"), undefined); + assert.equal(sessions.get("new")?.tabId, 1); + assert.deepEqual(changes.at(-1), { tabsAdded: [], tabsRemoved: [] }); +}); + +test("forgets only the sessions of a removed tab", async () => { + await sessions.announce(TAB, announcement("connected", "first")); + await sessions.announce(OTHER_TAB, announcement("connected", "second")); + await sessions.removeTab(1); + assert.deepEqual( + sessions.all().map(([sessionId]) => sessionId), + ["second"] + ); + assert.deepEqual(changes.at(-1), { tabsAdded: [], tabsRemoved: [1] }); +}); + +test("restores the saved sessions when the worker restarts", async () => { + await sessions.announce(TAB, announcement("connected")); + const restarted = new SessionMap(store); + await restarted.ready(); + assert.equal(restarted.get("session")?.tabId, 1); +}); + +test("changes made while restoring apply to the restored sessions", async () => { + const saved = new MemoryStore({ first: TAB }); + const restored = new SessionMap(saved); + await restored.announce(OTHER_TAB, announcement("connected", "second")); + assert.deepEqual(Object.keys(saved.saved!).sort(), ["first", "second"]); +}); + +test("the discard guard protects tabs holding a session and releases them after", async () => { + const calls: [number, boolean][] = []; + const guard = new DiscardGuard({ + setAutoDiscardable: async (tabId, autoDiscardable) => { + calls.push([tabId, autoDiscardable]); + }, + }); + const guarded = new SessionMap(new MemoryStore()); + guarded.observe(guard); + await guarded.announce(TAB, announcement("connected")); + await guarded.removeTab(1); + assert.deepEqual(calls, [ + [1, false], + [1, true], + ]); +}); + +test("the discard guard ignores tabs closed meanwhile", async () => { + const guard = new DiscardGuard({ setAutoDiscardable: () => Promise.reject(new Error("No tab with id")) }); + guard.sessionsChanged({ tabsAdded: [1], tabsRemoved: [2] }); + await new Promise((resolve) => setImmediate(resolve)); +}); diff --git a/mcp/packages/browser-extension/src/SessionMap.ts b/mcp/packages/browser-extension/src/SessionMap.ts new file mode 100644 index 0000000000..53eca8e1e2 --- /dev/null +++ b/mcp/packages/browser-extension/src/SessionMap.ts @@ -0,0 +1,129 @@ +import type { SessionAnnouncement } from "@penpot/mcp-common"; + +/** The tab holding an MCP session. */ +export interface SessionEntry { + tabId: number; + windowId: number; +} + +/** Persistence of the session map across service worker restarts. */ +export interface SessionStore { + load(): Promise | undefined>; + save(entries: Record): Promise; +} + +/** Change of the tabs holding sessions, reported after each change of the map. */ +export interface SessionChange { + /** tabs that hold a session and held none before. */ + tabsAdded: number[]; + /** tabs that held a session and hold none now. */ + tabsRemoved: number[]; +} + +/** Receiver of the changes of a session map. */ +export interface SessionMapObserver { + sessionsChanged(change: SessionChange): void; +} + +/** + * Map from MCP session IDs to the tabs holding them. + * + * A tab holds the session it last announced as connected, until it announces a disconnect, is + * hidden or is closed. The map is saved after each change and restored when the service worker + * restarts. + */ +export class SessionMap { + private entries = new Map(); + private readonly observers: SessionMapObserver[] = []; + private readonly loaded: Promise; + + constructor(private readonly store: SessionStore) { + this.loaded = store.load().then( + (saved) => { + this.entries = new Map(Object.entries(saved ?? {})); + }, + () => {} + ); + } + + /** Adds a receiver of the changes of this map. */ + observe(observer: SessionMapObserver): void { + this.observers.push(observer); + } + + /** Resolves once the saved entries have been restored. */ + ready(): Promise { + return this.loaded; + } + + /** + * Records a session announcement made by the page in a tab. + * + * @param tab - The tab and window of the announcing page + * @param announcement - The page's announcement + */ + async announce(tab: { tabId: number; windowId: number }, announcement: SessionAnnouncement): Promise { + await this.loaded; + if (announcement.status === "disconnected") { + await this.change(() => this.deleteTab(tab.tabId)); + } else if (announcement.status === "connected" && announcement.sessionId) { + const { sessionId } = announcement; + await this.change(() => { + // a tab holds a single session + this.deleteTab(tab.tabId); + this.entries.set(sessionId, { ...tab }); + }); + } + } + + /** + * Forgets the sessions held by a tab. + * + * @param tabId - The tab whose page was hidden, or which was closed or replaced + */ + async removeTab(tabId: number): Promise { + await this.loaded; + await this.change(() => this.deleteTab(tabId)); + } + + /** Returns the entry of a session, if a tab holds it. */ + get(sessionId: string): SessionEntry | undefined { + return this.entries.get(sessionId); + } + + /** Returns all sessions with their entries. */ + all(): [string, SessionEntry][] { + return [...this.entries]; + } + + /** Applies a change, then saves the map and reports the change if any tab gained or lost a session. */ + private async change(apply: () => void): Promise { + const before = this.tabs(); + const snapshot = JSON.stringify([...this.entries]); + apply(); + if (JSON.stringify([...this.entries]) === snapshot) { + return; + } + const after = this.tabs(); + await this.store.save(Object.fromEntries(this.entries)); + const change = { + tabsAdded: [...after].filter((tabId) => !before.has(tabId)), + tabsRemoved: [...before].filter((tabId) => !after.has(tabId)), + }; + for (const observer of this.observers) { + observer.sessionsChanged(change); + } + } + + private deleteTab(tabId: number): void { + for (const [sessionId, entry] of [...this.entries]) { + if (entry.tabId === tabId) { + this.entries.delete(sessionId); + } + } + } + + private tabs(): Set { + return new Set([...this.entries.values()].map((entry) => entry.tabId)); + } +} diff --git a/mcp/packages/browser-extension/src/background.ts b/mcp/packages/browser-extension/src/background.ts new file mode 100644 index 0000000000..fc4f6ddf43 --- /dev/null +++ b/mcp/packages/browser-extension/src/background.ts @@ -0,0 +1,44 @@ +/** + * Service worker of the Penpot MCP extension. + * + * It keeps the map from MCP sessions to the tabs holding them, and keeps the browser from + * discarding those tabs. + * + * Chrome may stop the worker, and Firefox its event page, at any time, so listeners are + * registered synchronously at start and state lives in session storage. + */ +import { ChromeDiscardControl, ChromeSessionStore } from "./chrome.ts"; +import { DiscardGuard } from "./DiscardGuard.ts"; +import { parseAnnouncement, type ContentMessage } from "./messages.ts"; +import { getPopupState, type PopupStateRequest } from "./PopupState.ts"; +import { SessionMap } from "./SessionMap.ts"; + +const sessions = new SessionMap(new ChromeSessionStore()); +sessions.observe(new DiscardGuard(new ChromeDiscardControl())); + +chrome.runtime.onMessage.addListener((message: ContentMessage | PopupStateRequest, sender, sendResponse) => { + if (sender.id !== chrome.runtime.id) { + return; + } + const tab = sender.tab; + if (message?.type === "popup:get-state" && !tab) { + void sessions.ready().then(() => sendResponse(getPopupState(sessions))); + // the response is sent asynchronously + return true; + } + if (tab?.id === undefined) { + return; + } + if (message?.type === "page-hidden") { + void sessions.removeTab(tab.id); + } else if (message?.type === "session") { + const announcement = parseAnnouncement(message.announcement); + if (announcement) { + void sessions.announce({ tabId: tab.id, windowId: tab.windowId }, announcement); + } + } +}); + +chrome.tabs.onRemoved.addListener((tabId) => void sessions.removeTab(tabId)); + +chrome.tabs.onReplaced.addListener((_addedTabId, removedTabId) => void sessions.removeTab(removedTabId)); diff --git a/mcp/packages/browser-extension/src/chrome.ts b/mcp/packages/browser-extension/src/chrome.ts new file mode 100644 index 0000000000..38954c7ced --- /dev/null +++ b/mcp/packages/browser-extension/src/chrome.ts @@ -0,0 +1,26 @@ +import type { DiscardControl } from "./DiscardGuard.ts"; +import type { SessionEntry, SessionStore } from "./SessionMap.ts"; + +const SESSIONS_KEY = "sessions"; + +/** + * Session store in `chrome.storage.session`, which lives in memory, survives service worker + * restarts and clears when the browser closes. + */ +export class ChromeSessionStore implements SessionStore { + async load(): Promise | undefined> { + const stored = await chrome.storage.session.get(SESSIONS_KEY); + return stored[SESSIONS_KEY] as Record | undefined; + } + + async save(entries: Record): Promise { + await chrome.storage.session.set({ [SESSIONS_KEY]: entries }); + } +} + +/** Discard control through the Chrome tabs API. */ +export class ChromeDiscardControl implements DiscardControl { + async setAutoDiscardable(tabId: number, autoDiscardable: boolean): Promise { + await chrome.tabs.update(tabId, { autoDiscardable }); + } +} diff --git a/mcp/packages/browser-extension/src/content.ts b/mcp/packages/browser-extension/src/content.ts new file mode 100644 index 0000000000..1e115228a4 --- /dev/null +++ b/mcp/packages/browser-extension/src/content.ts @@ -0,0 +1,3 @@ +import { PageBridge } from "./PageBridge.ts"; + +new PageBridge(window, chrome.runtime).start(); diff --git a/mcp/packages/browser-extension/src/manifest.test.ts b/mcp/packages/browser-extension/src/manifest.test.ts new file mode 100644 index 0000000000..f78006c58a --- /dev/null +++ b/mcp/packages/browser-extension/src/manifest.test.ts @@ -0,0 +1,45 @@ +import assert from "node:assert/strict"; +import { access } from "node:fs/promises"; +import test from "node:test"; +import { BROWSERS, createManifest } from "../scripts/manifest.mjs"; + +const manifests = BROWSERS.map((browser) => createManifest(browser)); +const [chrome, firefox] = manifests; + +test("runs the background as a module service worker in Chrome and as an event page in Firefox", () => { + assert.deepEqual(chrome.background, { service_worker: "background.js", type: "module" }); + assert.deepEqual(firefox.background, { scripts: ["background.js"] }); +}); + +test("requests only storage", () => { + for (const manifest of manifests) { + assert.equal(manifest.manifest_version, 3); + assert.deepEqual(manifest.permissions, ["storage"]); + assert.equal(manifest.optional_permissions, undefined); + assert.equal(manifest.host_permissions, undefined); + } +}); + +test("gives the Firefox build an add-on ID and a minimum version", () => { + assert.deepEqual(firefox.browser_specific_settings, { + gecko: { + id: "mcp-extension@penpot.app", + strict_min_version: "140.0", + data_collection_permissions: { required: ["none"] }, + }, + }); + assert.equal(chrome.browser_specific_settings, undefined); +}); + +test("references icons and a popup that exist in public/", async () => { + for (const manifest of manifests) { + const paths = [ + ...Object.values(manifest.icons as Record), + ...Object.values(manifest.action.default_icon as Record), + manifest.action.default_popup, + ]; + for (const path of paths) { + await access(new URL(`../public/${path}`, import.meta.url)); + } + } +}); diff --git a/mcp/packages/browser-extension/src/messages.test.ts b/mcp/packages/browser-extension/src/messages.test.ts new file mode 100644 index 0000000000..14040124e5 --- /dev/null +++ b/mcp/packages/browser-extension/src/messages.test.ts @@ -0,0 +1,28 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { parseAnnouncement } from "./messages.ts"; + +const announcement = { type: "penpot-mcp:session", sessionId: "abcdefghij", status: "connected" }; + +test("accepts session announcements, with or without a session ID", () => { + for (const data of [announcement, { ...announcement, sessionId: null, status: "connecting" }]) { + assert.deepEqual(parseAnnouncement(data), data); + } +}); + +test("keeps only the announcement fields", () => { + assert.deepEqual(parseAnnouncement({ ...announcement, extra: 1 }), announcement); +}); + +test("rejects other messages and malformed announcements", () => { + for (const data of [ + null, + "penpot-mcp:session", + { ...announcement, type: "penpot-mcp-extension:hello" }, + { ...announcement, sessionId: "" }, + { ...announcement, sessionId: 42 }, + { ...announcement, status: "frozen" }, + ]) { + assert.equal(parseAnnouncement(data), null, JSON.stringify(data)); + } +}); diff --git a/mcp/packages/browser-extension/src/messages.ts b/mcp/packages/browser-extension/src/messages.ts new file mode 100644 index 0000000000..e55644973e --- /dev/null +++ b/mcp/packages/browser-extension/src/messages.ts @@ -0,0 +1,32 @@ +import type { McpConnectionStatus, SessionAnnouncement } from "@penpot/mcp-common"; + +/** + * Message sent by the content script to the service worker. + * + * - `session`: the page announced its MCP session + * - `page-hidden`: the page is being unloaded or stored in the back/forward cache + */ +export type ContentMessage = { type: "session"; announcement: SessionAnnouncement } | { type: "page-hidden" }; + +const STATUSES: readonly McpConnectionStatus[] = ["idle", "connecting", "connected", "disconnected", "error"]; + +function isSessionId(value: unknown): value is string | null { + return value === null || (typeof value === "string" && value.length > 0 && value.length <= 128); +} + +/** + * Validates a session announcement posted on the page. + * + * @param data - The data of a window message + * @returns The announcement, or null if the data is not a valid announcement + */ +export function parseAnnouncement(data: unknown): SessionAnnouncement | null { + if (typeof data !== "object" || data === null) { + return null; + } + const { type, sessionId, status } = data as Record; + if (type !== "penpot-mcp:session" || !isSessionId(sessionId) || !STATUSES.includes(status as McpConnectionStatus)) { + return null; + } + return { type, sessionId, status: status as McpConnectionStatus }; +} diff --git a/mcp/packages/browser-extension/src/popup.ts b/mcp/packages/browser-extension/src/popup.ts new file mode 100644 index 0000000000..fa29fac3fb --- /dev/null +++ b/mcp/packages/browser-extension/src/popup.ts @@ -0,0 +1,28 @@ +import type { PopupState, PopupStateRequest } from "./PopupState.ts"; + +const REFRESH_INTERVAL_MS = 1_000; + +const sessionsList = document.getElementById("sessions") as HTMLUListElement; + +function render(state: PopupState): void { + const items = state.sessions.map((session) => `${session.sessionId} (tab ${session.tabId})`); + sessionsList.replaceChildren( + ...(items.length ? items : ["No Penpot tab is connected to MCP."]).map((text) => { + const item = document.createElement("li"); + item.textContent = text; + item.classList.toggle("empty", items.length === 0); + return item; + }) + ); +} + +async function refresh(): Promise { + const request: PopupStateRequest = { type: "popup:get-state" }; + const state: PopupState | undefined = await chrome.runtime.sendMessage(request); + if (state) { + render(state); + } +} + +void refresh(); +setInterval(() => void refresh(), REFRESH_INTERVAL_MS); diff --git a/mcp/packages/browser-extension/tsconfig.json b/mcp/packages/browser-extension/tsconfig.json new file mode 100644 index 0000000000..b05f0b7842 --- /dev/null +++ b/mcp/packages/browser-extension/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "lib": ["ES2022", "DOM"], + "types": ["chrome"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "exclude": ["src/*.test.ts"] +} diff --git a/mcp/packages/common/package.json b/mcp/packages/common/package.json index 6ddd0bd605..267a37fef2 100644 --- a/mcp/packages/common/package.json +++ b/mcp/packages/common/package.json @@ -4,7 +4,7 @@ "description": "Shared type definitions and interfaces for Penpot MCP", "main": "dist/index.js", "types": "dist/index.d.ts", - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "scripts": { "build": "tsc --build --clean && tsc --build", "watch": "tsc --watch", diff --git a/mcp/packages/common/src/types.ts b/mcp/packages/common/src/types.ts index 36e52cc531..278e272951 100644 --- a/mcp/packages/common/src/types.ts +++ b/mcp/packages/common/src/types.ts @@ -1,3 +1,17 @@ +/** Metadata identifying one connected Penpot plugin session. */ +export interface PenpotSession { + sessionId: string; + fileId: string | null; + fileName: string | null; + workspaceState?: "none" | "loading" | "ready"; +} + +/** First message sent by the plugin on each new WebSocket connection. */ +export interface PluginConnectionInit { + type: "initialize"; + session: PenpotSession; +} + /** * Result of a plugin task execution. * @@ -8,6 +22,47 @@ export interface PluginTaskResult { * Optional result data from the task execution. */ data?: T; + + /** + * Rollbacks of rejected changes that happened in the workspace since the previous task response. + */ + rollbacks?: ContentRollbackEvent[]; +} + +/** + * Message from the MCP server giving the plugin back the rollbacks of a task response that came + * after its task timed out, so that the plugin reports them with its next response. + */ +export interface UnclaimedRollbacksMessage { + type: "unclaimed-rollbacks"; + + /** + * The rollbacks the late response carried, oldest first. + */ + rollbacks: ContentRollbackEvent[]; +} + +/** + * Rollback of rejected changes in the Penpot workspace. + * + * The backend rejected some changes and the workspace undid them, together with the rest of their + * undo steps and any later change to the same file. Mirrors the plugin API's `ContentRollback`. + */ +export interface ContentRollbackEvent { + /** + * Ids of the files that lost changes. + */ + fileIds: string[]; + + /** + * Ids of the pages the undone changes touched. + */ + pageIds: string[]; + + /** + * Ids of the page shapes the undone changes touched. + */ + shapeIds: string[]; } /** @@ -57,6 +112,11 @@ export interface PluginTaskResponse { * The result of the task execution. */ data?: T; + + /** + * Rollbacks of rejected changes that happened in the workspace since the previous task response. + */ + rollbacks?: ContentRollbackEvent[]; } /** @@ -83,3 +143,36 @@ export interface ExecuteCodeTaskResultData { */ log: string; } + +/** + * Status of the Penpot MCP plugin connection, as reported to the Penpot app. + */ +export type McpConnectionStatus = "idle" | "connecting" | "connected" | "disconnected" | "error"; + +/** + * Message posted by a browser extension's content script to ask the Penpot page for its MCP session. + */ +export interface ExtensionHello { + type: "penpot-mcp-extension:hello"; +} + +/** + * Message posted by the Penpot page when it starts listening for an {@link ExtensionHello}. + * + * A content script that said hello before the page was listening says it again. + */ +export interface PageReady { + type: "penpot-mcp:ready"; +} + +/** + * Message posted by the Penpot page to announce its MCP session to a browser extension. + * + * The page sends it in reply to {@link ExtensionHello} and on every connection status change. + */ +export interface SessionAnnouncement { + type: "penpot-mcp:session"; + /** the MCP session ID of the page, or null while it is not known. */ + sessionId: string | null; + status: McpConnectionStatus; +} diff --git a/mcp/packages/plugin/index.html b/mcp/packages/plugin/index.html index de2ff5853c..bb525e1683 100644 --- a/mcp/packages/plugin/index.html +++ b/mcp/packages/plugin/index.html @@ -23,6 +23,22 @@ Disconnect MCP Server + +
    void>(); + const timers = new Map void>(); + let timerId = 0; + class FakeSocket { + static OPEN = 1; + static CONNECTING = 0; + readyState = 0; + sent: any[] = []; + onopen?: () => void; + onclose?: (event: any) => void; + onmessage?: (event: any) => void; + constructor(_url: string) { + sockets.push(this); + } + open() { + this.readyState = 1; + this.onopen?.(); + } + send(raw: string) { + this.sent.push(JSON.parse(raw)); + } + close() { + this.readyState = 3; + this.onclose?.({ code: 1000 }); + } + receive(message: any) { + this.onmessage?.({ data: JSON.stringify(message) }); + } + } + const addEventListener = (name: string, handler: (event?: any) => void) => events.set(name, handler); + runInNewContext(compile("main.ts"), { + exports: {}, + require: (name: string) => { + if (name === "./style.css") return {}; + if (name === "./ReconnectPolicy") return { shouldReconnectAfterClose }; + if (name === "./SessionId") return { SessionId }; + if (name === "./SessionIdDisplay") return { SessionIdDisplay }; + throw new Error(`Unexpected import: ${name}`); + }, + console: { log() {}, error() {}, warn() {} }, + crypto: { randomUUID }, + URLSearchParams, + WebSocket: FakeSocket, + window: { location: { hash: "" }, addEventListener }, + document: { body: { dataset: {} }, getElementById: () => null, addEventListener }, + parent: { postMessage: (message: any) => messages.push(JSON.parse(JSON.stringify(message))) }, + setTimeout: (callback: () => void) => { + timers.set(++timerId, callback); + return timerId; + }, + clearTimeout: (id: number) => timers.delete(id), + setInterval: () => ++timerId, + clearInterval: () => {}, + PENPOT_MCP_WEBSOCKET_URL: "ws://localhost:4402", + }); + const message = (data: any) => events.get("message")!({ data }); + const connect = () => { + message({ type: "start-server" }); + sockets.at(-1)!.open(); + return messages.filter((message) => message.type === "connection-metadata-request").at(-1)!.sessionId; + }; + return { messages, sockets, events, timers, message, connect }; +} + +test("sends file metadata before marking a plugin connection ready", async () => { + const ui = pluginUi(); + const sessionId = ui.connect(); + const initialization = { + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId, fileId: "file-1", fileName: "Design" }, + }; + await ui.message(initialization); + const registeredSession = ui.sockets[0].sent[0].session; + assert.equal(registeredSession.sessionId, "ftdmhltgwk"); + assert.notEqual(registeredSession.sessionId, sessionId); + assert.deepEqual(registeredSession, { ...initialization.session, sessionId: registeredSession.sessionId }); + assert.equal( + ui.messages.some((message) => message.status === "connected"), + false + ); + ui.sockets[0].receive({ type: "initialized" }); + assert.equal( + ui.messages.filter((message) => message.type === "update-connection-status").at(-1).status, + "connected" + ); +}); + +test("reconnects with the same session ID and ignores metadata for the old socket", async () => { + const ui = pluginUi(); + const firstId = ui.connect(); + await ui.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: firstId, fileId: "file-1", fileName: "Design" }, + }); + const sessionId = ui.sockets[0].sent[0].session.sessionId; + ui.sockets[0].close(); + const reconnect = [...ui.timers.values()][0]; + reconnect(); + ui.sockets[1].open(); + const secondId = ui.messages.filter((message) => message.type === "connection-metadata-request").at(-1).sessionId; + assert.notEqual(firstId, secondId); + await ui.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: firstId, fileId: "old", fileName: "Old file" }, + }); + assert.deepEqual(ui.sockets[1].sent, []); + await ui.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: secondId, fileId: "file-1", fileName: "Design" }, + }); + assert.equal(ui.sockets[1].sent[0].session.sessionId, sessionId); + assert.notEqual(ui.sockets[1].sent[0].session.sessionId, secondId); +}); + +test("resume and freeze events cannot send liveness messages before initialization", () => { + const ui = pluginUi(); + ui.connect(); + ui.events.get("resume")!(); + ui.events.get("freeze")!(); + assert.deepEqual(ui.sockets[0].sent, []); + ui.sockets[0].receive({ type: "initialized" }); + ui.events.get("resume")!(); + assert.deepEqual(ui.sockets[0].sent, [{ type: "heartbeat" }]); +}); + +test("answers heartbeat requests once initialized, without forwarding them to the plugin", () => { + const ui = pluginUi(); + ui.connect(); + ui.sockets[0].receive({ type: "heartbeat-request" }); + assert.deepEqual(ui.sockets[0].sent, []); + ui.sockets[0].receive({ type: "initialized" }); + ui.sockets[0].receive({ type: "heartbeat-request" }); + assert.deepEqual(ui.sockets[0].sent, [{ type: "heartbeat" }]); + assert.equal(ui.messages.filter((message) => message.type === "heartbeat-request").length, 0); +}); + +test("plugin reads the current file for each connection metadata request", () => { + let onMessage!: (message: any) => void; + const messages: any[] = []; + const penpot = { + currentFile: { id: "file-1", name: "First design" }, + currentUser: { sessionId: "tab-1" }, + theme: "dark", + ui: { + open() {}, + onMessage: (handler: (message: any) => void) => { + onMessage = handler; + }, + sendMessage: (message: any) => messages.push(JSON.parse(JSON.stringify(message))), + }, + on() {}, + }; + runInNewContext(compile("plugin.ts"), { + exports: {}, + penpot, + penpotMgmt: { + get workspace() { + return { status: "ready", fileId: penpot.currentFile.id, fileName: penpot.currentFile.name }; + }, + on() {}, + }, + mcp: undefined, + require: pluginRequire, + }); + onMessage({ type: "connection-metadata-request", sessionId: "first" }); + penpot.currentFile = { id: "file-2", name: "Second design" }; + onMessage({ type: "connection-metadata-request", sessionId: "second" }); + assert.deepEqual(messages, [ + { + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: "first", fileId: "file-1", fileName: "First design", workspaceState: "ready" }, + }, + { + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: "second", fileId: "file-2", fileName: "Second design", workspaceState: "ready" }, + }, + ]); +}); + +test("plugin without penpotMgmt derives metadata from the open file", () => { + let onMessage!: (message: any) => void; + const messages: any[] = []; + const penpot = { + currentFile: { id: "file-1", name: "Design" } as { id: string; name: string } | null, + currentUser: { sessionId: "tab-1" }, + theme: "dark", + ui: { + open() {}, + onMessage: (handler: (message: any) => void) => { + onMessage = handler; + }, + sendMessage: (message: any) => messages.push(JSON.parse(JSON.stringify(message))), + }, + on() {}, + }; + runInNewContext(compile("plugin.ts"), { + exports: {}, + penpot, + mcp: undefined, + require: pluginRequire, + }); + onMessage({ type: "connection-metadata-request", sessionId: "first" }); + penpot.currentFile = null; + onMessage({ type: "context-request" }); + assert.deepEqual(messages, [ + { + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: "first", fileId: "file-1", fileName: "Design", workspaceState: "ready" }, + }, + { + type: "context-update", + context: { fileId: null, fileName: null, workspaceState: "none" }, + }, + ]); +}); + +test("integrated connections reuse the short session ID across reconnects", async () => { + const ui = pluginUi(); + ui.message({ type: "mcp-mode", integratedRemoteMcp: true }); + const initialize = async (requestId: string) => { + await ui.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: requestId, fileId: "file-1", fileName: "Design" }, + }); + }; + await initialize(ui.connect()); + assert.equal(ui.sockets[0].sent[0].session.sessionId, "ftdmhltgwk"); + ui.sockets[0].receive({ type: "initialized" }); + assert.equal( + ui.messages.filter((message) => message.type === "update-connection-status").at(-1).sessionId, + "ftdmhltgwk" + ); + ui.sockets[0].close(); + [...ui.timers.values()][0](); + ui.sockets[1].open(); + const requestId = ui.messages.filter((message) => message.type === "connection-metadata-request").at(-1).sessionId; + await initialize(requestId); + assert.equal(ui.sockets[1].sent[0].session.sessionId, "ftdmhltgwk"); +}); + +test("disconnect cancels metadata initialization and ignores a late acknowledgement", async () => { + const ui = pluginUi(); + ui.message({ type: "mcp-mode", integratedRemoteMcp: true }); + const requestId = ui.connect(); + const pending = ui.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: requestId, fileId: "file-1", fileName: "Design" }, + }); + ui.message({ type: "stop-server" }); + await pending; + assert.deepEqual(ui.sockets[0].sent, []); + ui.sockets[0].receive({ type: "initialized" }); + assert.ok(!ui.messages.some((message) => message.status === "connected")); + assert.equal(ui.messages.at(-1).status, "disconnected"); + assert.equal(ui.timers.size, 0); +}); + +test("integrated plugin stays idle until explicitly connected, including while the UI loads", () => { + let onMessage!: (message: any) => void; + const events = new Map void>(); + const messages: any[] = []; + const statuses: string[] = []; + runInNewContext(compile("plugin.ts"), { + exports: {}, + console: { log() {} }, + PENPOT_MCP_VERSION: "0.0.0", + penpot: { + theme: "dark", + version: "0.0.0", + ui: { + open() {}, + onMessage: (handler: (message: any) => void) => { + onMessage = handler; + }, + sendMessage: (message: any) => messages.push(message), + }, + on() {}, + }, + penpotMgmt: { on() {} }, + mcp: { + getToken: () => "test-token", + getServerUrl: () => "ws://localhost:4402", + isConnectionRequested: () => false, + setMcpStatus: (status: string) => statuses.push(status), + on: (event: string, callback: () => void) => events.set(event, callback), + }, + require: pluginRequire, + }); + assert.ok(!statuses.includes("connecting")); + events.get("connect")!(); + events.get("disconnect")!(); + onMessage({ type: "ui-initialized" }); + assert.ok(!messages.some((message) => message.type === "start-server")); + events.get("connect")!(); + assert.equal(messages.at(-1).type, "start-server"); +}); + +test("standalone session ID survives reconnects and plugin restarts in the same app instance", async () => { + const ui = pluginUi(); + const firstRequestId = ui.connect(); + await ui.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: firstRequestId, fileId: "file-1", fileName: "Design" }, + }); + const sessionId = ui.sockets[0].sent[0].session.sessionId; + ui.message({ type: "stop-server" }); + assert.equal(ui.messages.at(-1).sessionId, null); + const secondRequestId = ui.connect(); + await ui.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: secondRequestId, fileId: "file-1", fileName: "Design" }, + }); + assert.equal(ui.sockets[1].sent[0].session.sessionId, sessionId); + ui.sockets[1].receive({ type: "initialized" }); + assert.equal( + ui.messages.filter((message) => message.type === "update-connection-status").at(-1).sessionId, + sessionId + ); + + const next = pluginUi(); + const nextRequestId = next.connect(); + await next.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: nextRequestId, fileId: "file-1", fileName: "Design" }, + }); + assert.equal(next.sockets[0].sent[0].session.sessionId, sessionId); +}); + +test("file metadata does not affect the short session ID", async () => { + for (const fileId of [null, "file-1", "file-2"]) { + const ui = pluginUi(); + const requestId = ui.connect(); + await ui.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: requestId, fileId, fileName: fileId }, + }); + assert.equal(ui.sockets[0].sent[0].session.sessionId, "ftdmhltgwk"); + } +}); + +test("dashboard sessions use the short session ID and publish context changes without reconnecting", async () => { + const ui = pluginUi(); + const requestId = ui.connect(); + await ui.message({ + type: "initialize", + penpotUserSessionId: "tab-1", + session: { sessionId: requestId, fileId: null, fileName: null, workspaceState: "none" }, + }); + assert.equal(ui.sockets[0].sent[0].session.sessionId, "ftdmhltgwk"); + ui.sockets[0].receive({ type: "initialized" }); + assert.equal(ui.messages.at(-1).type, "context-request"); + for (const context of [ + { fileId: "file-1", fileName: "Design", workspaceState: "ready" }, + { fileId: null, fileName: null, workspaceState: "none" }, + ]) { + await ui.message({ type: "context-update", context }); + assert.deepEqual(ui.sockets[0].sent.at(-1), { type: "context-update", context }); + } + assert.equal(ui.sockets.length, 1); +}); diff --git a/mcp/packages/plugin/src/ExecuteCodeTaskConsole.test.ts b/mcp/packages/plugin/src/ExecuteCodeTaskConsole.test.ts new file mode 100644 index 0000000000..41323fbdbc --- /dev/null +++ b/mcp/packages/plugin/src/ExecuteCodeTaskConsole.test.ts @@ -0,0 +1,23 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { ExecuteCodeTaskConsole } from "./ExecuteCodeTaskConsole.ts"; + +test("formats plain objects and primitives", () => { + const console = new ExecuteCodeTaskConsole(); + console.log({ a: 1 }, null, "text", 42, undefined); + assert.equal(console.getLog(), '[LOG] {\n "a": 1\n} null text 42 undefined\n'); +}); + +test("keeps the name and message of a logged error", () => { + const console = new ExecuteCodeTaskConsole(); + console.error("failed", new TypeError("shape is undefined")); + assert.equal(console.getLog(), "[ERROR] failed TypeError: shape is undefined\n"); +}); + +test("does not throw for values JSON cannot serialize", () => { + const console = new ExecuteCodeTaskConsole(); + const circular: { self?: unknown } = {}; + circular.self = circular; + console.log(circular, { big: 1n }); + assert.equal(console.getLog(), "[LOG] [object Object] [object Object]\n"); +}); diff --git a/mcp/packages/plugin/src/ExecuteCodeTaskConsole.ts b/mcp/packages/plugin/src/ExecuteCodeTaskConsole.ts new file mode 100644 index 0000000000..b17e8c3b87 --- /dev/null +++ b/mcp/packages/plugin/src/ExecuteCodeTaskConsole.ts @@ -0,0 +1,171 @@ +/** + * Console implementation that captures all log output for code execution. + * + * Provides the same interface as the native console object but appends + * all output to an internal log string that can be retrieved. + */ +export class ExecuteCodeTaskConsole { + /** + * Accumulated log output from all console method calls. + */ + private logOutput: string = ""; + + /** + * Resets the accumulated log output to empty string. + * Should be called before each code execution to start with clean logs. + */ + resetLog(): void { + this.logOutput = ""; + } + + /** + * Gets the accumulated log output from all console method calls. + * @returns The complete log output as a string + */ + getLog(): string { + return this.logOutput; + } + + /** + * Formats one argument of a captured console call. + * Errors have no enumerable fields, and values such as circular objects + * cannot be serialized to JSON, so fall back to their string form. + */ + private formatLogArgument(arg: unknown): string { + if (arg instanceof Error) { + return String(arg); + } + if (typeof arg === "object") { + try { + return JSON.stringify(arg, null, 2); + } catch { + return String(arg); + } + } + return String(arg); + } + + /** + * Appends a formatted message to the log output. + * @param level - Log level prefix (e.g., "LOG", "WARN", "ERROR") + * @param args - Arguments to log, will be stringified and joined + */ + private appendToLog(level: string, ...args: any[]): void { + const message = args.map((arg) => this.formatLogArgument(arg)).join(" "); + this.logOutput += `[${level}] ${message}\n`; + } + + /** + * Logs a message to the captured output. + */ + log(...args: any[]): void { + this.appendToLog("LOG", ...args); + } + + /** + * Logs a warning message to the captured output. + */ + warn(...args: any[]): void { + this.appendToLog("WARN", ...args); + } + + /** + * Logs an error message to the captured output. + */ + error(...args: any[]): void { + this.appendToLog("ERROR", ...args); + } + + /** + * Logs an informational message to the captured output. + */ + info(...args: any[]): void { + this.appendToLog("INFO", ...args); + } + + /** + * Logs a debug message to the captured output. + */ + debug(...args: any[]): void { + this.appendToLog("DEBUG", ...args); + } + + /** + * Logs a message with trace information to the captured output. + */ + trace(...args: any[]): void { + this.appendToLog("TRACE", ...args); + } + + /** + * Logs a table to the captured output (simplified as JSON). + */ + table(data: any): void { + this.appendToLog("TABLE", data); + } + + /** + * Starts a timer (simplified implementation that just logs). + */ + time(label?: string): void { + this.appendToLog("TIME", `Timer started: ${label || "default"}`); + } + + /** + * Ends a timer (simplified implementation that just logs). + */ + timeEnd(label?: string): void { + this.appendToLog("TIME_END", `Timer ended: ${label || "default"}`); + } + + /** + * Logs messages in a group (simplified to just log the label). + */ + group(label?: string): void { + this.appendToLog("GROUP", label || ""); + } + + /** + * Logs messages in a collapsed group (simplified to just log the label). + */ + groupCollapsed(label?: string): void { + this.appendToLog("GROUP_COLLAPSED", label || ""); + } + + /** + * Ends the current group (simplified implementation). + */ + groupEnd(): void { + this.appendToLog("GROUP_END", ""); + } + + /** + * Clears the console (no-op in this implementation since we want to capture logs). + */ + clear(): void { + // intentionally empty - we don't want to clear captured logs + } + + /** + * Counts occurrences of calls with the same label (simplified implementation). + */ + count(label?: string): void { + this.appendToLog("COUNT", label || "default"); + } + + /** + * Resets the count for a label (simplified implementation). + */ + countReset(label?: string): void { + this.appendToLog("COUNT_RESET", label || "default"); + } + + /** + * Logs an assertion (simplified to just log if condition is false). + */ + assert(condition: boolean, ...args: any[]): void { + if (!condition) { + this.appendToLog("ASSERT", ...args); + } + } +} diff --git a/mcp/packages/plugin/src/RollbackLog.test.ts b/mcp/packages/plugin/src/RollbackLog.test.ts new file mode 100644 index 0000000000..da42e8c548 --- /dev/null +++ b/mcp/packages/plugin/src/RollbackLog.test.ts @@ -0,0 +1,169 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { LocalStorageRollbackStore, RollbackLog, type RollbackStore } from "./RollbackLog.ts"; + +const rollback = (id: string) => ({ fileIds: [`file-${id}`], pageIds: [`page-${id}`], shapeIds: [`shape-${id}`] }); + +/** + * Installs a `penpot` global whose `on` hands each subscription to `on`. + */ +function installPenpot(on: (type: string, callback: any) => void): void { + (globalThis as any).penpot = { on }; +} + +test("records the rollbacks Penpot reports", () => { + let callback: any; + installPenpot((type, cb) => { + assert.equal(type, "contentrollback"); + callback = cb; + }); + const log = new RollbackLog(); + log.listen(); + + callback(rollback("a")); + + assert.deepEqual(log.pending(), [rollback("a")]); +}); + +test("stays empty when Penpot has no rollback event", () => { + installPenpot(() => { + throw new Error("Invalid enum value"); + }); + const log = new RollbackLog(); + + assert.doesNotThrow(() => log.listen()); + assert.deepEqual(log.pending(), []); +}); + +test("keeps the rollbacks recorded after the acknowledged ones", () => { + const log = new RollbackLog(); + log.record(rollback("a")); + const sent = log.pending(); + log.record(rollback("b")); + + log.acknowledge(sent.length); + + assert.deepEqual(log.pending(), [rollback("b")]); +}); + +test("puts returned rollbacks back ahead of the ones recorded since", () => { + const log = new RollbackLog(); + log.record(rollback("a")); + const sent = log.pending(); + log.acknowledge(sent.length); + log.record(rollback("b")); + + log.restore(sent); + + assert.deepEqual(log.pending(), [rollback("a"), rollback("b")]); +}); + +/** + * Rollback store that keeps its events in memory, standing in for the storage that outlives a reload. + */ +class MemoryRollbackStore implements RollbackStore { + events: unknown = []; + + load() { + return this.events as any; + } + + save(events: any) { + this.events = JSON.parse(JSON.stringify(events)); + } +} + +/** + * Plugin local storage held in a map. + */ +function memoryStorage(): { + items: Map; + getItem(key: string): string; + setItem(key: string, value: unknown): void; + removeItem(key: string): void; +} { + const items = new Map(); + return { + items, + getItem: (key) => items.get(key) ?? (null as any), + setItem: (key, value) => void items.set(key, String(value)), + removeItem: (key) => void items.delete(key), + }; +} + +test("keeps unsent rollbacks across a reload", () => { + const store = new MemoryRollbackStore(); + new RollbackLog(store).record(rollback("a")); + + const reloaded = new RollbackLog(store); + + assert.deepEqual(reloaded.pending(), [rollback("a")]); +}); + +test("forgets the stored rollbacks once a response carried them", () => { + const store = new MemoryRollbackStore(); + const log = new RollbackLog(store); + log.record(rollback("a")); + log.record(rollback("b")); + + log.acknowledge(1); + + assert.deepEqual(new RollbackLog(store).pending(), [rollback("b")]); + log.acknowledge(1); + assert.deepEqual(new RollbackLog(store).pending(), []); +}); + +test("stores the rollbacks the server returns", () => { + const store = new MemoryRollbackStore(); + new RollbackLog(store).restore([rollback("a")]); + + assert.deepEqual(new RollbackLog(store).pending(), [rollback("a")]); +}); + +test("keeps rollbacks in memory when the storage fails", () => { + const failing: RollbackStore = { + load: () => { + throw new Error("storage unavailable"); + }, + save: () => { + throw new Error("storage unavailable"); + }, + }; + const log = new RollbackLog(failing); + + assert.doesNotThrow(() => log.record(rollback("a"))); + assert.deepEqual(log.pending(), [rollback("a")]); +}); + +test("stores rollbacks per file in the plugin local storage", () => { + const storage = memoryStorage(); + const fileA = new LocalStorageRollbackStore(storage, "file-a"); + const fileB = new LocalStorageRollbackStore(storage, "file-b"); + + fileA.save([rollback("a")]); + + assert.deepEqual(new LocalStorageRollbackStore(storage, "file-a").load(), [rollback("a")]); + assert.deepEqual(fileB.load(), []); +}); + +test("removes the entry of a file with no rollbacks left", () => { + const storage = memoryStorage(); + const store = new LocalStorageRollbackStore(storage, "file-a"); + store.save([rollback("a")]); + + store.save([]); + + assert.equal(storage.items.size, 0); +}); + +test("ignores a stored entry that is not a rollback list", () => { + const storage = memoryStorage(); + const store = new LocalStorageRollbackStore(storage, "file-a"); + + for (const raw of ["not json", '{"fileIds": []}', '[{"fileIds": "x"}]']) { + storage.items.clear(); + store.save([rollback("a")]); + storage.items.set([...storage.items.keys()][0], raw); + assert.deepEqual(store.load(), [], raw); + } +}); diff --git a/mcp/packages/plugin/src/RollbackLog.ts b/mcp/packages/plugin/src/RollbackLog.ts new file mode 100644 index 0000000000..c4f49d3101 --- /dev/null +++ b/mcp/packages/plugin/src/RollbackLog.ts @@ -0,0 +1,191 @@ +import type { LocalStorage } from "@penpot/plugin-types"; +import type { ContentRollbackEvent } from "../../common/src"; + +/** + * Storage of the rollbacks a {@link RollbackLog} has not handed over yet, which outlives a reload + * of the plugin or of the browser tab. + */ +export interface RollbackStore { + /** + * Returns the stored rollbacks, oldest first. + */ + load(): ContentRollbackEvent[]; + + /** + * Replaces the stored rollbacks. + * + * @param events - the rollbacks not handed over yet, oldest first + */ + save(events: ContentRollbackEvent[]): void; +} + +/** + * Rollback store in Penpot's plugin local storage, with one entry per file. + * + * The plugin runs for one open file, and a reload opens the same file again, so the entry of that + * file holds what the reload would otherwise lose. The storage is the browser's, so it is kept + * across reloads but not shared with other browsers. + */ +export class LocalStorageRollbackStore implements RollbackStore { + private static readonly KEY_PREFIX = "rollbacks/"; + + private readonly storage: Pick; + private readonly fileId: string; + + /** + * @param storage - the plugin local storage (`penpot.localStorage`) + * @param fileId - the id of the file the plugin runs for + */ + constructor(storage: Pick, fileId: string) { + this.storage = storage; + this.fileId = fileId; + } + + private get key(): string { + return LocalStorageRollbackStore.KEY_PREFIX + this.fileId; + } + + load(): ContentRollbackEvent[] { + const raw = this.storage.getItem(this.key); + if (!raw) { + return []; + } + try { + const events = JSON.parse(raw); + return Array.isArray(events) && events.every(isRollbackEvent) ? events : []; + } catch { + return []; + } + } + + save(events: ContentRollbackEvent[]): void { + if (events.length === 0) { + this.storage.removeItem(this.key); + } else { + this.storage.setItem(this.key, JSON.stringify(events)); + } + } +} + +/** + * Tells whether the given value has the shape of a rollback event. + * + * @param value - a value read from storage + */ +function isRollbackEvent(value: any): value is ContentRollbackEvent { + const isIdList = (ids: unknown) => Array.isArray(ids) && ids.every((id) => typeof id === "string"); + return ( + typeof value === "object" && + value !== null && + isIdList(value.fileIds) && + isIdList(value.pageIds) && + isIdList(value.shapeIds) + ); +} + +/** + * Log of the rollbacks of rejected changes that the MCP server has not been told about yet. + * + * A rollback happens after the task that made the rejected changes has returned (the backend + * rejects a save seconds later), so the log hands its events to the next task response. With a + * store, the log also keeps its events across a reload, until a response carries them. + */ +export class RollbackLog { + private readonly store?: RollbackStore; + private events: ContentRollbackEvent[]; + + /** + * @param store - the storage that keeps the events across reloads; without one, or when it + * fails, the events live in memory only + */ + constructor(store?: RollbackStore) { + this.store = store; + this.events = this.load(); + } + + /** + * Starts recording the rollbacks Penpot reports. + * + * Penpot versions without the `contentrollback` event reject the subscription; the log then + * stays empty. + */ + listen(): void { + try { + // @ts-ignore - contentrollback is not yet in the released plugin types + penpot.on("contentrollback", (event: ContentRollbackEvent) => this.record(event)); + } catch (error) { + console.warn("Penpot does not report rollbacks:", error); + } + } + + /** + * Adds a rollback to the log. + * + * @param event - the rollback as reported by Penpot + */ + record(event: ContentRollbackEvent): void { + this.events.push(copyEvent(event)); + this.save(); + } + + /** + * Puts back rollbacks that a sent response carried but no tool execution received (the + * server had given up on that task), ahead of the ones recorded since. + * + * @param events - the returned rollbacks, oldest first + */ + restore(events: ContentRollbackEvent[]): void { + this.events = [...events.map(copyEvent), ...this.events]; + this.save(); + } + + /** + * Returns the rollbacks not yet acknowledged, oldest first. + */ + pending(): ContentRollbackEvent[] { + return [...this.events]; + } + + /** + * Removes the oldest `count` rollbacks, once a response carrying them has been sent. + * + * @param count - the number of rollbacks the sent response carried + */ + acknowledge(count: number): void { + if (count === 0) { + return; + } + this.events = this.events.slice(count); + this.save(); + } + + private load(): ContentRollbackEvent[] { + try { + return this.store?.load() ?? []; + } catch (error) { + console.warn("Unable to read the stored rollbacks:", error); + return []; + } + } + + private save(): void { + try { + this.store?.save(this.events); + } catch (error) { + console.warn("Unable to store the rollbacks:", error); + } + } +} + +/** + * Copies a rollback event into a plain object. + * + * @param event - the rollback, possibly a proxy or array-like from Penpot + */ +function copyEvent(event: ContentRollbackEvent): ContentRollbackEvent { + return { + fileIds: [...event.fileIds], + pageIds: [...event.pageIds], + shapeIds: [...event.shapeIds], + }; +} diff --git a/mcp/packages/plugin/src/SessionId.test.ts b/mcp/packages/plugin/src/SessionId.test.ts new file mode 100644 index 0000000000..dbaa3ed162 --- /dev/null +++ b/mcp/packages/plugin/src/SessionId.test.ts @@ -0,0 +1,14 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { SessionId } from "./SessionId.ts"; + +test("derives a stable 50-bit Base32 identifier from only the Penpot user session", async () => { + assert.equal(await SessionId.forSession("tab-1"), "ftdmhltgwk"); + assert.equal(await SessionId.forSession("tab-1"), "ftdmhltgwk"); + assert.equal(await SessionId.forSession("tab-2"), "cb3oexpdmd"); +}); + +test("shortens a UUID session to ten copyable Base32 characters", async () => { + const sessionId = await SessionId.forSession("a7457b7c-cf45-80d4-8008-ba5b1d08e6c8"); + assert.match(sessionId, /^[a-z2-7]{10}$/); +}); diff --git a/mcp/packages/plugin/src/SessionId.ts b/mcp/packages/plugin/src/SessionId.ts new file mode 100644 index 0000000000..22e14b4309 --- /dev/null +++ b/mcp/packages/plugin/src/SessionId.ts @@ -0,0 +1,21 @@ +/** Short routing identifier for a Penpot session. */ +export class SessionId { + private static readonly ALPHABET = "abcdefghijklmnopqrstuvwxyz234567"; + + public static async forSession(penpotUserSessionId: string): Promise { + const input = new TextEncoder().encode(penpotUserSessionId); + const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", input)); + return SessionId.encode(digest); + } + + protected static encode(bytes: Uint8Array): string { + let result = ""; + for (let bit = 0; bit < 50; bit += 5) { + const byte = Math.floor(bit / 8); + const offset = bit % 8; + const value = ((bytes[byte] << 8) | bytes[byte + 1]) >> (11 - offset); + result += SessionId.ALPHABET[value & 31]; + } + return result; + } +} diff --git a/mcp/packages/plugin/src/SessionIdDisplay.test.ts b/mcp/packages/plugin/src/SessionIdDisplay.test.ts new file mode 100644 index 0000000000..ea58460771 --- /dev/null +++ b/mcp/packages/plugin/src/SessionIdDisplay.test.ts @@ -0,0 +1,77 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { SessionIdDisplay } from "./SessionIdDisplay.ts"; + +class DisplayFixture { + public readonly value = { textContent: "" }; + public readonly feedback = { textContent: "" }; + public onCopy!: () => Promise; + public readonly button = { + disabled: true, + addEventListener: (_event: string, listener: () => Promise) => { + this.onCopy = listener; + }, + }; + public readonly root = { + hidden: true, + querySelector: (selector: string) => + ({ + "#session-id-value": this.value, + "#copy-session-id-btn": this.button, + "#session-id-feedback": this.feedback, + })[selector], + }; + public readonly copied: string[] = []; + public readonly display: SessionIdDisplay; + + constructor( + writeText = async (text: string) => { + this.copied.push(text); + } + ) { + this.display = new SessionIdDisplay(this.root as unknown as HTMLElement, { writeText }); + } +} + +test("shows and copies the exact session ID, then clears it on disconnect", async () => { + const ui = new DisplayFixture(); + ui.display.setSessionId("pq3gxqddgj"); + assert.equal(ui.root.hidden, false); + assert.equal(ui.value.textContent, "pq3gxqddgj"); + await ui.onCopy(); + assert.deepEqual(ui.copied, ["pq3gxqddgj"]); + assert.equal(ui.feedback.textContent, "Copied"); + ui.display.setSessionId(null); + assert.equal(ui.root.hidden, true); + assert.equal(ui.value.textContent, ""); + assert.equal(ui.feedback.textContent, ""); + assert.equal(ui.button.disabled, true); + await ui.onCopy(); + assert.equal(ui.copied.length, 1); +}); + +test("reports clipboard denial and keeps the ID available for manual copying", async () => { + const ui = new DisplayFixture(async () => { + throw new Error("Permission denied"); + }); + ui.display.setSessionId("pq3gxqddgj"); + await ui.onCopy(); + assert.match(ui.feedback.textContent, /Could not copy/); + assert.equal(ui.value.textContent, "pq3gxqddgj"); +}); + +test("ignores late clipboard feedback after the session changes", async () => { + let finish!: () => void; + const ui = new DisplayFixture( + () => + new Promise((resolve) => { + finish = resolve; + }) + ); + ui.display.setSessionId("pq3gxqddgj"); + const pending = ui.onCopy(); + ui.display.setSessionId("jablgbun4p"); + finish(); + await pending; + assert.equal(ui.feedback.textContent, ""); +}); diff --git a/mcp/packages/plugin/src/SessionIdDisplay.ts b/mcp/packages/plugin/src/SessionIdDisplay.ts new file mode 100644 index 0000000000..561daa0086 --- /dev/null +++ b/mcp/packages/plugin/src/SessionIdDisplay.ts @@ -0,0 +1,41 @@ +/** Displays the active session ID and provides clipboard feedback. */ +export class SessionIdDisplay { + private sessionId: string | null = null; + private readonly value: HTMLElement; + private readonly button: HTMLButtonElement; + private readonly feedback: HTMLElement; + + private readonly root: HTMLElement; + private readonly clipboard: Pick; + + constructor(root: HTMLElement, clipboard: Pick) { + this.root = root; + this.clipboard = clipboard; + this.value = root.querySelector("#session-id-value")!; + this.button = root.querySelector("#copy-session-id-btn")!; + this.feedback = root.querySelector("#session-id-feedback")!; + this.button.addEventListener("click", () => this.copy()); + this.setSessionId(null); + } + + public setSessionId(sessionId: string | null): void { + this.sessionId = sessionId; + this.root.hidden = sessionId === null; + this.value.textContent = sessionId ?? ""; + this.button.disabled = sessionId === null; + this.feedback.textContent = ""; + } + + protected async copy(): Promise { + const sessionId = this.sessionId; + if (!sessionId) return; + try { + await this.clipboard.writeText(sessionId); + if (this.sessionId === sessionId) this.feedback.textContent = "Copied"; + } catch { + if (this.sessionId === sessionId) { + this.feedback.textContent = "Could not copy. Select the ID to copy it manually."; + } + } + } +} diff --git a/mcp/packages/plugin/src/TaskHandler.ts b/mcp/packages/plugin/src/TaskHandler.ts index 7bc0aa3085..c5e6130e76 100644 --- a/mcp/packages/plugin/src/TaskHandler.ts +++ b/mcp/packages/plugin/src/TaskHandler.ts @@ -1,3 +1,5 @@ +import type { RollbackLog } from "./RollbackLog"; + /** * Represents a task received from the MCP server in the Penpot MCP plugin */ @@ -8,11 +10,13 @@ export class Task { * @param requestId Unique identifier for the task request * @param taskType The type of the task to execute * @param params Task parameters/arguments + * @param rollbackLog Log whose pending rollbacks the response carries, if any */ constructor( public requestId: string, public taskType: string, - public params: TParams + public params: TParams, + private readonly rollbackLog?: RollbackLog ) {} /** @@ -24,6 +28,9 @@ export class Task { return; } + // rollbacks the server has not been told about, whatever the task did + const rollbacks = this.rollbackLog?.pending() ?? []; + // create response object const response = { type: "task-response", @@ -33,6 +40,7 @@ export class Task { success: success, data: data, error: error, + ...(rollbacks.length > 0 ? { rollbacks } : {}), }, }; @@ -60,6 +68,7 @@ export class Task { } console.log("Sent task response:", response); this.isResponseSent = true; + this.rollbackLog?.acknowledge(rollbacks.length); } public sendSuccess(data: any = undefined): void { diff --git a/mcp/packages/plugin/src/index.d.ts b/mcp/packages/plugin/src/index.d.ts index a0eda651e1..1fa11567bb 100644 --- a/mcp/packages/plugin/src/index.d.ts +++ b/mcp/packages/plugin/src/index.d.ts @@ -1,7 +1,8 @@ interface McpOptions { getToken(): string; getServerUrl(): string; - setMcpStatus(status: string); + isConnectionRequested(): boolean; + setMcpStatus(status: string, sessionId?: string); on(eventType: "disconnect" | "connect", cb: () => void); } diff --git a/mcp/packages/plugin/src/main.ts b/mcp/packages/plugin/src/main.ts index f787cd9298..4f5e78b755 100644 --- a/mcp/packages/plugin/src/main.ts +++ b/mcp/packages/plugin/src/main.ts @@ -1,5 +1,8 @@ import "./style.css"; import { shouldReconnectAfterClose } from "./ReconnectPolicy"; +import { SessionId } from "./SessionId"; +import { SessionIdDisplay } from "./SessionIdDisplay"; +import type { PluginConnectionInit } from "../../common/src"; /** * the maximum allowed size for task responses sent back to the MCP server in the integrated remote MCP mode. @@ -17,6 +20,9 @@ document.body.dataset.theme = searchParams.get("theme") ?? "light"; // WebSocket connection to the MCP server let ws: WebSocket | null = null; +let connectionSessionId: string | null = null; +let connectionRequestId: string | null = null; +let connectionReady = false; const HEARTBEAT_INTERVAL_MS = 10_000; const RECONNECT_BASE_DELAY_MS = 1_000; @@ -46,6 +52,8 @@ const connectBtn = document.getElementById("connect-btn") as HTMLButtonElement; const disconnectBtn = document.getElementById("disconnect-btn") as HTMLButtonElement; const versionWarningEl = document.getElementById("version-warning") as HTMLElement; const versionWarningTextEl = document.getElementById("version-warning-text") as HTMLElement; +const sessionIdEl = document.getElementById("session-id"); +const sessionIdDisplay = sessionIdEl ? new SessionIdDisplay(sessionIdEl, navigator.clipboard) : null; /** * Updates the status pill and button visibility based on connection state. @@ -62,6 +70,7 @@ function updateConnectionStatus(code: string, label: string): void { } const isConnected = code === "connected"; + sessionIdDisplay?.setSessionId(isConnected ? connectionSessionId : null); if (connectBtn) connectBtn.hidden = isConnected; if (disconnectBtn) disconnectBtn.hidden = !isConnected; @@ -69,6 +78,7 @@ function updateConnectionStatus(code: string, label: string): void { { type: "update-connection-status", status: code, + sessionId: connectionSessionId, }, "*" ); @@ -121,6 +131,7 @@ function sendTaskResponse(response: any): void { id: response.id, success: false, error: errorMessage, + rollbacks: response.rollbacks, }; responseString = JSON.stringify(response); } @@ -135,10 +146,12 @@ function sendTaskResponse(response: any): void { * Emits a liveness signal from the plugin event loop. * * WebSocket ping/pong is not enough here: browsers can answer protocol pings while - * page JavaScript is frozen and unable to run MCP tasks. + * page JavaScript is frozen and unable to run MCP tasks. The plugin sends one on a timer and + * in answer to each heartbeat request of the server, since a browser runs the timers of a + * hidden page as rarely as once a minute but handles socket messages at once. */ function sendHeartbeat(): boolean { - if (ws && ws.readyState === WebSocket.OPEN) { + if (connectionReady && ws?.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: "heartbeat" })); return true; } @@ -201,7 +214,7 @@ function connectToMcpServer(baseUrl?: string, token?: string): void { lastConnectionToken = token; if (ws?.readyState === WebSocket.OPEN) { - updateConnectionStatus("connected", "Connected"); + if (connectionReady) updateConnectionStatus("connected", "Connected"); return; } if (ws?.readyState === WebSocket.CONNECTING) { @@ -217,23 +230,35 @@ function connectToMcpServer(baseUrl?: string, token?: string): void { } ws = new WebSocket(wsUrl); + const socket = ws; + connectionRequestId = crypto.randomUUID(); + connectionSessionId = null; + connectionReady = false; updateConnectionStatus("connecting", "Connecting..."); ws.onopen = () => { - cancelReconnect(); - startHeartbeat(); - setTimeout(() => { - if (ws) { - console.log("Connected to MCP server"); - updateConnectionStatus("connected", "Connected"); - } - }, 100); + if (ws !== socket) return; + parent.postMessage({ type: "connection-metadata-request", sessionId: connectionRequestId }, "*"); }; ws.onmessage = (event) => { + if (ws !== socket) return; try { console.log("Received from MCP server:", event.data); const request = JSON.parse(event.data); + if (request.type === "initialized") { + if (ws !== socket) return; + connectionReady = true; + cancelReconnect(); + startHeartbeat(); + updateConnectionStatus("connected", "Connected"); + parent.postMessage({ type: "context-request" }, "*"); + return; + } + if (request.type === "heartbeat-request") { + sendHeartbeat(); + return; + } // Track the current task received from the MCP server if (request.task) { updateCurrentTask(request.task); @@ -247,6 +272,7 @@ function connectToMcpServer(baseUrl?: string, token?: string): void { }; ws.onclose = (event: CloseEvent) => { + if (ws !== socket) return; stopHeartbeat(); // keep the explicit error state if one was already shown if (!wsError) { @@ -256,11 +282,10 @@ function connectToMcpServer(baseUrl?: string, token?: string): void { updateCurrentTask(null); } ws = null; + connectionSessionId = null; + connectionReady = false; if (!shouldReconnectAfterClose(event.code)) { - // Policy violation (e.g. duplicate connection for the same user - // token - another tab already holds the connection). Retrying - // would be refused again immediately, so stay disconnected - // until the user explicitly reconnects. + // a policy violation requires the user to correct the connection settings shouldReconnect = false; cancelReconnect(); return; @@ -287,7 +312,14 @@ function disconnectFromMcpServer(): void { shouldReconnect = false; cancelReconnect(); stopHeartbeat(); - ws?.close(); + const socket = ws; + ws = null; + connectionRequestId = null; + connectionSessionId = null; + connectionReady = false; + socket?.close(); + updateConnectionStatus("disconnected", "Disconnected"); + updateCurrentTask(null); } copyCodeBtn?.addEventListener("click", () => { @@ -309,7 +341,53 @@ disconnectBtn?.addEventListener("click", () => { }); // Listen plugin.ts messages -window.addEventListener("message", (event) => { +window.addEventListener("message", async (event) => { + if (event.data.type === "initialize") { + const initialization = event.data as PluginConnectionInit & { penpotUserSessionId?: string }; + const socket = ws; + const requestId = connectionRequestId; + if ( + !shouldReconnect || + socket?.readyState !== WebSocket.OPEN || + initialization.session.sessionId !== requestId + ) { + return; + } + try { + if (!initialization.penpotUserSessionId) { + throw new Error("Missing Penpot user session ID"); + } + const sessionId = await SessionId.forSession(initialization.penpotUserSessionId); + if ( + !shouldReconnect || + ws !== socket || + connectionRequestId !== requestId || + socket.readyState !== WebSocket.OPEN + ) { + return; + } + connectionSessionId = sessionId; + socket.send( + JSON.stringify({ + type: "initialize", + session: { ...initialization.session, sessionId }, + }) + ); + } catch (error) { + console.error("Failed to initialize MCP session:", error); + if (ws === socket) { + disconnectFromMcpServer(); + updateConnectionStatus("error", "Failed to initialize MCP session"); + } + } + return; + } + if (event.data.type === "context-update") { + if (connectionReady && ws?.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify(event.data)); + } + return; + } if (event.data.type === "mcp-mode") { isIntegratedRemoteMcp = event.data.integratedRemoteMcp; } @@ -354,7 +432,7 @@ function handleTabResumed(): void { // Chrome: about to pause page JavaScript. document.addEventListener("freeze", () => { - if (ws && ws.readyState === WebSocket.OPEN) { + if (connectionReady && ws?.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: "freeze" })); } }); diff --git a/mcp/packages/plugin/src/plugin.ts b/mcp/packages/plugin/src/plugin.ts index 60f09ccc03..f659baa208 100644 --- a/mcp/packages/plugin/src/plugin.ts +++ b/mcp/packages/plugin/src/plugin.ts @@ -2,6 +2,9 @@ import { ExecuteCodeTaskHandler } from "./task-handlers/ExecuteCodeTaskHandler"; import { Task, TaskHandler } from "./TaskHandler"; import { formatTaskError } from "./ErrorUtils"; import { extractMinorVersion, isCompatibleVersion } from "./VersionCompatibility"; +import type { ContentRollbackEvent, PluginConnectionInit } from "../../common/src"; +import type { WorkspaceContext } from "@penpot/plugin-types"; +import { LocalStorageRollbackStore, RollbackLog, type RollbackStore } from "./RollbackLog"; /** * indicates whether the plugin is running in an environment with the Penpot-integrated remote MCP server @@ -9,13 +12,42 @@ import { extractMinorVersion, isCompatibleVersion } from "./VersionCompatibility */ const isIntegratedRemoteMcp = !!mcp; -mcp?.setMcpStatus("connecting"); +/** + * the management API, which Penpot only exposes to plugins running with global scope; + * null otherwise (e.g. in Penpot versions without global plugins) + */ +const management = typeof penpotMgmt === "undefined" ? null : penpotMgmt; + +let uiInitialized = false; /** * Registry of all available task handlers. */ const taskHandlers: TaskHandler[] = [new ExecuteCodeTaskHandler()]; +/** + * Rollbacks of rejected changes, reported to the MCP server with the next task response. + * The ones not reported yet are kept in the plugin local storage of the open file, so a reload + * of the tab or of the plugin does not lose them. + */ +const rollbackLog = new RollbackLog(createRollbackStore()); +rollbackLog.listen(); + +/** + * Creates the store of the rollbacks not reported yet, for the open file. + * + * @returns the store, or undefined when no file is open or the local storage is unavailable + */ +function createRollbackStore(): RollbackStore | undefined { + try { + const fileId = penpot.currentFile?.id; + return fileId ? new LocalStorageRollbackStore(penpot.localStorage, fileId) : undefined; + } catch (error) { + console.warn("Rollbacks are kept in memory only:", error); + return undefined; + } +} + // Open the plugin UI (main.ts) penpot.ui.open("Penpot MCP Plugin", `?theme=${penpot.theme}`, { width: 236, @@ -24,8 +56,20 @@ penpot.ui.open("Penpot MCP Plugin", `?theme=${penpot.theme}`, { } as any); // Register message handlers -penpot.ui.onMessage((message) => { +penpot.ui.onMessage< + | string + | { + id: string; + type?: string; + status?: string; + sessionId?: string; + task: string; + params: any; + rollbacks?: ContentRollbackEvent[]; + } +>((message) => { if (typeof message === "object" && message.type === "ui-initialized") { + uiInitialized = true; // Inform the UI about the operating mode penpot.ui.sendMessage({ type: "mcp-mode", @@ -42,16 +86,36 @@ penpot.ui.onMessage { @@ -60,6 +124,38 @@ penpot.ui.onMessage { console.log("Executing plugin task:", request.task, request.params); - const task = new Task(request.id, request.task, request.params); + const task = new Task(request.id, request.task, request.params, rollbackLog); // Find the appropriate handler const handler = taskHandlers.find((h) => h.isApplicableTo(task)); @@ -102,6 +198,7 @@ if (mcp) { }); }); mcp.on("connect", async () => { + if (!uiInitialized) return; penpot.ui.sendMessage({ type: "start-server", url: mcp?.getServerUrl(), diff --git a/mcp/packages/plugin/src/style.css b/mcp/packages/plugin/src/style.css index 53e0a9da3d..a0941a5247 100644 --- a/mcp/packages/plugin/src/style.css +++ b/mcp/packages/plugin/src/style.css @@ -188,3 +188,14 @@ details[open] > .collapsible-header .collapsible-arrow { width: 100%; margin-top: var(--spacing-4); } + +.session-id-row { + display: flex; + align-items: center; + gap: var(--spacing-8); +} + +#session-id-value { + flex: 1; + user-select: all; +} diff --git a/mcp/packages/plugin/src/task-handlers/ExecuteCodeTaskHandler.ts b/mcp/packages/plugin/src/task-handlers/ExecuteCodeTaskHandler.ts index 87ea2c7616..43bfd37e35 100644 --- a/mcp/packages/plugin/src/task-handlers/ExecuteCodeTaskHandler.ts +++ b/mcp/packages/plugin/src/task-handlers/ExecuteCodeTaskHandler.ts @@ -1,161 +1,7 @@ import { Task, TaskHandler } from "../TaskHandler"; import { ExecuteCodeTaskParams, ExecuteCodeTaskResultData } from "../../../common/src"; import { PenpotUtils } from "../PenpotUtils.ts"; - -/** - * Console implementation that captures all log output for code execution. - * - * Provides the same interface as the native console object but appends - * all output to an internal log string that can be retrieved. - */ -class ExecuteCodeTaskConsole { - /** - * Accumulated log output from all console method calls. - */ - private logOutput: string = ""; - - /** - * Resets the accumulated log output to empty string. - * Should be called before each code execution to start with clean logs. - */ - resetLog(): void { - this.logOutput = ""; - } - - /** - * Gets the accumulated log output from all console method calls. - * @returns The complete log output as a string - */ - getLog(): string { - return this.logOutput; - } - - /** - * Appends a formatted message to the log output. - * @param level - Log level prefix (e.g., "LOG", "WARN", "ERROR") - * @param args - Arguments to log, will be stringified and joined - */ - private appendToLog(level: string, ...args: any[]): void { - const message = args - .map((arg) => (typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg))) - .join(" "); - this.logOutput += `[${level}] ${message}\n`; - } - - /** - * Logs a message to the captured output. - */ - log(...args: any[]): void { - this.appendToLog("LOG", ...args); - } - - /** - * Logs a warning message to the captured output. - */ - warn(...args: any[]): void { - this.appendToLog("WARN", ...args); - } - - /** - * Logs an error message to the captured output. - */ - error(...args: any[]): void { - this.appendToLog("ERROR", ...args); - } - - /** - * Logs an informational message to the captured output. - */ - info(...args: any[]): void { - this.appendToLog("INFO", ...args); - } - - /** - * Logs a debug message to the captured output. - */ - debug(...args: any[]): void { - this.appendToLog("DEBUG", ...args); - } - - /** - * Logs a message with trace information to the captured output. - */ - trace(...args: any[]): void { - this.appendToLog("TRACE", ...args); - } - - /** - * Logs a table to the captured output (simplified as JSON). - */ - table(data: any): void { - this.appendToLog("TABLE", data); - } - - /** - * Starts a timer (simplified implementation that just logs). - */ - time(label?: string): void { - this.appendToLog("TIME", `Timer started: ${label || "default"}`); - } - - /** - * Ends a timer (simplified implementation that just logs). - */ - timeEnd(label?: string): void { - this.appendToLog("TIME_END", `Timer ended: ${label || "default"}`); - } - - /** - * Logs messages in a group (simplified to just log the label). - */ - group(label?: string): void { - this.appendToLog("GROUP", label || ""); - } - - /** - * Logs messages in a collapsed group (simplified to just log the label). - */ - groupCollapsed(label?: string): void { - this.appendToLog("GROUP_COLLAPSED", label || ""); - } - - /** - * Ends the current group (simplified implementation). - */ - groupEnd(): void { - this.appendToLog("GROUP_END", ""); - } - - /** - * Clears the console (no-op in this implementation since we want to capture logs). - */ - clear(): void { - // intentionally empty - we don't want to clear captured logs - } - - /** - * Counts occurrences of calls with the same label (simplified implementation). - */ - count(label?: string): void { - this.appendToLog("COUNT", label || "default"); - } - - /** - * Resets the count for a label (simplified implementation). - */ - countReset(label?: string): void { - this.appendToLog("COUNT_RESET", label || "default"); - } - - /** - * Logs an assertion (simplified to just log if condition is false). - */ - assert(condition: boolean, ...args: any[]): void { - if (!condition) { - this.appendToLog("ASSERT", ...args); - } - } -} +import { ExecuteCodeTaskConsole } from "../ExecuteCodeTaskConsole.ts"; /** * Task handler for executing JavaScript code in the plugin context. @@ -178,6 +24,7 @@ export class ExecuteCodeTaskHandler extends TaskHandler { // initialize context, making penpot, penpotUtils, storage and the custom console available this.context = { penpot: penpot, + penpotMgmt: typeof penpotMgmt === "undefined" ? null : penpotMgmt, storage: {}, console: new ExecuteCodeTaskConsole(), penpotUtils: PenpotUtils, @@ -216,8 +63,9 @@ export class ExecuteCodeTaskHandler extends TaskHandler { try { // wait for layout updates prior to executing the supplied code (if method is available) try { + const management = this.context.penpotMgmt; // @ts-ignore - TODO Penpot.waitForLayoutUpdate is not yet in the released types - if (penpot.waitForLayoutUpdate) { + if ((!management || management.workspace.status === "ready") && penpot.waitForLayoutUpdate) { // @ts-ignore await penpot.waitForLayoutUpdate(); } diff --git a/mcp/packages/server/data/api_types.yml b/mcp/packages/server/data/api_types.yml index cbfb243052..69715ca183 100644 --- a/mcp/packages/server/data/api_types.yml +++ b/mcp/packages/server/data/api_types.yml @@ -92,7 +92,7 @@ Penpot: Hierarchy - * Omit + * Omit + Penpot members: Properties: @@ -5001,6 +5001,595 @@ CommonLayout: The `remove` method removes the layout. + Returns void +WorkspaceContext: + overview: |- + Interface WorkspaceContext + ========================== + + Current workspace of this browser tab. + + ``` + interface WorkspaceContext { + status: "none" | "loading" | "ready"; + fileId: string | null; + fileName: string | null; + teamId: string | null; + } + ``` + + Referenced by: EventsMap, PenpotMgmt + members: + Properties: + status: |- + ``` + readonly status: "none" | "loading" | "ready" + ``` + fileId: |- + ``` + readonly fileId: string | null + ``` + fileName: |- + ``` + readonly fileName: string | null + ``` + teamId: |- + ``` + readonly teamId: string | null + ``` +Team: + overview: |- + Interface Team + ============== + + A team the user belongs to. Requires `manage:teams`. + + Values are a snapshot taken when the team was listed; changes made through + this object update it. + Property assignments run asynchronously and update values after success. + Backend failures are logged to the console; assignments cannot be awaited. + + ``` + interface Team { + id: string; + isDefault: boolean; + name: string; + listProjects(): Promise; + listDeletedProjects(): Promise; + listDeletedFiles(): Promise; + createProject(options: { name: string }): Promise; + } + ``` + + Referenced by: PenpotMgmt, Project + members: + Properties: + id: |- + ``` + readonly id: string + ``` + isDefault: |- + ``` + readonly isDefault: boolean + ``` + + Whether this is the user's personal team. + name: |- + ``` + name: string + ``` + + The team name. Assigning renames the team; requires `manage:teams`. + Trims the name and requires 1 to 250 characters. + Methods: + listProjects: |- + ``` + listProjects(): Promise + ``` + + Lists the team's projects. Requires `content:read`. + + Returns Promise + + Example + ``` + const [team] = await penpotMgmt.listTeams();const projects = await team.listProjects(); + ``` + listDeletedProjects: |- + ``` + listDeletedProjects(): Promise + ``` + + Lists projects still recoverable from Trash. Requires `content:read`. + + Returns Promise + listDeletedFiles: |- + ``` + listDeletedFiles(): Promise + ``` + + Lists files still recoverable from Trash. Requires `content:read`. + + Returns Promise + createProject: |- + ``` + createProject(options: { name: string }): Promise + ``` + + Creates a project in the team. Requires `manage:projects`. + + * Trims `name` and requires 1 to 250 characters. + * Does not change navigation. + + Parameters + + * options: { name: string } + + Returns Promise + + Example + ``` + const project = await team.createProject({ name: 'Design system' }); + ``` +Project: + overview: |- + Interface Project + ================= + + A project in the dashboard. + + Values are a snapshot taken when the project was listed; changes made through + this object update it. + Property assignments run asynchronously and update values after success. + Backend failures are logged to the console; assignments cannot be awaited. + + ``` + interface Project { + id: string; + teamId: string; + isDefault: boolean; + fileCount: number; + name: string; + pinned: boolean; + listFiles(): Promise; + listDeletedFiles(): Promise; + restore(): Promise; + createFile(options: { name: string }): Promise; + duplicate(options?: { name?: string }): Promise; + moveTo(team: Team): Promise; + remove(): Promise; + } + ``` + + Referenced by: PenpotMgmt, Project, ProjectFile, Team + members: + Properties: + id: |- + ``` + readonly id: string + ``` + teamId: |- + ``` + readonly teamId: string + ``` + isDefault: |- + ``` + readonly isDefault: boolean + ``` + + Whether this is the team's Drafts project. + fileCount: |- + ``` + readonly fileCount: number + ``` + + Number of files when the project was listed; file operations do not update it. + name: |- + ``` + name: string + ``` + + The project name. Assigning renames the project; requires `manage:projects`. + Trims the name and requires 1 to 250 characters. + pinned: |- + ``` + pinned: boolean + ``` + + Whether the project is pinned in the sidebar. Assigning requires `manage:projects`. + Methods: + listFiles: |- + ``` + listFiles(): Promise + ``` + + Lists the files in the project. Requires `content:read`. + + Returns Promise + + Example + ``` + const files = await project.listFiles(); + ``` + listDeletedFiles: |- + ``` + listDeletedFiles(): Promise + ``` + + Lists the project's files still recoverable from Trash. Requires `content:read`. + + Returns Promise + restore: |- + ``` + restore(): Promise + ``` + + Restores the project through its files using the dashboard's Trash operation. + Requires `manage:projects` and team edit access. Does not require `manage:delete`. + Rejects if the project has no recoverable files, including empty projects. + + Returns Promise + createFile: |- + ``` + createFile(options: { name: string }): Promise + ``` + + Creates a file with an initial page in the project. Requires `content:write`. + + * Trims `name` and requires 1 to 250 characters. + * Does not open the file. Use `ProjectFile.open` to edit it. + + Parameters + + * options: { name: string } + + Returns Promise + + Example + ``` + const file = await project.createFile({ name: 'Login' });await file.open(); + ``` + duplicate: |- + ``` + duplicate(options?: { name?: string }): Promise + ``` + + Duplicates the project with all its files. Requires `manage:projects`. + + `name` defaults to the name the backend chooses. + + Parameters + + * options: { name?: string } + + Returns Promise + moveTo: |- + ``` + moveTo(team: Team): Promise + ``` + + Moves the project to another team. Requires `manage:teams`. + + Parameters + + * team: Team + + Returns Promise + remove: |- + ``` + remove(): Promise + ``` + + Moves the project and its files to Trash. Requires `manage:delete`. + They can be restored during the retention period. Cannot permanently delete. + + Returns Promise +ProjectFile: + overview: |- + Interface ProjectFile + ===================== + + A file in the dashboard, which may or may not be open. + + Values are a snapshot taken when the file was listed; changes made through + this object update it. To read or edit its content, `open` it and use + `penpot.currentFile`. + Property assignments run asynchronously and update values after success. + Backend failures are logged to the console; assignments cannot be awaited. + + ``` + interface ProjectFile { + id: string; + teamId: string; + projectId: string; + modifiedAt: string; + name: string; + shared: boolean; + open(): Promise; + restore(): Promise; + moveTo(project: Project): Promise; + duplicate(options?: { name?: string }): Promise; + remove(): Promise; + } + ``` + + Referenced by: PenpotMgmt, Project, ProjectFile + members: + Properties: + id: |- + ``` + readonly id: string + ``` + teamId: |- + ``` + readonly teamId: string + ``` + projectId: |- + ``` + readonly projectId: string + ``` + modifiedAt: |- + ``` + readonly modifiedAt: string + ``` + + Last modification time as an ISO 8601 string in UTC. + name: |- + ``` + name: string + ``` + + The file name. Assigning renames the file; requires `content:write`. + Trims the name and requires 1 to 250 characters. + shared: |- + ``` + shared: boolean + ``` + + Whether the file is shared as a library with its team. Assigning requires + `library:write`. A shared file is listed by + `penpot.library.availableLibraries()`; unsharing copies its assets into + every file that uses it and disconnects them. + Methods: + open: |- + ``` + open(): Promise + ``` + + Opens the file in this tab and resolves once its workspace and page are + ready. Requires `content:read`. + + Returns Promise + restore: |- + ``` + restore(): Promise + ``` + + Restores the file using the dashboard's Trash operation, including its parent + project if deleted. Requires `content:write` and team edit access. + Rejects if the file no longer exists. Does not require `manage:delete`. + + Returns Promise + moveTo: |- + ``` + moveTo(project: Project): Promise + ``` + + Moves the file to another project of the same team. Requires `content:write`. + Rejects a destination that currently belongs to another team. + + Parameters + + * project: Project + + Returns Promise + duplicate: |- + ``` + duplicate(options?: { name?: string }): Promise + ``` + + Duplicates the file in its project. Requires `content:write`. + + `name` defaults to the name the backend chooses. + + Parameters + + * options: { name?: string } + + Returns Promise + remove: |- + ``` + remove(): Promise + ``` + + Moves the file to Trash. Requires `manage:delete`. + It can be restored during the retention period. Cannot permanently delete. + + Returns Promise +PenpotMgmt: + overview: |- + Interface PenpotMgmt + ==================== + + Application management API exposed as `penpotMgmt` to global plugins. + + * Lists teams, projects and files, and opens files, from the dashboard or a + file workspace. Teams, projects and files are objects with their own + properties and methods. + * Listing and opening require `content:read`; teams require `manage:teams`. + * File operations on `penpot` require a ready workspace. + * Reacquire file, page and shape objects after changing files. + + ``` + interface PenpotMgmt { + workspace: WorkspaceContext; + listTeams(): Promise; + createTeam(options: { name: string }): Promise; + listProjects(options?: { teamId?: string }): Promise; + listDeletedProjects(options?: { teamId?: string }): Promise; + listDeletedFiles(options?: { teamId?: string }): Promise; + getFile(fileId: string): Promise; + openFile(fileId: string, options?: { teamId?: string }): Promise; + on( + type: "workspacechange", + callback: (workspace: WorkspaceContext) => void, + ): symbol; + off(listenerId: symbol): void; + } + ``` + + Referenced by: Context + members: + Properties: + workspace: |- + ``` + readonly workspace: WorkspaceContext + ``` + + Current workspace availability and metadata; usable for file operations when status is `ready`. + Methods: + listTeams: |- + ``` + listTeams(): Promise + ``` + + Lists the teams of the user. Requires `manage:teams`. + + Returns Promise + + Example + ``` + const teams = await penpotMgmt.listTeams(); + ``` + createTeam: |- + ``` + createTeam(options: { name: string }): Promise + ``` + + Creates a team with the user as owner. Requires `manage:teams`. + + * Trims `name` and requires 1 to 250 characters. + * Does not change navigation. + + Parameters + + * options: { name: string } + + Returns Promise + + Example + ``` + const team = await penpotMgmt.createTeam({ name: 'Marketing' }); + ``` + listProjects: |- + ``` + listProjects(options?: { teamId?: string }): Promise + ``` + + Lists the projects of a team. Requires `content:read`. + + * `teamId` defaults to the current team. + * Rejects invalid identifiers and backend access errors. + + Parameters + + * options: { teamId?: string } + + Returns Promise + + Example + ``` + const projects = await penpotMgmt.listProjects(); + ``` + listDeletedProjects: |- + ``` + listDeletedProjects(options?: { teamId?: string }): Promise + ``` + + Lists projects still recoverable from Trash. Requires `content:read`. + `teamId` defaults to the current team. Use `Project.restore` to recover them. + + Parameters + + * options: { teamId?: string } + + Returns Promise + listDeletedFiles: |- + ``` + listDeletedFiles(options?: { teamId?: string }): Promise + ``` + + Lists files still recoverable from Trash. Requires `content:read`. + `teamId` defaults to the current team. Use `ProjectFile.restore` to recover them. + + Parameters + + * options: { teamId?: string } + + Returns Promise + getFile: |- + ``` + getFile(fileId: string): Promise + ``` + + Loads a file by id, open or not. Requires `content:read`. + + Parameters + + * fileId: string + + Returns Promise + + Example + ``` + const file = await penpotMgmt.getFile(penpotMgmt.workspace.fileId!);file.name = 'Checkout'; + ``` + openFile: |- + ``` + openFile(fileId: string, options?: { teamId?: string }): Promise + ``` + + Opens a file in this tab and resolves once its workspace and page are ready. + + * `teamId` defaults to the current team. + * Rejects invalid identifiers, failed or superseded navigation, and waits exceeding 30 seconds. + + Parameters + + * fileId: string + * options: { teamId?: string } + + Returns Promise + + Example + ``` + await penpotMgmt.openFile(file.id, { teamId: file.teamId }); + ``` + on: |- + ``` + on( + type: "workspacechange", + callback: (workspace: WorkspaceContext) => void, + ): symbol + ``` + + Subscribes to workspace availability and metadata changes. + + Parameters + + * type: "workspacechange" + * callback: (workspace: WorkspaceContext) => void + + Returns symbol + off: |- + ``` + off(listenerId: symbol): void + ``` + + Parameters + + * listenerId: symbol + Returns void Context: overview: |- @@ -5011,6 +5600,7 @@ Context: ``` interface Context { + management?: Omit; version: string; root: Shape | null; currentFile: File | null; @@ -5082,6 +5672,13 @@ Context: ``` members: Properties: + management: |- + ``` + readonly management?: Omit + ``` + + Backs `penpotMgmt`. Its events go through the plugin listener registry, + so `on` and `off` are not part of it. version: |- ``` readonly version: string @@ -7109,6 +7706,8 @@ EventsMap: ``` interface EventsMap { + workspacechange: WorkspaceContext; + logout: void; pagechange: Page; filechange: File; selectionchange: string[]; @@ -7122,6 +7721,14 @@ EventsMap: Referenced by: Context, Penpot members: Properties: + workspacechange: |- + ``` + readonly workspacechange: WorkspaceContext + ``` + logout: |- + ``` + readonly logout: void + ``` pagechange: |- ``` readonly pagechange: Page diff --git a/mcp/packages/server/data/initial_instructions.md b/mcp/packages/server/data/initial_instructions.md index be9177619c..e759903a7f 100644 --- a/mcp/packages/server/data/initial_instructions.md +++ b/mcp/packages/server/data/initial_instructions.md @@ -24,6 +24,22 @@ So if you need to read the result of your executions/observe properties affected before trying to observe changes. Every `execude_code` call waits for updates before applying your code, so you never need to call `waitForLayoutUpdate` at the beginning of your code. +## Rejected Changes and Read-Only Mode + +Penpot saves your changes to the backend after your `execute_code` call returns. +When the backend rejects a save, Penpot undoes the rejected changes, together with the rest of their undo steps +and any later change to the same file. This can happen a few seconds after the call that made the changes succeeded. +The next tool response then starts with a warning that names the files, pages and shapes involved. +When you get this warning: + * Do not trust what you know about the design from before: shapes you created may be gone, and removed ones may be back. + * Read the affected pages and shapes again (e.g. `penpotUtils.shapeStructure(page.root, 3)`) and check that they hold what you expect. + * Redo the missing changes, then check again; if the same changes are rejected again, stop and tell the user. + +While saves are failing, the workspace becomes read-only: every write through the API throws an error saying that the +workspace is read-only because changes cannot be saved. Do not retry in a loop; wait and try again later, or tell the user. +Writes also throw when the user cannot edit the file, a version is being previewed or the canvas stopped rendering; +the error says which. These do not pass by waiting: tell the user. + # The Structure of Penpot Designs A Penpot design ultimately consists of shapes. @@ -166,13 +182,15 @@ Boards can have layout systems that automatically control the positioning and sp # The `penpot` and `penpotUtils` Objects, Exploring Designs -A key object to use in your code is the `penpot` object (which is of type `Penpot`): +A key object for navigating the currently active Penpot file is the `penpot` object (which is of type `Penpot`): * `penpot.selection` provides the list of shapes the user has selected in the Penpot UI. If it is unclear which elements to work on, you can ask the user to select them for you. ALWAYS immediately copy the selected shape(s) into `storage`! Do not assume that the selection remains unchanged. * `penpot.root` provides the root shape of the currently active page. * Generation of CSS content for elements via `penpot.generateStyle` * Generation of HTML/SVG content for elements via `penpot.generateMarkup` +Members of `penpot` (e.g. `penpot.currentFile` and `penpot.root`) will be null no Penpot file is currently opened. +You can use `penpotMgmt` to navigate to a file if needed (see below). For example, to generate CSS for the currently selected elements, you can execute this: return penpot.generateStyle(penpot.selection, { type: "css", withChildren: true }); @@ -430,5 +448,23 @@ For many tasks, it can be critical to visually inspect the design. Remember to u * The use of flex layouts is encouraged for cases where elements are arranged in rows or columns with consistent spacing/positioning. Consider converting boards to flex layout when appropriate. +# The `penpotMgmt` Object + +This object (of type `PenpotMgmt`) serves to manage Penpot design projects and files. +Teams (`Team`), projects (`Project`) and files (`ProjectFile`) are objects with their own properties and methods: + * `await penpotMgmt.listProjects()` lists the projects of the current team; `await penpotMgmt.listTeams()` lists all teams. + * `await project.listFiles()` lists a project's files; `await penpotMgmt.getFile(id)` loads any file by id, e.g. `penpotMgmt.workspace.fileId`. + * Create with `await team.createProject({ name: "Design system" })` and `await project.createFile({ name: "Login" })`. + * Rename by assigning `name`; pin a project with `project.pinned = true`; share a file as a team library with `file.shared = true`. + * `moveTo`, `duplicate` and `remove` move, copy and delete projects and files; `penpotMgmt.createTeam({ name })` creates teams. Team deletion is not exposed. + * Project and file `remove()` moves them to Trash. There is no API for their permanent deletion. Discover recoverable objects with `await penpotMgmt.listDeletedProjects()` and `await penpotMgmt.listDeletedFiles()` (optionally pass `{ teamId }`), or `team.listDeletedProjects()`, `team.listDeletedFiles()` and `project.listDeletedFiles()`. + * Restore with `await project.restore()` or `await file.restore()` before the retention period expires. Restoration uses the dashboard's Trash operation and requires team edit access. Project restoration restores its recoverable files and rejects if none remain, including empty projects. File restoration also restores its deleted parent project. + * None of these change the current view. To edit a file, call `await file.open()` (or `await penpotMgmt.openFile(fileId)`) and reacquire file, page and shape objects. + * Connect a shared library to the open file with `penpot.library.connectLibrary(file.id)`. +You navigate to different design files only when your task explicitly requires it/you are instructed to do so. +Only rename teams or rename, move, delete or restore projects and files when the user asks for it. +If `penpotMgmt` is `null`, file management is unavailable and only the file that is open in Penpot can be used; +ask the user to open another file if needed. + -- You have hereby read the 'Penpot High-Level Overview' and need not use a tool to read it again. diff --git a/mcp/packages/server/package.json b/mcp/packages/server/package.json index 198b964a76..0bd3d8ae01 100644 --- a/mcp/packages/server/package.json +++ b/mcp/packages/server/package.json @@ -12,7 +12,7 @@ "start:multi-user": "node dist/index.js --multi-user", "start:dev": "node --import ts-node/register src/index.ts", "start:dev:multi-user": "node --loader ts-node/esm src/index.ts --multi-user", - "test": "tsx --test src/*.test.ts", + "test": "tsx --test \"src/**/*.test.ts\"", "test:integration:export-sema": "tsx scripts/integration-test-export-image-semaphore.ts", "types:check": "tsc --noEmit", "clean": "rm -rf dist/" @@ -25,9 +25,9 @@ "author": "", "license": "MIT", "engines": { - "node": ">=20" + "node": ">=20.20.2" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "dependencies": { "@modelcontextprotocol/node": "^2.1.1", "@modelcontextprotocol/server": "^2.3.1", diff --git a/mcp/packages/server/src/Feedback.test.ts b/mcp/packages/server/src/Feedback.test.ts new file mode 100644 index 0000000000..be92aac8ea --- /dev/null +++ b/mcp/packages/server/src/Feedback.test.ts @@ -0,0 +1,240 @@ +import assert from "node:assert/strict"; +import { createServer, type Server } from "node:http"; +import { once } from "node:events"; +import { afterEach, beforeEach, test } from "node:test"; +import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client"; +import { PenpotMcpServer } from "./PenpotMcpServer"; +import { PenpotBackendClient } from "./PenpotBackendClient"; + +let previousEnv: NodeJS.ProcessEnv; +let backend: Server; +let backendUri: string; +let server: PenpotMcpServer | undefined; +let client: Client | undefined; +let requests: { url: string; authorization?: string; client?: string; body: unknown }[]; +let responseStatus: number; +let nextPort = 17_700; + +beforeEach(async () => { + previousEnv = { ...process.env }; + process.env.PENPOT_MCP_SERVER_HOST = "127.0.0.1"; + process.env.PENPOT_MCP_SERVER_PORT = String(nextPort++); + process.env.PENPOT_MCP_WEBSOCKET_PORT = "0"; + process.env.PENPOT_MCP_DEVENV = "false"; + process.env.PENPOT_MCP_REPL_ENABLE = "false"; + delete process.env.PENPOT_MCP_REDIS_URI; + delete process.env.PENPOT_MCP_BACKEND_URI; + requests = []; + responseStatus = 200; + backend = createServer(async (req, res) => { + let body = ""; + for await (const chunk of req) body += chunk; + requests.push({ + url: req.url!, + authorization: req.headers.authorization, + client: req.headers["x-client"] as string, + body: JSON.parse(body), + }); + res.writeHead(responseStatus, { + "content-type": "application/json", + ...(responseStatus === 307 ? { location: `${backendUri}/redirected` } : {}), + }); + res.end(JSON.stringify({ status: "queued", detail: "private backend diagnostics" })); + }); + backend.listen(0, "127.0.0.1"); + await once(backend, "listening"); + const address = backend.address(); + assert.ok(address && typeof address === "object"); + backendUri = `http://127.0.0.1:${address.port}/penpot`; +}); + +afterEach(async () => { + await client?.close(); + await server?.stop(); + client = undefined; + server = undefined; + backend.closeAllConnections(); + await new Promise((resolve, reject) => backend.close((error) => (error ? reject(error) : resolve()))); + process.env = previousEnv; +}); + +async function connect(multiUser: boolean, token?: string) { + server = new PenpotMcpServer(multiUser); + await server.start(); + client = new Client({ name: "feedback-test", version: "1" }); + const url = new URL(`http://127.0.0.1:${server.port}/mcp`); + if (token) url.searchParams.set("userToken", token); + await client.connect(new StreamableHTTPClientTransport(url)); + return client; +} + +const feedback = { subject: "Export fails", feedback: "Expected a PNG, received an error." }; + +/** + * Collects all `pattern` values in a JSON schema. + */ +function schemaPatterns(schema: unknown): string[] { + if (Array.isArray(schema)) { + return schema.flatMap(schemaPatterns); + } + if (schema === null || typeof schema !== "object") { + return []; + } + return Object.entries(schema).flatMap(([key, value]) => + key === "pattern" && typeof value === "string" ? [value] : schemaPatterns(value) + ); +} + +for (const multiUser of [false, true]) { + test(`feedback is absent without a backend URI (multi-user=${multiUser})`, async () => { + const connected = await connect(multiUser, "alice"); + assert.ok(!(await connected.listTools()).tools.some((tool) => tool.name === "submit_feedback")); + }); + + test(`feedback works without a plugin (multi-user=${multiUser})`, async () => { + process.env.PENPOT_MCP_BACKEND_URI = backendUri; + const connected = await connect(multiUser, "alice"); + const tool = (await connected.listTools()).tools.find((tool) => tool.name === "submit_feedback"); + assert.ok(tool); + assert.deepEqual(tool.inputSchema.required, ["subject", "feedback"]); + // advertised patterns must also compile outside ECMAScript (ChatGPT uses Python) + const patterns = schemaPatterns(tool.inputSchema); + assert.ok(patterns.length > 0); + assert.ok(!patterns.some((pattern) => /\\[pP]\{/.test(pattern)), patterns.join(", ")); + const result = await connected.callTool({ name: "submit_feedback", arguments: feedback }); + assert.deepEqual(result.content, [ + { type: "text", text: "Feedback queued. It is emailed in the background, so delivery is not confirmed." }, + ]); + assert.deepEqual(requests, [ + { + url: "/penpot/api/main/methods/send-mcp-feedback", + authorization: "Token alice", + client: "penpot-mcp", + body: feedback, + }, + ]); + }); + + test(`missing credentials leave the tool listed but prevent submission (multi-user=${multiUser})`, async () => { + process.env.PENPOT_MCP_BACKEND_URI = backendUri; + const connected = await connect(multiUser); + assert.ok((await connected.listTools()).tools.some((tool) => tool.name === "submit_feedback")); + const result = await connected.callTool({ name: "submit_feedback", arguments: feedback }); + assert.match(JSON.stringify(result.content), /userToken.*MCP.*URL/); + assert.equal(requests.length, 0); + }); +} + +test("blank backend URI does not enable feedback", async () => { + process.env.PENPOT_MCP_BACKEND_URI = " "; + const connected = await connect(false); + assert.ok(!(await connected.listTools()).tools.some((tool) => tool.name === "submit_feedback")); +}); + +test("rejects invalid input without contacting the backend", async () => { + process.env.PENPOT_MCP_BACKEND_URI = backendUri; + const connected = await connect(false, "alice"); + for (const args of [ + { ...feedback, subject: "" }, + { ...feedback, subject: " " }, + { ...feedback, subject: "a\r\nBcc: other@example.com" }, + { ...feedback, subject: "a\tb" }, + { ...feedback, subject: "a\u0085b" }, + { ...feedback, feedback: "a\u0000b" }, + { ...feedback, feedback: "a\u001bb" }, + { ...feedback, subject: "x".repeat(121) }, + { ...feedback, feedback: " \n" }, + { ...feedback, feedback: "x".repeat(5001) }, + ]) { + // SDK versions may return a tool error or reject with a protocol error. + try { + const result = await connected.callTool({ name: "submit_feedback", arguments: args }); + assert.equal(result.isError, true); + } catch (error) { + assert.match(String(error), /invalid|validation/i); + } + } + assert.equal(requests.length, 0); +}); + +test("preserves deployment prefixes with and without a trailing slash", async () => { + for (const uri of [backendUri, backendUri + "/"]) { + await new PenpotBackendClient(uri).submitFeedback("alice", feedback); + } + assert.deepEqual( + requests.map((request) => request.url), + ["/penpot/api/main/methods/send-mcp-feedback", "/penpot/api/main/methods/send-mcp-feedback"] + ); +}); + +test("keeps credentials isolated across concurrent submissions", async () => { + const backendClient = new PenpotBackendClient(backendUri); + await Promise.all( + ["alice", "bob"].map((token) => + backendClient.submitFeedback(token, { + subject: token, + feedback: "A concrete problem.", + }) + ) + ); + assert.deepEqual( + requests + .map((request) => ({ + authorization: request.authorization, + subject: (request.body as typeof feedback).subject, + })) + .sort((a, b) => a.subject.localeCompare(b.subject)), + [ + { authorization: "Token alice", subject: "alice" }, + { authorization: "Token bob", subject: "bob" }, + ] + ); +}); + +for (const status of [400, 401, 403, 404, 429, 503, 500, 307]) { + test(`does not retry or expose response bodies on status ${status}`, async () => { + responseStatus = status; + const backendClient = new PenpotBackendClient(backendUri); + await assert.rejects(backendClient.submitFeedback("secret-token", feedback), (error: Error) => { + assert.doesNotMatch(error.message, /private backend diagnostics|secret-token/); + return true; + }); + assert.equal(requests.length, 1); + }); +} + +test("bounds network waits and does not retry an uncertain submission", async (t) => { + backend.removeAllListeners("request"); + let count = 0; + backend.on("request", (_req, _res) => { + count++; + }); + const timeout = AbortSignal.timeout.bind(AbortSignal); + t.mock.method(AbortSignal, "timeout", (milliseconds: number) => { + assert.equal(milliseconds, 10_000); + return timeout(50); + }); + await assert.rejects( + new PenpotBackendClient(backendUri).submitFeedback("secret-token", feedback), + /could not be confirmed.*Do not retry automatically/ + ); + assert.ok(count <= 1); +}); + +test("rejects invalid backend configuration without echoing credentials", () => { + for (const uri of [ + "invalid", + "file:///tmp", + "https://user:secret@example.com", + "https://example.com/?secret=value", + "https://example.com/#fragment", + ]) { + assert.throws( + () => new PenpotBackendClient(uri), + (error: Error) => { + assert.doesNotMatch(error.message, /secret|value|user:/); + return true; + } + ); + } +}); diff --git a/mcp/packages/server/src/McpAuthentication.test.ts b/mcp/packages/server/src/McpAuthentication.test.ts new file mode 100644 index 0000000000..715e60fdc9 --- /dev/null +++ b/mcp/packages/server/src/McpAuthentication.test.ts @@ -0,0 +1,81 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import express from "express"; +import { McpAuthentication } from "./McpAuthentication"; + +test("OAuth is opt-in and requires multi-user mode", () => { + assert.equal(new McpAuthentication(undefined, false).isOAuthEnabled, false); + assert.equal(new McpAuthentication("", true).isOAuthEnabled, false); + assert.throws(() => new McpAuthentication("https://penpot.example/api/mcp/oauth", false), /multi-user/); + for (const uri of [ + "not a URL", + "http://remote.example/auth", + "https://user:secret@example.com/auth", + "https://example.com/auth?x=1", + "https://example.com/auth#fragment", + ]) { + assert.throws(() => new McpAuthentication(uri, true), /PENPOT_MCP_AUTH_URI/); + } + assert.equal(new McpAuthentication("http://localhost:3449/api/mcp/oauth", true).isOAuthEnabled, true); +}); + +async function withEndpoint(uri: string | undefined, run: (base: string) => Promise) { + const auth = new McpAuthentication(uri, true); + const app = express(); + app.all("/mcp", (req, res) => { + const result = auth.authenticate(req, res); + if (!result.isRejected) res.json({ token: result.userToken ?? null }); + }); + const server = app.listen(0, "127.0.0.1"); + await new Promise((resolve) => server.once("listening", resolve)); + const address = server.address(); + assert.ok(address && typeof address !== "string"); + try { + await run(`http://127.0.0.1:${address.port}/mcp`); + } finally { + await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve()))); + } +} + +test("missing credentials challenge the client only when OAuth is enabled", async () => { + await withEndpoint(undefined, async (base) => { + const response = await fetch(base); + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { token: null }); + }); + await withEndpoint("https://penpot.example/api/mcp/oauth/", async (base) => { + const response = await fetch(base); + assert.equal(response.status, 401); + assert.equal( + response.headers.get("www-authenticate"), + 'Bearer resource_metadata="https://penpot.example/api/mcp/oauth/resource", scope="mcp"' + ); + assert.equal(response.headers.get("cache-control"), "no-store"); + }); +}); + +test("bearer credentials take precedence and query credentials remain supported", async () => { + for (const uri of [undefined, "https://penpot.example/api/mcp/oauth"]) { + await withEndpoint(uri, async (base) => { + const bearer = await fetch(base + "?userToken=query-token", { + headers: { Authorization: "bEaReR header-token" }, + }); + assert.deepEqual(await bearer.json(), { token: "header-token" }); + const query = await fetch(base + "?userToken=query-token"); + assert.deepEqual(await query.json(), { token: "query-token" }); + }); + } +}); + +test("malformed credentials never fall back to another token", async () => { + await withEndpoint("https://penpot.example/api/mcp/oauth", async (base) => { + for (const header of ["Basic abc", "Bearer", "Bearer two tokens", "Bearer a, Bearer b"]) { + const response = await fetch(base + "?userToken=valid", { headers: { Authorization: header } }); + assert.equal(response.status, 401, header); + assert.deepEqual(await response.json(), { error: "invalid_token" }); + } + for (const query of ["?userToken=a&userToken=b", "?userToken=", "?userToken=a%20b"]) { + assert.equal((await fetch(base + query)).status, 401, query); + } + }); +}); diff --git a/mcp/packages/server/src/McpAuthentication.ts b/mcp/packages/server/src/McpAuthentication.ts new file mode 100644 index 0000000000..6df14f6733 --- /dev/null +++ b/mcp/packages/server/src/McpAuthentication.ts @@ -0,0 +1,120 @@ +import type { Request, Response } from "express"; + +/** + * Result of processing credentials from an MCP HTTP request. + */ +export class McpAuthenticationResult { + /** + * @param userToken the extracted user token, if present + * @param isRejected whether the request was rejected and its HTTP error response sent + */ + constructor( + public readonly userToken: string | undefined, + public readonly isRejected: boolean = false + ) {} +} + +/** + * HTTP credential extraction and optional OAuth discovery for MCP clients. + * + * Tokens retain their existing role of matching requests to Penpot connections. + */ +export class McpAuthentication { + public readonly isOAuthEnabled: boolean; + private readonly resourceMetadataUri?: string; + + /** + * Creates a new MCP authentication handler. + * + * @param authUri the authentication URI from PENPOT_MCP_AUTH_URI, or undefined/empty to disable OAuth + * @param multiUser whether the server is running in multi-user mode + */ + constructor(authUri: string | undefined, multiUser: boolean) { + if (!authUri?.trim()) { + this.isOAuthEnabled = false; + } else { + if (!multiUser) { + throw new Error("PENPOT_MCP_AUTH_URI (which enables OAuth) requires multi-user mode"); + } + + // validate the authentication URI + let uri: URL; + try { + uri = new URL(authUri); + } catch { + throw new Error("PENPOT_MCP_AUTH_URI must be an absolute HTTP(S) URL"); + } + const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(uri.hostname); + if ( + (uri.protocol !== "https:" && !(uri.protocol === "http:" && loopback)) || + uri.username || + uri.password || + uri.search || + uri.hash + ) { + throw new Error( + "PENPOT_MCP_AUTH_URI must use HTTPS (HTTP is allowed on loopback) without credentials, query, or fragment" + ); + } + + this.resourceMetadataUri = uri.href.replace(/\/$/, "") + "/resource"; + this.isOAuthEnabled = true; + } + } + + /** + * Extracts credentials. + * If credentials are absent while OAuth is enabled, sends an authentication error. + */ + public authenticate(req: Request, res: Response): McpAuthenticationResult { + let userToken: string | undefined; + + // check for a Bearer token in the Authorization header + const header = req.headers.authorization; + if (header !== undefined) { + const headerCount = req.rawHeaders.filter( + (_, i) => i % 2 === 0 && req.rawHeaders[i].toLowerCase() === "authorization" + ).length; + const match = /^Bearer +([A-Za-z0-9\-._~+/]+=*)$/i.exec(header); + if (headerCount !== 1 || !match) { + this.rejectOAuth(res, "invalid_token"); + return new McpAuthenticationResult(undefined, true); + } + userToken = match[1]; + } + // check for a userToken query parameter + else if (req.query.userToken !== undefined) { + const value = req.query.userToken; + if (typeof value !== "string" || !/^[A-Za-z0-9\-._~+/]+=*$/.test(value)) { + this.rejectOAuth(res, "invalid_token"); + return new McpAuthenticationResult(undefined, true); + } + userToken = value; + } + + // NOTE: The MCP server does not validate userToken against the backend. + // It uses the token to match MCP requests to a connection established + // from the Penpot webapp. Without that matching connection, requests + // cannot access or modify designs. The user must be logged in to Penpot + // to establish that connection; Penpot login and file permissions thus + // provide the authentication and access control boundary. + + if (this.isOAuthEnabled && !userToken) { + this.rejectOAuth(res); + return new McpAuthenticationResult(undefined, true); + } else { + return new McpAuthenticationResult(userToken); + } + } + + private rejectOAuth(res: Response, error?: "invalid_token"): void { + let challenge = "Bearer"; + if (this.resourceMetadataUri) { + challenge += ` resource_metadata="${this.resourceMetadataUri}", scope="mcp"`; + } + if (error) challenge += `${this.resourceMetadataUri ? "," : ""} error="${error}"`; + res.setHeader("WWW-Authenticate", challenge); + res.setHeader("Cache-Control", "no-store"); + res.status(401).json({ error: error ?? "unauthorized" }); + } +} diff --git a/mcp/packages/server/src/PenpotBackendClient.ts b/mcp/packages/server/src/PenpotBackendClient.ts new file mode 100644 index 0000000000..07bebddf97 --- /dev/null +++ b/mcp/packages/server/src/PenpotBackendClient.ts @@ -0,0 +1,86 @@ +import { createLogger } from "./logger"; + +/** A plain-text MCP feedback report. */ +export interface Feedback { + subject: string; + feedback: string; +} + +/** Backend operations available to MCP tools. */ +export interface PenpotBackend { + submitFeedback(userToken: string, feedback: Feedback): Promise; +} + +/** Client for the configured Penpot instance's backend services. */ +export class PenpotBackendClient implements PenpotBackend { + private readonly logger = createLogger("PenpotBackendClient"); + private readonly baseUri: URL; + + constructor(baseUri: string) { + try { + this.baseUri = new URL(baseUri); + } catch { + throw new Error("PENPOT_MCP_BACKEND_URI must be an absolute HTTP(S) instance URI."); + } + if ( + !["http:", "https:"].includes(this.baseUri.protocol) || + this.baseUri.username || + this.baseUri.password || + this.baseUri.search || + this.baseUri.hash + ) { + throw new Error( + "PENPOT_MCP_BACKEND_URI must be an HTTP(S) instance URI without credentials, query or fragment." + ); + } + if (!this.baseUri.pathname.endsWith("/")) this.baseUri.pathname += "/"; + } + + public async submitFeedback(userToken: string, feedback: Feedback): Promise { + await this.callRpc("send-mcp-feedback", userToken, feedback); + } + + /** Calls a backend RPC method whose result body is not needed. */ + private async callRpc(methodName: string, userToken: string, params: object): Promise { + let response: Response; + try { + response = await fetch(new URL(`api/main/methods/${encodeURIComponent(methodName)}`, this.baseUri), { + method: "POST", + headers: { + Authorization: `Token ${userToken}`, + "X-Client": "penpot-mcp", + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(params), + redirect: "manual", + signal: AbortSignal.timeout(10_000), + }); + // discard backend diagnostics, which may contain private data + await response.body?.cancel(); + } catch (error) { + this.logger.error(error, "Penpot RPC request failed"); + throw new Error("The backend operation could not be confirmed. Do not retry automatically."); + } + + if (response.ok) return; + + switch (response.status) { + case 401: + throw new Error( + "The backend did not accept the MCP user token. It may be outdated, " + + "or the Penpot instance may not have access tokens enabled." + ); + case 403: + throw new Error("The MCP user token is not authorized for this backend operation."); + case 400: + throw new Error("The backend rejected the request parameters or the operation is disabled."); + case 429: + throw new Error("The backend request limit has been reached. Do not retry automatically."); + default: + throw new Error( + `The backend operation could not be confirmed (HTTP ${response.status}). Do not retry automatically.` + ); + } + } +} diff --git a/mcp/packages/server/src/PenpotConnection.ts b/mcp/packages/server/src/PenpotConnection.ts new file mode 100644 index 0000000000..902f248a48 --- /dev/null +++ b/mcp/packages/server/src/PenpotConnection.ts @@ -0,0 +1,24 @@ +import type { WebSocket } from "ws"; +import type { PenpotSession } from "@penpot/mcp-common"; + +/** + * Observable liveness state of a plugin connection. + */ +export interface PluginLivenessState { + /** timestamp of the last plugin message, in ms since epoch. */ + lastHeartbeat: number; + /** whether the plugin reported a browser freeze. */ + frozen: boolean; +} + +/** One local Penpot WebSocket connection and its session metadata. */ +export interface PenpotConnection extends PluginLivenessState { + readonly socket: WebSocket; + readonly userToken: string | null; + readonly session: PenpotSession; + readonly pingInterval: NodeJS.Timeout; + /** the interval sending heartbeat requests to the plugin. */ + readonly heartbeatRequestInterval: NodeJS.Timeout; + /** whether initialization and dispatch subscriptions have completed. */ + ready: boolean; +} diff --git a/mcp/packages/server/src/PenpotMcpServer.http.test.ts b/mcp/packages/server/src/PenpotMcpServer.http.test.ts index e738a49a2d..d5534de879 100644 --- a/mcp/packages/server/src/PenpotMcpServer.http.test.ts +++ b/mcp/packages/server/src/PenpotMcpServer.http.test.ts @@ -16,6 +16,7 @@ beforeEach(async () => { process.env.PENPOT_MCP_DEVENV = "false"; process.env.PENPOT_MCP_REPL_ENABLE = "false"; delete process.env.PENPOT_MCP_REDIS_URI; + delete process.env.PENPOT_MCP_AUTH_URI; server = new PenpotMcpServer(true); baseUrl = `http://127.0.0.1:${server.port}`; await server.start(); @@ -26,7 +27,7 @@ afterEach(async () => { process.env = previousEnv; }); -async function modernRequest(method: string, params: Record = {}, query = "") { +async function modernRequest(method: string, params: Record = {}, query = "", bearer?: string) { return fetch(`${baseUrl}/mcp${query}`, { method: "POST", headers: { @@ -34,6 +35,7 @@ async function modernRequest(method: string, params: Record = { Accept: "application/json, text/event-stream", "MCP-Protocol-Version": "2026-07-28", "Mcp-Method": method, + ...(bearer ? { Authorization: `Bearer ${bearer}` } : {}), ...(typeof params.name === "string" ? { "Mcp-Name": params.name } : {}), }, body: JSON.stringify({ @@ -93,7 +95,8 @@ test("isolates user tokens across overlapping tool calls with the same request I name: "execute_code", arguments: { code: "return 1;" }, }, - `?userToken=${token}` + token === "alice" ? "" : `?userToken=${token}`, + token === "alice" ? token : undefined ); const body = (await response.json()) as { result: CallToolResult }; assert.equal(response.status, 200, JSON.stringify(body)); @@ -106,6 +109,30 @@ test("isolates user tokens across overlapping tool calls with the same request I assert.equal(server.getSessionContext(), undefined); }); +test("passes an explicit Penpot session ID from the tool call to plugin dispatch", async (t) => { + t.mock.method(server.pluginBridge, "executePluginTask", async (_task: unknown, sessionId?: string) => ({ + data: sessionId, + })); + const response = await modernRequest( + "tools/call", + { + name: "execute_code", + arguments: { code: "return penpot.currentFile.id;", sessionId: "chosen-tab" }, + }, + "?userToken=alice" + ); + assert.equal(response.status, 200); + const body = await response.json(); + assert.match(JSON.stringify(body), /chosen-tab/); +}); + +test("accepts tool calls with request bodies larger than 100 KiB", async (t) => { + t.mock.method(server.pluginBridge, "executePluginTask", async () => ({ data: "ok" })); + const code = `return "${"x".repeat(200_000)}".length;`; + const response = await modernRequest("tools/call", { name: "execute_code", arguments: { code } }); + assert.equal(response.status, 200); +}); + test("supports older Streamable HTTP clients without allocating a session", async () => { const client = new Client({ name: "legacy-test", version: "1" }); const transport = new StreamableHTTPClientTransport(new URL(`${baseUrl}/mcp`)); @@ -118,3 +145,117 @@ test("supports older Streamable HTTP clients without allocating a session", asyn await client.close(); } }); + +test("OAuth challenges missing credentials and accepts bearer and query tokens", async () => { + await server.stop(); + process.env.PENPOT_MCP_AUTH_URI = "https://penpot.example/api/mcp/oauth"; + server = new PenpotMcpServer(true); + await server.start(); + + const missing = await modernRequest("tools/list"); + assert.equal(missing.status, 401); + assert.match(missing.headers.get("www-authenticate") ?? "", /resource_metadata=/); + + for (const useBearer of [true, false]) { + const client = new Client({ name: "oauth-test", version: "1" }); + const transport = new StreamableHTTPClientTransport( + new URL(`${baseUrl}/mcp${useBearer ? "" : "?userToken=alice"}`), + useBearer ? { requestInit: { headers: { Authorization: "Bearer alice" } } } : undefined + ); + try { + await client.connect(transport); + const result = await client.listTools(); + const tool = result.tools.find((tool) => tool.name === "execute_code"); + assert.ok(tool, "authenticated clients can discover execute_code"); + } finally { + await client.close(); + } + } +}); + +/** + * Makes the plugin bridge answer each dispatched task at once with the given responses, in order. + */ +function answerTasks(t: { mock: any }, responses: Array>): void { + const bridge = server.pluginBridge as any; + t.mock.method(bridge.dispatcher, "dispatch", async (task: { id: string }) => { + bridge.pendingTasks.set(task.id, task); + bridge.handlePluginTaskResponse({ id: task.id, ...responses.shift() }); + }); +} + +async function callExecuteCode(): Promise { + const response = await modernRequest( + "tools/call", + { name: "execute_code", arguments: { code: "return 1;" } }, + "?userToken=alice" + ); + const body = (await response.json()) as { result: CallToolResult }; + assert.equal(response.status, 200, JSON.stringify(body)); + return body.result; +} + +const rollback = { fileIds: ["file-1"], pageIds: ["page-1"], shapeIds: ["shape-1", "shape-2"] }; + +test("puts a warning first in the tool response that reports a rollback", async (t) => { + answerTasks(t, [ + { success: true, data: { result: 1, log: "" }, rollbacks: [rollback] }, + { success: true, data: { result: 2, log: "" } }, + ]); + + const first = await callExecuteCode(); + const second = await callExecuteCode(); + + assert.equal(first.content.length, 2); + const [notice, data] = first.content; + assert.equal(notice.type, "text"); + assert.match(notice.text, /CHANGES WERE ROLLED BACK/); + assert.match(notice.text, /Pages: page-1/); + assert.match(notice.text, /Shapes: shape-1, shape-2/); + assert.equal(data.type, "text"); + assert.equal(JSON.parse(data.text).result, 1); + + assert.equal(second.content.length, 1); +}); + +test("puts a rollback warning first in a failed tool response too", async (t) => { + answerTasks(t, [{ success: false, error: "Shape not found", rollbacks: [rollback] }]); + + const result = await callExecuteCode(); + + assert.equal(result.content.length, 2); + const [notice, failure] = result.content; + assert.equal(notice.type, "text"); + assert.match(notice.text, /CHANGES WERE ROLLED BACK/); + assert.equal(failure.type, "text"); + assert.match(failure.text, /Tool execution failed: .*Shape not found/); +}); + +/** + * A plugin connection whose socket keeps the messages sent to it. + */ +function fakeConnection(): { socket: { readyState: number; send: (message: string) => void }; sent: unknown[] } { + const sent: unknown[] = []; + return { socket: { readyState: 1, send: (message: string) => sent.push(JSON.parse(message)) }, sent }; +} + +test("gives the rollbacks of a response that came after its task timed out back to the plugin", () => { + const bridge = server.pluginBridge as any; + const connection = fakeConnection(); + + bridge.handlePluginTaskResponse( + { id: "timed-out", success: true, data: { result: 1, log: "" }, rollbacks: [rollback] }, + connection + ); + + assert.deepEqual(connection.sent, [{ type: "unclaimed-rollbacks", rollbacks: [rollback] }]); +}); + +test("sends nothing back for a late response without rollbacks", () => { + const bridge = server.pluginBridge as any; + const connection = fakeConnection(); + + bridge.handlePluginTaskResponse({ id: "timed-out", success: true, data: { result: 1, log: "" } }, connection); + + assert.deepEqual(connection.sent, []); +}); diff --git a/mcp/packages/server/src/PenpotMcpServer.ts b/mcp/packages/server/src/PenpotMcpServer.ts index 73ffd83a9f..56f7ac5b2c 100644 --- a/mcp/packages/server/src/PenpotMcpServer.ts +++ b/mcp/packages/server/src/PenpotMcpServer.ts @@ -8,20 +8,23 @@ import { ExecuteCodeTool } from "./tools/ExecuteCodeTool"; import { PluginBridge } from "./PluginBridge"; import { RedisBridge } from "./RedisBridge"; import { ConfigurationLoader } from "./ConfigurationLoader"; +import { PenpotBackendClient } from "./PenpotBackendClient"; +import { SubmitFeedbackTool } from "./tools/SubmitFeedbackTool"; import { createLogger } from "./logger"; import { Tool } from "./Tool"; import { HighLevelOverviewTool } from "./tools/HighLevelOverviewTool"; import { PenpotApiInfoTool } from "./tools/PenpotApiInfoTool"; import { ExportShapeTool } from "./tools/ExportShapeTool"; import { ImportImageTool } from "./tools/ImportImageTool"; -import { CljsReplTool } from "./tools/CljsReplTool"; -import { ImportPenpotFileTool } from "./tools/ImportPenpotFileTool"; -import { CljsCompilerOutputTool } from "./tools/CljsCompilerOutputTool"; -import { CljCheckParentheses } from "./tools/CljCheckParentheses"; -import { ReadTaigaIssueTool } from "./tools/ReadTaigaIssueTool"; +import { CljsReplTool } from "./tools/dev/CljsReplTool"; +import { ImportPenpotFileTool } from "./tools/dev/ImportPenpotFileTool"; +import { CljsCompilerOutputTool } from "./tools/dev/CljsCompilerOutputTool"; +import { CljCheckParentheses } from "./tools/dev/CljCheckParentheses"; +import { ReadTaigaIssueTool } from "./tools/dev/ReadTaigaIssueTool"; import { NreplClient } from "./NreplClient"; import { ReplServer } from "./ReplServer"; import { ApiDocs } from "./ApiDocs"; +import { McpAuthentication } from "./McpAuthentication"; /** * Session context for request-scoped data. @@ -111,6 +114,7 @@ export class PenpotMcpServer { private app!: Express; private httpServer?: HttpServer; private readonly mcpHandler: McpHttpHandler; + private readonly authentication: McpAuthentication; public readonly pluginBridge: PluginBridge; private readonly replServer: ReplServer | null; private apiDocs: ApiDocs; @@ -143,7 +147,16 @@ export class PenpotMcpServer { */ private readonly tenant: string; + /** + * Maximum size of a JSON request body, read from the `PENPOT_MCP_MAX_BODY_SIZE` environment variable. + * + * Accepts any value supported by Express' `json` body parser (e.g. `"10mb"`). Defaults to `"10mb"`, + * since tool calls such as `execute_code` can carry large payloads. + */ + private readonly maxBodySize: string; + constructor(private isMultiUser: boolean = false) { + this.authentication = new McpAuthentication(process.env.PENPOT_MCP_AUTH_URI, isMultiUser); // read port configuration from environment variables this.host = process.env.PENPOT_MCP_SERVER_HOST ?? "localhost"; this.port = parseInt(process.env.PENPOT_MCP_SERVER_PORT ?? "4401", 10); @@ -151,6 +164,7 @@ export class PenpotMcpServer { this.replHost = process.env.PENPOT_MCP_REPL_HOST ?? "localhost"; this.replPort = parseInt(process.env.PENPOT_MCP_REPL_PORT ?? "4403", 10); this.tenant = process.env.PENPOT_TENANT ?? "default"; + this.maxBodySize = process.env.PENPOT_MCP_MAX_BODY_SIZE ?? "10mb"; const toolTimeoutSecs = parseInt(process.env.PENPOT_MCP_TOOL_TIMEOUT_S ?? "120", 10); this.configLoader = new ConfigurationLoader(process.cwd()); @@ -261,6 +275,10 @@ export class PenpotMcpServer { if (this.isFileSystemAccessEnabled()) { toolInstances.push(new ImportImageTool(this)); } + const backendUri = process.env.PENPOT_MCP_BACKEND_URI?.trim(); + if (backendUri) { + toolInstances.push(new SubmitFeedbackTool(this, new PenpotBackendClient(backendUri))); + } if (shouldRegisterDeveloperTools(this.isDevEnv(), this.isMultiUserMode())) { const nreplClient = new NreplClient(); toolInstances.push(new CljsReplTool(this, nreplClient)); @@ -298,24 +316,30 @@ export class PenpotMcpServer { private setupHttpEndpoints(): void { const handleMcpRequest = toNodeHandler(this.mcpHandler); this.app.all("/mcp", async (req, res) => { - const userToken = req.query.userToken as string | undefined; + const authResult = this.authentication.authenticate(req, res); + if (authResult.isRejected) { + return; + } this.logger.info( - `Received MCP request: method=${req.body?.method ?? ""}; userTokenFp=${PenpotMcpServer.tokenFingerprint(userToken)}` + `Received MCP request: method=${req.body?.method ?? ""}; userTokenFp=${PenpotMcpServer.tokenFingerprint(authResult.userToken)}` + ); + await this.sessionContext.run({ userToken: authResult.userToken }, () => + handleMcpRequest(req, res, req.body) ); - await this.sessionContext.run({ userToken }, () => handleMcpRequest(req, res, req.body)); }); } async start(): Promise { const { default: express } = await import("express"); this.app = express(); - this.app.use(express.json()); + this.app.use(express.json({ limit: this.maxBodySize })); this.setupHttpEndpoints(); return new Promise((resolve, reject) => { this.httpServer = this.app.listen(this.port, this.host, async () => { this.logger.info(`Multi-user mode: ${this.isMultiUserMode()}`); + this.logger.info(`OAuth enabled: ${this.authentication.isOAuthEnabled}`); this.logger.info( `Multi-instance mode with Redis-backed transport: ${this.redisBridge ? "true" : "false"}` ); diff --git a/mcp/packages/server/src/PluginBridge.sessions.test.ts b/mcp/packages/server/src/PluginBridge.sessions.test.ts new file mode 100644 index 0000000000..3e676725f2 --- /dev/null +++ b/mcp/packages/server/src/PluginBridge.sessions.test.ts @@ -0,0 +1,205 @@ +import assert from "node:assert/strict"; +import { once } from "node:events"; +import { afterEach, beforeEach, mock, test } from "node:test"; +import { WebSocket } from "ws"; +import { HEARTBEAT_REQUEST_INTERVAL_MS, PluginBridge } from "./PluginBridge"; +import { PluginTask } from "./PluginTask"; +import type { PenpotMcpServer } from "./PenpotMcpServer"; + +let bridge: PluginBridge; +let port = 17_600; +let userToken: string | undefined; +let multiUser: boolean; +let sockets: WebSocket[]; + +beforeEach(() => { + port++; + userToken = "alice"; + multiUser = true; + sockets = []; + bridge = new PluginBridge( + { + host: "127.0.0.1", + isMultiUserMode: () => multiUser, + getSessionContext: () => ({ userToken }), + } as PenpotMcpServer, + port, + 1 + ); +}); + +afterEach(async () => { + for (const socket of sockets) socket.terminate(); + await bridge.close(); +}); + +async function connect(sessionId: string, token = "alice", fileName = "Design", reply = sessionId) { + const socket = new WebSocket(`ws://127.0.0.1:${port}?userToken=${token}`); + sockets.push(socket); + await once(socket, "open"); + // answer tasks from the start, since one may arrive right behind the initialization + socket.on("message", (raw) => { + const request = JSON.parse(raw.toString()); + if (request.type !== "initialized" && request.type !== "heartbeat-request") { + socket.send(JSON.stringify({ id: request.id, success: true, data: reply })); + } + }); + const initialized = once(socket, "message", { signal: AbortSignal.timeout(1500) }); + socket.send(JSON.stringify({ type: "initialize", session: { sessionId, fileId: "file-1", fileName } })); + const [message] = await initialized; + assert.equal(JSON.parse(message.toString()).type, "initialized"); + return socket; +} + +test("asks each plugin for a heartbeat at a fixed interval", async () => { + mock.timers.enable({ apis: ["setInterval"] }); + try { + const socket = await connect("tab-1"); + const requested = once(socket, "message", { signal: AbortSignal.timeout(1500) }); + mock.timers.tick(HEARTBEAT_REQUEST_INTERVAL_MS); + const [message] = await requested; + assert.deepEqual(JSON.parse(message.toString()), { type: "heartbeat-request" }); + } finally { + mock.timers.reset(); + } +}); + +test("keeps two tabs for the same file and dispatches to the selected session", async () => { + await connect("tab-1"); + await connect("tab-2"); + const result = await bridge.executePluginTask(new PluginTask("test", {}), "tab-2"); + assert.equal(result.data, "tab-2"); +}); + +test("requires user selection when more than one session is connected", async () => { + await connect("tab-1", "alice", "First design"); + await connect("tab-2", "alice", "Second design"); + await assert.rejects(bridge.executePluginTask(new PluginTask("test", {})), (error: Error) => { + assert.match(error.message, /Ask the user/); + assert.match(error.message, /tab-1.*First design/); + assert.match(error.message, /tab-2.*Second design/); + return true; + }); +}); + +test("implicitly selects the only session belonging to the requesting user", async () => { + await connect("alice-tab"); + await connect("bob-tab", "bob"); + const result = await bridge.executePluginTask(new PluginTask("test", {})); + assert.equal(result.data, "alice-tab"); +}); + +test("does not dispatch to another user's session", async () => { + await connect("alice-tab"); + await connect("bob-tab", "bob"); + await assert.rejects(bridge.executePluginTask(new PluginTask("test", {}), "bob-tab"), /not connected/); +}); + +test("reports no sessions when no plugin has initialized", async () => { + await assert.rejects(bridge.executePluginTask(new PluginTask("test", {})), /No Penpot.*connected/); +}); + +test("regular plugins without user tokens can select among multiple sessions", async () => { + multiUser = false; + await connect("first", ""); + await connect("second", ""); + await assert.rejects(bridge.executePluginTask(new PluginTask("test", {})), /Ask the user/); + assert.equal((await bridge.executePluginTask(new PluginTask("test", {}), "second")).data, "second"); +}); + +test("disconnecting one session leaves its sibling available for implicit selection", async () => { + const first = await connect("first"); + await connect("second"); + const closed = once(first, "close"); + first.close(); + await closed; + assert.equal((await bridge.executePluginTask(new PluginTask("test", {}))).data, "second"); + await assert.rejects(bridge.executePluginTask(new PluginTask("test", {}), "first"), /not connected/); +}); + +test("a reconnect with the same session ID replaces the previous connection", async () => { + const previous = await connect("tab", "alice", "Design", "previous"); + const closed = once(previous, "close"); + await connect("tab", "alice", "Design", "current"); + const [code] = await closed; + assert.equal(code, 1008); + assert.equal((await bridge.executePluginTask(new PluginTask("test", {}))).data, "current"); + assert.equal((await bridge.executePluginTask(new PluginTask("test", {}), "tab")).data, "current"); +}); + +test("a half-open previous connection does not block a reconnect with the same session ID", async () => { + const previous = await connect("tab", "alice", "Design", "previous"); + // stop answering, as a socket whose network path is gone would + previous.pause(); + await connect("tab", "alice", "Design", "current"); + assert.equal((await bridge.executePluginTask(new PluginTask("test", {}), "tab")).data, "current"); +}); + +test("replacing a session does not affect another user's session with the same ID", async () => { + await connect("tab", "bob", "Design", "bob"); + await connect("tab", "alice", "Design", "alice"); + userToken = "bob"; + assert.equal((await bridge.executePluginTask(new PluginTask("test", {}), "tab")).data, "bob"); +}); + +test("uninitialized sockets are not candidates for discovery", async () => { + await connect("ready"); + const pending = new WebSocket(`ws://127.0.0.1:${port}?userToken=alice`); + sockets.push(pending); + await once(pending, "open"); + assert.equal((await bridge.executePluginTask(new PluginTask("test", {}))).data, "ready"); +}); + +test("invalid initialization metadata cannot register a session", async () => { + const socket = new WebSocket(`ws://127.0.0.1:${port}?userToken=alice`); + sockets.push(socket); + await once(socket, "open"); + const closed = once(socket, "close"); + socket.send(JSON.stringify({ type: "initialize", session: { sessionId: "bad", fileId: 42 } })); + assert.equal((await closed)[0], 1008); + await assert.rejects(bridge.executePluginTask(new PluginTask("test", {})), /No Penpot sessions/); +}); + +test("a dashboard session remains routable while its workspace metadata changes", async () => { + const socket = new WebSocket(`ws://127.0.0.1:${port}?userToken=alice`); + sockets.push(socket); + await once(socket, "open"); + const initialized = once(socket, "message"); + socket.send( + JSON.stringify({ + type: "initialize", + session: { sessionId: "tab", fileId: null, fileName: null, workspaceState: "none" }, + }) + ); + await initialized; + socket.on("message", (raw) => { + const request = JSON.parse(raw.toString()); + socket.send(JSON.stringify({ id: request.id, success: true, data: "ok" })); + }); + for (const context of [ + { fileId: "file-1", fileName: "Design", workspaceState: "ready" }, + { fileId: null, fileName: null, workspaceState: "none" }, + ]) { + socket.send(JSON.stringify({ type: "context-update", context })); + assert.equal((await bridge.executePluginTask(new PluginTask("test", {}), "tab")).data, "ok"); + assert.deepEqual(bridge.getUserConnections("alice")!.getSessions(), [{ sessionId: "tab", ...context }]); + } +}); + +test("a task fails at once when its plugin connection closes before answering", async () => { + const plugin = new WebSocket(`ws://127.0.0.1:${port}?userToken=alice`); + sockets.push(plugin); + await once(plugin, "open"); + const initialized = once(plugin, "message"); + plugin.send(JSON.stringify({ type: "initialize", session: { sessionId: "tab", fileId: null, fileName: null } })); + await initialized; + // the socket closes once the task reaches it + plugin.on("message", () => plugin.terminate()); + const started = Date.now(); + await assert.rejects( + bridge.executePluginTask(new PluginTask("test", {}), "tab"), + /connection closed before the task finished/ + ); + // well within the 1s task timeout of these tests + assert.ok(Date.now() - started < 500); +}); diff --git a/mcp/packages/server/src/PluginBridge.ts b/mcp/packages/server/src/PluginBridge.ts index 305f5dc35e..340016f44f 100644 --- a/mcp/packages/server/src/PluginBridge.ts +++ b/mcp/packages/server/src/PluginBridge.ts @@ -1,13 +1,33 @@ import { WebSocket, WebSocketServer } from "ws"; import * as http from "http"; -import { AbstractPluginTask, PluginTask } from "./PluginTask"; -import { RemotePluginTask } from "./RemotePluginTask"; -import { PluginTaskRequest, PluginTaskResponse, PluginTaskResult } from "@penpot/mcp-common"; +import { z } from "zod"; +import { AbstractPluginTask, PluginTask, PluginTaskError } from "./PluginTask"; +import { RollbackNotices } from "./RollbackNotices"; +import { + ContentRollbackEvent, + PluginTaskResponse, + PluginTaskResult, + UnclaimedRollbacksMessage, +} from "@penpot/mcp-common"; import { createLogger } from "./logger"; import type { PenpotMcpServer } from "./PenpotMcpServer"; import type { RedisBridge } from "./RedisBridge"; +import type { PenpotConnection, PluginLivenessState } from "./PenpotConnection"; +import { UserPenpotConnections } from "./UserPenpotConnections"; +import type { TaskDispatcher, TaskDispatchHost } from "./dispatch/TaskDispatcher"; +import { SingleInstanceTaskDispatcher } from "./dispatch/SingleInstanceTaskDispatcher"; +import { MultiInstanceTaskDispatcher } from "./dispatch/MultiInstanceTaskDispatcher"; const KEEP_ALIVE_TIME = 30000; // 30 seconds +const INITIALIZATION_TIMEOUT_MS = 10_000; + +/** + * The interval, in ms, between the heartbeat requests sent to each plugin. + * + * A browser runs the timers of a hidden page as rarely as once a minute, which would leave the + * plugin's own heartbeat older than the stale threshold, but it handles socket messages at once. + */ +export const HEARTBEAT_REQUEST_INTERVAL_MS = 10_000; /** * Maximum plugin heartbeat age before a connection is stale. @@ -17,21 +37,15 @@ const KEEP_ALIVE_TIME = 30000; // 30 seconds */ export const HEARTBEAT_STALE_THRESHOLD_MS = 30000; -/** - * Observable liveness state of a plugin connection. - */ -export interface PluginLivenessState { - /** timestamp of the last plugin message, in ms since epoch. */ - lastHeartbeat: number; - /** whether the plugin reported a browser freeze. */ - frozen: boolean; -} - -interface ClientConnection extends PluginLivenessState { - socket: WebSocket; - userToken: string | null; - pingInterval: NodeJS.Timeout; -} +const connectionInitSchema = z.object({ + type: z.literal("initialize"), + session: z.object({ + sessionId: z.string().min(1).max(128), + fileId: z.string().min(1).max(128).nullable(), + fileName: z.string().nullable(), + workspaceState: z.enum(["none", "loading", "ready"]).optional(), + }), +}); /** * Throws if the plugin tab cannot currently run tasks. @@ -65,16 +79,24 @@ export function assertPluginResponsive( * Manages WebSocket connections to Penpot plugin instances and handles plugin tasks * over these connections. */ -export class PluginBridge { +export class PluginBridge implements TaskDispatchHost { public static readonly MULTIUSER_CONNECTION_ERROR_MESSAGE = `No Penpot instance connected for user token. Please ensure that Penpot is connected and that the MCP client connection is using the correct token.`; private readonly logger = createLogger("PluginBridge"); private readonly wsServer: WebSocketServer; - private readonly connectedClients: Map = new Map(); - private readonly clientsByToken: Map = new Map(); + private readonly connectionsBySocket: Map = new Map(); + private readonly userPenpotConnectionsByToken: Map = new Map(); + private readonly singleUserConnections = new UserPenpotConnections(); private readonly pendingTasks: Map> = new Map(); private readonly taskTimeouts: Map = new Map(); + private readonly taskConnections: Map = new Map(); + private readonly dispatcher: TaskDispatcher; + + /** + * Collector of the rollbacks that task responses report, per tool execution. + */ + public readonly rollbackNotices = new RollbackNotices(); /** * Creates the plugin bridge and starts its WebSocket server. @@ -94,6 +116,9 @@ export class PluginBridge { private readonly taskTimeoutSecs: number, private readonly redisBridge?: RedisBridge ) { + this.dispatcher = redisBridge + ? new MultiInstanceTaskDispatcher(this, redisBridge) + : new SingleInstanceTaskDispatcher(this); this.wsServer = new WebSocketServer({ port: port, host: mcpServer.host }); this.setupWebSocketHandlers(); } @@ -123,125 +148,196 @@ export class PluginBridge { this.logger.info("New WebSocket connection established"); } - // start the per-connection keep-alive ping interval - const pingInterval = setInterval(() => { - ws.ping(); - }, KEEP_ALIVE_TIME); - - // register the client connection with both indexes - const connection: ClientConnection = { - socket: ws, - userToken, - pingInterval, - lastHeartbeat: Date.now(), - frozen: false, - }; - this.connectedClients.set(ws, connection); - if (userToken) { - // ensure only one connection per userToken - if (this.clientsByToken.has(userToken)) { - this.logger.warn("Duplicate connection for given user token; rejecting new connection"); - this.removeConnection(ws); - ws.close(1008, "Duplicate connection for given user token; close previous connection first."); - return; - } - - this.clientsByToken.set(userToken, connection); - - // In multi-instance mode, subscribe to this token's Redis request channel so - // that task requests issued by other instances are dispatched to this plugin. - if (this.redisBridge) { - const tokenForSubscription = userToken; - this.redisBridge - .subscribeToTasks(userToken, (request) => - this.dispatchForwardedTask(tokenForSubscription, request) - ) - .catch((error) => this.logger.error(error, "Failed to subscribe to Redis task channel")); - } - } - + // require metadata before registering a connection for dispatch + const initializationTimeout = setTimeout(() => { + ws.close(1008, "Connection initialization timed out; please update the Penpot MCP plugin."); + }, INITIALIZATION_TIMEOUT_MS); + let initializing = false; ws.on("message", (data: Buffer) => { this.logger.debug("Received WebSocket message: %s", data.toString()); try { + const message = JSON.parse(data.toString()); + const connection = this.connectionsBySocket.get(ws); + if (!connection) { + if (initializing) { + return; + } + const initialization = connectionInitSchema.safeParse(message); + if (!initialization.success) { + ws.close(1008, "Expected connection metadata; please update the Penpot MCP plugin."); + return; + } + initializing = true; + void this.initializeConnection( + ws, + this.mcpServer.isMultiUserMode() ? userToken : null, + initialization.data.session + ) + .then(() => clearTimeout(initializationTimeout)) + .catch((error) => { + this.logger.error(error, "Failed to initialize Penpot connection"); + clearTimeout(initializationTimeout); + this.removeConnection(ws); + ws.close(1011, "Failed to initialize Penpot connection; please retry."); + }); + return; + } // any plugin message proves the page event loop is running connection.lastHeartbeat = Date.now(); - - const message = JSON.parse(data.toString()); if (message?.type === "freeze") { connection.frozen = true; this.logger.info("Plugin tab reported it is being frozen by the browser"); return; } connection.frozen = false; + if (message?.type === "context-update") { + const context = connectionInitSchema.shape.session + .omit({ sessionId: true }) + .parse(message.context); + Object.assign(connection.session, context); + return; + } if (message?.type === "heartbeat") { return; } - this.handlePluginTaskResponse(message as PluginTaskResponse); + this.handlePluginTaskResponse(message as PluginTaskResponse, connection); } catch (error) { this.logger.error(error, "Failure while processing WebSocket message"); } }); - ws.on("close", () => { - this.logger.info("WebSocket connection closed"); + ws.on("close", (code: number, reason: Buffer) => { + this.logger.info("WebSocket connection closed (code %d%s)", code, reason.length ? `: ${reason}` : ""); + clearTimeout(initializationTimeout); this.removeConnection(ws); }); ws.on("error", (error) => { this.logger.error(error, "WebSocket connection error"); + clearTimeout(initializationTimeout); this.removeConnection(ws); + ws.terminate(); }); }); this.logger.info("WebSocket mcpServer started on port %d", this.port); } + private async initializeConnection( + ws: WebSocket, + userToken: string | null, + session: PenpotConnection["session"] + ): Promise { + let connections = this.getUserConnections(userToken); + if (!connections) { + connections = new UserPenpotConnections(); + this.userPenpotConnectionsByToken.set(userToken!, connections); + } + // start the per-connection keep-alive ping interval + const pingInterval = setInterval(() => { + ws.ping(); + }, KEEP_ALIVE_TIME); + // the plugin answers with a heartbeat, which a frozen page cannot send + const heartbeatRequestInterval = setInterval(() => { + ws.send(JSON.stringify({ type: "heartbeat-request" })); + }, HEARTBEAT_REQUEST_INTERVAL_MS); + + // register the client connection with both indexes + const connection: PenpotConnection = { + socket: ws, + userToken, + pingInterval, + heartbeatRequestInterval, + session, + ready: false, + lastHeartbeat: Date.now(), + frozen: false, + }; + // the same tab reconnecting reuses its session ID while its previous socket may still be half-open + const replaced = connections.add(connection); + this.connectionsBySocket.set(ws, connection); + if (replaced) { + this.displaceConnection(replaced); + } + await this.dispatcher.onNewConnection(connection); + // disconnect may have removed this connection while subscriptions were pending + if (ws.readyState === WebSocket.OPEN && this.connectionsBySocket.get(ws) === connection) { + connection.ready = true; + ws.send(JSON.stringify({ type: "initialized" })); + } + } + + /** + * Closes a connection superseded by a newer connection for the same session. + * + * Uses close code 1008 so that a still-running plugin behind the old socket stops + * reconnecting instead of competing with the newer connection for the session. + * + * @param connection - The superseded connection + */ + public displaceConnection(connection: PenpotConnection): void { + this.logger.info("Closing connection replaced by a newer connection for the same session"); + this.removeConnection(connection.socket); + connection.socket.close(1008, "Replaced by a newer connection for this Penpot session."); + } + /** * Removes a client connection and releases all resources associated with it. * - * Clears the per-connection keep-alive interval and removes the connection from the - * socket-keyed index. The token-keyed index entry (and, in multi-instance mode, the - * token's Redis task subscription) is removed only if it is owned by the given - * connection. Safe to call with a socket that is not (or no longer) registered. + * Clears the per-connection keep-alive and heartbeat-request intervals and removes the + * connection from the socket-keyed index and the user's session collection. The + * token-keyed index entry is removed only when the user's last local connection closes. + * Tasks sent over the connection and still awaiting a response fail at once. Safe to call + * with a socket that is not (or no longer) registered. * * @param ws - The WebSocket whose connection state should be removed */ private removeConnection(ws: WebSocket): void { - const connection = this.connectedClients.get(ws); + const connection = this.connectionsBySocket.get(ws); if (!connection) { return; } + connection.ready = false; clearInterval(connection.pingInterval); - this.connectedClients.delete(ws); - if (connection.userToken) { - // Perform the token-keyed cleanup only if this connection owns the token registration. - // A connection rejected as a duplicate carries the same token but must not remove token associations. - if (this.clientsByToken.get(connection.userToken) !== connection) { - this.logger.debug("Removed connection does not own its token registration; skipping token cleanup"); - } else { - this.clientsByToken.delete(connection.userToken); - - if (this.redisBridge) { - this.redisBridge - .unsubscribeFromTasks(connection.userToken) - .catch((error) => this.logger.error(error, "Failed to unsubscribe from Redis task channel")); - } + clearInterval(connection.heartbeatRequestInterval); + this.connectionsBySocket.delete(ws); + const connections = this.getUserConnections(connection.userToken)!; + connections.remove(connection); + // a task sent over the closed socket can no longer be answered, since responses only count + // from the connection the task went to + for (const [taskId, taskConnection] of [...this.taskConnections]) { + if (taskConnection === connection) { + this.rejectPendingTask(taskId, new Error("The plugin connection closed before the task finished.")); } } + if (connection.userToken !== null && connections.size === 0) { + this.userPenpotConnectionsByToken.delete(connection.userToken); + } + void this.dispatcher + .onConnectionClosed(connection) + .catch((error) => this.logger.error(error, "Failed to remove connection subscriptions")); } /** * Handles responses from the plugin for completed tasks. * * Finds the pending task by ID and resolves or rejects its promise - * based on the execution result. + * based on the execution result. A response that comes after its task + * timed out has no task to report its rollbacks to, so they go back to + * the plugin, which sends them with its next response. * * @param response - The plugin task response containing ID and result + * @param connection - The responding local connection, or undefined for Redis responses */ - private handlePluginTaskResponse(response: PluginTaskResponse): void { + private handlePluginTaskResponse(response: PluginTaskResponse, connection?: PenpotConnection): void { const task = this.pendingTasks.get(response.id); if (!task) { this.logger.info(`Received response for unknown task ID: ${response.id}`); + this.returnRollbacks(connection, response.rollbacks); + return; + } + + if (this.taskConnections.get(response.id) !== connection) { return; } @@ -252,18 +348,44 @@ export class PluginBridge { this.taskTimeouts.delete(response.id); } this.pendingTasks.delete(response.id); + this.taskConnections.delete(response.id); // Resolve or reject the task's promise based on the result if (response.success) { - task.resolveWithResult({ data: response.data }); + task.resolveWithResult({ data: response.data, rollbacks: response.rollbacks }); } else { - const error = new Error(response.error || "Task execution failed (details not provided)"); + const error = new PluginTaskError( + response.error || "Task execution failed (details not provided)", + response.rollbacks + ); task.rejectWithError(error); } this.logger.info(`Task ${response.id} completed: success=${response.success}`); } + /** + * Sends rollbacks that no tool execution received back to the plugin. + * + * @param connection - The plugin connection that reported them, if known + * @param rollbacks - The rollbacks of a response whose task is gone + */ + private returnRollbacks( + connection: PenpotConnection | undefined, + rollbacks: ContentRollbackEvent[] | undefined + ): void { + if (!connection || !rollbacks || rollbacks.length === 0) { + return; + } + if (connection.socket.readyState !== 1) { + this.logger.warn("Plugin disconnected; %d rollback(s) of a late response are lost", rollbacks.length); + return; + } + const message: UnclaimedRollbacksMessage = { type: "unclaimed-rollbacks", rollbacks }; + connection.socket.send(JSON.stringify(message)); + this.logger.info("Returned %d rollback(s) of a late response to the plugin", rollbacks.length); + } + /** * Rejects a still-pending task with the given error, releasing its correlation state. * @@ -287,72 +409,59 @@ export class PluginBridge { this.taskTimeouts.delete(taskId); } this.pendingTasks.delete(taskId); + this.taskConnections.delete(taskId); pendingTask.rejectWithError(error); this.logger.info(`Task ${taskId} rejected: ${error.message}`); return true; } - /** - * Determines the client connection to use for executing a task. - * - * In single-user mode, returns the single connected client. - * In multi-user mode, returns the client matching the session's userToken. - * - * @returns The client connection to use - * @throws Error if no suitable connection is found or if configuration is invalid - */ - private getClientConnection(): ClientConnection { - if (this.mcpServer.isMultiUserMode()) { - const sessionContext = this.mcpServer.getSessionContext(); - if (!sessionContext?.userToken) { - throw new Error("No userToken found in session context. Multi-user mode requires authentication."); - } - - const connection = this.clientsByToken.get(sessionContext.userToken); - if (!connection) { - throw new Error(PluginBridge.MULTIUSER_CONNECTION_ERROR_MESSAGE); - } - - return connection; - } else { - // single-user mode: return the single connected client - if (this.connectedClients.size === 0) { - throw new Error( - `No Penpot plugin instances are currently connected. Please ensure the plugin is running and connected.` - ); - } - if (this.connectedClients.size > 1) { - throw new Error( - `Multiple (${this.connectedClients.size}) Penpot MCP Plugin instances are connected. ` + - `Ask the user to ensure that only one instance is connected at a time.` - ); - } - - // return the first (and only) connection - const connection = this.connectedClients.values().next().value; - return connection; - } + getUserConnections(userToken: string | null): UserPenpotConnections | undefined { + return userToken === null ? this.singleUserConnections : this.userPenpotConnectionsByToken.get(userToken); } /** * Executes a plugin task by sending it to the connected Penpot plugin instance, * either directly via WebSocket or indirectly via Redis (depending on the configuration), * and awaiting the result. + * Rollbacks the response reports go to {@link rollbackNotices}. * * @param task - The plugin task to execute + * @param sessionId - The target session; when omitted, discovery must yield exactly one session * @throws Error if no plugin instances are connected or available */ public async executePluginTask>( - task: PluginTask + task: PluginTask, + sessionId?: string ): Promise { - this.sendPluginTask(task, this.redisBridge !== undefined); - return await task.getResultPromise(); + let userToken: string | null = null; + if (this.mcpServer.isMultiUserMode()) { + const sessionContext = this.mcpServer.getSessionContext(); + if (!sessionContext?.userToken) { + throw new Error("No userToken found in session context. Multi-user mode requires authentication."); + } + userToken = sessionContext.userToken; + } + // attach the result handler before dispatch can reject the task + const resultPromise = task.getResultPromise(); + void this.dispatcher.dispatch(task, userToken, sessionId).catch((error) => { + task.rejectWithError(error instanceof Error ? error : new Error(String(error))); + }); + try { + const result = await resultPromise; + this.rollbackNotices.record(result.rollbacks); + return result; + } catch (error) { + if (error instanceof PluginTaskError) { + this.rollbackNotices.record(error.rollbacks); + } + throw error; + } } /** - * Registers a task for response correlation, sends its request over the appropriate - * transport, and arms a timeout that rejects the task if no response is received. + * Registers a task for response correlation, sends its request over Redis, + * and arms a timeout that rejects the task if no response is received. * * The response (whether arriving over the local WebSocket or over Redis) is later * matched by ID in {@link handlePluginTaskResponse}, which settles the task via its @@ -361,60 +470,60 @@ export class PluginBridge { * * When routing via Redis, the task is rejected immediately (rather than timing out) * if the published request reached no instance, i.e. if no instance holds a plugin - * connection for the session's user token, or if publishing fails outright. + * connection for the user's session, or if publishing fails outright. * * @param task - The task to dispatch - * @param useRedis - Whether to route the request via Redis (multi-instance) rather - * than directly over the local WebSocket connection - * @param connection - The connection to use for a local (non-remote) dispatch; when - * omitted, the session's connection is resolved via {@link getClientConnection}. - * Ignored when `useRedis` is true. - * @throws Error if a local dispatch is required but no suitable connection is available + * @param userToken - The user token identifying the target session's owner + * @param sessionId - The target Penpot session */ - private sendPluginTask(task: AbstractPluginTask, useRedis: boolean, connection?: ClientConnection): void { - let onTimeout: (() => void) | undefined; + public sendRemoteTask(task: AbstractPluginTask, userToken: string, sessionId: string): void { + const redisBridge = this.redisBridge!; + this.logger.debug("Dispatching task %s via Redis", task.id); - if (useRedis) { - const sessionContext = this.mcpServer.getSessionContext(); - if (!sessionContext?.userToken) { - throw new Error("No userToken found in session context. Multi-user mode requires authentication."); - } - const userToken = sessionContext.userToken; - const redisBridge = this.redisBridge!; - this.logger.debug("Dispatching task %s via Redis", task.id); + // register the task for result correlation, then publish the request via Redis + this.pendingTasks.set(task.id, task); + void redisBridge + .sendTaskRequest(userToken, sessionId, task.toRequest(), (response) => + this.handlePluginTaskResponse(response) + ) + .then((receiverCount) => { + // fail fast when no instance received the request (no connection with matching user token and session ID in any instance) + if (receiverCount === 0) { + this.rejectPendingTask( + task.id, + new Error(`Penpot session ${JSON.stringify(sessionId)} is not connected for this user.`) + ); + } + }) + .catch((error) => { + this.rejectPendingTask(task.id, error instanceof Error ? error : new Error(String(error))); + }); - // register the task for result correlation, then publish the request via Redis - this.pendingTasks.set(task.id, task); - void redisBridge - .sendTaskRequest(userToken, task.toRequest(), (response) => this.handlePluginTaskResponse(response)) - .then((receiverCount) => { - // fail fast when no instance received the request (no connection with matching user token in any instance) - if (receiverCount === 0) { - this.rejectPendingTask(task.id, new Error(PluginBridge.MULTIUSER_CONNECTION_ERROR_MESSAGE)); - } - }) - .catch((error) => { - this.rejectPendingTask(task.id, error instanceof Error ? error : new Error(String(error))); - }); + // on timeout, release the response-channel subscription, since no response + // will arrive to trigger its self-unsubscribe. + this.startTaskTimeout(task, () => void redisBridge.unsubscribeFromResponse(task.id)); + } - // on timeout, release the response-channel subscription, since no response - // will arrive to trigger its self-unsubscribe. - onTimeout = () => void redisBridge.unsubscribeFromResponse(task.id); - } else { - const target = connection ?? this.getClientConnection(); - if (target.socket.readyState !== 1) { - // WebSocket is not open - throw new Error(`Plugin instance is disconnected. Task could not be sent.`); - } - - // the socket can be open while browser-throttled plugin JS cannot run tasks - assertPluginResponsive(target, Date.now()); - - // register the task for result correlation, then send over the socket - this.pendingTasks.set(task.id, task); - target.socket.send(JSON.stringify(task.toRequest())); + /** Sends a task directly to a ready local connection, with execution tracking. */ + public sendLocalTask(task: AbstractPluginTask, connection: PenpotConnection): void { + const target = connection; + if (!target.ready || target.socket.readyState !== 1) { + // WebSocket is not open + throw new Error(`Plugin instance is disconnected. Task could not be sent.`); } + // the socket can be open while browser-throttled plugin JS cannot run tasks + assertPluginResponsive(target, Date.now()); + + // register the task for result correlation, then send over the socket + this.pendingTasks.set(task.id, task); + this.taskConnections.set(task.id, target); + target.socket.send(JSON.stringify(task.toRequest())); + this.startTaskTimeout(task); + } + + /** Arms the shared execution timeout for a locally or remotely dispatched task. */ + private startTaskTimeout(task: AbstractPluginTask, onTimeout?: () => void): void { // Set up a timeout to reject the task if no response is received const timeoutHandle = setTimeout(() => { if ( @@ -431,47 +540,19 @@ export class PluginBridge { this.logger.info(`Sent task ${task.id}`); } - /** - * Dispatches a task request received over Redis to the locally-connected plugin. - * - * Invoked on the instance subscribed to a user token's request channel when another - * instance (or this one) issues a task request. A {@link RemotePluginTask} is created - * so that, once the plugin responds, the outcome is published back to the issuing - * instance's Redis response channel via the standard response-handling path. - * - * On failure to dispatch (e.g. the plugin is not connected here), an error response - * is published immediately so the requester need not wait for its timeout. - * - * @param userToken - The user token on whose request channel the request arrived; - * identifies the locally-connected plugin to dispatch to - * @param request - The serialized task request, passed through from Redis - */ - private dispatchForwardedTask(userToken: string, request: PluginTaskRequest): void { - if (!this.redisBridge) { - return; - } - - // The response is published on the channel keyed by the original request ID. - const task = new RemotePluginTask(request.task, request.params, this.redisBridge, request.id); - this.logger.debug("Dispatching remote task %s as %s to Penpot via WebSocket", request.id, task.id); - - const connection = this.clientsByToken.get(userToken); - if (!connection) { - task.rejectWithError(new Error("Plugin not connected on the receiving instance")); - return; - } - - try { - this.sendPluginTask(task, false, connection); - } catch (error) { - task.rejectWithError(error instanceof Error ? error : new Error(String(error))); - } - } - /** * Closes the WebSocket server and all connected client sockets. + * Also releases task tracking and dispatch subscriptions. */ public async close(): Promise { + for (const socket of this.wsServer.clients) { + this.removeConnection(socket); + socket.terminate(); + } + for (const taskId of this.pendingTasks.keys()) { + this.rejectPendingTask(taskId, new Error("MCP server is shutting down.")); + } + await this.dispatcher.close(); return new Promise((resolve) => { this.wsServer.close(() => { this.logger.info("WebSocket server closed"); diff --git a/mcp/packages/server/src/PluginTask.ts b/mcp/packages/server/src/PluginTask.ts index 0e0d327564..ddab54a76c 100644 --- a/mcp/packages/server/src/PluginTask.ts +++ b/mcp/packages/server/src/PluginTask.ts @@ -5,12 +5,12 @@ * A task defines a specific operation for the plugin to execute along with * strongly-typed parameters and provides request/response correlation. */ -import { PluginTaskRequest, PluginTaskResult } from "@penpot/mcp-common"; +import { ContentRollbackEvent, PluginTaskRequest, PluginTaskResult } from "@penpot/mcp-common"; import { randomUUID } from "crypto"; /** * Abstract base for plugin tasks, defining the parts that the plugin dispatch and - * response-correlation machinery (`PluginBridge.sendPluginTask` / + * response-correlation machinery (`PluginBridge.sendLocalTask` / * `PluginBridge.handlePluginTaskResponse`) depend upon. * * The dispatch path only needs to serialize a task to a request and, upon receiving @@ -153,3 +153,23 @@ export class PluginTask< this.rejectResult(error); } } + +/** + * Error of a plugin task that failed in the plugin. + * + * Carries the rollbacks the failed task's response reported, which must reach the LLM like those of a + * successful response. + */ +export class PluginTaskError extends Error { + /** + * @param message - the error message reported by the plugin + * @param rollbacks - the rollbacks the response reported + */ + constructor( + message: string, + public readonly rollbacks: ContentRollbackEvent[] = [] + ) { + super(message); + this.name = "PluginTaskError"; + } +} diff --git a/mcp/packages/server/src/RedisBridge.test.ts b/mcp/packages/server/src/RedisBridge.test.ts new file mode 100644 index 0000000000..52080fcc3c --- /dev/null +++ b/mcp/packages/server/src/RedisBridge.test.ts @@ -0,0 +1,245 @@ +import assert from "node:assert/strict"; +import { once } from "node:events"; +import { beforeEach, test, type TestContext } from "node:test"; +import Redis, { type Command } from "ioredis"; +import { WebSocket } from "ws"; +import { RedisBridge } from "./RedisBridge"; +import { PluginBridge } from "./PluginBridge"; +import { PluginTask } from "./PluginTask"; +import type { PenpotMcpServer } from "./PenpotMcpServer"; + +// Replace only the Redis command boundary; exercise the real transport, +// dispatchers, task tracking, and WebSockets above it. +let subscriptions: Map>; +let failDiscoverySubscription: boolean; +beforeEach((context) => { + // These hooks run for tests, so mocks live through each test's cleanup hooks. + const { mock } = context as TestContext; + subscriptions = new Map(); + failDiscoverySubscription = false; + mock.method(Redis.prototype, "connect", async function (this: Redis) { + this.status = "ready"; + }); + mock.method(Redis.prototype, "sendCommand", async function (this: Redis, command: Command) { + const [channel, message] = command.args.map(String); + if (command.name === "subscribe") { + const subscribers = subscriptions.get(channel) ?? new Set(); + subscribers.add(this); + subscriptions.set(channel, subscribers); + if (failDiscoverySubscription && channel.includes(".discovery.req.")) { + failDiscoverySubscription = false; + throw new Error("Subscription failed"); + } + return subscribers.size; + } + if (command.name === "unsubscribe") { + subscriptions.get(channel)?.delete(this); + return 0; + } + if (command.name === "publish") { + const subscribers = [...(subscriptions.get(channel) ?? [])]; + for (const subscriber of subscribers) subscriber.emit("message", channel, message); + return subscribers.length; + } + if (command.name === "quit") { + for (const subscribers of subscriptions.values()) subscribers.delete(this); + return "OK"; + } + throw new Error(`Unexpected Redis command: ${command.name}`); + }); +}); + +function transport(t: TestContext, tenant = "test") { + const bridge = new RedisBridge("redis://localhost:6379", tenant); + t.after(() => bridge.close()); + return bridge; +} + +test("Redis task channels isolate users, sessions, and tenants without delimiter collisions", async (t) => { + const owner = transport(t); + const caller = transport(t); + const otherTenant = transport(t, "other"); + const delivered: unknown[] = []; + await owner.subscribeToTasks("alice.one", "two", (request) => { + owner.publishTaskResponse(request.id, { id: request.id, success: true, data: "selected" }); + }); + for (const [token, session] of [ + ["alice", "one.two"], + ["bob", "two"], + ["alice.one", "other"], + ]) { + assert.equal( + await caller.sendTaskRequest(token, session, { id: "missing", task: "test", params: {} }, () => {}), + 0 + ); + } + assert.equal( + await otherTenant.sendTaskRequest( + "alice.one", + "two", + { id: "other-tenant", task: "test", params: {} }, + () => {} + ), + 0 + ); + assert.equal( + await caller.sendTaskRequest("alice.one", "two", { id: "selected", task: "test", params: {} }, (response) => + delivered.push(response.data) + ), + 1 + ); + assert.deepEqual(delivered, ["selected"]); +}); + +test("Redis discovery receives one reply per subscribed instance and returns publish's recipient count", async (t) => { + const first = transport(t); + const second = transport(t); + const requester = transport(t); + await first.subscribeToDiscovery("alice", ({ id }) => { + first.publishDiscoveryResponse(id, { + instanceId: "first", + sessions: [{ sessionId: "one", fileId: "file", fileName: "Design" }], + }); + }); + await second.subscribeToDiscovery("alice", ({ id }) => { + second.publishDiscoveryResponse(id, { instanceId: "second", sessions: [] }); + }); + const responders: string[] = []; + const count = await requester.sendDiscoveryRequest("alice", "query", (response) => + responders.push(response.instanceId) + ); + assert.equal(count, 2); + assert.deepEqual(responders.sort(), ["first", "second"]); + await requester.unsubscribeFromDiscoveryResponses("query"); + first.publishDiscoveryResponse("query", { instanceId: "late", sessions: [] }); + assert.deepEqual(responders.sort(), ["first", "second"]); +}); + +test("unsubscribing a session retains the user's other task channels", async (t) => { + const owner = transport(t); + const caller = transport(t); + await owner.subscribeToTasks("alice", "first", () => {}); + await owner.subscribeToTasks("alice", "second", () => {}); + await owner.unsubscribeFromTasks("alice", "first"); + assert.equal( + await caller.sendTaskRequest("alice", "first", { id: "first", task: "test", params: {} }, () => {}), + 0 + ); + assert.equal( + await caller.sendTaskRequest("alice", "second", { id: "second", task: "test", params: {} }, () => {}), + 1 + ); + await caller.unsubscribeFromResponse("second"); +}); + +test("a failed discovery subscription is cleaned up and can be retried", async (t) => { + const owner = transport(t); + const caller = transport(t); + failDiscoverySubscription = true; + await assert.rejects( + owner.subscribeToDiscovery("alice", () => {}), + /Subscription failed/ + ); + assert.equal(await caller.sendDiscoveryRequest("alice", "after-failure", () => {}), 0); + await caller.unsubscribeFromDiscoveryResponses("after-failure"); + await owner.subscribeToDiscovery("alice", ({ id }) => { + owner.publishDiscoveryResponse(id, { instanceId: "owner", sessions: [] }); + }); + assert.equal(await caller.sendDiscoveryRequest("alice", "retry", () => {}), 1); + await caller.unsubscribeFromDiscoveryResponses("retry"); +}); + +let port = 18_700; +function instance(t: TestContext) { + const redis = new RedisBridge("redis://localhost:6379", "test"); + const wsPort = ++port; + const bridge = new PluginBridge( + { + host: "127.0.0.1", + isMultiUserMode: () => true, + getSessionContext: () => ({ userToken: "alice" }), + } as PenpotMcpServer, + wsPort, + 0.1, + redis + ); + const sockets: WebSocket[] = []; + t.after(async () => { + for (const socket of sockets) socket.terminate(); + await bridge.close(); + await redis.close(); + }); + const connect = async ( + sessionId: string, + outcome: "success" | "error" | "silent" = "success", + reply: string = sessionId + ) => { + const socket = new WebSocket(`ws://127.0.0.1:${wsPort}?userToken=alice`); + sockets.push(socket); + await once(socket, "open"); + const initialized = once(socket, "message", { signal: AbortSignal.timeout(2000) }); + socket.send(JSON.stringify({ type: "initialize", session: { sessionId, fileId: "file", fileName: "Design" } })); + await initialized; + socket.on("message", (raw) => { + const request = JSON.parse(raw.toString()); + if (outcome !== "silent") { + socket.send( + JSON.stringify({ + id: request.id, + success: outcome === "success", + data: reply, + error: "Plugin execution failed", + }) + ); + } + }); + return socket; + }; + return { bridge, connect }; +} + +test("full Redis forwarding resolves the selected WebSocket's result", async (t) => { + const owner = instance(t); + const requester = instance(t); + await owner.connect("first"); + await owner.connect("second"); + assert.equal((await requester.bridge.executePluginTask(new PluginTask("test", {}), "second")).data, "second"); + assert.equal((await owner.bridge.executePluginTask(new PluginTask("test", {}), "first")).data, "first"); +}); + +test("full Redis discovery implicitly selects a sole remote WebSocket", async (t) => { + const owner = instance(t); + const requester = instance(t); + await owner.connect("only"); + assert.equal((await requester.bridge.executePluginTask(new PluginTask("test", {}))).data, "only"); +}); + +test("forwarded plugin failures retain the regular execution failure route", async (t) => { + const owner = instance(t); + const requester = instance(t); + await owner.connect("failing", "error"); + await assert.rejects( + requester.bridge.executePluginTask(new PluginTask("test", {}), "failing"), + /Plugin execution failed/ + ); +}); + +test("forwarded task timeouts release their Redis response subscriptions", async (t) => { + const owner = instance(t); + const requester = instance(t); + await owner.connect("silent", "silent"); + await assert.rejects(requester.bridge.executePluginTask(new PluginTask("test", {}), "silent"), /timed out/); + const responseSubscriptions = [...subscriptions].filter(([channel]) => channel.includes(".task.res.")); + assert.ok(responseSubscriptions.every(([, subscribers]) => subscribers.size === 0)); +}); + +test("a reconnect on another instance closes the previous instance's connection for the session", async (t) => { + const previous = instance(t); + const current = instance(t); + const requester = instance(t); + const previousSocket = await previous.connect("tab", "success", "previous"); + const closed = once(previousSocket, "close"); + await current.connect("tab", "success", "current"); + assert.equal((await closed)[0], 1008); + assert.equal((await requester.bridge.executePluginTask(new PluginTask("test", {}))).data, "current"); +}); diff --git a/mcp/packages/server/src/RedisBridge.ts b/mcp/packages/server/src/RedisBridge.ts index 89c01a4871..b653c70faf 100644 --- a/mcp/packages/server/src/RedisBridge.ts +++ b/mcp/packages/server/src/RedisBridge.ts @@ -1,6 +1,43 @@ import Redis from "ioredis"; -import { PluginTaskRequest, PluginTaskResponse } from "@penpot/mcp-common"; +import { PluginTaskRequest, PluginTaskResponse, PenpotSession } from "@penpot/mcp-common"; import { createLogger } from "./logger"; +import { z } from "zod"; + +/** Query for the user's sessions held by each subscribed MCP instance. */ +export interface SessionDiscoveryRequest { + id: string; +} + +/** One MCP instance's current sessions for the queried user. */ +export interface SessionDiscoveryResponse { + instanceId: string; + sessions: PenpotSession[]; +} + +/** Announcement that an MCP instance holds a new connection for a Penpot session. */ +export interface SessionClaim { + instanceId: string; + /** identifies the claiming connection, so an instance can recognize its own claims. */ + claimId: string; + sessionId: string; +} + +const sessionClaimSchema = z.object({ + instanceId: z.string().min(1), + claimId: z.string().min(1), + sessionId: z.string().min(1).max(128), +}); + +const discoveryResponseSchema = z.object({ + instanceId: z.string().min(1), + sessions: z.array( + z.object({ + sessionId: z.string().min(1).max(128), + fileId: z.string().min(1).max(128), + fileName: z.string(), + }) + ), +}); /** * Handler invoked for a task request arriving on a subscribed request channel. @@ -25,7 +62,7 @@ export type TaskResponseHandler = (response: PluginTaskResponse) => void; * * It enables a tool call handled on one instance to be executed against a plugin * whose WebSocket connection lives on another instance: the request is published on a - * channel keyed by user token (to which the instance holding the plugin connection is + * channel keyed by user token and session ID (to which the instance holding the plugin connection is * subscribed), and the response is published on a channel keyed by task ID (to which * the issuing instance subscribes). * @@ -64,14 +101,19 @@ export class RedisBridge { if (handler) { handler(rawMessage); } else { - this.logger.warn(`Received message on channel with no registered handler: ${channel}`); + this.logger.debug("Received Redis message without a registered handler"); } }); } - /** Builds the Redis Pub/Sub channel name for a task request addressed to a user token. */ - private requestChannel(userToken: string): string { - return `penpot.mcp.${this.tenant}.task.req.${userToken}`; + /** Builds a task channel scoped to both the user and the Penpot session. */ + private requestChannel(userToken: string, sessionId: string): string { + return `penpot.mcp.${this.tenant}.task.req.${this.channelKey(userToken)}.${this.channelKey(sessionId)}`; + } + + /** Encodes identifiers without ambiguous channel-name separators. */ + private channelKey(value: string): string { + return Buffer.from(value).toString("base64url"); } /** Builds the Redis Pub/Sub channel name for a task response keyed by task ID. */ @@ -81,7 +123,7 @@ export class RedisBridge { /** * Subscribes to the response channel for the given task ID and publishes the task - * request to the given user token's request channel. + * request to the given user's session channel. * * The response subscription is established *before* the request is published, to * avoid a race in which the response would be published before the subscription is @@ -89,20 +131,22 @@ export class RedisBridge { * removed automatically upon delivery (response channels are single-use). * * @param userToken - The user token identifying the target plugin's request channel + * @param sessionId - The Penpot session identifying the target connection * @param request - The serialized plugin task request, passed through verbatim * @param onResponse - Handler invoked with the response when it arrives * @returns The number of instances that received the request. A count of 0 means no - * instance is subscribed to the token's request channel (i.e. the plugin is not + * instance is subscribed to the session's request channel (i.e. the plugin is not * connected anywhere); the request was dropped, no response will ever arrive, and * the response subscription has already been released. */ async sendTaskRequest( userToken: string, + sessionId: string, request: PluginTaskRequest, onResponse: TaskResponseHandler ): Promise { const responseChannel = this.responseChannel(request.id); - const requestChannel = this.requestChannel(userToken); + const requestChannel = this.requestChannel(userToken, sessionId); this.handlers.set(responseChannel, (rawMessage) => { // a response channel is single-use: remove the handler and unsubscribe on delivery @@ -158,19 +202,22 @@ export class RedisBridge { */ publishTaskResponse(taskId: string, response: PluginTaskResponse): void { const responseChannel = this.responseChannel(taskId); - void this.publisher.publish(responseChannel, JSON.stringify(response)); + void this.publisher + .publish(responseChannel, JSON.stringify(response)) + .catch((error) => this.logger.error(error, "Failed to publish task response")); } /** - * Subscribes to task requests for the given user token. + * Subscribes to task requests for the given user's Penpot session. * - * The handler is invoked for each request arriving on the token's request channel. + * The handler is invoked for each request arriving on the session's request channel. * * @param userToken - The user token whose request channel to subscribe to + * @param sessionId - The Penpot session whose task channel to subscribe to * @param handler - The handler to invoke for incoming requests */ - async subscribeToTasks(userToken: string, handler: TaskRequestHandler): Promise { - const requestChannel = this.requestChannel(userToken); + async subscribeToTasks(userToken: string, sessionId: string, handler: TaskRequestHandler): Promise { + const requestChannel = this.requestChannel(userToken, sessionId); this.handlers.set(requestChannel, (rawMessage) => { try { handler(JSON.parse(rawMessage) as PluginTaskRequest); @@ -182,19 +229,121 @@ export class RedisBridge { } /** - * Unsubscribes from task requests for the given user token. + * Unsubscribes from task requests for the given user's Penpot session. * * @param userToken - The user token whose request channel to unsubscribe from + * @param sessionId - The Penpot session whose task channel to unsubscribe from */ - async unsubscribeFromTasks(userToken: string): Promise { - const requestChannel = this.requestChannel(userToken); + async unsubscribeFromTasks(userToken: string, sessionId: string): Promise { + const requestChannel = this.requestChannel(userToken, sessionId); this.handlers.delete(requestChannel); await this.subscriber.unsubscribe(requestChannel); } + private discoveryChannel(userToken: string): string { + return `penpot.mcp.${this.tenant}.discovery.req.${this.channelKey(userToken)}`; + } + + private discoveryResponseChannel(requestId: string): string { + return `penpot.mcp.${this.tenant}.discovery.res.${this.channelKey(requestId)}`; + } + + /** Subscribes to replies before publishing; returns the actual recipient count. */ + async sendDiscoveryRequest( + userToken: string, + requestId: string, + onResponse: (response: SessionDiscoveryResponse) => void + ): Promise { + const channel = this.discoveryResponseChannel(requestId); + this.handlers.set(channel, (raw) => { + try { + onResponse(discoveryResponseSchema.parse(JSON.parse(raw))); + } catch (error) { + this.logger.error(error, "Invalid discovery response"); + } + }); + try { + await this.subscriber.subscribe(channel); + return await this.publisher.publish(this.discoveryChannel(userToken), JSON.stringify({ id: requestId })); + } catch (error) { + await this.unsubscribeFromDiscoveryResponses(requestId); + throw error; + } + } + + async unsubscribeFromDiscoveryResponses(requestId: string): Promise { + const channel = this.discoveryResponseChannel(requestId); + this.handlers.delete(channel); + await this.subscriber.unsubscribe(channel); + } + + /** Registers one discovery responder for this user's local connections. */ + async subscribeToDiscovery(userToken: string, handler: (request: SessionDiscoveryRequest) => void): Promise { + const channel = this.discoveryChannel(userToken); + this.handlers.set(channel, (raw) => { + try { + handler(z.object({ id: z.string().min(1) }).parse(JSON.parse(raw))); + } catch (error) { + this.logger.error(error, "Invalid discovery request"); + } + }); + try { + await this.subscriber.subscribe(channel); + } catch (error) { + await this.unsubscribeFromDiscovery(userToken); + throw error; + } + } + + async unsubscribeFromDiscovery(userToken: string): Promise { + const channel = this.discoveryChannel(userToken); + this.handlers.delete(channel); + await this.subscriber.unsubscribe(channel); + } + + publishDiscoveryResponse(requestId: string, response: SessionDiscoveryResponse): void { + void this.publisher + .publish(this.discoveryResponseChannel(requestId), JSON.stringify(response)) + .catch((error) => this.logger.error(error, "Failed to publish discovery response")); + } + + private sessionClaimChannel(userToken: string): string { + return `penpot.mcp.${this.tenant}.session.claim.${this.channelKey(userToken)}`; + } + /** - * Closes both Redis connections. Call on server shutdown. + * Subscribes to the session claims of the given user. + * + * Claims are delivered in publication order, including the subscribing instance's own claims. */ + async subscribeToSessionClaims(userToken: string, handler: (claim: SessionClaim) => void): Promise { + const channel = this.sessionClaimChannel(userToken); + this.handlers.set(channel, (raw) => { + try { + handler(sessionClaimSchema.parse(JSON.parse(raw))); + } catch (error) { + this.logger.error(error, "Invalid session claim"); + } + }); + try { + await this.subscriber.subscribe(channel); + } catch (error) { + await this.unsubscribeFromSessionClaims(userToken); + throw error; + } + } + + async unsubscribeFromSessionClaims(userToken: string): Promise { + const channel = this.sessionClaimChannel(userToken); + this.handlers.delete(channel); + await this.subscriber.unsubscribe(channel); + } + + async publishSessionClaim(userToken: string, claim: SessionClaim): Promise { + await this.publisher.publish(this.sessionClaimChannel(userToken), JSON.stringify(claim)); + } + + /** Closes both Redis connections. Call on server shutdown. */ async close(): Promise { await this.subscriber.quit(); await this.publisher.quit(); diff --git a/mcp/packages/server/src/RemotePluginTask.ts b/mcp/packages/server/src/RemotePluginTask.ts index 368623a5a0..c4714d021c 100644 --- a/mcp/packages/server/src/RemotePluginTask.ts +++ b/mcp/packages/server/src/RemotePluginTask.ts @@ -1,4 +1,4 @@ -import { AbstractPluginTask } from "./PluginTask"; +import { AbstractPluginTask, PluginTaskError } from "./PluginTask"; import { PluginTaskResult } from "@penpot/mcp-common"; import type { RedisBridge } from "./RedisBridge"; @@ -43,6 +43,7 @@ export class RemotePluginTask extends AbstractPluginTask ({ + fileIds: ["file-1"], + pageIds: [`page-${id}`], + shapeIds: shapes, +}); + +test("collects only the rollbacks recorded while its function runs", async () => { + const notices = new RollbackNotices(); + notices.record([rollback("outside")]); + + const [value, rollbacks] = await notices.collect(async () => { + notices.record([rollback("a")]); + await Promise.resolve(); + notices.record([rollback("b")]); + return 42; + }); + + assert.equal(value, 42); + assert.deepEqual(rollbacks, [rollback("a"), rollback("b")]); +}); + +test("keeps the rollbacks of overlapping executions apart", async () => { + const notices = new RollbackNotices(); + let release!: () => void; + const gate = new Promise((resolve) => (release = resolve)); + + const first = notices.collect(async () => { + await gate; + notices.record([rollback("a")]); + }); + const second = notices.collect(async () => { + notices.record([rollback("b")]); + release(); + }); + + const [[, a], [, b]] = await Promise.all([first, second]); + assert.deepEqual(a, [rollback("a")]); + assert.deepEqual(b, [rollback("b")]); +}); + +test("names each affected file, page and shape once", () => { + const text = RollbackNotices.describe([rollback("a", ["s1", "s2"]), rollback("b", ["s2"])]); + + assert.match(text, /\(2 times\)/); + assert.match(text, /Files: file-1\n/); + assert.match(text, /Pages: page-a, page-b\n/); + assert.match(text, /Shapes: s1, s2$/); +}); + +test("lists at most 50 shapes", () => { + const shapes = Array.from({ length: 53 }, (_, i) => `s${i}`); + + const text = RollbackNotices.describe([rollback("a", shapes)]); + + assert.match(text, /s49 \(and 3 more\)$/); + assert.doesNotMatch(text, /s50/); +}); diff --git a/mcp/packages/server/src/RollbackNotices.ts b/mcp/packages/server/src/RollbackNotices.ts new file mode 100644 index 0000000000..67b746a38c --- /dev/null +++ b/mcp/packages/server/src/RollbackNotices.ts @@ -0,0 +1,88 @@ +import { AsyncLocalStorage } from "async_hooks"; +import type { ContentRollbackEvent } from "@penpot/mcp-common"; +import { TextContent, ToolResponse } from "./ToolResponse"; + +/** + * Collector of the rollbacks of rejected changes that plugin task responses report during one tool execution. + * + * The plugin reports a rollback with the first task response after it, so the rollback reaches the tool + * execution that follows the one which made the changes. {@link withNotice} then puts a warning at the + * top of that tool's response, so the LLM learns that some of its earlier changes are gone. + */ +export class RollbackNotices { + /** + * Maximum number of shape ids listed in a notice. + */ + private static readonly MAX_LISTED_SHAPES = 50; + + private readonly storage = new AsyncLocalStorage(); + + /** + * Runs the given function, collecting the rollbacks recorded while it runs. + * + * @param fn - the function to run (typically a tool's core logic) + * @returns the function's value and the collected rollbacks + */ + async collect(fn: () => Promise): Promise<[T, ContentRollbackEvent[]]> { + const rollbacks: ContentRollbackEvent[] = []; + const value = await this.storage.run(rollbacks, fn); + return [value, rollbacks]; + } + + /** + * Records rollbacks reported by a plugin task response for the current tool execution. + * Rollbacks reported outside of {@link collect} are dropped. + * + * @param rollbacks - the rollbacks the response carried, if any + */ + record(rollbacks: ContentRollbackEvent[] | undefined): void { + if (rollbacks && rollbacks.length > 0) { + this.storage.getStore()?.push(...rollbacks); + } + } + + /** + * Puts a rollback warning at the top of the given response. + * + * @param response - the tool response + * @param rollbacks - the rollbacks collected while the tool ran + * @returns the response, with the warning first if there were rollbacks + */ + static withNotice(response: ToolResponse, rollbacks: ContentRollbackEvent[]): ToolResponse { + if (rollbacks.length === 0) { + return response; + } + // clients may join text items without a separator + const notice = new TextContent(RollbackNotices.describe(rollbacks) + "\n\n"); + response.content = [notice, ...response.content]; + return response; + } + + /** + * Describes the given rollbacks for the LLM. + * + * @param rollbacks - the rollbacks to describe (at least one) + */ + static describe(rollbacks: ContentRollbackEvent[]): string { + const fileIds = new Set(rollbacks.flatMap((r) => r.fileIds)); + const pageIds = new Set(rollbacks.flatMap((r) => r.pageIds)); + const shapeIds = [...new Set(rollbacks.flatMap((r) => r.shapeIds))]; + + const listedShapes = shapeIds.slice(0, RollbackNotices.MAX_LISTED_SHAPES); + const unlistedShapes = shapeIds.length - listedShapes.length; + + return ( + "WARNING: CHANGES WERE ROLLED BACK. Since your previous Penpot tool call, the Penpot backend rejected " + + `some changes ${rollbacks.length > 1 ? `(${rollbacks.length} times) ` : ""}and the workspace undid them, ` + + "together with the rest of their undo steps and any later change to the same file. " + + "Changes you made in earlier calls may be gone, even though those calls succeeded, and the tool call " + + "that returned this warning may have run on the reverted data.\n" + + "Before you continue, read the affected pages and shapes again, check that they hold what you expect, " + + "and redo what is missing. Do not rely on shape references or data you kept from before.\n" + + `Files: ${[...fileIds].join(", ") || "none"}\n` + + `Pages: ${[...pageIds].join(", ") || "none"}\n` + + `Shapes: ${listedShapes.join(", ") || "none"}` + + (unlistedShapes > 0 ? ` (and ${unlistedShapes} more)` : "") + ); + } +} diff --git a/mcp/packages/server/src/Tool.ts b/mcp/packages/server/src/Tool.ts index df4e1f2266..f6cee26a50 100644 --- a/mcp/packages/server/src/Tool.ts +++ b/mcp/packages/server/src/Tool.ts @@ -3,6 +3,7 @@ import "reflect-metadata"; import { TextResponse, ToolResponse } from "./ToolResponse"; import type { PenpotMcpServer, SessionContext } from "./PenpotMcpServer"; import { createLogger } from "./logger"; +import { RollbackNotices } from "./RollbackNotices"; /** * An empty arguments class for tools that do not require any parameters. @@ -20,6 +21,16 @@ export class EmptyToolArgs { * @template TArgs - The strongly-typed arguments class for this tool */ export abstract class Tool { + public static readonly SESSION_ID_SCHEMA = z + .string() + .min(1) + .max(128) + .optional() + .describe( + "Penpot session to use. Optional when exactly one session is connected. " + + "If several sessions are listed in an error, ask the user to select one and pass its sessionId." + ); + private readonly logger = createLogger("Tool"); /** monotonically increasing counter for unique tool execution IDs */ @@ -34,9 +45,22 @@ export abstract class Tool { * Executes the tool with automatic validation and type safety. * * This method handles the unknown args from the MCP protocol, - * delegating to the type-safe implementation. + * delegating to the type-safe implementation. A rollback reported by a + * plugin task during the execution puts a warning first in the response. */ async execute(args: unknown): Promise { + const [response, rollbacks] = await this.mcpServer.pluginBridge.rollbackNotices.collect(() => + this.executeAndReport(args) + ); + return RollbackNotices.withNotice(response, rollbacks); + } + + /** + * Executes the tool logic, turning a failure into an error response. + * + * @param args - the tool arguments + */ + private async executeAndReport(args: unknown): Promise { const executionId = ++Tool.executionCounter; try { let argsInstance: TArgs = args as TArgs; diff --git a/mcp/packages/server/src/UserPenpotConnections.ts b/mcp/packages/server/src/UserPenpotConnections.ts new file mode 100644 index 0000000000..b9b816049d --- /dev/null +++ b/mcp/packages/server/src/UserPenpotConnections.ts @@ -0,0 +1,43 @@ +import type { PenpotSession } from "@penpot/mcp-common"; +import type { PenpotConnection } from "./PenpotConnection"; +import { WebSocket } from "ws"; + +/** Local Penpot connections belonging to one user, indexed by session ID. */ +export class UserPenpotConnections { + private readonly connectionsBySessionId = new Map(); + + get size(): number { + return this.connectionsBySessionId.size; + } + + /** Registers a connection, returning the connection it replaces for the same session, if any. */ + add(connection: PenpotConnection): PenpotConnection | undefined { + const { sessionId } = connection.session; + const replaced = this.connectionsBySessionId.get(sessionId); + this.connectionsBySessionId.set(sessionId, connection); + return replaced; + } + + remove(connection: PenpotConnection): void { + const { sessionId } = connection.session; + if (this.connectionsBySessionId.get(sessionId) === connection) { + this.connectionsBySessionId.delete(sessionId); + } + } + + /** Returns the registered connection for a session, whether or not it is ready. */ + getRegistered(sessionId: string): PenpotConnection | undefined { + return this.connectionsBySessionId.get(sessionId); + } + + get(sessionId: string): PenpotConnection | undefined { + const connection = this.connectionsBySessionId.get(sessionId); + return connection?.ready && connection.socket.readyState === WebSocket.OPEN ? connection : undefined; + } + + getSessions(): PenpotSession[] { + return [...this.connectionsBySessionId.values()] + .filter((connection) => connection.ready && connection.socket.readyState === WebSocket.OPEN) + .map((connection) => ({ ...connection.session })); + } +} diff --git a/mcp/packages/server/src/dispatch/MultiInstanceTaskDispatcher.test.ts b/mcp/packages/server/src/dispatch/MultiInstanceTaskDispatcher.test.ts new file mode 100644 index 0000000000..4c1b153916 --- /dev/null +++ b/mcp/packages/server/src/dispatch/MultiInstanceTaskDispatcher.test.ts @@ -0,0 +1,371 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import type { PenpotSession, PluginTaskRequest, PluginTaskResponse } from "@penpot/mcp-common"; +import type { PenpotConnection } from "../PenpotConnection"; +import { PluginTask, type AbstractPluginTask } from "../PluginTask"; +import type { RedisBridge, SessionClaim, SessionDiscoveryRequest, SessionDiscoveryResponse } from "../RedisBridge"; +import { UserPenpotConnections } from "../UserPenpotConnections"; +import { MultiInstanceTaskDispatcher } from "./MultiInstanceTaskDispatcher"; +import { SessionDiscoveryError, type TaskDispatchHost } from "./TaskDispatcher"; + +/** In-memory Pub/Sub transport retaining Redis's per-subscriber delivery count. */ +class RedisNetwork { + readonly tasks = new Map void>>(); + readonly discovery = new Map void>>(); + readonly taskResponses = new Map) => void>(); + readonly discoveryResponses = new Map void>(); + readonly claims = new Map void>>(); + failDiscovery = false; + dropClaims = false; + beforeDiscoveryPublish?: () => Promise; + beforeClaimPublish?: () => Promise; +} + +/** One MCP instance's connection to the test Pub/Sub network. */ +class RedisPeer { + replyToDiscovery = true; + repeatDiscoveryReplies = false; + + constructor(readonly network: RedisNetwork) {} + + asBridge(): RedisBridge { + return this as unknown as RedisBridge; + } + + async subscribeToTasks(token: string, session: string, handler: (request: PluginTaskRequest) => void) { + const key = JSON.stringify([token, session]); + const subscribers = this.network.tasks.get(key) ?? new Map(); + subscribers.set(this, handler); + this.network.tasks.set(key, subscribers); + } + + async unsubscribeFromTasks(token: string, session: string) { + this.network.tasks.get(JSON.stringify([token, session]))?.delete(this); + } + + async sendTaskRequest( + token: string, + session: string, + request: PluginTaskRequest, + onResponse: (response: PluginTaskResponse) => void + ) { + this.network.taskResponses.set(request.id, onResponse); + const subscribers = this.network.tasks.get(JSON.stringify([token, session])) ?? new Map(); + for (const handler of subscribers.values()) handler(request); + if (!subscribers.size) this.network.taskResponses.delete(request.id); + return subscribers.size; + } + + publishTaskResponse(id: string, response: PluginTaskResponse) { + const handler = this.network.taskResponses.get(id); + this.network.taskResponses.delete(id); + handler?.(response); + } + + async subscribeToDiscovery(token: string, handler: (request: SessionDiscoveryRequest) => void) { + const subscribers = this.network.discovery.get(token) ?? new Map(); + subscribers.set(this, handler); + this.network.discovery.set(token, subscribers); + } + + async unsubscribeFromDiscovery(token: string) { + this.network.discovery.get(token)?.delete(this); + } + + async sendDiscoveryRequest(token: string, id: string, onResponse: (response: SessionDiscoveryResponse) => void) { + if (this.network.failDiscovery) throw new Error("Redis unavailable for discovery"); + this.network.discoveryResponses.set(id, onResponse); + await this.network.beforeDiscoveryPublish?.(); + const subscribers = this.network.discovery.get(token) ?? new Map(); + for (const [peer, handler] of subscribers) { + if (peer.replyToDiscovery) { + handler({ id }); + if (peer.repeatDiscoveryReplies) handler({ id }); + } + } + return subscribers.size; + } + + publishDiscoveryResponse(id: string, response: SessionDiscoveryResponse) { + this.network.discoveryResponses.get(id)?.(response); + } + + async unsubscribeFromDiscoveryResponses(id: string) { + this.network.discoveryResponses.delete(id); + } + + async subscribeToSessionClaims(token: string, handler: (claim: SessionClaim) => void) { + const subscribers = this.network.claims.get(token) ?? new Map(); + subscribers.set(this, handler); + this.network.claims.set(token, subscribers); + } + + async unsubscribeFromSessionClaims(token: string) { + this.network.claims.get(token)?.delete(this); + } + + async publishSessionClaim(token: string, claim: SessionClaim) { + await this.network.beforeClaimPublish?.(); + if (this.network.dropClaims) return; + for (const handler of [...(this.network.claims.get(token)?.values() ?? [])]) handler(claim); + } +} + +/** Local registry and execution endpoint used with the real dispatchers. */ +class Instance implements TaskDispatchHost { + readonly users = new Map(); + readonly redis: RedisPeer; + readonly dispatcher: MultiInstanceTaskDispatcher; + readonly executions: string[] = []; + readonly displaced: string[] = []; + + constructor(network: RedisNetwork) { + this.redis = new RedisPeer(network); + this.dispatcher = new MultiInstanceTaskDispatcher(this, this.redis.asBridge(), 20); + } + + async connect(token: string, sessionId: string, fileName = "Design") { + const session: PenpotSession = { sessionId, fileId: "same-file", fileName }; + const connection = { userToken: token, session, ready: false, socket: { readyState: 1 } } as PenpotConnection; + const connections = this.users.get(token) ?? new UserPenpotConnections(); + this.users.set(token, connections); + connections.add(connection); + await this.dispatcher.onNewConnection(connection); + connection.ready = true; + return connection; + } + + async disconnect(connection: PenpotConnection) { + this.users.get(connection.userToken!)!.remove(connection); + connection.ready = false; + await this.dispatcher.onConnectionClosed(connection); + } + + displaceConnection(connection: PenpotConnection) { + this.displaced.push(connection.session.sessionId); + void this.disconnect(connection); + } + + getUserConnections(token: string | null) { + return this.users.get(token!); + } + + sendLocalTask(task: AbstractPluginTask, connection: PenpotConnection) { + if (!connection.ready) throw new Error("Plugin disconnected"); + this.executions.push(connection.session.sessionId); + task.resolveWithResult({ data: connection.session.sessionId }); + } + + sendRemoteTask(task: AbstractPluginTask, token: string, sessionId: string) { + void this.redis + .sendTaskRequest(token, sessionId, task.toRequest(), (response) => { + if (response.success) task.resolveWithResult({ data: response.data }); + else task.rejectWithError(new Error(response.error)); + }) + .then((count) => { + if (!count) task.rejectWithError(new Error("Session not connected")); + }); + } + + async execute(token: string, sessionId?: string) { + const task = new PluginTask("test", {}); + await this.dispatcher.dispatch(task, token, sessionId); + return await task.getResultPromise(); + } +} + +test("discovers all sessions across instances, including several tabs for one file", async () => { + const network = new RedisNetwork(); + const first = new Instance(network); + const second = new Instance(network); + await first.connect("alice", "one"); + await first.connect("alice", "two"); + await second.connect("alice", "three"); + await second.connect("bob", "private"); + const sessions = await new Instance(network).dispatcher.discoverSessions("alice"); + assert.deepEqual(sessions.map((session) => session.sessionId).sort(), ["one", "three", "two"]); + assert.equal(network.discoveryResponses.size, 0); +}); + +test("explicit sessions dispatch remotely without discovery", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + const requester = new Instance(network); + await owner.connect("alice", "selected"); + network.failDiscovery = true; + assert.equal((await requester.execute("alice", "selected")).data, "selected"); +}); + +test("explicit sessions work through Redis on their owning instance too", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + await owner.connect("alice", "selected"); + network.failDiscovery = true; + assert.equal((await owner.execute("alice", "selected")).data, "selected"); +}); + +test("explicit session IDs cannot route tasks across user boundaries", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + await owner.connect("bob", "private"); + await assert.rejects(new Instance(network).execute("alice", "private"), /not connected/); + assert.deepEqual(owner.executions, []); +}); + +test("complete discovery dispatches when exactly one session exists", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + await owner.connect("alice", "only"); + assert.equal((await new Instance(network).execute("alice")).data, "only"); +}); + +test("complete discovery requires selection when more than one session exists", async () => { + const network = new RedisNetwork(); + await new Instance(network).connect("alice", "first", "First file"); + await new Instance(network).connect("alice", "second", "Second file"); + await assert.rejects(new Instance(network).execute("alice"), (error: Error) => { + assert.match(error.message, /Ask the user/); + assert.match(error.message, /first.*First file/); + assert.match(error.message, /second.*Second file/); + return true; + }); +}); + +test("zero discovery subscribers reports no connected sessions", async () => { + await assert.rejects(new Instance(new RedisNetwork()).execute("alice"), /No Penpot sessions/); +}); + +test("incomplete discovery never dispatches even when it yields exactly one session", async () => { + const network = new RedisNetwork(); + const responsive = new Instance(network); + const slow = new Instance(network); + await responsive.connect("alice", "visible"); + await slow.connect("alice", "hidden"); + slow.redis.replyToDiscovery = false; + await assert.rejects(new Instance(network).execute("alice"), (error: Error) => { + assert.ok(error instanceof SessionDiscoveryError); + assert.match(error.message, /temporary server load.*retry/); + return true; + }); + assert.deepEqual(responsive.executions, []); + assert.equal(network.discoveryResponses.size, 0); +}); + +test("duplicate replies from one instance do not substitute for a missing reply", async () => { + const network = new RedisNetwork(); + const responsive = new Instance(network); + const slow = new Instance(network); + await responsive.connect("alice", "visible"); + await slow.connect("alice", "hidden"); + responsive.redis.repeatDiscoveryReplies = true; + slow.redis.replyToDiscovery = false; + await assert.rejects(new Instance(network).execute("alice"), SessionDiscoveryError); +}); + +test("discovery transport failures use the retryable discovery error", async () => { + const network = new RedisNetwork(); + network.failDiscovery = true; + await assert.rejects(new Instance(network).execute("alice"), SessionDiscoveryError); +}); + +test("disconnecting one tab retains discovery and dispatch for the remaining tab", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + const first = await owner.connect("alice", "first"); + await owner.connect("alice", "second"); + await owner.disconnect(first); + assert.equal((await new Instance(network).execute("alice")).data, "second"); +}); + +test("disconnecting the last tab removes its discovery subscription", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + const connection = await owner.connect("alice", "only"); + await owner.disconnect(connection); + assert.equal(network.discovery.get("alice")?.size, 0); + await assert.rejects(new Instance(network).execute("alice"), /No Penpot sessions/); +}); + +test("concurrent discoveries keep response channels and users separate", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + const requester = new Instance(network); + await owner.connect("alice", "alice-tab"); + await owner.connect("bob", "bob-tab"); + const results = await Promise.all([requester.execute("alice"), requester.execute("bob")]); + assert.deepEqual( + results.map((result) => result.data), + ["alice-tab", "bob-tab"] + ); + assert.equal(network.discoveryResponses.size, 0); +}); + +test("a publish completing after timeout cannot dispatch or retain its response subscription", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + await owner.connect("alice", "late"); + let release!: () => void; + network.beforeDiscoveryPublish = () => + new Promise((resolve) => { + release = resolve; + }); + await assert.rejects(new Instance(network).execute("alice"), SessionDiscoveryError); + release(); + await new Promise((resolve) => setImmediate(resolve)); + assert.deepEqual(owner.executions, []); + assert.equal(network.discoveryResponses.size, 0); +}); + +test("a newer connection for a session on another instance displaces the older one", async () => { + const network = new RedisNetwork(); + const previous = new Instance(network); + const current = new Instance(network); + await previous.connect("alice", "tab"); + await current.connect("alice", "tab"); + assert.deepEqual(previous.displaced, ["tab"]); + assert.deepEqual(current.displaced, []); + assert.equal((await new Instance(network).execute("alice")).data, "tab"); + assert.deepEqual(previous.executions, []); + assert.deepEqual(current.executions, ["tab"]); +}); + +test("a claim published before a local connection's own claim does not displace it", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + const other = new Instance(network); + await owner.connect("alice", "sibling"); + let release!: () => void; + network.beforeClaimPublish = () => + new Promise((resolve) => { + release = resolve; + }); + const pending = owner.connect("alice", "tab"); + await new Promise((resolve) => setImmediate(resolve)); + network.beforeClaimPublish = undefined; + // the other instance's claim is delivered first, so the owner's connection is the newer one + await other.connect("alice", "tab"); + release(); + await pending; + assert.deepEqual(owner.displaced, []); + assert.deepEqual(other.displaced, ["tab"]); +}); + +test("discovery lists a session reported by several instances once", async () => { + const network = new RedisNetwork(); + network.dropClaims = true; + await new Instance(network).connect("alice", "tab"); + await new Instance(network).connect("alice", "tab"); + const sessions = await new Instance(network).dispatcher.discoverSessions("alice"); + assert.deepEqual( + sessions.map((session) => session.sessionId), + ["tab"] + ); +}); + +test("disconnecting the last tab removes its session claim subscription", async () => { + const network = new RedisNetwork(); + const owner = new Instance(network); + const connection = await owner.connect("alice", "only"); + await owner.disconnect(connection); + assert.equal(network.claims.get("alice")?.size, 0); +}); diff --git a/mcp/packages/server/src/dispatch/MultiInstanceTaskDispatcher.ts b/mcp/packages/server/src/dispatch/MultiInstanceTaskDispatcher.ts new file mode 100644 index 0000000000..10c895e659 --- /dev/null +++ b/mcp/packages/server/src/dispatch/MultiInstanceTaskDispatcher.ts @@ -0,0 +1,216 @@ +import { randomUUID } from "node:crypto"; +import type { PenpotSession, PluginTaskRequest } from "@penpot/mcp-common"; +import type { PenpotConnection } from "../PenpotConnection"; +import type { AbstractPluginTask } from "../PluginTask"; +import { RemotePluginTask } from "../RemotePluginTask"; +import type { RedisBridge, SessionClaim, SessionDiscoveryResponse } from "../RedisBridge"; +import { createLogger } from "../logger"; +import { SessionDiscoveryError, TaskDispatcher, type TaskDispatchHost } from "./TaskDispatcher"; + +/** Routes tasks over Redis and discovers live sessions across MCP instances. */ +export class MultiInstanceTaskDispatcher extends TaskDispatcher { + private readonly logger = createLogger("MultiInstanceTaskDispatcher"); + private readonly instanceId = randomUUID(); + private readonly subscribedUsers = new Set(); + private readonly connectionChanges = new Map>(); + private readonly pendingDiscoveries = new Map void>(); + private readonly claimIds = new WeakMap(); + /** local connections whose own claim has been delivered back to this instance. */ + private readonly confirmedClaims = new WeakSet(); + + constructor( + private readonly host: TaskDispatchHost, + private readonly redisBridge: RedisBridge, + private readonly discoveryTimeoutMs = 5000 + ) { + super(); + } + + async dispatch(task: AbstractPluginTask, userToken: string | null, sessionId?: string): Promise { + const token = this.requireUserToken(userToken); + const targetId = sessionId ?? this.selectOnlySession(await this.discoverSessions(token)); + this.host.sendRemoteTask(task, token, targetId); + } + + async discoverSessions(userToken: string | null): Promise { + const token = this.requireUserToken(userToken); + const requestId = randomUUID(); + const responses = new Map(); + let expectedResponses: number | undefined; + let settled = false; + let resolve!: (sessions: PenpotSession[]) => void; + let reject!: (error: Error) => void; + const result = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + const fail = () => { + if (!settled) { + settled = true; + reject(new SessionDiscoveryError()); + } + }; + const complete = () => { + if (!settled && expectedResponses !== undefined && responses.size === expectedResponses) { + settled = true; + resolve(this.uniqueSessions([...responses.values()].flat())); + } + }; + const timeout = setTimeout(fail, this.discoveryTimeoutMs); + this.pendingDiscoveries.set(requestId, fail); + const releaseResponses = () => { + void this.redisBridge + .unsubscribeFromDiscoveryResponses(requestId) + .catch((error) => this.logger.error(error, "Failed to release discovery response subscription")); + }; + // Replies can arrive before publish returns its recipient count. + void this.redisBridge + .sendDiscoveryRequest(token, requestId, (response: SessionDiscoveryResponse) => { + if (settled) return; + responses.set(response.instanceId, response.sessions); + complete(); + }) + .then((count) => { + expectedResponses = count; + complete(); + // A slow subscribe/publish may finish after the discovery timeout. + if (settled) releaseResponses(); + }) + .catch(fail); + try { + return await result; + } finally { + clearTimeout(timeout); + this.pendingDiscoveries.delete(requestId); + releaseResponses(); + } + } + + async onNewConnection(connection: PenpotConnection): Promise { + const token = this.requireUserToken(connection.userToken); + return this.changeConnections(token, async () => { + // subscribe to this session's Redis request channel so that task requests + // issued by other instances are dispatched to this instance + await this.redisBridge.subscribeToTasks(token, connection.session.sessionId, (request) => + this.dispatchForwardedTask(connection, request) + ); + if (!this.subscribedUsers.has(token)) { + await this.redisBridge.subscribeToDiscovery(token, (request) => { + this.redisBridge.publishDiscoveryResponse(request.id, { + instanceId: this.instanceId, + sessions: this.host.getUserConnections(token)?.getSessions() ?? [], + }); + }); + try { + await this.redisBridge.subscribeToSessionClaims(token, (claim) => + this.handleSessionClaim(token, claim) + ); + } catch (error) { + await this.redisBridge.unsubscribeFromDiscovery(token); + throw error; + } + this.subscribedUsers.add(token); + } + // announce the connection so that other instances close older connections for the session + const claimId = randomUUID(); + this.claimIds.set(connection, claimId); + try { + await this.redisBridge.publishSessionClaim(token, { + instanceId: this.instanceId, + claimId, + sessionId: connection.session.sessionId, + }); + } catch (error) { + this.logger.error(error, "Failed to publish session claim"); + } + }); + } + + /** + * Closes the local connection for a claimed session when the claim comes from a newer connection. + * + * Claims arrive in publication order, so a foreign claim arriving after the local connection's own + * claim comes from a newer connection. A foreign claim arriving earlier comes from an older + * connection, which its instance closes when the local claim reaches it. + */ + private handleSessionClaim(userToken: string, claim: SessionClaim): void { + const local = this.host.getUserConnections(userToken)?.getRegistered(claim.sessionId); + if (!local) return; + if (claim.instanceId === this.instanceId) { + if (this.claimIds.get(local) === claim.claimId) this.confirmedClaims.add(local); + return; + } + if (this.confirmedClaims.has(local)) { + this.host.displaceConnection(local); + } + } + + /** Removes repeated sessions reported while a displaced connection is being closed. */ + private uniqueSessions(sessions: PenpotSession[]): PenpotSession[] { + const unique = new Map(); + for (const session of sessions) { + if (!unique.has(session.sessionId)) unique.set(session.sessionId, session); + } + return [...unique.values()]; + } + + async onConnectionClosed(connection: PenpotConnection): Promise { + const token = this.requireUserToken(connection.userToken); + return this.changeConnections(token, async () => { + await this.redisBridge.unsubscribeFromTasks(token, connection.session.sessionId); + if (!this.host.getUserConnections(token)?.size && this.subscribedUsers.has(token)) { + this.subscribedUsers.delete(token); + await this.redisBridge.unsubscribeFromDiscovery(token); + await this.redisBridge.unsubscribeFromSessionClaims(token); + } + }); + } + + async close(): Promise { + for (const fail of this.pendingDiscoveries.values()) fail(); + await Promise.allSettled(this.connectionChanges.values()); + } + + /** + * Dispatches a task request received over Redis to the locally-connected plugin. + * + * Invoked on the instance subscribed to a session's request channel when another + * instance (or this one) issues a task request. A {@link RemotePluginTask} is created + * so that, once the plugin responds, the outcome is published back to the issuing + * instance's Redis response channel via the standard response-handling path. + * + * On failure to dispatch (e.g. the plugin is not connected here), an error response + * is published immediately so the requester need not wait for its timeout. + * + * @param connection - The local connection owning the session's request channel + * @param request - The serialized task request, passed through from Redis + */ + private dispatchForwardedTask(connection: PenpotConnection, request: PluginTaskRequest): void { + // The response is published on the channel keyed by the original request ID. + const task = new RemotePluginTask(request.task, request.params, this.redisBridge, request.id); + this.logger.debug("Dispatching remote task %s as %s to Penpot via WebSocket", request.id, task.id); + + try { + this.host.sendLocalTask(task, connection); + } catch (error) { + task.rejectWithError(error instanceof Error ? error : new Error(String(error))); + } + } + + /** Serializes subscription changes for a user across connects and disconnects. */ + private changeConnections(userToken: string, change: () => Promise): Promise { + const previous = this.connectionChanges.get(userToken) ?? Promise.resolve(); + const current = previous.catch(() => {}).then(change); + this.connectionChanges.set(userToken, current); + const cleanup = () => { + if (this.connectionChanges.get(userToken) === current) this.connectionChanges.delete(userToken); + }; + void current.then(cleanup, cleanup); + return current; + } + + private requireUserToken(userToken: string | null): string { + if (!userToken) throw new Error("Multi-instance task routing requires a user token."); + return userToken; + } +} diff --git a/mcp/packages/server/src/dispatch/SingleInstanceTaskDispatcher.ts b/mcp/packages/server/src/dispatch/SingleInstanceTaskDispatcher.ts new file mode 100644 index 0000000000..60ed052105 --- /dev/null +++ b/mcp/packages/server/src/dispatch/SingleInstanceTaskDispatcher.ts @@ -0,0 +1,27 @@ +import type { PenpotSession } from "@penpot/mcp-common"; +import type { AbstractPluginTask } from "../PluginTask"; +import { TaskDispatcher, type TaskDispatchHost } from "./TaskDispatcher"; + +/** Routes tasks to connections held by this MCP server instance. */ +export class SingleInstanceTaskDispatcher extends TaskDispatcher { + constructor(private readonly host: TaskDispatchHost) { + super(); + } + + async discoverSessions(userToken: string | null): Promise { + return this.host.getUserConnections(userToken)?.getSessions() ?? []; + } + + async dispatch(task: AbstractPluginTask, userToken: string | null, sessionId?: string): Promise { + const targetId = sessionId ?? this.selectOnlySession(await this.discoverSessions(userToken)); + const connection = this.host.getUserConnections(userToken)?.get(targetId); + if (!connection) { + throw new Error(`Penpot session ${JSON.stringify(targetId)} is not connected for this user.`); + } + this.host.sendLocalTask(task, connection); + } + + async onNewConnection(): Promise {} + async onConnectionClosed(): Promise {} + async close(): Promise {} +} diff --git a/mcp/packages/server/src/dispatch/TaskDispatcher.ts b/mcp/packages/server/src/dispatch/TaskDispatcher.ts new file mode 100644 index 0000000000..062af6b80c --- /dev/null +++ b/mcp/packages/server/src/dispatch/TaskDispatcher.ts @@ -0,0 +1,49 @@ +import type { PenpotSession } from "@penpot/mcp-common"; +import type { PenpotConnection } from "../PenpotConnection"; +import type { AbstractPluginTask } from "../PluginTask"; +import type { UserPenpotConnections } from "../UserPenpotConnections"; + +/** Local connection access and tracked task execution provided by PluginBridge. */ +export interface TaskDispatchHost { + getUserConnections(userToken: string | null): UserPenpotConnections | undefined; + sendLocalTask(task: AbstractPluginTask, connection: PenpotConnection): void; + sendRemoteTask(task: AbstractPluginTask, userToken: string, sessionId: string): void; + displaceConnection(connection: PenpotConnection): void; +} + +/** Session discovery, task routing, and connection subscription lifecycle. */ +export abstract class TaskDispatcher { + abstract discoverSessions(userToken: string | null): Promise; + abstract dispatch(task: AbstractPluginTask, userToken: string | null, sessionId?: string): Promise; + abstract onNewConnection(connection: PenpotConnection): Promise; + abstract onConnectionClosed(connection: PenpotConnection): Promise; + abstract close(): Promise; + + /** Selects the sole session from a complete discovery result. */ + protected selectOnlySession(sessions: PenpotSession[]): string { + if (sessions.length === 0) { + throw new Error("No Penpot sessions are connected. Please connect the Penpot MCP plugin and retry."); + } + if (sessions.length !== 1) { + throw new Error( + "Multiple Penpot sessions are connected. Ask the user to select one of the active sessions, " + + "then retry with its sessionId:\n" + + sessions + .map( + (session) => + `${session.sessionId}: ${session.fileId ? `${JSON.stringify(session.fileName)} (file ${session.fileId})` : "no active workspace"}` + ) + .join("\n") + ); + } + return sessions[0].sessionId; + } +} + +/** Error returned when discovery cannot establish the complete set of sessions. */ +export class SessionDiscoveryError extends Error { + constructor() { + super("Penpot session discovery failed. This may be due to temporary server load. You can retry the request."); + this.name = "SessionDiscoveryError"; + } +} diff --git a/mcp/packages/server/src/tools/ExecuteCodeTool.ts b/mcp/packages/server/src/tools/ExecuteCodeTool.ts index 6a514e0cf4..98932a154a 100644 --- a/mcp/packages/server/src/tools/ExecuteCodeTool.ts +++ b/mcp/packages/server/src/tools/ExecuteCodeTool.ts @@ -16,12 +16,15 @@ export class ExecuteCodeArgs { .string() .min(1, "Code cannot be empty") .describe("The JavaScript code to execute in the plugin context."), + sessionId: Tool.SESSION_ID_SCHEMA, }; /** * The JavaScript code to execute in the plugin context. */ code!: string; + + sessionId?: string; } /** @@ -46,8 +49,8 @@ export class ExecuteCodeTool extends Tool { "Executes JavaScript code in the Penpot plugin context.\n" + "IMPORTANT: Before using this tool, make sure you have read the 'Penpot High-Level Overview' and know " + "which Penpot API functionality is necessary and how to use it.\n" + - "You have access two main objects: `penpot` (the Penpot API, of type `Penpot`), `penpotUtils`, " + - "and `storage`.\n" + + "You have access to `penpot` (of type `Penpot`), `penpotMgmt` (of type `PenpotMgmt`), `penpotUtils`, and `storage`.\n" + + "If `penpotMgmt` is null, file management is unavailable and only the open file can be used.\n" + "`storage` is an object in which arbitrary data can be stored, simply by adding a new attribute; " + "stored attributes can be referenced in future calls to this tool, so any intermediate results that " + "could come in handy later should be stored in `storage` instead of just a fleeting variable; " + @@ -67,7 +70,7 @@ export class ExecuteCodeTool extends Tool { protected async executeCore(args: ExecuteCodeArgs): Promise { const taskParams: ExecuteCodeTaskParams = { code: args.code }; const task = new ExecuteCodePluginTask(taskParams); - const result = await this.mcpServer.pluginBridge.executePluginTask(task); + const result = await this.mcpServer.pluginBridge.executePluginTask(task, args.sessionId); if (result.data !== undefined) { return new TextResponse(JSON.stringify(result.data, null, 2)); diff --git a/mcp/packages/server/src/tools/ExportShapeTool.ts b/mcp/packages/server/src/tools/ExportShapeTool.ts index 0d84df9380..7eb3e321c9 100644 --- a/mcp/packages/server/src/tools/ExportShapeTool.ts +++ b/mcp/packages/server/src/tools/ExportShapeTool.ts @@ -36,6 +36,7 @@ export class ExportShapeArgs { "Optional file path to save the exported image to. If not provided, " + "the image data is returned directly for you to see." ), + sessionId: Tool.SESSION_ID_SCHEMA, }; shapeId!: string; @@ -45,6 +46,8 @@ export class ExportShapeArgs { mode: "shape" | "fill" = "shape"; filePath?: string; + + sessionId?: string; } /** @@ -151,7 +154,7 @@ export class ExportShapeTool extends Tool { // execute the code and obtain the image data const task = new ExecuteCodePluginTask({ code: code }); - const result = await this.mcpServer.pluginBridge.executePluginTask(task); + const result = await this.mcpServer.pluginBridge.executePluginTask(task, args.sessionId); const imageData = result.data!.result; // handle output and return response diff --git a/mcp/packages/server/src/tools/ImportImageTool.ts b/mcp/packages/server/src/tools/ImportImageTool.ts index 7581aaf525..d5ffb5eb9b 100644 --- a/mcp/packages/server/src/tools/ImportImageTool.ts +++ b/mcp/packages/server/src/tools/ImportImageTool.ts @@ -30,6 +30,7 @@ export class ImportImageArgs { .describe( "Optional height for the rectangle. If only height is provided, width is calculated to maintain aspect ratio." ), + sessionId: Tool.SESSION_ID_SCHEMA, }; filePath!: string; @@ -41,6 +42,8 @@ export class ImportImageArgs { width?: number; height?: number; + + sessionId?: string; } /** @@ -116,7 +119,7 @@ export class ImportImageTool extends Tool { return { shapeId: rectangle.id }; `; const task = new ExecuteCodePluginTask({ code: code }); - const executionResult = await this.mcpServer.pluginBridge.executePluginTask(task); + const executionResult = await this.mcpServer.pluginBridge.executePluginTask(task, args.sessionId); return new TextResponse(JSON.stringify(executionResult.data?.result, null, 2)); } diff --git a/mcp/packages/server/src/tools/SubmitFeedbackTool.ts b/mcp/packages/server/src/tools/SubmitFeedbackTool.ts new file mode 100644 index 0000000000..57273991fb --- /dev/null +++ b/mcp/packages/server/src/tools/SubmitFeedbackTool.ts @@ -0,0 +1,69 @@ +import { z } from "zod"; +import type { Feedback, PenpotBackend } from "../PenpotBackendClient"; +import type { PenpotMcpServer } from "../PenpotMcpServer"; +import { Tool } from "../Tool"; +import { TextResponse, type ToolResponse } from "../ToolResponse"; + +/** Arguments for submitting a plain-text feedback report. */ +export class SubmitFeedbackArgs implements Feedback { + static schema = { + subject: z + .string() + .min(1) + .max(120) + .regex(/\S/) + // the Unicode Cc category; `\P{Cc}` is avoided because clients that compile + // advertised patterns with other regex engines (e.g. Python) reject it + .regex(/^[^\x00-\x1F\x7F-\x9F]*$/) + .describe("Short, specific summary without line breaks or other control characters (max 120 characters)."), + feedback: z + .string() + .min(1) + .max(5000) + .regex(/\S/) + // line breaks and tabs are the only control characters allowed + .regex(/^[^\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]*$/) + .describe("Concise plain-text feedback (max 5,000 characters)."), + }; + + subject!: string; + feedback!: string; +} + +/** Tool for user-approved feedback about the Penpot MCP integration. */ +export class SubmitFeedbackTool extends Tool { + constructor( + mcpServer: PenpotMcpServer, + private readonly backend: PenpotBackend + ) { + super(mcpServer, SubmitFeedbackArgs.schema); + } + + public getToolName(): string { + return "submit_feedback"; + } + + public getToolDescription(): string { + return ( + "Submit feedback about Penpot MCP, provided that the user explicitly requests or approves submission " + + "and there is a concrete, actionable issue to report. " + + "Focus on bugs, limitations, or unclear documentation that blocked work or caused substantial rework. " + + "Briefly propose what to report and ask the user at a natural pause. " + + "Skip vague dissatisfaction, routine transient errors, and your own coding mistakes. " + + "Describe the operation, expected and actual behavior, and any workaround. Exclude sensitive content." + ); + } + + protected formatArgs(_args: SubmitFeedbackArgs): string { + return "[feedback omitted]"; + } + + protected async executeCore(args: SubmitFeedbackArgs): Promise { + const userToken = this.getSessionContext()?.userToken; + if (!userToken?.trim()) { + throw new Error("Include userToken in the MCP server URL to submit feedback."); + } + await this.backend.submitFeedback(userToken, { subject: args.subject, feedback: args.feedback }); + return new TextResponse("Feedback queued. It is emailed in the background, so delivery is not confirmed."); + } +} diff --git a/mcp/packages/server/src/tools/CljCheckParentheses.ts b/mcp/packages/server/src/tools/dev/CljCheckParentheses.ts similarity index 97% rename from mcp/packages/server/src/tools/CljCheckParentheses.ts rename to mcp/packages/server/src/tools/dev/CljCheckParentheses.ts index 81e2a9628c..d7a5af6fd2 100644 --- a/mcp/packages/server/src/tools/CljCheckParentheses.ts +++ b/mcp/packages/server/src/tools/dev/CljCheckParentheses.ts @@ -1,9 +1,9 @@ import { z } from "zod"; -import { Tool } from "../Tool"; +import { Tool } from "../../Tool"; import "reflect-metadata"; -import type { ToolResponse } from "../ToolResponse"; -import { TextResponse } from "../ToolResponse"; -import type { PenpotMcpServer } from "../PenpotMcpServer"; +import type { ToolResponse } from "../../ToolResponse"; +import { TextResponse } from "../../ToolResponse"; +import type { PenpotMcpServer } from "../../PenpotMcpServer"; import * as fs from "fs"; /** diff --git a/mcp/packages/server/src/tools/CljsCompilerOutputTool.ts b/mcp/packages/server/src/tools/dev/CljsCompilerOutputTool.ts similarity index 86% rename from mcp/packages/server/src/tools/CljsCompilerOutputTool.ts rename to mcp/packages/server/src/tools/dev/CljsCompilerOutputTool.ts index 11989c6303..772d9461df 100644 --- a/mcp/packages/server/src/tools/CljsCompilerOutputTool.ts +++ b/mcp/packages/server/src/tools/dev/CljsCompilerOutputTool.ts @@ -1,9 +1,9 @@ -import { Tool, EmptyToolArgs } from "../Tool"; +import { Tool, EmptyToolArgs } from "../../Tool"; import "reflect-metadata"; -import type { ToolResponse } from "../ToolResponse"; -import { TextResponse } from "../ToolResponse"; -import { PenpotMcpServer } from "../PenpotMcpServer"; -import { NreplClient } from "../NreplClient"; +import type { ToolResponse } from "../../ToolResponse"; +import { TextResponse } from "../../ToolResponse"; +import { PenpotMcpServer } from "../../PenpotMcpServer"; +import { NreplClient } from "../../NreplClient"; /** * Reports the compiler status of the shadow-cljs `:main` build. diff --git a/mcp/packages/server/src/tools/CljsReplTool.ts b/mcp/packages/server/src/tools/dev/CljsReplTool.ts similarity index 89% rename from mcp/packages/server/src/tools/CljsReplTool.ts rename to mcp/packages/server/src/tools/dev/CljsReplTool.ts index bd894caef3..ab2babf56d 100644 --- a/mcp/packages/server/src/tools/CljsReplTool.ts +++ b/mcp/packages/server/src/tools/dev/CljsReplTool.ts @@ -1,10 +1,10 @@ import { z } from "zod"; -import { Tool } from "../Tool"; +import { Tool } from "../../Tool"; import "reflect-metadata"; -import type { ToolResponse } from "../ToolResponse"; -import { TextResponse } from "../ToolResponse"; -import { PenpotMcpServer } from "../PenpotMcpServer"; -import { NreplClient } from "../NreplClient"; +import type { ToolResponse } from "../../ToolResponse"; +import { TextResponse } from "../../ToolResponse"; +import { PenpotMcpServer } from "../../PenpotMcpServer"; +import { NreplClient } from "../../NreplClient"; /** * Arguments for the CljsReplTool. diff --git a/mcp/packages/server/src/tools/ImportPenpotFileTool.ts b/mcp/packages/server/src/tools/dev/ImportPenpotFileTool.ts similarity index 98% rename from mcp/packages/server/src/tools/ImportPenpotFileTool.ts rename to mcp/packages/server/src/tools/dev/ImportPenpotFileTool.ts index f55ae194f2..50d18f5ae5 100644 --- a/mcp/packages/server/src/tools/ImportPenpotFileTool.ts +++ b/mcp/packages/server/src/tools/dev/ImportPenpotFileTool.ts @@ -1,10 +1,10 @@ import { z } from "zod"; -import { Tool } from "../Tool"; -import { TextResponse, ToolResponse } from "../ToolResponse"; +import { Tool } from "../../Tool"; +import { TextResponse, ToolResponse } from "../../ToolResponse"; import "reflect-metadata"; -import { PenpotMcpServer } from "../PenpotMcpServer"; -import { NreplClient } from "../NreplClient"; -import { createLogger } from "../logger"; +import { PenpotMcpServer } from "../../PenpotMcpServer"; +import { NreplClient } from "../../NreplClient"; +import { createLogger } from "../../logger"; import * as crypto from "crypto"; import * as fs from "fs"; import * as path from "path"; diff --git a/mcp/packages/server/src/tools/ReadTaigaIssueTool.ts b/mcp/packages/server/src/tools/dev/ReadTaigaIssueTool.ts similarity index 96% rename from mcp/packages/server/src/tools/ReadTaigaIssueTool.ts rename to mcp/packages/server/src/tools/dev/ReadTaigaIssueTool.ts index 29e8bea460..decd926182 100644 --- a/mcp/packages/server/src/tools/ReadTaigaIssueTool.ts +++ b/mcp/packages/server/src/tools/dev/ReadTaigaIssueTool.ts @@ -1,9 +1,9 @@ import { z } from "zod"; -import { Tool } from "../Tool"; +import { Tool } from "../../Tool"; import "reflect-metadata"; -import type { ToolResponse } from "../ToolResponse"; -import { TextResponse } from "../ToolResponse"; -import { PenpotMcpServer } from "../PenpotMcpServer"; +import type { ToolResponse } from "../../ToolResponse"; +import { TextResponse } from "../../ToolResponse"; +import { PenpotMcpServer } from "../../PenpotMcpServer"; /** * Arguments for the {@link ReadTaigaIssueTool}. diff --git a/mcp/pnpm-lock.yaml b/mcp/pnpm-lock.yaml index 917b306858..130dc315db 100644 --- a/mcp/pnpm-lock.yaml +++ b/mcp/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' @@ -177,6 +177,24 @@ importers: specifier: ^3.9.10 version: 3.9.10 + packages/browser-extension: + devDependencies: + '@penpot/mcp-common': + specifier: workspace:../common + version: link:../common + '@types/chrome': + specifier: ^0.3.6 + version: 0.3.6 + esbuild: + specifier: ^0.28.2 + version: 0.28.2 + tsx: + specifier: ^4.23.15 + version: 4.23.15 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + packages/common: devDependencies: typescript: @@ -189,8 +207,8 @@ importers: specifier: 1.5.0 version: 1.5.0 '@penpot/plugin-types': - specifier: 1.5.0 - version: 1.5.0 + specifier: link:../../../plugins/libs/plugin-types + version: link:../../../plugins/libs/plugin-types devDependencies: cross-env: specifier: ^10.1.0 @@ -660,9 +678,6 @@ packages: '@penpot/plugin-styles@1.5.0': resolution: {integrity: sha512-rwXFBRPd0IZs3ens+eRT8oA5WqkQgF6xlW6fCfH7U1NHRjej9HH7vTGXmfjoIVml6Xx1sbyYHZVxkQC8Ub8+zA==} - '@penpot/plugin-types@1.5.0': - resolution: {integrity: sha512-hFk4Rx66rLaSUWoN6bhxXkDI2ymwKg9xokdslvZvXM5MSmEmfhsJKUMwtuzgYWljbbfaQhK+DjK8g8yVbbavtA==} - '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -786,6 +801,9 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/chrome@0.3.6': + resolution: {integrity: sha512-NXCrCBefCHCKMVbkaxfBQqyP766Wg85S2FQhixCCxq7AtjJwoEaja2y5NSqCzpihIi9mPqhaF5RuybTnrdYD2Q==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -795,6 +813,9 @@ packages: '@types/express@5.0.6': resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} + '@types/har-format@1.2.16': + resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} + '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} @@ -1855,8 +1876,6 @@ snapshots: '@penpot/plugin-styles@1.5.0': {} - '@penpot/plugin-types@1.5.0': {} - '@pinojs/redact@0.4.0': {} '@rolldown/binding-android-arm-eabi@1.2.15': @@ -1923,6 +1942,10 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 26.6.5 + '@types/chrome@0.3.6': + dependencies: + '@types/har-format': 1.2.16 + '@types/connect@3.4.38': dependencies: '@types/node': 26.6.5 @@ -1940,6 +1963,8 @@ snapshots: '@types/express-serve-static-core': 5.1.3 '@types/serve-static': 2.2.0 + '@types/har-format@1.2.16': {} + '@types/http-errors@2.0.5': {} '@types/js-yaml@4.0.9': {} diff --git a/mcp/pnpm-workspace.yaml b/mcp/pnpm-workspace.yaml index 3da100dca6..5aa8300f88 100644 --- a/mcp/pnpm-workspace.yaml +++ b/mcp/pnpm-workspace.yaml @@ -19,6 +19,7 @@ packages: - "packages/common" - "packages/server" - "packages/plugin" + - "packages/browser-extension" overrides: '@hono/node-server@<2.0.5': ^2.0.5 diff --git a/mcp/scripts/build b/mcp/scripts/build index 3e5337fda2..671091ac9f 100755 --- a/mcp/scripts/build +++ b/mcp/scripts/build @@ -21,7 +21,7 @@ set +e set -e popd -pnpm -r --filter "!mcp-plugin" install; +pnpm -r --filter "!mcp-plugin" --filter "!mcp-browser-extension" install; pnpm -r --filter "mcp-server" run build; rsync -avr packages/server/dist/ ./dist/; diff --git a/mcp/scripts/start-mcp-devenv-oauth b/mcp/scripts/start-mcp-devenv-oauth new file mode 100755 index 0000000000..230dbf4029 --- /dev/null +++ b/mcp/scripts/start-mcp-devenv-oauth @@ -0,0 +1,19 @@ +#!/bin/sh + +# This starts the MCP server in multi-user mode with OAuth enabled, for +# testing OAuth in the Penpot development environment (assuming devenv). +# Agentic tooling expects the single-user server from start-mcp-devenv. + +set -e + +if [ -z "$PENPOT_PUBLIC_URI" ]; then + echo "PENPOT_PUBLIC_URI is not set; run this inside the devenv container." >&2 + exit 1 +fi + +PENPOT_MCP_AUTH_URI="${PENPOT_PUBLIC_URI%/}/api/mcp/oauth" + +echo "OAuth authorization URI: $PENPOT_MCP_AUTH_URI" +echo "MCP endpoint for clients: ${PENPOT_PUBLIC_URI%/}/mcp/stream" + +PENPOT_MCP_SERVER_HOST=0.0.0.0 PENPOT_MCP_REMOTE_MODE=true PENPOT_MCP_AUTH_URI="$PENPOT_MCP_AUTH_URI" pnpm run bootstrap:multi-user diff --git a/media-processor/package.json b/media-processor/package.json index 110fdbb7c3..aa7b3f7449 100644 --- a/media-processor/package.json +++ b/media-processor/package.json @@ -14,27 +14,27 @@ "fmt:check": "prettier --check src/ test/", "clean": "rm -rf dist/" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "dependencies": { "express": "^5.2.1", - "multer": "^2.2.0", + "multer": "^2.4.0", "p-queue": "^9.3.3", - "pino": "^10.3.1", + "pino": "^10.4.0", "pino-loki": "^3.0.0", - "pino-pretty": "^13.1.3", - "sharp": "^0.35.4", - "zod": "^4.4.3" + "pino-pretty": "^13.2.0", + "sharp": "^0.35.5", + "zod": "^4.6.5" }, "devDependencies": { "@types/express": "^5.0.6", - "@types/multer": "^2.0.0", - "@types/node": "^26.3.0", + "@types/multer": "^2.3.0", + "@types/node": "^26.6.4", "@types/supertest": "^7.2.1", "esbuild": "^0.28.2", - "prettier": "^3.6.2", - "supertest": "^7.2.2", - "tsx": "^4.23.12", + "prettier": "^3.9.9", + "supertest": "^7.3.1", + "tsx": "^4.23.15", "typescript": "^7.0.2", - "vitest": "^4.1.11" + "vitest": "^5.0.3" } } diff --git a/media-processor/pnpm-lock.yaml b/media-processor/pnpm-lock.yaml index f09b091862..e5c4f4debe 100644 --- a/media-processor/pnpm-lock.yaml +++ b/media-processor/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' @@ -170,36 +170,36 @@ importers: specifier: ^5.2.1 version: 5.2.1 multer: - specifier: ^2.2.0 - version: 2.2.0 + specifier: ^2.4.0 + version: 2.4.0 p-queue: specifier: ^9.3.3 version: 9.3.3 pino: - specifier: ^10.3.1 - version: 10.3.1 + specifier: ^10.4.0 + version: 10.4.0 pino-loki: specifier: ^3.0.0 version: 3.0.0 pino-pretty: - specifier: ^13.1.3 - version: 13.1.3 + specifier: ^13.2.0 + version: 13.2.0 sharp: - specifier: ^0.35.4 - version: 0.35.4(@types/node@26.3.0) + specifier: ^0.35.5 + version: 0.35.5(@types/node@26.6.4) zod: - specifier: ^4.4.3 - version: 4.4.3 + specifier: ^4.6.5 + version: 4.6.5 devDependencies: '@types/express': specifier: ^5.0.6 version: 5.0.6 '@types/multer': - specifier: ^2.0.0 - version: 2.2.0 + specifier: ^2.3.0 + version: 2.3.0 '@types/node': - specifier: ^26.3.0 - version: 26.3.0 + specifier: ^26.6.4 + version: 26.6.4 '@types/supertest': specifier: ^7.2.1 version: 7.2.1 @@ -207,20 +207,20 @@ importers: specifier: ^0.28.2 version: 0.28.2 prettier: - specifier: ^3.6.2 - version: 3.9.6 + specifier: ^3.9.9 + version: 3.9.9 supertest: - specifier: ^7.2.2 - version: 7.2.2 + specifier: ^7.3.1 + version: 7.3.1 tsx: - specifier: ^4.23.12 - version: 4.23.12 + specifier: ^4.23.15 + version: 4.23.15 typescript: specifier: ^7.0.2 version: 7.0.2 vitest: - specifier: ^4.1.11 - version: 4.1.11(@types/node@26.3.0)(vite@7.3.6(@types/node@26.3.0)(tsx@4.23.12)) + specifier: ^5.0.3 + version: 5.0.3(@types/node@26.6.4)(vite@7.3.6(@types/node@26.6.4)(tsx@4.23.15)) packages: @@ -387,167 +387,177 @@ packages: resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.35.4': - resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==} + '@img/sharp-darwin-arm64@0.35.5': + resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.35.4': - resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==} + '@img/sharp-darwin-x64@0.35.5': + resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-freebsd-wasm32@0.35.4': - resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==} + '@img/sharp-freebsd-wasm32@0.35.5': + resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} engines: {node: '>=20.9.0'} os: [freebsd] - '@img/sharp-libvips-darwin-arm64@1.3.3': - resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==} + '@img/sharp-libvips-darwin-arm64@1.3.4': + resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.3.3': - resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==} + '@img/sharp-libvips-darwin-x64@1.3.4': + resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.3.3': - resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==} + '@img/sharp-libvips-linux-arm64@1.3.4': + resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.3.3': - resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==} + '@img/sharp-libvips-linux-arm@1.3.4': + resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.3.3': - resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==} + '@img/sharp-libvips-linux-ppc64@1.3.4': + resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.3.3': - resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==} + '@img/sharp-libvips-linux-riscv64@1.3.4': + resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.3.3': - resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==} + '@img/sharp-libvips-linux-s390x@1.3.4': + resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.3.3': - resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==} + '@img/sharp-libvips-linux-x64@1.3.4': + resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.3.3': - resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.3.3': - resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==} + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.35.4': - resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==} + '@img/sharp-linux-arm64@0.35.5': + resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.35.4': - resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==} + '@img/sharp-linux-arm@0.35.5': + resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.35.4': - resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==} + '@img/sharp-linux-ppc64@0.35.5': + resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.35.4': - resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==} + '@img/sharp-linux-riscv64@0.35.5': + resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.35.4': - resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==} + '@img/sharp-linux-s390x@0.35.5': + resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.35.4': - resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==} + '@img/sharp-linux-x64@0.35.5': + resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.35.4': - resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==} + '@img/sharp-linuxmusl-arm64@0.35.5': + resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.35.4': - resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==} + '@img/sharp-linuxmusl-x64@0.35.5': + resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.35.4': - resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==} + '@img/sharp-wasm32@0.35.5': + resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} engines: {node: '>=20.9.0'} - '@img/sharp-webcontainers-wasm32@0.35.4': - resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==} + '@img/sharp-webcontainers-wasm32@0.35.5': + resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.35.4': - resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==} + '@img/sharp-win32-arm64@0.35.5': + resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.35.4': - resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==} + '@img/sharp-win32-ia32@0.35.5': + resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} engines: {node: ^20.9.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.35.4': - resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==} + '@img/sharp-win32-x64@0.35.5': + resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} @@ -696,9 +706,6 @@ packages: cpu: [x64] os: [win32] - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -729,11 +736,11 @@ packages: '@types/methods@1.1.4': resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} - '@types/multer@2.2.0': - resolution: {integrity: sha512-3U1troeqGV8Ntp7Q3klwf4zr23VEoqYVocYXaswm9+8z3O9UHDYAqLxjJ/h550iRADTjKdOdhhasXw6gD6kYtg==} + '@types/multer@2.3.0': + resolution: {integrity: sha512-i7STIm9V4K2MPH4ZYMtrZAwNxs3kglk2LgleaTuB9pUXgADBcQYQuYMd7+6xgTIxfoggIkFA/DkkvycdZpIARA==} - '@types/node@26.3.0': - resolution: {integrity: sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw==} + '@types/node@26.6.4': + resolution: {integrity: sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==} '@types/qs@6.15.1': resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} @@ -873,11 +880,8 @@ packages: cpu: [x64] os: [win32] - '@vitest/expect@4.1.11': - resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} - - '@vitest/mocker@4.1.11': - resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + '@vitest/mocker@5.0.3': + resolution: {integrity: sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -887,20 +891,8 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.11': - resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} - - '@vitest/runner@4.1.11': - resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} - - '@vitest/snapshot@4.1.11': - resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} - - '@vitest/spy@4.1.11': - resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} - - '@vitest/utils@4.1.11': - resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + '@vitest/spy@5.0.3': + resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} @@ -927,9 +919,6 @@ packages: resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -960,10 +949,6 @@ packages: component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} - concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} - content-disposition@1.1.0: resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} engines: {node: '>=18'} @@ -976,9 +961,6 @@ packages: resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} engines: {node: '>=18'} - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -1039,8 +1021,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@2.3.1: - resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} @@ -1168,8 +1150,8 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.4.3: + resolution: {integrity: sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==} math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} @@ -1218,8 +1200,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - multer@2.2.0: - resolution: {integrity: sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==} + multer@2.4.0: + resolution: {integrity: sha512-7dqa0ZcFfzbefdTuIkzOSMvZWC0J7FLqBOjJUZvDCXShIURWKxAyTT1wHhnE5q19c7jOJf43IYYKjBmZVZmvhg==} engines: {node: '>= 10.16.0'} nanoid@3.3.16: @@ -1265,9 +1247,6 @@ packages: path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1275,6 +1254,10 @@ packages: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + pino-abstract-transport@3.0.0: resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} @@ -1283,23 +1266,23 @@ packages: engines: {node: '>=20'} hasBin: true - pino-pretty@13.1.3: - resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} + pino-pretty@13.2.0: + resolution: {integrity: sha512-jjghD1ML+Di/WxjJDz6j6Y4FT7hIXOlb8h27AyJ+ohtAXJXH4PS/xkv7si+RCCIW59HMgZsMKTDgzdr0RhuRrg==} hasBin: true pino-std-serializers@7.1.0: resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} - pino@10.3.1: - resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} + pino@10.4.0: + resolution: {integrity: sha512-bk1ZMTwG/Vymx+zPoa28MhNzucFvXJWp3csrSuxuHPSMeUettaCoQ5Jvg07mtC4JGNpcfTwz41oVCnNgBK9+vA==} hasBin: true postcss@8.5.20: resolution: {integrity: sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==} engines: {node: ^10 || ^12 || >=14} - prettier@3.9.6: - resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + prettier@3.9.9: + resolution: {integrity: sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==} engines: {node: '>=14'} hasBin: true @@ -1328,14 +1311,6 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} - real-require@1.0.0: resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} @@ -1348,9 +1323,6 @@ packages: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-stable-stringify@2.5.0: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} @@ -1377,8 +1349,8 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sharp@0.35.4: - resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} + sharp@0.35.5: + resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} engines: {node: '>=20.9.0'} peerDependencies: '@types/node': '*' @@ -1402,12 +1374,12 @@ packages: resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + sonic-boom@5.0.1: + resolution: {integrity: sha512-I1E9tHuKe6SdS/Uab4bl2o1ZUBm2SCTDFF9eQ66ehyVcFIBixfecy9DlwXCtnRF8CUzPw7Q4Dn6JQE/8iVl/FQ==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1416,9 +1388,6 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -1430,9 +1399,6 @@ packages: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - strip-json-comments@5.0.3: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} @@ -1441,29 +1407,26 @@ packages: resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} engines: {node: '>=14.18.0'} - supertest@7.2.2: - resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==} + supertest@7.3.1: + resolution: {integrity: sha512-vNaNCFoCR/edDbErQZp5KLNT8R4dSPHX7rlDLoE8hWLAm/878j6ShMbe8HK/E6RwwuhlSXVelXAIg9AxR5WWPw==} engines: {node: '>=14.18.0'} thread-stream@4.2.0: resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} engines: {node: '>=20'} - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@6.2.0: + resolution: {integrity: sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==} + engines: {node: '>=20.0.0'} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} engines: {node: '>=18'} tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} - engines: {node: '>=14.0.0'} - toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -1471,8 +1434,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.23.12: - resolution: {integrity: sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==} + tsx@4.23.15: + resolution: {integrity: sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==} engines: {node: '>=18.0.0'} hasBin: true @@ -1484,24 +1447,18 @@ packages: resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} engines: {node: '>= 18'} - typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript@7.0.2: resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} engines: {node: '>=16.20.0'} hasBin: true - undici-types@8.3.0: - resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -1546,23 +1503,23 @@ packages: yaml: optional: true - vitest@4.1.11: - resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + vitest@5.0.3: + resolution: {integrity: sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.11 - '@vitest/browser-preview': 4.1.11 - '@vitest/browser-webdriverio': 4.1.11 - '@vitest/coverage-istanbul': 4.1.11 - '@vitest/coverage-v8': 4.1.11 - '@vitest/ui': 4.1.11 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.3 + '@vitest/browser-preview': 5.0.3 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.3 + '@vitest/coverage-v8': 5.0.3 + '@vitest/ui': 5.0.3 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -1587,16 +1544,16 @@ packages: jsdom: optional: true - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + why-is-node-running@3.2.1: + resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} + engines: {node: '>=20.11'} hasBin: true wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - zod@4.4.3: - resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} snapshots: @@ -1685,112 +1642,121 @@ snapshots: '@img/colour@1.1.0': {} - '@img/sharp-darwin-arm64@0.35.4': + '@img/sharp-darwin-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.3 + '@img/sharp-libvips-darwin-arm64': 1.3.4 optional: true - '@img/sharp-darwin-x64@0.35.4': + '@img/sharp-darwin-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.3 + '@img/sharp-libvips-darwin-x64': 1.3.4 optional: true - '@img/sharp-freebsd-wasm32@0.35.4': + '@img/sharp-freebsd-wasm32@0.35.5': dependencies: - '@img/sharp-wasm32': 0.35.4 + '@img/sharp-wasm32': 0.35.5 optional: true - '@img/sharp-libvips-darwin-arm64@1.3.3': + '@img/sharp-libvips-darwin-arm64@1.3.4': optional: true - '@img/sharp-libvips-darwin-x64@1.3.3': + '@img/sharp-libvips-darwin-x64@1.3.4': optional: true - '@img/sharp-libvips-linux-arm64@1.3.3': + '@img/sharp-libvips-linux-arm64@1.3.4': optional: true - '@img/sharp-libvips-linux-arm@1.3.3': + '@img/sharp-libvips-linux-arm@1.3.4': optional: true - '@img/sharp-libvips-linux-ppc64@1.3.3': + '@img/sharp-libvips-linux-ppc64@1.3.4': optional: true - '@img/sharp-libvips-linux-riscv64@1.3.3': + '@img/sharp-libvips-linux-riscv64@1.3.4': optional: true - '@img/sharp-libvips-linux-s390x@1.3.3': + '@img/sharp-libvips-linux-s390x@1.3.4': optional: true - '@img/sharp-libvips-linux-x64@1.3.3': + '@img/sharp-libvips-linux-x64@1.3.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.3.3': + '@img/sharp-libvips-linuxmusl-x64@1.3.4': optional: true - '@img/sharp-linux-arm64@0.35.4': + '@img/sharp-linux-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.3 + '@img/sharp-libvips-linux-arm64': 1.3.4 optional: true - '@img/sharp-linux-arm@0.35.4': + '@img/sharp-linux-arm@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.3 + '@img/sharp-libvips-linux-arm': 1.3.4 optional: true - '@img/sharp-linux-ppc64@0.35.4': + '@img/sharp-linux-ppc64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.3 + '@img/sharp-libvips-linux-ppc64': 1.3.4 optional: true - '@img/sharp-linux-riscv64@0.35.4': + '@img/sharp-linux-riscv64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.3 + '@img/sharp-libvips-linux-riscv64': 1.3.4 optional: true - '@img/sharp-linux-s390x@0.35.4': + '@img/sharp-linux-s390x@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.3 + '@img/sharp-libvips-linux-s390x': 1.3.4 optional: true - '@img/sharp-linux-x64@0.35.4': + '@img/sharp-linux-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.3 + '@img/sharp-libvips-linux-x64': 1.3.4 optional: true - '@img/sharp-linuxmusl-arm64@0.35.4': + '@img/sharp-linuxmusl-arm64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 optional: true - '@img/sharp-linuxmusl-x64@0.35.4': + '@img/sharp-linuxmusl-x64@0.35.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 optional: true - '@img/sharp-wasm32@0.35.4': + '@img/sharp-wasm32@0.35.5': dependencies: '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-webcontainers-wasm32@0.35.4': + '@img/sharp-webcontainers-wasm32@0.35.5': dependencies: - '@img/sharp-wasm32': 0.35.4 + '@img/sharp-wasm32': 0.35.5 optional: true - '@img/sharp-win32-arm64@0.35.4': + '@img/sharp-win32-arm64@0.35.5': optional: true - '@img/sharp-win32-ia32@0.35.4': + '@img/sharp-win32-ia32@0.35.5': optional: true - '@img/sharp-win32-x64@0.35.4': + '@img/sharp-win32-x64@0.35.5': optional: true + '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@noble/hashes@1.8.0': {} '@paralleldrive/cuid2@2.3.1': @@ -1874,12 +1840,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.62.2': optional: true - '@standard-schema/spec@1.1.0': {} - '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 26.3.0 + '@types/node': 26.6.4 '@types/chai@5.2.3': dependencies: @@ -1888,7 +1852,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 26.3.0 + '@types/node': 26.6.4 '@types/cookiejar@2.1.5': {} @@ -1898,7 +1862,7 @@ snapshots: '@types/express-serve-static-core@5.1.2': dependencies: - '@types/node': 26.3.0 + '@types/node': 26.6.4 '@types/qs': 6.15.1 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -1913,13 +1877,13 @@ snapshots: '@types/methods@1.1.4': {} - '@types/multer@2.2.0': + '@types/multer@2.3.0': dependencies: '@types/express': 5.0.6 - '@types/node@26.3.0': + '@types/node@26.6.4': dependencies: - undici-types: 8.3.0 + undici-types: 8.9.0 '@types/qs@6.15.1': {} @@ -1927,18 +1891,18 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 26.3.0 + '@types/node': 26.6.4 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 26.3.0 + '@types/node': 26.6.4 '@types/superagent@8.1.11': dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 26.3.0 + '@types/node': 26.6.4 form-data: 4.0.6 '@types/supertest@7.2.1': @@ -2006,46 +1970,16 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true - '@vitest/expect@4.1.11': + '@vitest/mocker@5.0.3(vite@7.3.6(@types/node@26.6.4)(tsx@4.23.15))': dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 - chai: 6.2.2 - tinyrainbow: 3.1.0 - - '@vitest/mocker@4.1.11(vite@7.3.6(@types/node@26.3.0)(tsx@4.23.12))': - dependencies: - '@vitest/spy': 4.1.11 + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.3 estree-walker: 3.0.3 - magic-string: 0.30.21 + magic-string: 1.4.3 optionalDependencies: - vite: 7.3.6(@types/node@26.3.0)(tsx@4.23.12) + vite: 7.3.6(@types/node@26.6.4)(tsx@4.23.15) - '@vitest/pretty-format@4.1.11': - dependencies: - tinyrainbow: 3.1.0 - - '@vitest/runner@4.1.11': - dependencies: - '@vitest/utils': 4.1.11 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@4.1.11': {} - - '@vitest/utils@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + '@vitest/spy@5.0.3': {} accepts@2.0.0: dependencies: @@ -2076,8 +2010,6 @@ snapshots: transitivePeerDependencies: - supports-color - buffer-from@1.1.2: {} - busboy@1.6.0: dependencies: streamsearch: 1.1.0 @@ -2104,21 +2036,12 @@ snapshots: component-emitter@1.3.1: {} - concat-stream@2.0.0: - dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 3.6.2 - typedarray: 0.0.6 - content-disposition@1.1.0: {} content-type@1.0.5: {} content-type@2.0.0: {} - convert-source-map@2.0.0: {} - cookie-signature@1.2.2: {} cookie@0.7.2: {} @@ -2160,7 +2083,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@2.3.1: {} + es-module-lexer@2.3.2: {} es-object-atoms@1.1.2: dependencies: @@ -2341,9 +2264,9 @@ snapshots: joycon@3.1.1: {} - magic-string@0.30.21: + magic-string@1.4.3: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 math-intrinsics@1.1.0: {} @@ -2373,11 +2296,10 @@ snapshots: ms@2.1.3: {} - multer@2.2.0: + multer@2.4.0: dependencies: append-field: 1.0.0 busboy: 1.6.0 - concat-stream: 2.0.0 type-is: 1.6.18 nanoid@3.3.16: {} @@ -2409,12 +2331,12 @@ snapshots: path-to-regexp@8.4.2: {} - pathe@2.0.3: {} - picocolors@1.1.1: {} picomatch@4.0.5: {} + picomatch@4.0.7: {} + pino-abstract-transport@3.0.0: dependencies: split2: 4.2.0 @@ -2424,7 +2346,7 @@ snapshots: pino-abstract-transport: 3.0.0 pump: 3.0.4 - pino-pretty@13.1.3: + pino-pretty@13.2.0: dependencies: colorette: 2.0.20 dateformat: 4.6.3 @@ -2437,12 +2359,12 @@ snapshots: pino-abstract-transport: 3.0.0 pump: 3.0.4 secure-json-parse: 4.1.0 - sonic-boom: 4.2.1 + sonic-boom: 5.0.1 strip-json-comments: 5.0.3 pino-std-serializers@7.1.0: {} - pino@10.3.1: + pino@10.4.0: dependencies: '@pinojs/redact': 0.4.0 atomic-sleep: 1.0.0 @@ -2451,7 +2373,7 @@ snapshots: pino-std-serializers: 7.1.0 process-warning: 5.0.0 quick-format-unescaped: 4.0.4 - real-require: 0.2.0 + real-require: 1.0.0 safe-stable-stringify: 2.5.0 sonic-boom: 4.2.1 thread-stream: 4.2.0 @@ -2462,7 +2384,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prettier@3.9.6: {} + prettier@3.9.9: {} process-warning@5.0.0: {} @@ -2492,14 +2414,6 @@ snapshots: iconv-lite: 0.7.3 unpipe: 1.0.0 - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - real-require@0.2.0: {} - real-require@1.0.0: {} rollup@4.62.2: @@ -2543,8 +2457,6 @@ snapshots: transitivePeerDependencies: - supports-color - safe-buffer@5.2.1: {} - safe-stable-stringify@2.5.0: {} safer-buffer@2.1.2: {} @@ -2580,38 +2492,38 @@ snapshots: setprototypeof@1.2.0: {} - sharp@0.35.4(@types/node@26.3.0): + sharp@0.35.5(@types/node@26.6.4): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.4 - '@img/sharp-darwin-x64': 0.35.4 - '@img/sharp-freebsd-wasm32': 0.35.4 - '@img/sharp-libvips-darwin-arm64': 1.3.3 - '@img/sharp-libvips-darwin-x64': 1.3.3 - '@img/sharp-libvips-linux-arm': 1.3.3 - '@img/sharp-libvips-linux-arm64': 1.3.3 - '@img/sharp-libvips-linux-ppc64': 1.3.3 - '@img/sharp-libvips-linux-riscv64': 1.3.3 - '@img/sharp-libvips-linux-s390x': 1.3.3 - '@img/sharp-libvips-linux-x64': 1.3.3 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 - '@img/sharp-libvips-linuxmusl-x64': 1.3.3 - '@img/sharp-linux-arm': 0.35.4 - '@img/sharp-linux-arm64': 0.35.4 - '@img/sharp-linux-ppc64': 0.35.4 - '@img/sharp-linux-riscv64': 0.35.4 - '@img/sharp-linux-s390x': 0.35.4 - '@img/sharp-linux-x64': 0.35.4 - '@img/sharp-linuxmusl-arm64': 0.35.4 - '@img/sharp-linuxmusl-x64': 0.35.4 - '@img/sharp-webcontainers-wasm32': 0.35.4 - '@img/sharp-win32-arm64': 0.35.4 - '@img/sharp-win32-ia32': 0.35.4 - '@img/sharp-win32-x64': 0.35.4 - '@types/node': 26.3.0 + '@img/sharp-darwin-arm64': 0.35.5 + '@img/sharp-darwin-x64': 0.35.5 + '@img/sharp-freebsd-wasm32': 0.35.5 + '@img/sharp-libvips-darwin-arm64': 1.3.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + '@img/sharp-libvips-linux-arm': 1.3.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 + '@img/sharp-libvips-linux-x64': 1.3.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + '@img/sharp-linux-arm': 0.35.5 + '@img/sharp-linux-arm64': 0.35.5 + '@img/sharp-linux-ppc64': 0.35.5 + '@img/sharp-linux-riscv64': 0.35.5 + '@img/sharp-linux-s390x': 0.35.5 + '@img/sharp-linux-x64': 0.35.5 + '@img/sharp-linuxmusl-arm64': 0.35.5 + '@img/sharp-linuxmusl-x64': 0.35.5 + '@img/sharp-webcontainers-wasm32': 0.35.5 + '@img/sharp-win32-arm64': 0.35.5 + '@img/sharp-win32-ia32': 0.35.5 + '@img/sharp-win32-x64': 0.35.5 + '@types/node': 26.6.4 side-channel-list@1.0.1: dependencies: @@ -2641,28 +2553,24 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - siginfo@2.0.0: {} - sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 + sonic-boom@5.0.1: + dependencies: + atomic-sleep: 1.0.0 + source-map-js@1.2.1: {} split2@4.2.0: {} - stackback@0.0.2: {} - statuses@2.0.2: {} std-env@4.2.0: {} streamsearch@1.1.0: {} - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - strip-json-comments@5.0.3: {} superagent@10.3.0: @@ -2679,7 +2587,7 @@ snapshots: transitivePeerDependencies: - supports-color - supertest@7.2.2: + supertest@7.3.1: dependencies: cookie-signature: 1.2.2 methods: 1.1.2 @@ -2691,23 +2599,21 @@ snapshots: dependencies: real-require: 1.0.0 - tinybench@2.9.0: {} + tinybench@6.2.0: {} - tinyexec@1.2.4: {} + tinyexec@1.3.1: {} tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 - tinyrainbow@3.1.0: {} - toidentifier@1.0.1: {} tslib@2.8.1: optional: true - tsx@4.23.12: + tsx@4.23.15: dependencies: esbuild: 0.28.2 optionalDependencies: @@ -2724,8 +2630,6 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 - typedarray@0.0.6: {} - typescript@7.0.2: optionalDependencies: '@typescript/typescript-aix-ppc64': 7.0.2 @@ -2749,15 +2653,13 @@ snapshots: '@typescript/typescript-win32-arm64': 7.0.2 '@typescript/typescript-win32-x64': 7.0.2 - undici-types@8.3.0: {} + undici-types@8.9.0: {} unpipe@1.0.0: {} - util-deprecate@1.0.2: {} - vary@1.1.2: {} - vite@7.3.6(@types/node@26.3.0)(tsx@4.23.12): + vite@7.3.6(@types/node@26.6.4)(tsx@4.23.15): dependencies: esbuild: 0.28.2 fdir: 6.5.0(picomatch@4.0.5) @@ -2766,42 +2668,33 @@ snapshots: rollup: 4.62.2 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.3.0 + '@types/node': 26.6.4 fsevents: 2.3.3 - tsx: 4.23.12 + tsx: 4.23.15 - vitest@4.1.11(@types/node@26.3.0)(vite@7.3.6(@types/node@26.3.0)(tsx@4.23.12)): + vitest@5.0.3(@types/node@26.6.4)(vite@7.3.6(@types/node@26.6.4)(tsx@4.23.15)): dependencies: - '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(vite@7.3.6(@types/node@26.3.0)(tsx@4.23.12)) - '@vitest/pretty-format': 4.1.11 - '@vitest/runner': 4.1.11 - '@vitest/snapshot': 4.1.11 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 - es-module-lexer: 2.3.1 + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.3(vite@7.3.6(@types/node@26.6.4)(tsx@4.23.15)) + chai: 6.2.2 + es-module-lexer: 2.3.2 expect-type: 1.4.0 - magic-string: 0.30.21 + magic-string: 1.4.3 obug: 2.1.4 - pathe: 2.0.3 - picomatch: 4.0.5 + picomatch: 4.0.7 std-env: 4.2.0 - tinybench: 2.9.0 - tinyexec: 1.2.4 + tinybench: 6.2.0 + tinyexec: 1.3.1 tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 7.3.6(@types/node@26.3.0)(tsx@4.23.12) - why-is-node-running: 2.3.0 + vite: 7.3.6(@types/node@26.6.4)(tsx@4.23.15) + why-is-node-running: 3.2.1 optionalDependencies: - '@types/node': 26.3.0 + '@types/node': 26.6.4 transitivePeerDependencies: - msw - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 + why-is-node-running@3.2.1: {} wrappy@1.0.2: {} - zod@4.4.3: {} + zod@4.6.5: {} diff --git a/media-processor/test/upload-storage.test.ts b/media-processor/test/upload-storage.test.ts index b2613e533a..79a8250535 100644 --- a/media-processor/test/upload-storage.test.ts +++ b/media-processor/test/upload-storage.test.ts @@ -13,7 +13,7 @@ function mockReq(contentLength?: string): Request { } function mockFile(content: string = "test content") { - const stream = Readable.from([content]); + const stream = Readable.from([Buffer.from(content)]); return { fieldname: "file", originalname: "test.txt", diff --git a/package.json b/package.json index 5a14003bcd..a27aad2cc9 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "MPL-2.0", "author": "Kaleidos INC Sucursal en España SL", "private": true, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "repository": { "type": "git", "url": "https://github.com/penpot/penpot" diff --git a/plugins/CHANGELOG.md b/plugins/CHANGELOG.md index de0f09ed73..deef90a3e6 100644 --- a/plugins/CHANGELOG.md +++ b/plugins/CHANGELOG.md @@ -2,16 +2,25 @@ ### 🚀 Features +- **plugin-types, plugins-runtime:** `penpotMgmt` returns `Team`, `Project` and `ProjectFile` objects. Assigning `name` renames teams, projects and files, `project.pinned` pins a project and `file.shared` shares a file as a team library; `createProject`, `createFile`, `moveTo`, `duplicate`, `remove` and `restore` live on the objects. Added `penpotMgmt.listTeams`, `penpotMgmt.createTeam`, `penpotMgmt.getFile`, `listDeletedProjects` and `listDeletedFiles`, and the `manage:projects`, `manage:teams` and `manage:delete` permissions. Restoration uses the dashboard's Trash operation; projects need recoverable files and empty projects cannot be restored; the API exposes no permanent deletion method. Team deletion is not exposed. +- **plugins-runtime, plugin-types**: Added global plugins (`scope: "global"`) that remain active across dashboard and workspace navigation until closed or logged out, with `penpotMgmt` for project and file discovery, file navigation and workspace events. Global scope adds the `allow:global` permission, which users must accept when installing the plugin. - **plugin-types:** Added `LibraryComponent.restore()` to restore a deleted component and its main instance without changing the current page, selection, zoom, or viewport. Requires `library:write` permission and only applies to the current file's library. - **plugin-types:** Added `Shape.updateMain()`, `LibraryComponent.duplicate()`, writable component `annotation`, and `Library.deletedComponents`. Declared the existing `LibraryComponent.remove()` method. - **plugin-types:** Added `LibraryContext.tokensSource` to read or set the current file's token source to a connected library, or restore local tokens. - **plugin-types:** Added `paddingType` (`'simple' | 'multiple'`) to flex and grid layouts and `marginType` (`'simple' | 'multiple'`) to layout children, exposing whether the four padding/margin sides are mirrored or honoured independently. - **plugin-types**: Added `waitForLayoutUpdate` to wait until pending layout updates have finished. It rejects when the optional timeout elapses, defaulting to 30 seconds so a wait never hangs. - **plugin-types**: Added `waitForLayoutUpdate` to the `Shape` interface to wait until the pending layout updates of a shape and its children have finished +- **plugin-types:** Added the `contentrollback` event and the `ContentRollback` type. The event fires after the backend rejected some changes and the workspace undid them, with the ids of the files, pages and shapes affected. It fires once the undone data is in the workspace, also when the workspace has to reload the file to undo the changes. - **plugin-types:** `Stroke` exposes per-side widths for boards and rectangles, also supported by `borderWidth` and `dimension` tokens. Read-only `strokeWidthType` reports `simple` when all four effective widths match, otherwise `multiple`. ### 🩹 Fixes +- **plugin-runtime:** `LibraryComponent.instance()`, `createShapeFromSvg()` and `createShapeFromSvgWithImages()` now create shapes at the page root instead of inside a board at the page origin. Closes #12086. +- **plugin-types, plugin-runtime:** Dashboard property setters now update values after backend success and log failures without interrupting the workspace. File moves reject projects in other teams, backend rejections include their type and code, and `project.fileCount` stays at its listing snapshot. Duplicated projects include their file count and pinned state. +- **plugin-runtime:** Cancel plugin loads before they execute after unloading or logout, and allow reopening plugins that close during startup. +- **plugins-runtime**: `library.connectLibrary()` now resolves once the library's assets are loaded; it used to resolve with an empty library the first time. `library.connected` and `library.availableLibraries()` ignore files opened earlier in the same tab, which used to show up as connected and be left out of the available libraries. +- **plugins-runtime:** Writes now fail with an error while the workspace cannot take them (saves failing, a user who cannot edit, a version preview, a canvas that stopped rendering), whatever the `throwValidationErrors` flag. They used to change the workspace with edits that would never be saved. The error says why. The Inspect tab does not block writes, and undo blocks still open and close. +- **plugins-runtime:** Writes that wait for the server (`createShapeFromSvgWithImages`, `uploadMediaData`, `uploadMediaUrl`, `connectLibrary`) check again when the server answers. If the workspace turned read-only meanwhile (a save failed), the promise rejects with the read-only error: no shape is added and no library is linked. An image the server already stored stays in the file's media, unused. - **plugins-runtime:** `LibraryComponent.remove()` now rejects linked-library and missing component handles, and does nothing for an already deleted component. - **plugins-runtime**: An interaction obtained from `Shape.interactions` now keeps addressing that interaction instead of the position it held when the array was read. Removing every interaction of a shape from a single read removes all of them rather than leaving some behind, and writing through a held interaction after an earlier one is removed no longer lands on a different interaction. - **plugins-runtime**: `Shape.removeInteraction()` now rejects an interaction belonging to a different shape with a validation error, instead of removing whichever interaction sat at the same position on the target shape. @@ -21,6 +30,7 @@ - **plugins-runtime**: Removed the premature deep-hardening of the host plugin context, which froze shared host functions (including `Function.prototype`) before SES override taming, causing `TypeError: Cannot assign to read only property 'toString'` on later host-side function extension. Related to #11001. - **plugins-runtime**: The plugin modal no longer shows two resize grips in Firefox. Firefox now shows only its native grip. Closes #11795. - **plugins-runtime**: Fixed the `fontFamilies` token property mapping so `Shape.applyToken(token, ["fontFamilies"])` resolves to the canonical `:font-family` attribute and applied-token readback exposes the documented `fontFamilies` key instead of the undocumented singular `fontFamily`. Closes #11405. +- **plugins-runtime**: `TokenFontFamilies.resolvedValue` and the `fontFamilies` member of `TokenTypographyValue` now keep a family whose name has several words (`Hanken Grotesk`) as one entry, instead of splitting it into an array of its words, and keep names with words the resolver would otherwise read as colors or numbers (`Red Hat Display`, `Press Start 2P`) unchanged. - **plugins-runtime**: Opening a plugin no longer drops running background plugins (`allowBackground`, such as the MCP plugin) from the runtime registry. They kept running, but their UI messages were no longer delivered to the plugin and they could no longer be unloaded, so MCP tasks timed out after another plugin was opened. - **plugins-runtime**: `Shape.applyToken()`, `Token.applyToShapes()` and `Token.applyToSelected()` now reject an explicit per-side stroke width property (`strokeWidthTop`, `strokeWidthRight`, `strokeWidthBottom`, `strokeWidthLeft`) on shapes other than boards and rectangles, matching the `strokes` setter. The uniform `strokeWidth` property keeps working on every shape. Closes #12319. diff --git a/plugins/apps/colors-to-tokens-plugin/package.json b/plugins/apps/colors-to-tokens-plugin/package.json index 4c9690e2a2..cfed5c5f6b 100644 --- a/plugins/apps/colors-to-tokens-plugin/package.json +++ b/plugins/apps/colors-to-tokens-plugin/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "build": "ng build colors-to-tokens-plugin && pnpm run build:plugin", + "build": "pnpm run build:plugin && ng build colors-to-tokens-plugin", "build:dev": "ng build colors-to-tokens-plugin --configuration development", "build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=colors-to-tokens-plugin", "watch": "node ../../tools/scripts/build-plugin.mjs --plugin=colors-to-tokens-plugin --watch", @@ -13,5 +13,5 @@ "lint": "eslint .", "test": "vitest" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/composable-test-suite/package.json b/plugins/apps/composable-test-suite/package.json index f12e809fe0..81261c19e9 100644 --- a/plugins/apps/composable-test-suite/package.json +++ b/plugins/apps/composable-test-suite/package.json @@ -27,5 +27,5 @@ "vite": "^8.3.2", "vite-live-preview": "^0.4.0" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/contrast-plugin/package.json b/plugins/apps/contrast-plugin/package.json index 641618631d..d773fe0297 100644 --- a/plugins/apps/contrast-plugin/package.json +++ b/plugins/apps/contrast-plugin/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "build": "ng build contrast-plugin && pnpm run build:plugin", + "build": "pnpm run build:plugin && ng build contrast-plugin", "build:dev": "ng build contrast-plugin --configuration development", "build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=contrast-plugin", "watch": "node ../../tools/scripts/build-plugin.mjs --plugin=contrast-plugin --watch", @@ -13,5 +13,5 @@ "lint": "eslint .", "test": "vitest" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/create-palette-plugin/package.json b/plugins/apps/create-palette-plugin/package.json index d52baaa757..016063ea11 100644 --- a/plugins/apps/create-palette-plugin/package.json +++ b/plugins/apps/create-palette-plugin/package.json @@ -12,5 +12,5 @@ "lint": "eslint .", "test": "vitest" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/e2e/package.json b/plugins/apps/e2e/package.json index d9c64e6bac..f432a1e113 100644 --- a/plugins/apps/e2e/package.json +++ b/plugins/apps/e2e/package.json @@ -7,5 +7,5 @@ "test": "vitest", "lint": "eslint ." }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/example-styles/package.json b/plugins/apps/example-styles/package.json index 227f8b7a9f..a30835a099 100644 --- a/plugins/apps/example-styles/package.json +++ b/plugins/apps/example-styles/package.json @@ -11,5 +11,5 @@ "serve": "vite preview", "lint": "eslint ." }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/icons-plugin/package.json b/plugins/apps/icons-plugin/package.json index 31f25c5e8a..936de38fe3 100644 --- a/plugins/apps/icons-plugin/package.json +++ b/plugins/apps/icons-plugin/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "build": "ng build icons-plugin && pnpm run build:plugin", + "build": "pnpm run build:plugin && ng build icons-plugin", "build:dev": "ng build icons-plugin --configuration development", "build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=icons-plugin", "build:plugin:watch": "node ../../tools/scripts/build-plugin.mjs --plugin=icons-plugin --watch", @@ -13,5 +13,5 @@ "lint": "eslint .", "test": "vitest" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/lorem-ipsum-plugin/package.json b/plugins/apps/lorem-ipsum-plugin/package.json index f5d5be5541..34eab294e3 100644 --- a/plugins/apps/lorem-ipsum-plugin/package.json +++ b/plugins/apps/lorem-ipsum-plugin/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "build": "ng build lorem-ipsum-plugin && pnpm run build:plugin", + "build": "pnpm run build:plugin && ng build lorem-ipsum-plugin", "build:dev": "ng build lorem-ipsum-plugin --configuration development", "build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=lorem-ipsum-plugin", "watch": "node ../../tools/scripts/build-plugin.mjs --plugin=lorem-ipsum-plugin --watch", @@ -13,5 +13,5 @@ "lint": "eslint .", "test": "vitest" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/plugin-api-test-suite/README.md b/plugins/apps/plugin-api-test-suite/README.md index 5ea524b484..2504756974 100644 --- a/plugins/apps/plugin-api-test-suite/README.md +++ b/plugins/apps/plugin-api-test-suite/README.md @@ -17,9 +17,10 @@ fully before writing any test. ## The one rule that matters most -> **Always call the API through `ctx.penpot`, never the global `penpot`.** +> **Always call the API through `ctx.penpot` and `ctx.penpotMgmt`, never the +> globals `penpot` and `penpotMgmt`.** -`ctx.penpot` is a recording proxy. Calls made through it are what count towards +`ctx.penpot` and `ctx.penpotMgmt` are recording proxies. Calls made through it are what count towards coverage and are correctly attributed to the right interface. Calls on the global `penpot` still work but are invisible to coverage. Same for shapes: operate on the objects returned by `ctx.penpot.*` (and on `ctx.board`), not on objects obtained @@ -31,7 +32,9 @@ From `plugins/`: - Dev server: `pnpm run start:plugin:api-test-suite` (serves on port 4202). - In Penpot: open the Plugin Manager (Ctrl+Alt+P) and install - `http://localhost:4202/manifest.json`. + `http://localhost:4202/manifest.json`. The suite is a global plugin + (`"scope": "global"`), so the install dialog asks for the `allow:global` + permission. It keeps running while tests move between files. - Alternatively, a one-shot `pnpm --filter plugin-api-test-suite run build` output is served by the devenv alongside the other bundled plugins, at `https://localhost:3449/plugins/plugin-api-test-suite/manifest.json` (no hot @@ -62,6 +65,8 @@ E2E_LOGIN_EMAIL=… E2E_LOGIN_PASSWORD=… \ - It builds `headless.js`, logs in, creates a scratch file, injects the test bundle, and prints per-test results + the coverage report. +- After the run it deletes the projects the tests created (see + [Working with other files](#working-with-other-files)). - Exit code is non-zero iff any test failed (coverage does not affect it). - Optional env: `PENPOT_BASE_URL` (default `https://localhost:3449`). Against a local devenv with a self-signed certificate, prefix the command with @@ -214,15 +219,54 @@ committing. - `ctx.penpot` — the recording proxy over the real `penpot` global. Use it for every API call. +- `ctx.penpotMgmt` — the recording proxy over the real `penpotMgmt` global, for + teams, projects and files (see [Working with other files](#working-with-other-files)). - `ctx.board` — a **fresh scratch `Board`** created for this test and **removed automatically afterwards**. Append shapes you create to it (`ctx.board.appendChild(shape)`) so the user's canvas is left clean. Do not rely on it persisting between tests. +- `ctx.fixture(key, create)` — returns the value `create` resolves to, created + the first time `key` is used in the run and reused by later tests of the same + run. Keep shared fixtures here rather than in module variables: the plugin UI + keeps modules between runs, so a module-level cache would leak state from one + run into the next. -The runner also resets shared state between tests: the selection is cleared and the -active page is restored to whatever was active when the run started (both through -the raw `penpot`, so they aren't credited toward coverage). A test that changes the -active page therefore won't leak into later tests. +The runner also resets shared state between tests: it reopens the file the run +started in if a test left another one open, restores the page that was active +when the run started and clears the selection (all through the raw globals, so +they aren't credited toward coverage). A test that changes the active page or +file therefore won't leak into later tests. + +### Working with other files + +The suite runs with global scope, so tests can reach teams, projects and files +through `ctx.penpotMgmt`. They are objects like the rest of the API: `Team` +(`listTeams()`), `Project` (`listProjects()`, `team.createProject()`) and +`ProjectFile` (`project.listFiles()`, `project.createFile()`, `getFile(id)`), +with `name`/`pinned`/`shared` properties and `open`, `moveTo`, `duplicate` and +`remove` methods. `management.test.ts` shows the patterns: + +- Put every team and project a test creates under a name from + `temporaryName()` (`src/framework/temporary.ts`). The API cannot delete + teams, so the live CI driver deletes the teams and projects with that prefix + after the run. Runs from the plugin UI leave them behind. The tests need + nothing set up beforehand. +- Assigning `name`, `pinned` or `shared` saves to the backend first and updates + the object only when the save succeeds. Wait for the new value with + `waitFor()` (`src/tests/wait.ts`) before reading it; to check what the + backend stored, list again inside `waitForAsync()`. +- `Project.fileCount` is the count when the project was listed; creating or + moving files does not change it. List the project again to see the new + count. +- Shape, page, file and library objects taken before `file.open()` (or + `openFile`) are stale afterwards; look them up again in the new file. + `ctx.board` belongs to the file the test started in, so only use it there. +- While a file opens, `penpot.currentFile`, `currentPage` and `root` are `null` + and file operations throw. `open` resolves once the file is ready. +- These tests need the real backend, so tag them `skipIfMocked`. +- The per-test timeout is 15s; opening a file takes about half a second + locally. Build expensive fixtures (such as a shared library) once per run with + `ctx.fixture`, as `sharedLibrary()` does. ### Sync or async @@ -318,8 +362,8 @@ pnpm --filter plugin-api-test-suite run gen:api - `createText(str)` returns `Text | null` — guard the result (`if (text) { … }`). - `width`/`height` are read-only; use `resize(w, h)`. `x`/`y` are writable. - The plugin manifest already requests broad permissions (`content:*`, - `library:*`, `user:read`, `comment:*`, `allow:downloads`, `allow:localstorage`), - so most of the API is callable from tests without changes. + `library:*`, `user:read`, `comment:*`, `allow:downloads`, `allow:localstorage`, + `allow:global`), so most of the API is callable from tests without changes. - The runner sets `throwValidationErrors = true` and `naturalChildOrdering = true`, so invalid API usage throws (surfacing as a red test) and `children` is always in z-index order. @@ -405,8 +449,6 @@ valid"); `fills-strokes.test.ts` pins this with a `toThrow`. ### External state / not reachable headless - **`ActiveUser.position/zoom`** — needs a second collaborator in the file. -- **`LibrarySummary.*`, `LibraryContext.connectLibrary`** — need a published shared - library. - **`FileVersion.restore`, `Penpot.closePlugin`, `Penpot.ui`, `Context.openViewer`** — tear down or navigate away from the running plugin/workspace. - **`FileVersion.pin`** — only converts a _system_ autosave to a permanent version; @@ -414,14 +456,16 @@ valid"); `fills-strokes.test.ts` pins this with a `toThrow`. rejects. - **`Context.addListener/removeListener`** — omitted from the `penpot` global (`Omit`), so unreachable via `penpot`. -- **`EventsMap` events `pagechange/filechange/themechange/contentsave/finish`** — - can't be triggered deterministically in the headless runner. +- **`EventsMap` events `pagechange/themechange/contentsave`** — can't be + triggered deterministically in the headless runner. `filechange` and `finish` + are exercised in `management.test.ts` by switching files. ## Checklist before finishing - [ ] Test file is `src/tests/.test.ts` and uses `test(...)` + `expect`, ideally wrapped in a `describe('', …)`. -- [ ] All API calls go through `ctx.penpot`; shapes are appended to `ctx.board`. +- [ ] All API calls go through `ctx.penpot` / `ctx.penpotMgmt`; shapes are + appended to `ctx.board`. - [ ] Created shapes don't leak (rely on the scratch board cleanup; don't touch the user's existing content). - [ ] Lint/format/typecheck pass: @@ -438,6 +482,7 @@ valid"); `fills-strokes.test.ts` pins this with a `toThrow`. - `src/framework/static-coverage.ts` — the statically-covered allowlist. - `src/framework/expect.ts` — the assertion library. - `src/framework/types.ts` — `TestContext`, `TestResult`, `CoverageReport`, etc. +- `src/framework/temporary.ts` — name prefix for temporary projects, shared with the CI driver. - `tools/gen-api-surface.ts` — generates `src/generated/api-surface.json`. - `src/plugin.ts` (sandbox), `src/ui.ts` (iframe), `src/model.ts` (messages). - `src/ci/headless.ts` + `ci/run-ci.ts` — CI path. diff --git a/plugins/apps/plugin-api-test-suite/ci/run-ci.ts b/plugins/apps/plugin-api-test-suite/ci/run-ci.ts index 8b97cf088a..535418577c 100644 --- a/plugins/apps/plugin-api-test-suite/ci/run-ci.ts +++ b/plugins/apps/plugin-api-test-suite/ci/run-ci.ts @@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { chromium, type Page } from 'playwright'; +import { TEMPORARY_PREFIX } from '../src/framework/temporary'; import type { CoverageReport, TestResult } from '../src/framework/types'; import { startStaticServer, type StaticServer } from './static-server.ts'; @@ -42,6 +43,9 @@ const APP_ERROR_RE = /^(Internal Error|Unexpected Error|Assertion Error|Uncaught Exception|Uncaught Rejection):/; const MOCKED = !!process.env['MOCK_BACKEND']; + +// Limit for the whole run; each test also has its own 15s limit in the runner. +const RUN_TIMEOUT_MS = 300000; const MOCK_BASE_URL = 'http://localhost:3000'; const apiUrl = MOCKED ? MOCK_BASE_URL @@ -52,12 +56,13 @@ const headlessBundlePath = resolve( '../../../dist/apps/plugin-api-test-suite/headless.js', ); -// Source the permissions from the same manifest the real plugin ships with, so -// the CI sandbox never drifts from what users actually grant. +// Source the permissions and scope from the same manifest the real plugin ships +// with, so the CI sandbox never drifts from what users actually grant. const manifestPath = resolve(here, '../public/manifest.json'); -const PERMISSIONS: string[] = ( - JSON.parse(readFileSync(manifestPath, 'utf-8')) as { permissions: string[] } -).permissions; +const MANIFEST = JSON.parse(readFileSync(manifestPath, 'utf-8')) as { + permissions: string[]; + scope?: string; +}; function cleanId(id: string): string { return id.replace('~u', ''); @@ -94,7 +99,70 @@ async function login() { if (!authToken) throw new Error('Login failed: no auth-token cookie returned'); - return { authToken, defaultProjectId: loginData['~:default-project-id'] }; + return { + authToken, + defaultProjectId: loginData['~:default-project-id'], + defaultTeamId: cleanId(loginData['~:default-team-id']), + }; +} + +// Calls a backend RPC with plain JSON in and out. Commands without a result +// answer with an empty body. +async function rpc( + authToken: string, + method: string, + params: Record, +): Promise { + const response = await fetch(`${apiUrl}/api/main/methods/${method}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + cookie: authToken, + }, + body: JSON.stringify(params), + }); + if (!response.ok) { + throw new Error(`${method} failed with status ${response.status}`); + } + const body = await response.text(); + return (body ? JSON.parse(body) : undefined) as T; +} + +// Deletes the teams the tests created, with their projects and files. +async function deleteTemporaryTeams(authToken: string): Promise { + const teams = await rpc<{ id: string; name: string }[]>( + authToken, + 'get-teams', + {}, + ); + const temporary = teams.filter((team) => + team.name.startsWith(TEMPORARY_PREFIX), + ); + for (const team of temporary) { + await rpc(authToken, 'delete-team', { id: team.id }); + } + return temporary.length; +} + +// Deletes the projects the tests created (see `src/framework/temporary.ts`), +// with the files in them. +async function deleteTemporaryProjects( + authToken: string, + teamId: string, +): Promise { + const projects = await rpc< + { id: string; name: string; deletedAt?: string | null }[] + >(authToken, 'get-projects', { 'team-id': teamId }); + // Projects already in the trash are listed too; skip them. + const temporary = projects.filter( + (project) => + !project.deletedAt && project.name.startsWith(TEMPORARY_PREFIX), + ); + for (const project of temporary) { + await rpc(authToken, 'delete-project', { id: project.id }); + } + return temporary.length; } async function createFile( @@ -130,11 +198,10 @@ async function createFile( return (await response.json()) as FileRpc; } -function getFileUrl(file: FileRpc): string { - const projectId = cleanId(file['~:project-id']); +function getFileUrl(file: FileRpc, teamId: string): string { const fileId = cleanId(file['~:id']); const pageId = cleanId(file['~:data']['~:pages'][0]); - return `${apiUrl}/#/workspace/${projectId}/${fileId}?page-id=${pageId}`; + return `${apiUrl}/?screen=workspace&team-id=${teamId}&file-id=${fileId}&page-id=${pageId}`; } // --- Mocked mode setup ------------------------------------------------------- @@ -348,6 +415,7 @@ async function main() { let server: StaticServer | undefined; let fileUrl: string; let authToken: string | undefined; + let teamId: string | undefined; if (MOCKED) { server = await startE2eServer(); @@ -356,8 +424,9 @@ async function main() { } else { const session = await login(); authToken = session.authToken; + teamId = session.defaultTeamId; const file = await createFile(authToken, session.defaultProjectId); - fileUrl = getFileUrl(file); + fileUrl = getFileUrl(file, session.defaultTeamId); } const renderWasm = process.env['RENDER_WASM']; @@ -468,7 +537,7 @@ async function main() { ); await page.evaluate( - ({ code, permissions }) => { + ({ code, permissions, scope }) => { ( globalThis as unknown as { ɵloadPlugin: (m: unknown) => void } ).ɵloadPlugin({ @@ -478,23 +547,45 @@ async function main() { icon: '', description: '', permissions, + scope, }); }, - { code: injectedCode, permissions: PERMISSIONS }, + { + code: injectedCode, + permissions: MANIFEST.permissions, + scope: MANIFEST.scope, + }, ); - await Promise.race([ - done, - new Promise((_, reject) => - setTimeout( - () => reject(new Error('Timed out waiting for test results')), - 120000, + try { + await Promise.race([ + done, + new Promise((_, reject) => + setTimeout( + () => reject(new Error('Timed out waiting for test results')), + RUN_TIMEOUT_MS, + ), ), - ), - ]); + ]); + } finally { + await browser.close(); + await server?.close(); - await browser.close(); - await server?.close(); + // Runs after a timeout too, so an interrupted run leaves nothing behind. + if (authToken && teamId) { + try { + const projects = await deleteTemporaryProjects(authToken, teamId); + const teams = await deleteTemporaryTeams(authToken); + if (projects > 0 || teams > 0) { + console.log( + `\nDeleted ${projects} temporary project(s) and ${teams} team(s).`, + ); + } + } catch (err) { + console.warn(`\nCould not delete temporary data: ${String(err)}`); + } + } + } printReport(results, coverage, skipped); diff --git a/plugins/apps/plugin-api-test-suite/package.json b/plugins/apps/plugin-api-test-suite/package.json index 0927a7388f..03a09c7c00 100644 --- a/plugins/apps/plugin-api-test-suite/package.json +++ b/plugins/apps/plugin-api-test-suite/package.json @@ -20,5 +20,5 @@ "devDependencies": { "playwright": "^1.63.0" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/plugin-api-test-suite/public/manifest.json b/plugins/apps/plugin-api-test-suite/public/manifest.json index a56ee76498..99c2578a1d 100644 --- a/plugins/apps/plugin-api-test-suite/public/manifest.json +++ b/plugins/apps/plugin-api-test-suite/public/manifest.json @@ -3,6 +3,7 @@ "description": "Launcher for a battery of Penpot Plugin API tests", "code": "plugin.js", "version": 2, + "scope": "global", "icon": "assets/icon.png", "permissions": [ "content:read", @@ -13,6 +14,10 @@ "comment:read", "comment:write", "allow:downloads", - "allow:localstorage" + "allow:localstorage", + "allow:global", + "manage:projects", + "manage:teams", + "manage:delete" ] } diff --git a/plugins/apps/plugin-api-test-suite/src/framework/runner.ts b/plugins/apps/plugin-api-test-suite/src/framework/runner.ts index 9c20d38969..62ee3b471b 100644 --- a/plugins/apps/plugin-api-test-suite/src/framework/runner.ts +++ b/plugins/apps/plugin-api-test-suite/src/framework/runner.ts @@ -98,6 +98,28 @@ export interface RunOptions { export type ResultReporter = (result: TestResult) => void; +/** File and page that were active when the run started. */ +interface Home { + fileId: string | null; + teamId: string | null; + pageId: string | null; +} + +// Reopens the home file when a test navigated to another one. Shape, page and +// file references taken before navigating are stale afterwards, so callers +// look objects up again by id. Uses the *raw* globals so it isn't credited +// toward coverage. +async function returnToHomeFile(home: Home): Promise { + if (!home.fileId) return; + const workspace = penpotMgmt.workspace; + if (workspace.status === 'ready' && workspace.fileId === home.fileId) return; + await penpotMgmt.openFile(home.fileId, { teamId: home.teamId ?? undefined }); +} + +function homePage(home: Home) { + return penpot.currentFile?.pages.find((page) => page.id === home.pageId); +} + function withTimeout(promise: void | Promise, ms: number): Promise { return new Promise((resolve, reject) => { let settled = false; @@ -152,6 +174,14 @@ export async function runTests( : focused; const recorder = createRecorder(penpot, apiSurface as ApiSurface); + const management = recorder.wrap(penpotMgmt, 'PenpotMgmt'); + + // Fixtures shared by the tests of this run (see `TestContext.fixture`). + const fixtures = new Map>(); + const fixture = (key: string, create: () => Promise): Promise => { + if (!fixtures.has(key)) fixtures.set(key, create()); + return fixtures.get(key) as Promise; + }; // Run every test with strict, deterministic API behavior. Set through the // recording proxy so the flags also count towards coverage: @@ -162,13 +192,17 @@ export async function runTests( recorder.proxy.flags.throwValidationErrors = true; recorder.proxy.flags.naturalChildOrdering = true; - // Remember the page that was active when the run started. Tests share global - // state (selection, the active page) with no per-test reset, so a test that - // changes the active page — or fails before restoring it — would silently make - // every later test run on the wrong page. After each test we clear the - // selection and restore this page, all through the *raw* penpot so the cleanup - // isn't credited toward coverage. - const homePage = penpot.currentPage; + // Remember the file and page that were active when the run started. Tests + // share global state (selection, the active page, the open file) with no + // per-test reset, so a test that changes them — or fails before restoring + // them — would silently make every later test run in the wrong place. After + // each test we reopen this file and page and clear the selection, all through + // the *raw* globals so the cleanup isn't credited toward coverage. + const home: Home = { + fileId: penpotMgmt.workspace.fileId, + teamId: penpotMgmt.workspace.teamId, + pageId: penpot.currentPage?.id ?? null, + }; const results: TestResult[] = []; @@ -190,7 +224,7 @@ export async function runTests( // Create/name/remove the scratch board through the *raw* penpot so this // harness bookkeeping isn't credited toward coverage. The test still gets a // recording-wrapped board, so its own access to it is counted. - let rawBoard: ReturnType | undefined; + let boardId: string | undefined; let result: TestResult; // Baseline the file's integrity errors before the test so we can attribute @@ -198,13 +232,21 @@ export async function runTests( const integrityBaseline = integritySignatures(); try { - rawBoard = penpot.createBoard(); + const rawBoard = penpot.createBoard(); rawBoard.name = SCRATCH_NAME; + boardId = rawBoard.id; const board = recorder.wrap(rawBoard, 'Board'); await withTimeout( - testCase.fn({ penpot: recorder.proxy, board }), + testCase.fn({ + penpot: recorder.proxy, + penpotMgmt: management, + board, + fixture, + }), TEST_TIMEOUT_MS, ); + // The integrity baseline belongs to the home file, so check it there. + await returnToHomeFile(home); // Postcondition: the test must not have broken referential integrity. if (integrityBaseline) assertNoNewIntegrityErrors(integrityBaseline); result = { @@ -225,22 +267,27 @@ export async function runTests( // `test.nocleanup` keeps the scratch board and shared state as the test // left them so the result can be inspected in the workspace. if (!testCase.noCleanup) { - try { - rawBoard?.remove(); - } catch { - // best-effort cleanup; never fail a test because teardown failed - } // Reset shared state so the next test starts clean. All best-effort: a // teardown failure must never turn into a test failure. + try { + await returnToHomeFile(home); + } catch { + /* ignore */ + } + try { + if (boardId) homePage(home)?.getShapeById(boardId)?.remove(); + } catch { + /* ignore */ + } try { penpot.selection = []; } catch { /* ignore */ } try { - const active = penpot.currentPage; - if (homePage && active && active.id !== homePage.id) { - await penpot.openPage(homePage); + const page = homePage(home); + if (page && penpot.currentPage?.id !== page.id) { + await penpot.openPage(page); } } catch { /* ignore */ diff --git a/plugins/apps/plugin-api-test-suite/src/framework/temporary.ts b/plugins/apps/plugin-api-test-suite/src/framework/temporary.ts new file mode 100644 index 0000000000..80b86300af --- /dev/null +++ b/plugins/apps/plugin-api-test-suite/src/framework/temporary.ts @@ -0,0 +1,14 @@ +// Shared by the tests (in the sandbox) and the CI driver (in Node), so it must +// stay free of imports. + +/** + * Name prefix of every team and project the tests create. Tests delete the + * teams they create; the live CI driver deletes the teams and projects with + * this prefix after a run. Runs from the plugin UI leave the projects behind. + */ +export const TEMPORARY_PREFIX = '__api_test_tmp__'; + +/** Returns a unique name with {@link TEMPORARY_PREFIX} for a test project. */ +export function temporaryName(label: string): string { + return `${TEMPORARY_PREFIX} ${label} ${Date.now()}`; +} diff --git a/plugins/apps/plugin-api-test-suite/src/framework/types.ts b/plugins/apps/plugin-api-test-suite/src/framework/types.ts index 665597478b..d9ff30e1dc 100644 --- a/plugins/apps/plugin-api-test-suite/src/framework/types.ts +++ b/plugins/apps/plugin-api-test-suite/src/framework/types.ts @@ -1,15 +1,24 @@ -import type { Board, Penpot } from '@penpot/plugin-types'; +import type { Board, Penpot, PenpotMgmt } from '@penpot/plugin-types'; export type TestStatus = 'pending' | 'running' | 'pass' | 'fail'; /** - * The context handed to every test. Tests MUST use `ctx.penpot` (the recording - * proxy) rather than the global `penpot` so their API usage is counted towards - * coverage. A fresh scratch `board` is provided per test and removed afterwards. + * The context handed to every test. Tests MUST use `ctx.penpot` and + * `ctx.penpotMgmt` (the recording proxies) rather than the globals so their API + * usage is counted towards coverage. A fresh scratch `board` is provided per + * test and removed afterwards. */ export interface TestContext { penpot: Penpot; + /** Recording proxy over `penpotMgmt`, available because the suite runs with global scope. */ + penpotMgmt: PenpotMgmt; board: Board; + /** + * Returns the value `create` resolves to, creating it the first time `key` + * is used in the current run. Use it for expensive fixtures shared by + * several tests; a later run creates them again. + */ + fixture(key: string, create: () => Promise): Promise; } export type TestFn = (ctx: TestContext) => void | Promise; diff --git a/plugins/apps/plugin-api-test-suite/src/generated/api-surface.json b/plugins/apps/plugin-api-test-suite/src/generated/api-surface.json index 4fb9d6b689..46d748bfc4 100644 --- a/plugins/apps/plugin-api-test-suite/src/generated/api-surface.json +++ b/plugins/apps/plugin-api-test-suite/src/generated/api-surface.json @@ -138,10 +138,12 @@ "contentsave", "filechange", "finish", + "logout", "pagechange", "selectionchange", "shapechange", - "themechange" + "themechange", + "workspacechange" ], "Export": ["scale", "skipChildren", "suffix", "type"], "File": [ @@ -371,6 +373,16 @@ "Path": ["commands", "d", "fills", "type"], "PathCommand": ["command", "params"], "Penpot": ["closePlugin", "off", "on", "ui", "utils"], + "PenpotMgmt": [ + "createTeam", + "getFile", + "listProjects", + "listTeams", + "off", + "on", + "openFile", + "workspace" + ], "PluginData": [ "getPluginData", "getPluginDataKeys", @@ -381,6 +393,31 @@ ], "Point": ["x", "y"], "PreviousScreen": ["type"], + "Project": [ + "createFile", + "duplicate", + "fileCount", + "id", + "isDefault", + "listFiles", + "moveTo", + "name", + "pinned", + "remove", + "teamId" + ], + "ProjectFile": [ + "duplicate", + "id", + "modifiedAt", + "moveTo", + "name", + "open", + "projectId", + "remove", + "shared", + "teamId" + ], "Push": ["direction", "duration", "easing", "type"], "Rectangle": ["fills", "type"], "RulerGuide": ["board", "orientation", "position", "remove"], @@ -487,6 +524,14 @@ "strokeWidthType" ], "SvgRaw": ["type"], + "Team": [ + "createProject", + "id", + "isDefault", + "listProjects", + "name", + "remove" + ], "Text": [ "align", "applyTypography", @@ -644,7 +689,8 @@ "zoomIntoView", "zoomReset", "zoomToFitAll" - ] + ], + "WorkspaceContext": ["fileId", "fileName", "status", "teamId"] }, "graph": { "ActiveUser": { @@ -2100,6 +2146,12 @@ } }, "Context": { + "management": { + "decl": "Context", + "kind": "get", + "type": null, + "array": false + }, "version": { "decl": "Context", "kind": "get", @@ -2940,6 +2992,18 @@ } }, "EventsMap": { + "workspacechange": { + "decl": "EventsMap", + "kind": "get", + "type": "WorkspaceContext", + "array": false + }, + "logout": { + "decl": "EventsMap", + "kind": "get", + "type": null, + "array": false + }, "pagechange": { "decl": "EventsMap", "kind": "get", @@ -6781,6 +6845,56 @@ "array": false } }, + "PenpotMgmt": { + "workspace": { + "decl": "PenpotMgmt", + "kind": "get", + "type": "WorkspaceContext", + "array": false + }, + "listTeams": { + "decl": "PenpotMgmt", + "kind": "method", + "type": "Team", + "array": true + }, + "createTeam": { + "decl": "PenpotMgmt", + "kind": "method", + "type": "Team", + "array": false + }, + "listProjects": { + "decl": "PenpotMgmt", + "kind": "method", + "type": "Project", + "array": true + }, + "getFile": { + "decl": "PenpotMgmt", + "kind": "method", + "type": "ProjectFile", + "array": false + }, + "openFile": { + "decl": "PenpotMgmt", + "kind": "method", + "type": null, + "array": false + }, + "on": { + "decl": "PenpotMgmt", + "kind": "method", + "type": null, + "array": false + }, + "off": { + "decl": "PenpotMgmt", + "kind": "method", + "type": null, + "array": false + } + }, "PluginData": { "getPluginData": { "decl": "PluginData", @@ -6841,6 +6955,136 @@ "array": false } }, + "Project": { + "id": { + "decl": "Project", + "kind": "get", + "type": null, + "array": false + }, + "teamId": { + "decl": "Project", + "kind": "get", + "type": null, + "array": false + }, + "isDefault": { + "decl": "Project", + "kind": "get", + "type": null, + "array": false + }, + "fileCount": { + "decl": "Project", + "kind": "get", + "type": null, + "array": false + }, + "name": { + "decl": "Project", + "kind": "getset", + "type": null, + "array": false + }, + "pinned": { + "decl": "Project", + "kind": "getset", + "type": null, + "array": false + }, + "listFiles": { + "decl": "Project", + "kind": "method", + "type": "ProjectFile", + "array": true + }, + "createFile": { + "decl": "Project", + "kind": "method", + "type": "ProjectFile", + "array": false + }, + "duplicate": { + "decl": "Project", + "kind": "method", + "type": "Project", + "array": false + }, + "moveTo": { + "decl": "Project", + "kind": "method", + "type": null, + "array": false + }, + "remove": { + "decl": "Project", + "kind": "method", + "type": null, + "array": false + } + }, + "ProjectFile": { + "id": { + "decl": "ProjectFile", + "kind": "get", + "type": null, + "array": false + }, + "teamId": { + "decl": "ProjectFile", + "kind": "get", + "type": null, + "array": false + }, + "projectId": { + "decl": "ProjectFile", + "kind": "get", + "type": null, + "array": false + }, + "modifiedAt": { + "decl": "ProjectFile", + "kind": "get", + "type": null, + "array": false + }, + "name": { + "decl": "ProjectFile", + "kind": "getset", + "type": null, + "array": false + }, + "shared": { + "decl": "ProjectFile", + "kind": "getset", + "type": null, + "array": false + }, + "open": { + "decl": "ProjectFile", + "kind": "method", + "type": null, + "array": false + }, + "moveTo": { + "decl": "ProjectFile", + "kind": "method", + "type": null, + "array": false + }, + "duplicate": { + "decl": "ProjectFile", + "kind": "method", + "type": "ProjectFile", + "array": false + }, + "remove": { + "decl": "ProjectFile", + "kind": "method", + "type": null, + "array": false + } + }, "Push": { "type": { "decl": "Push", @@ -8483,6 +8727,44 @@ "array": true } }, + "Team": { + "id": { + "decl": "Team", + "kind": "get", + "type": null, + "array": false + }, + "isDefault": { + "decl": "Team", + "kind": "get", + "type": null, + "array": false + }, + "name": { + "decl": "Team", + "kind": "getset", + "type": null, + "array": false + }, + "listProjects": { + "decl": "Team", + "kind": "method", + "type": "Project", + "array": true + }, + "createProject": { + "decl": "Team", + "kind": "method", + "type": "Project", + "array": false + }, + "remove": { + "decl": "Team", + "kind": "method", + "type": null, + "array": false + } + }, "Text": { "type": { "decl": "Text", @@ -11470,6 +11752,32 @@ "type": null, "array": false } + }, + "WorkspaceContext": { + "status": { + "decl": "WorkspaceContext", + "kind": "get", + "type": null, + "array": false + }, + "fileId": { + "decl": "WorkspaceContext", + "kind": "get", + "type": null, + "array": false + }, + "fileName": { + "decl": "WorkspaceContext", + "kind": "get", + "type": null, + "array": false + }, + "teamId": { + "decl": "WorkspaceContext", + "kind": "get", + "type": null, + "array": false + } } }, "unions": { diff --git a/plugins/apps/plugin-api-test-suite/src/tests/components.test.ts b/plugins/apps/plugin-api-test-suite/src/tests/components.test.ts index 92d68fd6aa..b2c16486f4 100644 --- a/plugins/apps/plugin-api-test-suite/src/tests/components.test.ts +++ b/plugins/apps/plugin-api-test-suite/src/tests/components.test.ts @@ -64,6 +64,20 @@ describe('Component instances', () => { expect(inst.isVariantHead()).toBeFalsy(); }); + // instance() places the copy at the page origin; a board covering that point + // must not capture it. + test('instance creates the copy at the page root', (ctx) => { + ctx.board.x = 0; + ctx.board.y = 0; + const inst = makeComponent(ctx).instance(); + try { + expect(inst.parent?.id).toBe(ctx.penpot.root?.id); + expect(ctx.board.children.map((c) => c.id)).not.toContain(inst.id); + } finally { + inst.remove(); + } + }); + test('component navigation methods return shapes', (ctx) => { const inst = instanceOf(ctx); expect(inst.componentRoot()).toBeDefined(); diff --git a/plugins/apps/plugin-api-test-suite/src/tests/management.test.ts b/plugins/apps/plugin-api-test-suite/src/tests/management.test.ts new file mode 100644 index 0000000000..67e96d83b6 --- /dev/null +++ b/plugins/apps/plugin-api-test-suite/src/tests/management.test.ts @@ -0,0 +1,564 @@ +import type { Project, ProjectFile, Team } from '@penpot/plugin-types'; +import { expect, expectReject } from '../framework/expect'; +import { describe, test } from '../framework/registry'; +import { temporaryName } from '../framework/temporary'; +import type { TestContext } from '../framework/types'; +import { waitFor, waitForAsync } from './wait'; + +// Application management (`penpotMgmt`), available because the suite runs with +// global scope. These tests create projects and files and move between them, +// so they need the real backend. Every project they create has a temporary +// name, and the live CI driver deletes those projects afterwards. + +async function currentTeam(ctx: TestContext): Promise { + const teamId = ctx.penpotMgmt.workspace.teamId; + const team = (await ctx.penpotMgmt.listTeams()).find((t) => t.id === teamId); + if (!team) throw new Error('The current team is not listed'); + return team; +} + +// One temporary project per run holds the files that tests only read. +function temporaryProject(ctx: TestContext): Promise { + return ctx.fixture('management:project', async () => + (await currentTeam(ctx)).createProject({ name: temporaryName('files') }), + ); +} + +async function temporaryFile( + ctx: TestContext, + name: string, +): Promise { + return (await temporaryProject(ctx)).createFile({ name }); +} + +// Opens `file`, runs `fn` there and comes back to the file the test started in. +async function inFile( + ctx: TestContext, + file: ProjectFile, + fn: () => void | Promise, +): Promise { + const home = ctx.penpotMgmt.workspace; + await file.open(); + try { + await fn(); + } finally { + await ctx.penpotMgmt.openFile(home.fileId!, { + teamId: home.teamId ?? undefined, + }); + } +} + +async function listedFile( + project: Project, + fileId: string, +): Promise { + return (await project.listFiles()).find((f) => f.id === fileId); +} + +async function listedProject( + ctx: TestContext, + projectId: string, +): Promise { + return (await ctx.penpotMgmt.listProjects()).find((p) => p.id === projectId); +} + +interface BrandLibrary { + file: ProjectFile; + colorName: string; + typographyName: string; + componentName: string; +} + +// A shared library with one color, typography and component, built once per +// run in its own file. Tests connect it to the open file, and connected +// libraries are no longer listed as available, so each run needs a new one. +function sharedLibrary(ctx: TestContext): Promise { + return ctx.fixture('management:brand-library', async () => { + const file = await temporaryFile(ctx, 'Brand library'); + const library: BrandLibrary = { + file, + colorName: 'brand-primary', + typographyName: 'brand-heading', + componentName: 'brand-button', + }; + + await inFile(ctx, file, () => { + const local = ctx.penpot.library.local; + const color = local.createColor(); + color.name = library.colorName; + color.color = '#112233'; + + const typography = local.createTypography(); + typography.name = library.typographyName; + + const rect = ctx.penpot.createRectangle(); + const component = local.createComponent([rect]); + component.name = library.componentName; + }); + + file.shared = true; + const isListed = async () => + (await ctx.penpot.library.availableLibraries()).some( + (l) => l.id === file.id, + ); + await waitForAsync(isListed); + if (!(await isListed())) { + throw new Error('The shared library is not listed as available'); + } + return library; + }); +} + +describe('Management', () => { + test('workspace describes the open file', (ctx) => { + const workspace = ctx.penpotMgmt.workspace; + expect(workspace.status).toBe('ready'); + expect(workspace.fileId).toBe(ctx.penpot.currentFile?.id); + expect(workspace.fileName).toBe(ctx.penpot.currentFile?.name); + expect(typeof workspace.teamId).toBe('string'); + }); + + test('openFile resolves at once for the open file', async (ctx) => { + const workspace = ctx.penpotMgmt.workspace; + await ctx.penpotMgmt.openFile(workspace.fileId!, { + teamId: workspace.teamId ?? undefined, + }); + expect(ctx.penpotMgmt.workspace).toEqual(workspace); + }); + + test('management methods reject invalid identifiers', async (ctx) => { + const mgmt = ctx.penpotMgmt; + await expectReject(mgmt.openFile('not-a-uuid'), 'file UUID'); + await expectReject(mgmt.getFile('not-a-uuid'), 'file UUID'); + await expectReject( + mgmt.listProjects({ teamId: 'not-a-uuid' }), + 'team UUID', + ); + }); + + describe.skipIfMocked('Discovery', () => { + test('getFile loads the open file', async (ctx) => { + const workspace = ctx.penpotMgmt.workspace; + const file = await ctx.penpotMgmt.getFile(workspace.fileId!); + + expect(file.id).toBe(workspace.fileId); + expect(file.name).toBe(workspace.fileName); + expect(file.teamId).toBe(workspace.teamId); + expect(file.shared).toBe(false); + expect(Number.isNaN(Date.parse(file.modifiedAt))).toBe(false); + + const parent = await listedProject(ctx, file.projectId); + expect(parent).toBeDefined(); + expect((await listedFile(parent!, file.id))?.name).toBe(file.name); + }); + + test('getFile loads a file that is not open', async (ctx) => { + const created = await temporaryFile(ctx, 'Loaded by id'); + const file = await ctx.penpotMgmt.getFile(created.id); + + expect(file.name).toBe('Loaded by id'); + expect(file.projectId).toBe(created.projectId); + expect(file.teamId).toBe(created.teamId); + }); + }); + + describe.skipIfMocked('Teams', () => { + test('listTeams includes the current team', async (ctx) => { + const team = await currentTeam(ctx); + expect(typeof team.name).toBe('string'); + expect(typeof team.isDefault).toBe('boolean'); + }); + + test('team and root list the same projects', async (ctx) => { + const team = await currentTeam(ctx); + const fromTeam = (await team.listProjects()).map((p) => p.id).sort(); + const fromRoot = (await ctx.penpotMgmt.listProjects()) + .map((p) => p.id) + .sort(); + + expect(fromTeam).toEqual(fromRoot); + expect( + (await ctx.penpotMgmt.listProjects()).some((p) => p.isDefault), + ).toBe(true); + }); + + test('teams do not expose deletion', async (ctx) => { + expect('remove' in (await currentTeam(ctx))).toBe(false); + }); + + test('assigning name renames the team', async (ctx) => { + const team = await currentTeam(ctx); + const original = team.name; + const renamed = `${original} (renamed)`; + + try { + team.name = renamed; + await waitFor(() => team.name === renamed, { timeout: 5000 }); + expect(team.name).toBe(renamed); + await waitForAsync( + async () => (await currentTeam(ctx)).name === renamed, + ); + expect((await currentTeam(ctx)).name).toBe(renamed); + } finally { + team.name = original; + await waitForAsync( + async () => (await currentTeam(ctx)).name === original, + ); + } + }); + }); + + describe.skipIfMocked('Projects', () => { + test('createProject and createFile return objects', async (ctx) => { + const home = ctx.penpotMgmt.workspace; + const team = await currentTeam(ctx); + const name = temporaryName('created'); + const created = await team.createProject({ name: ` ${name} ` }); + + expect(created.name).toBe(name); + expect(created.teamId).toBe(team.id); + expect(created.isDefault).toBe(false); + expect(created.pinned).toBe(false); + expect(created.fileCount).toBe(0); + + const file = await created.createFile({ name: ' Login ' }); + expect(file.name).toBe('Login'); + expect(file.projectId).toBe(created.id); + expect(file.teamId).toBe(team.id); + expect(file.shared).toBe(false); + // fileCount is a listing snapshot, so list the project again. + expect(created.fileCount).toBe(0); + expect((await listedProject(ctx, created.id))?.fileCount).toBe(1); + expect((await created.listFiles()).map((f) => f.id)).toEqual([file.id]); + // Creating does not navigate. + expect(ctx.penpotMgmt.workspace).toEqual(home); + }); + + test('assigning name and pinned updates the project', async (ctx) => { + const team = await currentTeam(ctx); + const created = await team.createProject({ + name: temporaryName('to rename'), + }); + const renamed = temporaryName('renamed'); + + created.name = renamed; + created.pinned = true; + await waitFor(() => created.name === renamed && created.pinned, { + timeout: 5000, + }); + expect(created.name).toBe(renamed); + expect(created.pinned).toBe(true); + + await waitForAsync(async () => { + const listed = await listedProject(ctx, created.id); + return listed?.name === renamed && listed.pinned; + }); + const listed = await listedProject(ctx, created.id); + expect(listed?.name).toBe(renamed); + expect(listed?.pinned).toBe(true); + }); + + test('duplicate copies a project with its files', async (ctx) => { + const team = await currentTeam(ctx); + const original = await team.createProject({ + name: temporaryName('original'), + }); + await original.createFile({ name: 'Copied' }); + const name = temporaryName('copy'); + + const copy = await original.duplicate({ name }); + expect(copy.id).not.toBe(original.id); + expect(copy.name).toBe(name); + expect((await copy.listFiles()).map((f) => f.name)).toEqual(['Copied']); + }); + + test('remove deletes a project', async (ctx) => { + const team = await currentTeam(ctx); + const created = await team.createProject({ + name: temporaryName('removed'), + }); + + await created.remove(); + expect(await listedProject(ctx, created.id)).toBeUndefined(); + }); + + test('moveTo moves a project to another team', async (ctx) => { + const team = await currentTeam(ctx); + const target = await ctx.penpotMgmt.createTeam({ + name: temporaryName('team'), + }); + + // Teams cannot be deleted through the API; the live CI driver deletes + // the temporary team after the run. + const moved = await team.createProject({ + name: temporaryName('moved'), + }); + await moved.moveTo(target); + + expect(moved.teamId).toBe(target.id); + expect((await target.listProjects()).map((p) => p.id)).toContain( + moved.id, + ); + expect(await listedProject(ctx, moved.id)).toBeUndefined(); + }); + }); + + describe.skipIfMocked('Files', () => { + test('assigning name renames a file', async (ctx) => { + const file = await temporaryFile(ctx, 'To rename'); + const parent = await temporaryProject(ctx); + + file.name = ' Renamed '; + await waitFor(() => file.name === 'Renamed', { timeout: 5000 }); + expect(file.name).toBe('Renamed'); + await waitForAsync( + async () => (await listedFile(parent, file.id))?.name === 'Renamed', + ); + expect((await listedFile(parent, file.id))?.name).toBe('Renamed'); + }); + + test('duplicate copies a file into its project', async (ctx) => { + const file = await temporaryFile(ctx, 'Original'); + const copy = await file.duplicate({ name: 'Duplicated' }); + + expect(copy.id).not.toBe(file.id); + expect(copy.name).toBe('Duplicated'); + expect(copy.projectId).toBe(file.projectId); + expect( + (await listedFile(await temporaryProject(ctx), copy.id))?.name, + ).toBe('Duplicated'); + }); + + test('moveTo moves a file to another project', async (ctx) => { + const file = await temporaryFile(ctx, 'Moved'); + const team = await currentTeam(ctx); + const target = await team.createProject({ + name: temporaryName('target'), + }); + + await file.moveTo(target); + expect(file.projectId).toBe(target.id); + expect(await listedFile(target, file.id)).toBeDefined(); + expect( + await listedFile(await temporaryProject(ctx), file.id), + ).toBeUndefined(); + }); + + test('remove deletes a file', async (ctx) => { + const file = await temporaryFile(ctx, 'Removed'); + + await file.remove(); + expect( + await listedFile(await temporaryProject(ctx), file.id), + ).toBeUndefined(); + }); + + test('renaming the open file updates the workspace', async (ctx) => { + const workspace = ctx.penpotMgmt.workspace; + const file = await ctx.penpotMgmt.getFile(workspace.fileId!); + const original = file.name; + + try { + file.name = `${original} (renamed)`; + await waitFor( + () => ctx.penpotMgmt.workspace.fileName === `${original} (renamed)`, + ); + expect(ctx.penpotMgmt.workspace.fileName).toBe(`${original} (renamed)`); + } finally { + file.name = original; + await waitFor(() => ctx.penpotMgmt.workspace.fileName === original, { + timeout: 5000, + }); + } + }); + }); + + describe.skipIfMocked('Navigation', () => { + test('open switches to another file and back', async (ctx) => { + const home = ctx.penpotMgmt.workspace; + const other = await temporaryFile(ctx, 'Navigation'); + + await other.open(); + expect(ctx.penpotMgmt.workspace).toEqual({ + status: 'ready', + fileId: other.id, + fileName: 'Navigation', + teamId: other.teamId, + }); + expect(ctx.penpot.currentFile?.id).toBe(other.id); + expect(ctx.penpot.currentPage).not.toBeNull(); + + await ctx.penpotMgmt.openFile(home.fileId!, { + teamId: home.teamId ?? undefined, + }); + expect(ctx.penpotMgmt.workspace).toEqual(home); + expect(ctx.penpot.currentFile?.id).toBe(home.fileId); + }); + + test('file events fire when switching files', async (ctx) => { + const home = ctx.penpotMgmt.workspace; + const other = await temporaryFile(ctx, 'Events'); + + const workspaces: string[] = []; + const files: string[] = []; + const finished: string[] = []; + const workspaceListener = ctx.penpotMgmt.on('workspacechange', (w) => { + workspaces.push(`${w.status}:${w.fileId}`); + }); + const fileListener = ctx.penpot.on('filechange', (file) => { + files.push(file.id); + }); + const finishListener = ctx.penpot.on('finish', (fileId) => { + finished.push(fileId); + }); + + try { + await inFile(ctx, other, () => undefined); + await waitFor(() => files.includes(home.fileId!)); + } finally { + ctx.penpotMgmt.off(workspaceListener); + ctx.penpot.off(fileListener); + ctx.penpot.off(finishListener); + } + + expect(workspaces).toContain(`loading:${other.id}`); + expect(workspaces).toContain(`ready:${other.id}`); + expect(workspaces).toContain(`ready:${home.fileId}`); + expect(files).toContain(other.id); + expect(files).toContain(home.fileId); + expect(finished).toContain(home.fileId); + expect(finished).toContain(other.id); + }); + + test('off stops workspacechange notifications', async (ctx) => { + const other = await temporaryFile(ctx, 'Unsubscribed'); + let count = 0; + const listener = ctx.penpotMgmt.on('workspacechange', () => { + count += 1; + }); + ctx.penpotMgmt.off(listener); + + await inFile(ctx, other, () => undefined); + expect(count).toBe(0); + }); + + test('changes made in another file persist', async (ctx) => { + const other = await temporaryFile(ctx, 'Persistence'); + let shapeId = ''; + + await inFile(ctx, other, () => { + const rect = ctx.penpot.createRectangle(); + rect.name = 'remote-shape'; + shapeId = rect.id; + }); + + await inFile(ctx, other, () => { + const shape = ctx.penpot.currentPage?.getShapeById(shapeId); + expect(shape?.name).toBe('remote-shape'); + }); + }); + }); + + describe.skipIfMocked('Shared libraries', () => { + test('assigning shared publishes and unpublishes a library', async (ctx) => { + const file = await temporaryFile(ctx, 'Publishing'); + const isListed = async () => + (await ctx.penpot.library.availableLibraries()).some( + (library) => library.id === file.id, + ); + + file.shared = true; + await waitFor(() => file.shared, { timeout: 5000 }); + expect(file.shared).toBe(true); + await waitForAsync(isListed); + expect(await isListed()).toBe(true); + expect( + (await listedFile(await temporaryProject(ctx), file.id))?.shared, + ).toBe(true); + + file.shared = false; + await waitForAsync(async () => !(await isListed())); + expect(await isListed()).toBe(false); + }); + + test('availableLibraries summarises a shared library', async (ctx) => { + const library = await sharedLibrary(ctx); + const summary = (await ctx.penpot.library.availableLibraries()).find( + (item) => item.id === library.file.id, + ); + + expect(summary).toBeDefined(); + expect(summary?.name).toBe('Brand library'); + expect(summary?.numColors).toBe(1); + expect(summary?.numTypographies).toBe(1); + expect(summary?.numComponents).toBe(1); + }); + + test('connectLibrary links a shared library to the file', async (ctx) => { + const library = await sharedLibrary(ctx); + const connected = await ctx.penpot.library.connectLibrary( + library.file.id, + ); + + expect(connected.id).toBe(library.file.id); + expect(connected.name).toBe('Brand library'); + expect(connected.colors.map((c) => c.name)).toContain(library.colorName); + expect(connected.typographies.map((t) => t.name)).toContain( + library.typographyName, + ); + expect(connected.components.map((c) => c.name)).toContain( + library.componentName, + ); + expect( + ctx.penpot.library.connected.some((l) => l.id === library.file.id), + ).toBe(true); + }); + + test('connected library colors fill shapes by reference', async (ctx) => { + const library = await sharedLibrary(ctx); + const connected = await ctx.penpot.library.connectLibrary( + library.file.id, + ); + const color = connected.colors.find((c) => c.name === library.colorName); + expect(color).toBeDefined(); + + const rect = ctx.penpot.createRectangle(); + ctx.board.appendChild(rect); + rect.fills = [color!.asFill()]; + + expect(rect.fills[0]?.fillColor).toBe('#112233'); + expect(rect.fills[0]?.fillColorRefId).toBe(color!.id); + expect(rect.fills[0]?.fillColorRefFile).toBe(library.file.id); + }); + + test('connected library components create linked instances', async (ctx) => { + const library = await sharedLibrary(ctx); + const connected = await ctx.penpot.library.connectLibrary( + library.file.id, + ); + const component = connected.components.find( + (c) => c.name === library.componentName, + ); + expect(component).toBeDefined(); + + const instance = component!.instance(); + ctx.board.appendChild(instance); + + expect(instance.isComponentInstance()).toBe(true); + expect(instance.component()?.id).toBe(component!.id); + expect(instance.component()?.libraryId).toBe(library.file.id); + }); + + test('connected libraries reject plugin data writes', async (ctx) => { + const library = await sharedLibrary(ctx); + const connected = await ctx.penpot.library.connectLibrary( + library.file.id, + ); + + expect(() => connected.setPluginData('key', 'value')).toThrow(); + expect(() => + connected.setSharedPluginData('ns', 'key', 'value'), + ).toThrow(); + }); + }); +}); diff --git a/plugins/apps/plugin-api-test-suite/src/tests/pages.test.ts b/plugins/apps/plugin-api-test-suite/src/tests/pages.test.ts index 7c546a9e6a..3009ff8008 100644 --- a/plugins/apps/plugin-api-test-suite/src/tests/pages.test.ts +++ b/plugins/apps/plugin-api-test-suite/src/tests/pages.test.ts @@ -52,10 +52,11 @@ describe('Pages', () => { const page = ctx.penpot.createPage(); await ctx.penpot.openPage(page); page.remove(); - // The switch to another page happens asynchronously; wait for it. + // The switch to another page happens asynchronously; wait for it. Until it + // lands the workspace is not ready, so `currentPage` can briefly be null. const start = Date.now(); let active = ctx.penpot.currentPage; - while (active && active.id === page.id && Date.now() - start < 2000) { + while ((!active || active.id === page.id) && Date.now() - start < 2000) { await new Promise((resolve) => setTimeout(resolve, 50)); active = ctx.penpot.currentPage; } diff --git a/plugins/apps/plugin-api-test-suite/src/tests/shapes-factories.test.ts b/plugins/apps/plugin-api-test-suite/src/tests/shapes-factories.test.ts index 6dc8be8fa5..eb0782cae0 100644 --- a/plugins/apps/plugin-api-test-suite/src/tests/shapes-factories.test.ts +++ b/plugins/apps/plugin-api-test-suite/src/tests/shapes-factories.test.ts @@ -325,4 +325,41 @@ describe('Shapes', () => { expect(a.x).toBeCloseTo(10, 0); }); }); + + // The factories place new shapes at the page origin. A board covering that + // point must not capture them: they belong to the page root until the plugin + // reparents them. + describe('Placement', () => { + const svg = + '' + + ''; + + test('createShapeFromSvg creates the shape at the page root', (ctx) => { + ctx.board.x = 0; + ctx.board.y = 0; + const group = ctx.penpot.createShapeFromSvg(svg); + expect(group).not.toBeNull(); + if (!group) return; + try { + expect(group.parent?.id).toBe(ctx.penpot.root?.id); + expect(ctx.board.children.map((c) => c.id)).not.toContain(group.id); + } finally { + group.remove(); + } + }); + + test('createShapeFromSvgWithImages creates the shape at the page root', async (ctx) => { + ctx.board.x = 0; + ctx.board.y = 0; + const group = await ctx.penpot.createShapeFromSvgWithImages(svg); + expect(group).not.toBeNull(); + if (!group) return; + try { + expect(group.parent?.id).toBe(ctx.penpot.root?.id); + expect(ctx.board.children.map((c) => c.id)).not.toContain(group.id); + } finally { + group.remove(); + } + }); + }); }); diff --git a/plugins/apps/plugin-api-test-suite/src/tests/tokens.test.ts b/plugins/apps/plugin-api-test-suite/src/tests/tokens.test.ts index 2614617c77..0f5faec89f 100644 --- a/plugins/apps/plugin-api-test-suite/src/tests/tokens.test.ts +++ b/plugins/apps/plugin-api-test-suite/src/tests/tokens.test.ts @@ -418,6 +418,31 @@ describe('Tokens', () => { expect(Object.keys(b.tokens)).toContain('paddingLeft'); }); + // The target is a text shape because the attribute set of a board excludes + // the font-family property, so the application is filtered out there and + // the assertions pass vacuously. + // The family comes from the bundled Google fonts catalog, available in every + // run. `Red` in its name reads as a color if the resolver parses the words. + test('applyToken binds a fontFamilies token to a text shape', async (ctx) => { + const set = activeSet(ctx, unique('set')); + const font = ctx.penpot.fonts.findByName('Red Hat Display'); + if (!font) throw new Error('Red Hat Display font not found'); + const token = set.addToken({ + type: 'fontFamilies', + name: unique('fontFamilies.'), + value: [font.fontFamily], + }); + + const t = ctx.penpot.createText('Hello Penpot'); + if (!t) throw new Error('createText returned null'); + ctx.board.appendChild(t); + + t.applyToken(token, ['fontFamilies']); + await waitFor(() => t.fontFamily === font.fontFamily); + expect(Object.keys(t.tokens)).toContain('fontFamilies'); + expect(t.fontFamily).toBe(font.fontFamily); + }); + test('applyToken accepts the uniform strokeWidth property', async (ctx) => { const set = activeSet(ctx, unique('set')); const token = set.addToken({ @@ -779,6 +804,39 @@ describe('Token types', () => { expect(resolved as unknown as string[]).toContain('Arial'); }); + // The resolver parses a family whose name has several words as a list of + // word symbols, so each family name must survive as a single entry. A + // single-word family takes the simpler path the test above covers. + test('fontFamilies token resolvedValue keeps multi-word families whole', (ctx) => { + const set = activeSet(ctx, unique('set')); + const token = set.addToken({ + type: 'fontFamilies', + name: unique('fontFamilies.'), + value: ['Hanken Grotesk'], + }); + // The stored value keeps the family whole, isolating resolution below. + expect(token.value).toEqual(['Hanken Grotesk']); + + const resolved = token.resolvedValue as unknown as string[]; + expect(Array.isArray(resolved)).toBe(true); + expect(resolved).toHaveLength(1); + expect(resolved[0]).toBe('Hanken Grotesk'); + }); + + // Words like `Red` or `Black` read as colors and `2P` as a number and a word + // when the resolver parses the family name. + test('fontFamilies token resolvedValue keeps names with expression words', (ctx) => { + const set = activeSet(ctx, unique('set')); + const families = ['Red Hat Display', 'Archivo Black', 'Press Start 2P']; + const token = set.addToken({ + type: 'fontFamilies', + name: unique('fontFamilies.'), + value: families, + }); + + expect(token.resolvedValue as unknown as string[]).toEqual(families); + }); + test('shadow token exposes its composite value', (ctx) => { const set = activeSet(ctx, unique('set')); const token = set.addToken({ diff --git a/plugins/apps/plugin-api-test-suite/src/tests/wait.ts b/plugins/apps/plugin-api-test-suite/src/tests/wait.ts index 36eecc2bb4..dbe60af3b4 100644 --- a/plugins/apps/plugin-api-test-suite/src/tests/wait.ts +++ b/plugins/apps/plugin-api-test-suite/src/tests/wait.ts @@ -20,3 +20,20 @@ export async function waitFor( await new Promise((resolve) => setTimeout(resolve, interval)); } } + +/** + * Like {@link waitFor}, for conditions that need an async check, such as + * listing from the backend after a change that saves in the background. + */ +export async function waitForAsync( + predicate: () => Promise, + { + timeout = 5000, + interval = 100, + }: { timeout?: number; interval?: number } = {}, +): Promise { + const start = Date.now(); + while (!(await predicate()) && Date.now() - start < timeout) { + await new Promise((resolve) => setTimeout(resolve, interval)); + } +} diff --git a/plugins/apps/poc-state-plugin/package.json b/plugins/apps/poc-state-plugin/package.json index df5bc13b27..f2138cd8c0 100644 --- a/plugins/apps/poc-state-plugin/package.json +++ b/plugins/apps/poc-state-plugin/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "build": "ng build poc-state-plugin && pnpm run build:plugin", + "build": "pnpm run build:plugin && ng build poc-state-plugin", "build:dev": "ng build poc-state-plugin --configuration development", "build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=poc-state-plugin", "watch": "node ../../tools/scripts/build-plugin.mjs --plugin=poc-state-plugin --watch", @@ -13,5 +13,5 @@ "lint": "eslint .", "test": "vitest" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/poc-tokens-plugin/package.json b/plugins/apps/poc-tokens-plugin/package.json index 41337275df..a152f95df1 100644 --- a/plugins/apps/poc-tokens-plugin/package.json +++ b/plugins/apps/poc-tokens-plugin/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "build": "ng build poc-tokens-plugin && pnpm run build:plugin", + "build": "pnpm run build:plugin && ng build poc-tokens-plugin", "build:dev": "ng build poc-tokens-plugin --configuration development", "build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=poc-tokens-plugin", "watch": "node ../../tools/scripts/build-plugin.mjs --plugin=poc-tokens-plugin --watch", @@ -13,5 +13,5 @@ "lint": "eslint .", "test": "exit 0" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/rename-layers-plugin/package.json b/plugins/apps/rename-layers-plugin/package.json index 38eb1e0939..a8a0ce18d3 100644 --- a/plugins/apps/rename-layers-plugin/package.json +++ b/plugins/apps/rename-layers-plugin/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "build": "ng build rename-layers-plugin && pnpm run build:plugin", + "build": "pnpm run build:plugin && ng build rename-layers-plugin", "build:dev": "ng build rename-layers-plugin --configuration development", "build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=rename-layers-plugin", "watch": "node ../../tools/scripts/build-plugin.mjs --plugin=rename-layers-plugin --watch", @@ -13,5 +13,5 @@ "lint": "eslint .", "test": "vitest" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/apps/table-plugin/package.json b/plugins/apps/table-plugin/package.json index f2bc2d696b..3ae9e4bd23 100644 --- a/plugins/apps/table-plugin/package.json +++ b/plugins/apps/table-plugin/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "build": "ng build table-plugin && pnpm run build:plugin", + "build": "pnpm run build:plugin && ng build table-plugin", "build:dev": "ng build table-plugin --configuration development", "build:plugin": "node ../../tools/scripts/build-plugin.mjs --plugin=table-plugin", "watch": "node ../../tools/scripts/build-plugin.mjs --plugin=table-plugin --watch", @@ -13,5 +13,5 @@ "lint": "eslint .", "test": "vitest" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/libs/plugin-types/index.d.ts b/plugins/libs/plugin-types/index.d.ts index c756cc50b4..a21eb02a64 100644 --- a/plugins/libs/plugin-types/index.d.ts +++ b/plugins/libs/plugin-types/index.d.ts @@ -4,7 +4,7 @@ */ export interface Penpot extends Omit< Context, - 'addListener' | 'removeListener' + 'addListener' | 'removeListener' | 'management' > { readonly ui: { /** @@ -110,6 +110,7 @@ export interface Penpot extends Omit< * - themechange: event emitted when the user changes its theme. The callback will receive the new theme (currently: either `dark` or `light`) * - filechange: event emitted when a different file is opened. The callback will receive the new file. * - contentsave: event emitted after the file content is saved in the backend. The callback receives no arguments. + * - contentrollback: event emitted after the backend rejected some changes and they were undone in the workspace. The callback receives a `ContentRollback` with the files, pages and shapes affected. * - finish: event emitted when the current file is closed. The callback will receive the id of the closed file. * * @param type The event type to listen for. @@ -778,10 +779,277 @@ export interface CommonLayout { remove(): void; } +/** Current workspace of this browser tab. */ +export interface WorkspaceContext { + readonly status: 'none' | 'loading' | 'ready'; + readonly fileId: string | null; + readonly fileName: string | null; + readonly teamId: string | null; +} + +/** + * A team the user belongs to. Requires `manage:teams`. + * + * Values are a snapshot taken when the team was listed; changes made through + * this object update it. + * Property assignments run asynchronously and update values after success. + * Backend failures are logged to the console; assignments cannot be awaited. + */ +export interface Team { + readonly id: string; + /** Whether this is the user's personal team. */ + readonly isDefault: boolean; + /** + * The team name. Assigning renames the team; requires `manage:teams`. + * Trims the name and requires 1 to 250 characters. + */ + name: string; + /** + * Lists the team's projects. Requires `content:read`. + * + * @example + * ```ts + * const [team] = await penpotMgmt.listTeams(); + * const projects = await team.listProjects(); + * ``` + */ + listProjects(): Promise; + /** Lists projects still recoverable from Trash. Requires `content:read`. */ + listDeletedProjects(): Promise; + /** Lists files still recoverable from Trash. Requires `content:read`. */ + listDeletedFiles(): Promise; + /** + * Creates a project in the team. Requires `manage:projects`. + * + * - Trims `name` and requires 1 to 250 characters. + * - Does not change navigation. + * + * @example + * ```ts + * const project = await team.createProject({ name: 'Design system' }); + * ``` + */ + createProject(options: { name: string }): Promise; +} + +/** + * A project in the dashboard. + * + * Values are a snapshot taken when the project was listed; changes made through + * this object update it. + * Property assignments run asynchronously and update values after success. + * Backend failures are logged to the console; assignments cannot be awaited. + */ +export interface Project { + readonly id: string; + readonly teamId: string; + /** Whether this is the team's Drafts project. */ + readonly isDefault: boolean; + /** Number of files when the project was listed; file operations do not update it. */ + readonly fileCount: number; + /** + * The project name. Assigning renames the project; requires `manage:projects`. + * Trims the name and requires 1 to 250 characters. + */ + name: string; + /** Whether the project is pinned in the sidebar. Assigning requires `manage:projects`. */ + pinned: boolean; + /** + * Lists the files in the project. Requires `content:read`. + * + * @example + * ```ts + * const files = await project.listFiles(); + * ``` + */ + listFiles(): Promise; + /** Lists the project's files still recoverable from Trash. Requires `content:read`. */ + listDeletedFiles(): Promise; + /** + * Restores the project through its files using the dashboard's Trash operation. + * Requires `manage:projects` and team edit access. Does not require `manage:delete`. + * Rejects if the project has no recoverable files, including empty projects. + */ + restore(): Promise; + /** + * Creates a file with an initial page in the project. Requires `content:write`. + * + * - Trims `name` and requires 1 to 250 characters. + * - Does not open the file. Use `ProjectFile.open` to edit it. + * + * @example + * ```ts + * const file = await project.createFile({ name: 'Login' }); + * await file.open(); + * ``` + */ + createFile(options: { name: string }): Promise; + /** + * Duplicates the project with all its files. Requires `manage:projects`. + * + * `name` defaults to the name the backend chooses. + */ + duplicate(options?: { name?: string }): Promise; + /** Moves the project to another team. Requires `manage:teams`. */ + moveTo(team: Team): Promise; + /** + * Moves the project and its files to Trash. Requires `manage:delete`. + * They can be restored during the retention period. Cannot permanently delete. + */ + remove(): Promise; +} + +/** + * A file in the dashboard, which may or may not be open. + * + * Values are a snapshot taken when the file was listed; changes made through + * this object update it. To read or edit its content, `open` it and use + * `penpot.currentFile`. + * Property assignments run asynchronously and update values after success. + * Backend failures are logged to the console; assignments cannot be awaited. + */ +export interface ProjectFile { + readonly id: string; + readonly teamId: string; + readonly projectId: string; + /** Last modification time as an ISO 8601 string in UTC. */ + readonly modifiedAt: string; + /** + * The file name. Assigning renames the file; requires `content:write`. + * Trims the name and requires 1 to 250 characters. + */ + name: string; + /** + * Whether the file is shared as a library with its team. Assigning requires + * `library:write`. A shared file is listed by + * `penpot.library.availableLibraries()`; unsharing copies its assets into + * every file that uses it and disconnects them. + */ + shared: boolean; + /** + * Opens the file in this tab and resolves once its workspace and page are + * ready. Requires `content:read`. + */ + open(): Promise; + /** + * Restores the file using the dashboard's Trash operation, including its parent + * project if deleted. Requires `content:write` and team edit access. + * Rejects if the file no longer exists. Does not require `manage:delete`. + */ + restore(): Promise; + /** + * Moves the file to another project of the same team. Requires `content:write`. + * Rejects a destination that currently belongs to another team. + */ + moveTo(project: Project): Promise; + /** + * Duplicates the file in its project. Requires `content:write`. + * + * `name` defaults to the name the backend chooses. + */ + duplicate(options?: { name?: string }): Promise; + /** + * Moves the file to Trash. Requires `manage:delete`. + * It can be restored during the retention period. Cannot permanently delete. + */ + remove(): Promise; +} + +/** + * Application management API exposed as `penpotMgmt` to global plugins. + * + * - Lists teams, projects and files, and opens files, from the dashboard or a + * file workspace. Teams, projects and files are objects with their own + * properties and methods. + * - Listing and opening require `content:read`; teams require `manage:teams`. + * - File operations on `penpot` require a ready workspace. + * - Reacquire file, page and shape objects after changing files. + */ +export interface PenpotMgmt { + /** Current workspace availability and metadata; usable for file operations when status is `ready`. */ + readonly workspace: WorkspaceContext; + /** + * Lists the teams of the user. Requires `manage:teams`. + * + * @example + * ```ts + * const teams = await penpotMgmt.listTeams(); + * ``` + */ + listTeams(): Promise; + /** + * Creates a team with the user as owner. Requires `manage:teams`. + * + * - Trims `name` and requires 1 to 250 characters. + * - Does not change navigation. + * + * @example + * ```ts + * const team = await penpotMgmt.createTeam({ name: 'Marketing' }); + * ``` + */ + createTeam(options: { name: string }): Promise; + /** + * Lists the projects of a team. Requires `content:read`. + * + * - `teamId` defaults to the current team. + * - Rejects invalid identifiers and backend access errors. + * + * @example + * ```ts + * const projects = await penpotMgmt.listProjects(); + * ``` + */ + listProjects(options?: { teamId?: string }): Promise; + /** + * Lists projects still recoverable from Trash. Requires `content:read`. + * `teamId` defaults to the current team. Use `Project.restore` to recover them. + */ + listDeletedProjects(options?: { teamId?: string }): Promise; + /** + * Lists files still recoverable from Trash. Requires `content:read`. + * `teamId` defaults to the current team. Use `ProjectFile.restore` to recover them. + */ + listDeletedFiles(options?: { teamId?: string }): Promise; + /** + * Loads a file by id, open or not. Requires `content:read`. + * + * @example + * ```ts + * const file = await penpotMgmt.getFile(penpotMgmt.workspace.fileId!); + * file.name = 'Checkout'; + * ``` + */ + getFile(fileId: string): Promise; + /** + * Opens a file in this tab and resolves once its workspace and page are ready. + * + * - `teamId` defaults to the current team. + * - Rejects invalid identifiers, failed or superseded navigation, and waits exceeding 30 seconds. + * + * @example + * ```ts + * await penpotMgmt.openFile(file.id, { teamId: file.teamId }); + * ``` + */ + openFile(fileId: string, options?: { teamId?: string }): Promise; + /** Subscribes to workspace availability and metadata changes. */ + on( + type: 'workspacechange', + callback: (workspace: WorkspaceContext) => void, + ): symbol; + off(listenerId: symbol): void; +} + /** * Represents the context of Penpot, providing access to various Penpot functionalities and data. */ export interface Context { + /** + * Backs `penpotMgmt`. Its events go through the plugin listener registry, + * so `on` and `off` are not part of it. + */ + readonly management?: Omit; /** * Returns the current penpot version. */ @@ -1556,6 +1824,8 @@ export interface Ellipse extends ShapeBase { * ``` */ export interface EventsMap { + readonly workspacechange: WorkspaceContext; + readonly logout: void; /** * The `pagechange` event is triggered when the active page in the project is changed. */ @@ -1590,6 +1860,37 @@ export interface EventsMap { * The `contentsave` event will trigger after the file content is saved in the backend. */ readonly contentsave: void; + + /** + * The `contentrollback` event will trigger after the backend rejected some changes + * and the workspace undid them, together with the rest of their undo steps and any + * later change to the same file. Changes made by a plugin can be undone this way + * after the call that made them returned, so a plugin that relies on them should + * read the affected shapes again. The event fires once the undone data is in the + * workspace. When the workspace could not undo the changes in place, it reloads the + * file from the server first; the whole file then holds the server data, so shapes + * outside the listed ones may have changed too. + */ + readonly contentrollback: ContentRollback; +} + +/** + * What a rollback of rejected changes undid, as given to the `contentrollback` event. + */ +export interface ContentRollback { + /** + * Ids of the files that lost changes. + */ + readonly fileIds: string[]; + /** + * Ids of the pages the undone changes touched. + */ + readonly pageIds: string[]; + /** + * Ids of the page shapes the undone changes touched. A shape the undone changes + * created no longer exists; one they removed exists again. + */ + readonly shapeIds: string[]; } /** @@ -5880,4 +6181,5 @@ export interface Viewport { declare global { const penpot: Penpot; + const penpotMgmt: PenpotMgmt; } diff --git a/plugins/libs/plugin-types/package.json b/plugins/libs/plugin-types/package.json index e4959293fa..6804ca045a 100644 --- a/plugins/libs/plugin-types/package.json +++ b/plugins/libs/plugin-types/package.json @@ -7,5 +7,5 @@ "build": "node ../../tools/scripts/build-types.mjs", "lint": "tsc -p . --noEmit" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/libs/plugins-runtime/package-lock.json b/plugins/libs/plugins-runtime/package-lock.json deleted file mode 100644 index b484be67cd..0000000000 --- a/plugins/libs/plugins-runtime/package-lock.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "@penpot/plugins-runtime", - "version": "0.12.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@penpot/plugins-runtime", - "version": "0.12.0", - "dependencies": { - "@penpot/plugin-types": "^0.12.0", - "ses": "^1.1.0", - "zod": "^3.22.4" - } - }, - "node_modules/@endo/env-options": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@endo/env-options/-/env-options-1.1.6.tgz", - "integrity": "sha512-Uqy94PwLTco90Yfign43muvDtjsYTbL6Ck4W5sSWQUqJiFOL+YP8kiBOoGyQynDsGTY0MCQvMxEPfphaJKnzlQ==" - }, - "node_modules/@penpot/plugin-types": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@penpot/plugin-types/-/plugin-types-0.12.0.tgz", - "integrity": "sha512-rtSJ8memBAWxRBEx+S47NFDJN5VIvY0J7QzwTBer45Ls3BjLCaWXCnfFEcRn4SzY+Th01tmop0pwdvN5YDxkBw==" - }, - "node_modules/ses": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ses/-/ses-1.8.0.tgz", - "integrity": "sha512-pis9agyAy7s9lDApaYY9OSktkzc10gkBggSbhHXwTf3cWL9H8xA7oKiXIfT/uVNf8dzZN3o2TtPYjer9/axIhw==", - "dependencies": { - "@endo/env-options": "^1.1.6" - } - }, - "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/plugins/libs/plugins-runtime/package.json b/plugins/libs/plugins-runtime/package.json index 23aeeb2ee5..781ccb98c5 100644 --- a/plugins/libs/plugins-runtime/package.json +++ b/plugins/libs/plugins-runtime/package.json @@ -16,5 +16,5 @@ "lint": "eslint .", "test": "vitest" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/libs/plugins-runtime/src/lib/api/index.ts b/plugins/libs/plugins-runtime/src/lib/api/index.ts index 8956a5124e..b7fd1a0c02 100644 --- a/plugins/libs/plugins-runtime/src/lib/api/index.ts +++ b/plugins/libs/plugins-runtime/src/lib/api/index.ts @@ -19,6 +19,7 @@ import type { Page, Path, Penpot, + PenpotMgmt, Rectangle, Shape, SvgRaw, @@ -36,21 +37,38 @@ import { createPluginManager } from '../plugin-manager.js'; export const validEvents = [ 'finish', + 'logout', + 'workspacechange', 'pagechange', 'filechange', 'selectionchange', 'themechange', 'shapechange', 'contentsave', + 'contentrollback', ] as const; export function createApi( plugin: Awaited>, ) { - const checkPermission = (permission: Permissions) => { + const workspaceUnavailable = () => + plugin.manifest.scope === 'global' && + plugin.context.management?.workspace.status !== 'ready'; + + const requireWorkspace = () => { + if (workspaceUnavailable()) { + throw new Error( + 'No workspace is ready. Use penpotMgmt.openFile() first.', + ); + } + }; + + const checkPermission = (permission: Permissions, workspace = true) => { if (!plugin.manifest.permissions.includes(permission)) { throw new Error(`Permission ${permission} is not granted`); } + if (workspace && /^(content|library|comment):/.test(permission)) + requireWorkspace(); }; const penpot: Penpot = { @@ -162,7 +180,7 @@ export function createApi( z.function().parse(callback); // To suscribe to events needs the read permission - checkPermission('content:read'); + checkPermission('content:read', false); return plugin.registerListener(type, callback, props); }, @@ -178,18 +196,18 @@ export function createApi( }, get root(): Shape | null { - checkPermission('content:read'); - return plugin.context.root; + checkPermission('content:read', false); + return workspaceUnavailable() ? null : plugin.context.root; }, get currentFile(): File | null { - checkPermission('content:read'); - return plugin.context.currentFile; + checkPermission('content:read', false); + return workspaceUnavailable() ? null : plugin.context.currentFile; }, get currentPage(): Page | null { - checkPermission('content:read'); - return plugin.context.currentPage; + checkPermission('content:read', false); + return workspaceUnavailable() ? null : plugin.context.currentPage; }, get selection(): Shape[] { @@ -203,10 +221,12 @@ export function createApi( }, get viewport(): Viewport { + requireWorkspace(); return plugin.context.viewport; }, get history(): HistoryContext { + requireWorkspace(); return plugin.context.history; }, @@ -395,7 +415,52 @@ export function createApi( }, }; + const penpotMgmt: PenpotMgmt = { + get workspace() { + checkPermission('content:read', false); + return plugin.context.management!.workspace; + }, + listTeams() { + checkPermission('manage:teams', false); + return plugin.context.management!.listTeams(); + }, + createTeam(options) { + checkPermission('manage:teams', false); + return plugin.context.management!.createTeam(options); + }, + listProjects(options) { + checkPermission('content:read', false); + return plugin.context.management!.listProjects(options); + }, + listDeletedProjects(options) { + checkPermission('content:read', false); + return plugin.context.management!.listDeletedProjects(options); + }, + listDeletedFiles(options) { + checkPermission('content:read', false); + return plugin.context.management!.listDeletedFiles(options); + }, + getFile(fileId) { + checkPermission('content:read', false); + return plugin.context.management!.getFile(fileId); + }, + openFile(fileId, options) { + checkPermission('content:read', false); + return plugin.context.management!.openFile(fileId, options); + }, + on(type, callback) { + z.literal('workspacechange').parse(type); + z.function().parse(callback); + checkPermission('content:read', false); + return plugin.registerListener(type, callback); + }, + off(listenerId) { + plugin.destroyListener(listenerId); + }, + }; + return { penpot, + penpotMgmt, }; } diff --git a/plugins/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts b/plugins/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts index 308fb81245..b90ec91f63 100644 --- a/plugins/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts +++ b/plugins/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts @@ -54,6 +54,138 @@ describe('Plugin api', () => { vi.clearAllMocks(); }); + describe('management', () => { + it.each(['none', 'loading', 'ready'])( + 'lists teams and projects and loads files with workspace status %s', + async (status) => { + const teams = [{ id: 'team-id' }]; + const projects = [{ id: 'project-id' }]; + const file = { id: 'file-id' }; + const management = { + workspace: { status }, + listTeams: vi.fn().mockResolvedValue(teams), + createTeam: vi.fn().mockResolvedValue(teams[0]), + listProjects: vi.fn().mockResolvedValue(projects), + listDeletedProjects: vi.fn().mockResolvedValue(projects), + listDeletedFiles: vi.fn().mockResolvedValue([file]), + getFile: vi.fn().mockResolvedValue(file), + }; + const { penpotMgmt } = createApi({ + ...pluginManager, + manifest: { + ...pluginManager.manifest, + permissions: ['content:read', 'manage:teams'], + scope: 'global', + }, + context: { ...pluginManager.context, management }, + } as any); + + await expect(penpotMgmt.listTeams()).resolves.toEqual(teams); + await expect( + penpotMgmt.createTeam({ name: 'Marketing' }), + ).resolves.toEqual(teams[0]); + expect(management.createTeam).toHaveBeenCalledWith({ + name: 'Marketing', + }); + await expect(penpotMgmt.listProjects()).resolves.toEqual(projects); + expect(management.listProjects).toHaveBeenLastCalledWith(undefined); + await penpotMgmt.listProjects({ teamId: 'team-id' }); + expect(management.listProjects).toHaveBeenLastCalledWith({ + teamId: 'team-id', + }); + await expect( + penpotMgmt.listDeletedProjects({ teamId: 'team-id' }), + ).resolves.toEqual(projects); + expect(management.listDeletedProjects).toHaveBeenCalledWith({ + teamId: 'team-id', + }); + await expect(penpotMgmt.listDeletedFiles()).resolves.toEqual([file]); + expect(management.listDeletedFiles).toHaveBeenCalledWith(undefined); + await expect(penpotMgmt.getFile('file-id')).resolves.toEqual(file); + expect(management.getFile).toHaveBeenCalledWith('file-id'); + }, + ); + + it('requires content:read before listing projects or loading files', () => { + const management = { + listProjects: vi.fn(), + listDeletedProjects: vi.fn(), + listDeletedFiles: vi.fn(), + getFile: vi.fn(), + }; + const { penpotMgmt } = createApi({ + ...pluginManager, + manifest: { + ...pluginManager.manifest, + permissions: [], + scope: 'global', + }, + context: { ...pluginManager.context, management }, + } as any); + + expect(() => penpotMgmt.listProjects()).toThrow( + 'Permission content:read is not granted', + ); + expect(() => penpotMgmt.getFile('file-id')).toThrow( + 'Permission content:read is not granted', + ); + expect(() => penpotMgmt.listDeletedProjects()).toThrow( + 'Permission content:read is not granted', + ); + expect(() => penpotMgmt.listDeletedFiles()).toThrow( + 'Permission content:read is not granted', + ); + expect(management.listProjects).not.toHaveBeenCalled(); + expect(management.listDeletedProjects).not.toHaveBeenCalled(); + expect(management.listDeletedFiles).not.toHaveBeenCalled(); + expect(management.getFile).not.toHaveBeenCalled(); + }); + + it('requires manage:teams before listing or creating teams', () => { + const management = { listTeams: vi.fn(), createTeam: vi.fn() }; + const { penpotMgmt } = createApi({ + ...pluginManager, + manifest: { + ...pluginManager.manifest, + permissions: ['content:read'], + scope: 'global', + }, + context: { ...pluginManager.context, management }, + } as any); + + expect(() => penpotMgmt.listTeams()).toThrow( + 'Permission manage:teams is not granted', + ); + expect(() => penpotMgmt.createTeam({ name: 'Marketing' })).toThrow( + 'Permission manage:teams is not granted', + ); + expect(management.listTeams).not.toHaveBeenCalled(); + expect(management.createTeam).not.toHaveBeenCalled(); + }); + + it('preserves backend errors', async () => { + const error = new Error('Access denied'); + const { penpotMgmt } = createApi({ + ...pluginManager, + manifest: { + ...pluginManager.manifest, + permissions: ['content:read', 'manage:teams'], + scope: 'global', + }, + context: { + ...pluginManager.context, + management: { + listTeams: vi.fn().mockRejectedValue(error), + getFile: vi.fn().mockRejectedValue(error), + }, + }, + } as any); + + await expect(penpotMgmt.listTeams()).rejects.toBe(error); + await expect(penpotMgmt.getFile('file-id')).rejects.toBe(error); + }); + }); + describe('ui', () => { describe.concurrent('permissions', () => { const api = createApi({ diff --git a/plugins/libs/plugins-runtime/src/lib/create-plugin.spec.ts b/plugins/libs/plugins-runtime/src/lib/create-plugin.spec.ts index 29d0b6d691..7960f77ec2 100644 --- a/plugins/libs/plugins-runtime/src/lib/create-plugin.spec.ts +++ b/plugins/libs/plugins-runtime/src/lib/create-plugin.spec.ts @@ -83,6 +83,7 @@ describe('createPlugin', () => { manifest, expect.any(Function), expect.any(Function), + undefined, ); expect(createSandbox).toHaveBeenCalledWith(mockPluginManager, undefined); expect(mockSandbox.evaluate).toHaveBeenCalled(); diff --git a/plugins/libs/plugins-runtime/src/lib/create-plugin.ts b/plugins/libs/plugins-runtime/src/lib/create-plugin.ts index fc4ca233f3..c2b4a2f548 100644 --- a/plugins/libs/plugins-runtime/src/lib/create-plugin.ts +++ b/plugins/libs/plugins-runtime/src/lib/create-plugin.ts @@ -8,8 +8,13 @@ export async function createPlugin( manifest: Manifest, onCloseCallback: () => void, apiExtensions?: object, + signal?: AbortSignal, ) { + // Closing can happen while the manager is still fetching code. + let sandbox: ReturnType | undefined = undefined; + const evaluateSandbox = async () => { + if (plugin.destroyed || !sandbox) return; try { sandbox.evaluate(); } catch (error) { @@ -23,15 +28,18 @@ export async function createPlugin( context, manifest, function onClose() { - sandbox.cleanGlobalThis(); + sandbox?.cleanGlobalThis(); onCloseCallback(); }, function onReloadModal() { evaluateSandbox(); }, + signal, ); - const sandbox = createSandbox(plugin, apiExtensions); + if (plugin.destroyed) return; + + sandbox = createSandbox(plugin, apiExtensions); await evaluateSandbox(); diff --git a/plugins/libs/plugins-runtime/src/lib/create-sandbox.spec.ts b/plugins/libs/plugins-runtime/src/lib/create-sandbox.spec.ts index 97cc4bb123..45af8b7621 100644 --- a/plugins/libs/plugins-runtime/src/lib/create-sandbox.spec.ts +++ b/plugins/libs/plugins-runtime/src/lib/create-sandbox.spec.ts @@ -33,6 +33,7 @@ describe('createSandbox', () => { beforeEach(() => { mockPlugin = { + manifest: { scope: 'workspace' }, code: 'console.log("Plugin running");', timeouts: new Set>(), } as unknown as Awaited>; diff --git a/plugins/libs/plugins-runtime/src/lib/create-sandbox.ts b/plugins/libs/plugins-runtime/src/lib/create-sandbox.ts index 292ae49183..c8aaf3c40a 100644 --- a/plugins/libs/plugins-runtime/src/lib/create-sandbox.ts +++ b/plugins/libs/plugins-runtime/src/lib/create-sandbox.ts @@ -1,4 +1,3 @@ -import type { Penpot } from '@penpot/plugin-types'; import type { createPluginManager } from './plugin-manager'; import { createApi } from './api'; import { ses } from './ses.js'; @@ -67,7 +66,7 @@ export function createSandbox( const pluginApi = createApi(plugin); const safeHandler = { - get(target: Penpot, prop: string, receiver: unknown) { + get(target: object, prop: string, receiver: unknown) { const originalValue = Reflect.get(target, prop, receiver); if (typeof originalValue === 'function') { @@ -202,6 +201,12 @@ export function createSandbox( publicPluginApi = Object.assign(publicPluginApi, apiExtensions); } + if (plugin.manifest.scope === 'global') { + Object.assign(publicPluginApi, { + penpotMgmt: new Proxy(pluginApi.penpotMgmt, safeHandler), + }); + } + const compartment = ses.createCompartment(publicPluginApi); return { diff --git a/plugins/libs/plugins-runtime/src/lib/load-plugin-context.spec.ts b/plugins/libs/plugins-runtime/src/lib/load-plugin-context.spec.ts index 52a0267db0..9305878b3f 100644 --- a/plugins/libs/plugins-runtime/src/lib/load-plugin-context.spec.ts +++ b/plugins/libs/plugins-runtime/src/lib/load-plugin-context.spec.ts @@ -30,7 +30,7 @@ describe('loadPlugin host context boundary (regression for #11001)', () => { close: vi.fn(), sendMessage: vi.fn(), }, - } as unknown as Awaited>); + } as unknown as NonNullable>>); }); it('does not freeze host-owned functions reachable through the context', async () => { @@ -60,6 +60,7 @@ describe('loadPlugin host context boundary (regression for #11001)', () => { manifest, expect.any(Function), undefined, + expect.any(AbortSignal), ); // Host-owned functions must remain extensible: page navigation and diff --git a/plugins/libs/plugins-runtime/src/lib/load-plugin-real-path.spec.ts b/plugins/libs/plugins-runtime/src/lib/load-plugin-real-path.spec.ts index 06ef7b20b7..f921455fbe 100644 --- a/plugins/libs/plugins-runtime/src/lib/load-plugin-real-path.spec.ts +++ b/plugins/libs/plugins-runtime/src/lib/load-plugin-real-path.spec.ts @@ -1,6 +1,11 @@ import { describe, it, vi, expect, beforeAll } from 'vitest'; import 'ses'; -import { loadPlugin, setContextBuilder, getPlugins } from './load-plugin'; +import { + loadPlugin, + setContextBuilder, + getPlugins, + ɵunloadPlugin, +} from './load-plugin'; import type { Context } from '@penpot/plugin-types'; import type { Manifest } from './models/manifest.model.js'; @@ -47,15 +52,23 @@ function makeManifest( function makeHostFixture() { const listenerTypes: string[] = []; - const listeners = new Map(); + const listeners = new Map< + symbol, + { type: string; callback: (...args: unknown[]) => unknown } + >(); + const shapes: object[] = []; // Inline code (empty host + non-URL code) resolves without network, so no // fetch mock is needed. UI/modal APIs are never touched by the probe code. - const createRectangle = vi.fn(() => ({ type: 'rectangle-marker' })); + const createRectangle = vi.fn(() => { + const shape = { type: 'rectangle-marker' }; + shapes.push(shape); + return shape; + }); const selection: object[] = [{ id: 'shape-1' }]; const context = { - addListener: (type: string, _callback: (...args: unknown[]) => unknown) => { + addListener: (type: string, callback: (...args: unknown[]) => unknown) => { const id = Symbol(type); - listeners.set(id, type); + listeners.set(id, { type, callback }); listenerTypes.push(type); return id; }, @@ -63,13 +76,21 @@ function makeHostFixture() { listeners.delete(id); }, theme: 'dark', + management: { workspace: { status: 'ready' } }, createRectangle, selection, // Host-only member: present on the raw context but NOT part of the // public penpot API. Plugin code must never see it (see B-2 below). __internalSecret: 'host-internal', } as unknown as Context; - return { context, listenerTypes, createRectangle, selection }; + return { + context, + listeners, + listenerTypes, + createRectangle, + selection, + shapes, + }; } function lastCompartmentGlobalThis(): Record { @@ -98,9 +119,14 @@ describe('loadPlugin real initialization path (regression for #11001)', () => { ); // The plugin code really ran inside the sandbox: the manager registers - // `themechange` + `finish`, and the plugin code adds its own `finish` + // `themechange` + `finish` + `logout`, and the plugin code adds its own `finish` // listener through the public API. - expect(fixture.listenerTypes).toEqual(['themechange', 'finish', 'finish']); + expect(fixture.listenerTypes).toEqual([ + 'themechange', + 'finish', + 'logout', + 'finish', + ]); expect(getPlugins()).toHaveLength(1); // The user-facing behavior from #11001: host-side augmentation of a @@ -112,7 +138,7 @@ describe('loadPlugin real initialization path (regression for #11001)', () => { expect(() => { freshWrapper.toString = () => 'patched-by-runtime'; }).not.toThrow(); - expect(fixture.listenerTypes.length).toBe(3); + expect(fixture.listenerTypes.length).toBe(4); }); it('denies the write API without permission and leaves the host untouched', async () => { @@ -166,4 +192,302 @@ describe('loadPlugin real initialization path (regression for #11001)', () => { expect(fixture.createRectangle).toHaveBeenCalledTimes(1); expect(lastCompartmentGlobalThis()['__selectionFrozen']).toBe(true); }); + it.each(['before', 'after'])( + 'discards a cancelled global load that completes %s its replacement', + async (order) => { + const fixture = makeHostFixture(); + setContextBuilder(() => fixture.context); + const manifest = { + ...makeManifest('plugin.js', ['content:read', 'content:write']), + host: 'https://plugins.test/', + scope: 'global' as const, + }; + ɵunloadPlugin(manifest.pluginId); + let finishFetch!: (response: object) => void; + const response = new Promise((resolve) => { + finishFetch = resolve; + }); + const fetch = vi.fn().mockReturnValue(response); + vi.stubGlobal('fetch', fetch); + try { + const cancelled = loadPlugin(manifest); + await loadPlugin(manifest); + expect(fetch).toHaveBeenCalledOnce(); + ɵunloadPlugin(manifest.pluginId); + const replacement = () => + loadPlugin({ + ...manifest, + host: '', + code: 'globalThis.marker = "replacement";', + }); + if (order === 'after') await replacement(); + finishFetch({ + ok: true, + text: async () => 'penpot.createRectangle();', + }); + await cancelled; + expect(fixture.shapes).toEqual([]); + if (order === 'before') { + expect(getPlugins()).toHaveLength(0); + expect(fixture.listeners.size).toBe(0); + await replacement(); + } + expect(getPlugins()).toHaveLength(1); + expect(lastCompartmentGlobalThis()['marker']).toBe('replacement'); + expect(fixture.listeners.size).toBe(3); + } finally { + ɵunloadPlugin(manifest.pluginId); + vi.unstubAllGlobals(); + } + }, + ); + + it('cancels a workspace load when another load replaces the same plugin', async () => { + const fixture = makeHostFixture(); + setContextBuilder(() => fixture.context); + const manifest = { + ...makeManifest('plugin.js', ['content:read', 'content:write']), + host: 'https://plugins.test/', + }; + let finishFetch!: (response: object) => void; + vi.stubGlobal( + 'fetch', + () => + new Promise((resolve) => { + finishFetch = resolve; + }), + ); + try { + const cancelled = loadPlugin(manifest); + await loadPlugin({ + ...manifest, + host: '', + code: 'globalThis.marker = "replacement";', + }); + finishFetch({ ok: true, text: async () => 'penpot.createRectangle();' }); + await cancelled; + expect(fixture.shapes).toEqual([]); + expect(getPlugins()).toHaveLength(1); + expect(lastCompartmentGlobalThis()['marker']).toBe('replacement'); + } finally { + for (const plugin of [...getPlugins()]) plugin.plugin.close(); + vi.unstubAllGlobals(); + } + }); + + it('ignores a fetch failure after a load has been cancelled', async () => { + const fixture = makeHostFixture(); + setContextBuilder(() => fixture.context); + const manifest = { + ...makeManifest('plugin.js', []), + host: 'https://plugins.test/', + scope: 'global' as const, + }; + let failFetch!: (error: Error) => void; + vi.stubGlobal( + 'fetch', + () => + new Promise((_resolve, reject) => { + failFetch = reject; + }), + ); + try { + const cancelled = loadPlugin(manifest); + ɵunloadPlugin(manifest.pluginId); + await loadPlugin({ + ...manifest, + host: '', + code: 'globalThis.marker = "replacement";', + }); + failFetch(new Error('Cancelled fetch failed')); + await cancelled; + expect(getPlugins()).toHaveLength(1); + expect(lastCompartmentGlobalThis()['marker']).toBe('replacement'); + expect(fixture.listeners.size).toBe(3); + } finally { + ɵunloadPlugin(manifest.pluginId); + vi.unstubAllGlobals(); + } + }); + + it('allows retrying a global plugin after a failed load', async () => { + const fixture = makeHostFixture(); + setContextBuilder(() => fixture.context); + const manifest = { + ...makeManifest('throw new Error("load failed");', []), + scope: 'global' as const, + }; + await expect(loadPlugin(manifest)).rejects.toThrow('load failed'); + expect(getPlugins()).toHaveLength(0); + await loadPlugin({ ...manifest, code: 'globalThis.marker = "retry";' }); + expect(lastCompartmentGlobalThis()['marker']).toBe('retry'); + ɵunloadPlugin(manifest.pluginId); + expect(fixture.listeners.size).toBe(0); + }); + + it('keeps a global sandbox through navigation and other plugin loads, then closes it on logout', async () => { + const fixture = makeHostFixture(); + let workspace = { + status: 'none', + fileId: null, + fileName: null, + teamId: null, + }; + const listeners = new Map< + symbol, + { type: string; callback: (value?: unknown) => void } + >(); + const context = { + ...fixture.context, + management: { + get workspace() { + return workspace; + }, + openFile: async () => {}, + }, + addListener(type: string, callback: (value?: unknown) => void) { + const id = Symbol(); + listeners.set(id, { type, callback }); + return id; + }, + removeListener(id: symbol) { + listeners.delete(id); + }, + } as unknown as Context; + setContextBuilder(() => context); + const manifest = { + ...makeManifest( + 'globalThis.savedPenpot = penpot; globalThis.probe = penpotMgmt.workspace.status;', + ['content:read', 'content:write'], + ), + pluginId: 'global-plugin', + scope: 'global' as const, + }; + await loadPlugin(manifest); + const globalPlugin = getPlugins().find( + (plugin) => plugin.manifest.pluginId === 'global-plugin', + )!; + const globals = globalPlugin.compartment.compartment.globalThis; + expect(globals['probe']).toBe('none'); + expect(() => + globalPlugin.compartment.compartment.evaluate('penpot.createRectangle()'), + ).toThrow(/No workspace/); + for (const listener of [...listeners.values()]) { + if (listener.type === 'finish') listener.callback(); + } + await loadPlugin(makeManifest('globalThis.workspacePlugin = true;', [])); + expect(getPlugins()).toContain(globalPlugin); + await loadPlugin(manifest); + expect( + getPlugins().filter( + (plugin) => plugin.manifest.pluginId === 'global-plugin', + ), + ).toHaveLength(1); + workspace = { ...workspace, status: 'ready' }; + globalPlugin.compartment.compartment.evaluate( + 'penpot.createRectangle(); globalThis.sameFacade = savedPenpot === penpot;', + ); + expect(globals['sameFacade']).toBe(true); + expect(fixture.createRectangle).toHaveBeenCalledOnce(); + await expect( + loadPlugin(makeManifest('throw new Error("failure");', [])), + ).rejects.toThrow('failure'); + expect(getPlugins()).toContain(globalPlugin); + for (const listener of [...listeners.values()]) { + if (listener.type === 'logout') listener.callback(); + } + expect(getPlugins()).toHaveLength(0); + expect(globals['penpotMgmt']).toBeUndefined(); + expect(listeners.size).toBe(0); + }); + + it('cancels a global load at logout before it can execute in another session', async () => { + const fixture = makeHostFixture(); + setContextBuilder(() => fixture.context); + const manifest = { + ...makeManifest('plugin.js', ['content:read', 'content:write']), + pluginId: 'logout-plugin', + host: 'https://plugins.test/', + scope: 'global' as const, + }; + let finishFetch!: (response: object) => void; + vi.stubGlobal( + 'fetch', + () => + new Promise((resolve) => { + finishFetch = resolve; + }), + ); + try { + const cancelled = loadPlugin(manifest); + for (const listener of [...fixture.listeners.values()]) { + if (listener.type === 'logout') listener.callback(); + } + await loadPlugin({ + ...manifest, + host: '', + code: 'globalThis.marker = "new-session";', + }); + finishFetch({ ok: true, text: async () => 'penpot.createRectangle();' }); + await cancelled; + expect(fixture.shapes).toEqual([]); + expect(getPlugins()).toHaveLength(1); + expect(lastCompartmentGlobalThis()['marker']).toBe('new-session'); + expect(fixture.listeners.size).toBe(3); + } finally { + ɵunloadPlugin(manifest.pluginId); + vi.unstubAllGlobals(); + } + }); + + it('cleans up a failed fetch and allows loading the plugin again', async () => { + const fixture = makeHostFixture(); + setContextBuilder(() => fixture.context); + const manifest = { + ...makeManifest('plugin.js', []), + pluginId: 'failed-fetch-plugin', + host: 'https://plugins.test/', + scope: 'global' as const, + }; + vi.stubGlobal('fetch', () => Promise.reject(new Error('Fetch failed'))); + try { + await expect(loadPlugin(manifest)).rejects.toThrow('Fetch failed'); + expect(getPlugins()).toHaveLength(0); + expect(fixture.listeners.size).toBe(0); + await loadPlugin({ + ...manifest, + host: '', + code: 'globalThis.marker = "retry";', + }); + expect(lastCompartmentGlobalThis()['marker']).toBe('retry'); + } finally { + ɵunloadPlugin(manifest.pluginId); + vi.unstubAllGlobals(); + } + }); + + it('does not register a global plugin that closes during startup and permits reopening', async () => { + const fixture = makeHostFixture(); + setContextBuilder(() => fixture.context); + const manifest = { + ...makeManifest('penpot.closePlugin();', [ + 'content:read', + 'content:write', + ]), + pluginId: 'self-closing-plugin', + scope: 'global' as const, + }; + try { + await loadPlugin(manifest); + expect(getPlugins()).toHaveLength(0); + expect(fixture.listeners.size).toBe(0); + await loadPlugin({ ...manifest, code: 'penpot.createRectangle();' }); + expect(fixture.shapes).toEqual([{ type: 'rectangle-marker' }]); + expect(getPlugins()).toHaveLength(1); + } finally { + ɵunloadPlugin(manifest.pluginId); + } + expect(getPlugins()).toHaveLength(0); + expect(fixture.listeners.size).toBe(0); + }); }); diff --git a/plugins/libs/plugins-runtime/src/lib/load-plugin.spec.ts b/plugins/libs/plugins-runtime/src/lib/load-plugin.spec.ts index 779619279c..853c1d12c7 100644 --- a/plugins/libs/plugins-runtime/src/lib/load-plugin.spec.ts +++ b/plugins/libs/plugins-runtime/src/lib/load-plugin.spec.ts @@ -33,7 +33,7 @@ vi.mock('./ses.js', () => ({ describe('plugin-loader', () => { let mockContext: Context; let manifest: Manifest; - let mockPluginApi: Awaited>; + let mockPluginApi: NonNullable>>; let mockClose: ReturnType; beforeEach(() => { @@ -61,7 +61,7 @@ describe('plugin-loader', () => { close: mockClose, sendMessage: vi.fn(), }, - } as unknown as Awaited>; + } as unknown as NonNullable>>; mockContext = { addListener: vi.fn(), @@ -84,6 +84,7 @@ describe('plugin-loader', () => { manifest, expect.any(Function), undefined, + expect.any(AbortSignal), ); expect(mockPluginApi.plugin.close).not.toHaveBeenCalled(); expect(getPlugins()).toHaveLength(1); @@ -129,7 +130,7 @@ describe('plugin-loader', () => { }, iframeWindow: mockIframeWindow, manifest: { ...manifest, host: 'http://localhost:4202' }, - } as unknown as Awaited>; + } as unknown as NonNullable>>; vi.mocked(createPlugin).mockResolvedValue(mockPluginWithIframe); @@ -173,7 +174,7 @@ describe('plugin-loader', () => { }, iframeWindow: mockIframeWindow1, manifest: { ...manifest, host: 'http://localhost:4202' }, - } as unknown as Awaited>; + } as unknown as NonNullable>>; const mockPluginApi2 = { plugin: { @@ -182,7 +183,7 @@ describe('plugin-loader', () => { }, iframeWindow: mockIframeWindow2, manifest: { ...manifest, host: 'http://localhost:4203' }, - } as unknown as Awaited>; + } as unknown as NonNullable>>; vi.mocked(createPlugin).mockResolvedValue(mockPluginApi1); await loadPlugin(manifest); @@ -215,7 +216,7 @@ describe('plugin-loader', () => { pluginId: 'background-plugin', allowBackground: true, }, - } as unknown as Awaited>; + } as unknown as NonNullable>>; vi.mocked(createPlugin).mockResolvedValue(backgroundPluginApi); await loadPlugin(manifest); @@ -250,6 +251,7 @@ describe('plugin-loader', () => { manifest, expect.any(Function), undefined, + expect.any(AbortSignal), ); }); @@ -265,6 +267,7 @@ describe('plugin-loader', () => { manifest, expect.any(Function), undefined, + expect.any(AbortSignal), ); }); }); diff --git a/plugins/libs/plugins-runtime/src/lib/load-plugin.ts b/plugins/libs/plugins-runtime/src/lib/load-plugin.ts index e22e62c70c..8da32c4d64 100644 --- a/plugins/libs/plugins-runtime/src/lib/load-plugin.ts +++ b/plugins/libs/plugins-runtime/src/lib/load-plugin.ts @@ -4,7 +4,10 @@ import { loadManifest } from './parse-manifest.js'; import { Manifest } from './models/manifest.model.js'; import { createPlugin } from './create-plugin.js'; -let plugins: Awaited>[] = []; +type Plugin = NonNullable>>; + +let plugins: Plugin[] = []; +const pendingPlugins = new Map(); export type ContextBuilder = (id: string) => Context; @@ -17,20 +20,19 @@ export function setContextBuilder(builder: ContextBuilder) { export const getPlugins = () => plugins; const closeAllPlugins = () => { - // Background plugins keep running, so they must stay registered: the - // registry routes their UI messages and lets them be unloaded later. - const backgroundPlugins: typeof plugins = []; - - plugins.forEach((pluginApi) => { + // Background and global plugins keep running, so they must stay registered: + // the registry routes their UI messages and lets them be unloaded later. + plugins = plugins.filter((pluginApi) => { /* eslint-disable @typescript-eslint/no-explicit-any */ - if ((pluginApi.manifest as any)?.allowBackground) { - backgroundPlugins.push(pluginApi); - } else { - pluginApi.plugin.close(); + if ( + pluginApi.manifest?.scope === 'global' || + (pluginApi.manifest as any)?.allowBackground + ) { + return true; } + pluginApi.plugin.close(); + return false; }); - - plugins = backgroundPlugins; }; window.addEventListener('message', (event) => { @@ -50,6 +52,7 @@ export const loadPlugin = async function ( closeCallback?: () => void, apiExtensions?: object, ) { + const load = new AbortController(); try { const context = contextBuilder && contextBuilder(manifest.pluginId); @@ -57,7 +60,18 @@ export const loadPlugin = async function ( return; } - closeAllPlugins(); + if (manifest.scope === 'global') { + if ( + pendingPlugins.has(manifest.pluginId) || + plugins.some( + (plugin) => plugin.manifest?.pluginId === manifest.pluginId, + ) + ) { + return; + } + } else { + closeAllPlugins(); + } // The host context is not deeply frozen at this load stage. // @@ -74,10 +88,17 @@ export const loadPlugin = async function ( // `createSandbox`'s proxy handler applies `ses.safeReturn` to values // crossing into the sandbox. Compartment isolation and intrinsics // hardening are performed by createSandbox, not here. - const plugin = await createPlugin( + pendingPlugins.get(manifest.pluginId)?.abort(); + pendingPlugins.set(manifest.pluginId, load); + let plugin: Plugin | undefined = undefined; + plugin = await createPlugin( context, manifest, () => { + load.abort(); + if (pendingPlugins.get(manifest.pluginId) === load) { + pendingPlugins.delete(manifest.pluginId); + } plugins = plugins.filter((api) => api !== plugin); if (closeCallback) { @@ -85,11 +106,20 @@ export const loadPlugin = async function ( } }, apiExtensions, + load.signal, ); + if (!plugin || load.signal.aborted) { + plugin?.plugin.close(); + return; + } plugins.push(plugin); } catch (error) { - closeAllPlugins(); + if (manifest.scope !== 'global') closeAllPlugins(); throw error; + } finally { + if (pendingPlugins.get(manifest.pluginId) === load) { + pendingPlugins.delete(manifest.pluginId); + } } }; @@ -107,6 +137,7 @@ export const ɵloadPluginByUrl = async function (manifestUrl: string) { }; export const ɵunloadPlugin = function (id: Manifest['pluginId']) { + pendingPlugins.get(id)?.abort(); const plugin = plugins.find((plugin) => plugin.manifest.pluginId === id); if (plugin) { diff --git a/plugins/libs/plugins-runtime/src/lib/models/manifest.schema.ts b/plugins/libs/plugins-runtime/src/lib/models/manifest.schema.ts index 16d4fd5e28..b2cd2f81af 100644 --- a/plugins/libs/plugins-runtime/src/lib/models/manifest.schema.ts +++ b/plugins/libs/plugins-runtime/src/lib/models/manifest.schema.ts @@ -7,6 +7,7 @@ export const manifestSchema = z.object({ code: z.string(), icon: z.string().optional(), version: z.number().optional(), + scope: z.enum(['workspace', 'global']).optional(), description: z.string().max(200).optional(), permissions: z.array( z.enum([ @@ -19,6 +20,10 @@ export const manifestSchema = z.object({ 'comment:write', 'allow:downloads', 'allow:localstorage', + 'allow:global', + 'manage:projects', + 'manage:teams', + 'manage:delete', 'clipboard:read', 'clipboard:write', ]), diff --git a/plugins/libs/plugins-runtime/src/lib/plugin-manager.ts b/plugins/libs/plugins-runtime/src/lib/plugin-manager.ts index 7878a1abcd..3cb6a9c261 100644 --- a/plugins/libs/plugins-runtime/src/lib/plugin-manager.ts +++ b/plugins/libs/plugins-runtime/src/lib/plugin-manager.ts @@ -14,8 +14,9 @@ export async function createPluginManager( manifest: Manifest, onCloseCallback: () => void, onReloadModal: (code: string) => void, + signal?: AbortSignal, ) { - let code = await loadManifestCode(manifest); + let code = ''; let loaded = false; let destroyed = false; @@ -40,11 +41,10 @@ export async function createPluginManager( modal?.setTheme(theme); }); - const listenerId: symbol = context.addListener('finish', () => { - closePlugin(); - - context?.removeListener(listenerId); + const listenerId = context.addListener('finish', () => { + if (manifest.scope !== 'global') closePlugin(); }); + const logoutId = context.addListener('logout', () => closePlugin()); let listeners: symbol[] = []; @@ -60,7 +60,12 @@ export async function createPluginManager( }; const closePlugin = () => { + if (destroyed) return; + destroyed = true; + signal?.removeEventListener('abort', closePlugin); removeAllEventListeners(); + destroyListener(listenerId); + destroyListener(logoutId); timeouts.forEach(clearTimeout); timeouts.clear(); @@ -74,8 +79,6 @@ export async function createPluginManager( modal = null; } - destroyed = true; - onCloseCallback(); }; @@ -147,7 +150,24 @@ export async function createPluginManager( context.removeListener(listenerId); }; + signal?.addEventListener('abort', closePlugin, { once: true }); + try { + if (signal?.aborted) { + closePlugin(); + } else { + code = await loadManifestCode(manifest); + } + } catch (error) { + if (!destroyed) { + closePlugin(); + throw error; + } + } + return { + get destroyed() { + return destroyed; + }, close: closePlugin, destroyListener, openModal, diff --git a/plugins/libs/plugins-styles/package.json b/plugins/libs/plugins-styles/package.json index 5badd11056..328ddc7c00 100644 --- a/plugins/libs/plugins-styles/package.json +++ b/plugins/libs/plugins-styles/package.json @@ -6,5 +6,5 @@ "build": "node ../../tools/scripts/build-css.mjs", "lint": "echo 0" }, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1" + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3" } diff --git a/plugins/libs/plugins-styles/src/lib/core/fonts.css b/plugins/libs/plugins-styles/src/lib/core/fonts.css index 496ce3de84..9c6b3b91f0 100644 --- a/plugins/libs/plugins-styles/src/lib/core/fonts.css +++ b/plugins/libs/plugins-styles/src/lib/core/fonts.css @@ -1,5 +1,3 @@ -@import url('https://fonts.bunny.net/css?family=work-sans:400,500&display=swap'); - :root { /* Font weight */ --font-weight-regular: 400; @@ -14,7 +12,7 @@ } code { - font-family: 'Work Sans', sans-serif; + font-family: system-ui, sans-serif; } .display { diff --git a/plugins/libs/plugins-styles/src/lib/core/generic.css b/plugins/libs/plugins-styles/src/lib/core/generic.css index 1aab0a7f50..3851ea17ee 100644 --- a/plugins/libs/plugins-styles/src/lib/core/generic.css +++ b/plugins/libs/plugins-styles/src/lib/core/generic.css @@ -1,6 +1,6 @@ html, body { - font-family: 'Work Sans', sans-serif; + font-family: system-ui, sans-serif; font-optical-sizing: auto; font-style: normal; margin: 0; diff --git a/plugins/package.json b/plugins/package.json index 01d2a885b5..4b04171c49 100644 --- a/plugins/package.json +++ b/plugins/package.json @@ -3,7 +3,7 @@ "version": "0.6.0", "type": "module", "license": "MIT", - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "scripts": { "start": "pnpm run start:app:runtime", "start:app:runtime": "concurrently --kill-others --names build,server \"pnpm --filter @penpot/plugins-runtime run build:watch\" \"pnpm --filter @penpot/plugins-runtime run preview\"", diff --git a/plugins/pnpm-lock.yaml b/plugins/pnpm-lock.yaml index b84286c8f0..c27b995baa 100644 --- a/plugins/pnpm-lock.yaml +++ b/plugins/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b8e0f539b..bbe85fa79b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' diff --git a/render-wasm/Cargo.lock b/render-wasm/Cargo.lock index 1142a20b67..2b24a8387f 100644 --- a/render-wasm/Cargo.lock +++ b/render-wasm/Cargo.lock @@ -23,6 +23,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bezier-rs" version = "0.4.0" @@ -49,7 +55,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.106", ] [[package]] @@ -334,7 +340,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -393,7 +399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.106", ] [[package]] @@ -425,9 +431,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -437,9 +443,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -448,15 +454,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "render" version = "0.1.0" dependencies = [ - "base64", + "base64 0.22.1", "bezier-rs", "gl", "glam", @@ -527,7 +533,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -565,9 +571,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "skia-bindings" -version = "0.93.1" +version = "0.153.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2359f7e30c9da3f322f8ca3d4ec0abbc12a40035ce758309db0cdab07b5d4476" +checksum = "eb325787c91b3f6c34a7c6f2b8a2297b20292d733ca53a08c233e5028aec49af" dependencies = [ "bindgen", "cc", @@ -582,11 +588,11 @@ dependencies = [ [[package]] name = "skia-safe" -version = "0.93.1" +version = "0.153.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e837ea9d531c9efee8f980bfcdb7226b21db0285b0c3171d8be745829f940" +checksum = "c61cb643dac43067f3eb76c866635a74377ac0abca0ff2590674e1e295514c3d" dependencies = [ - "base64", + "base64 0.23.1", "bitflags", "percent-encoding", "skia-bindings", @@ -595,13 +601,13 @@ dependencies = [ [[package]] name = "skia-svg-macros" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "044dd2233c9717a74f75197f3e7f0a966db2127c0ffb5e05013b480a9b75b2c7" +checksum = "fec38aa7a389d249e1ce357b050ae5bdb302fd9943661fa650f39cf6ea51be97" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.6", ] [[package]] @@ -616,10 +622,21 @@ dependencies = [ ] [[package]] -name = "tar" -version = "0.4.42" +name = "syn" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", @@ -656,7 +673,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", ] [[package]] @@ -748,7 +765,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -770,7 +787,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/render-wasm/Cargo.toml b/render-wasm/Cargo.toml index e71f616191..4740a898bb 100644 --- a/render-wasm/Cargo.toml +++ b/render-wasm/Cargo.toml @@ -27,7 +27,7 @@ glam = "0.24.2" indexmap = "2.7.1" macros = { path = "macros" } miniz_oxide = "0.8.0" -skia-safe = { version = "0.93.1", default-features = false, features = [ +skia-safe = { version = "0.153.3", default-features = false, features = [ "gl", "svg", "textlayout", diff --git a/render-wasm/_build_env b/render-wasm/_build_env index f92406866f..c8d1fdd1e5 100644 --- a/render-wasm/_build_env +++ b/render-wasm/_build_env @@ -1,8 +1,8 @@ #!/usr/bin/env bash -export RUST_VERSION=1.91.0 -export VERSION_TAG=${VERSION:-develop}; +export RUST_VERSION=1.99.0 +export VERSION_TAG="${VERSION_TAG:-${VERSION:-develop}}"; export RENDER_TARGET="${RENDER_TARGET:-${1:-frontend}}"; case "$RENDER_TARGET" in @@ -13,16 +13,25 @@ case "$RENDER_TARGET" in ;; esac -if [ "$NODE_ENV" = "production" ]; then - export BUILD_MODE="release"; +if [ "${NODE_ENV:-}" = "production" ]; then + export BUILD_MODE="${BUILD_MODE:-release}"; else export BUILD_MODE=${BUILD_MODE:-debug}; fi export BUILD_NAME="${BUILD_NAME:-render-wasm}" -export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"}; -export CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-"target/$RENDER_TARGET"}; -export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.93.1/skia-binaries-319323662b1685a112f5-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp.tar.gz"} +export CARGO_BUILD_TARGET="${CARGO_BUILD_TARGET:-wasm32-unknown-emscripten}"; +export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-target/$RENDER_TARGET}"; +# Skia prebuild with wasm SIMD and Wasm EH/SjLj (-ljmp-wasm). Must match +# EMCC_CFLAGS below. Override with SKIA_BINARIES_URL when needed. +export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.153.3/skia-binaries-b7f043e0b1e2a850e702-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp-pdf-simd-ljmp-wasm.tar.gz"} + +# Emscripten only: rustc wasm SIMD (native `./test` unchanged). Wasm +# setjmp/longjmp comes from EMCC_CFLAGS (-sSUPPORT_LONGJMP=wasm), which emcc +# appends on compile and link. +if [ "$CARGO_BUILD_TARGET" = "wasm32-unknown-emscripten" ]; then + export RUSTFLAGS="${RUSTFLAGS:-} -C target-feature=+simd128" +fi # 256 MB of initial heap to perform less # initial calls to memory grow. @@ -41,8 +50,14 @@ export EM_MEMORY_GROWTH_GEOMETRIC_STEP="0.8" # Default: dlmalloc export EM_MALLOC="dlmalloc" +# -msimd128: wasm SIMD in the linked module (Skia -simd prebuild must match). +# -fwasm-exceptions / SUPPORT_LONGJMP=wasm: native Wasm EH and setjmp/longjmp +# (Skia -ljmp-wasm prebuild must match). export EMCC_CFLAGS="--no-entry \ --js-library src/js/wapi.js \ + -msimd128 \ + -fwasm-exceptions \ + -sSUPPORT_LONGJMP=wasm \ -sMALLOC=$EM_MALLOC \ -sINVOKE_RUN=0 \ -sALLOW_TABLE_GROWTH=0 \ @@ -55,85 +70,138 @@ export EMCC_CFLAGS="--no-entry \ -sEXPORTED_RUNTIME_METHODS=GL,UTF8ToString,stringToUTF8,HEAPU8,HEAP32,HEAPU32,HEAPF32 \ -sENVIRONMENT=web,worker,node \ -sMODULARIZE=1 \ - -sDISABLE_EXCEPTION_CATCHING=1 \ -sFILESYSTEM=0 \ -sEXPORT_ES6=1"; export EM_CACHE="/tmp/emsdk_cache"; -export CARGO_PARAMS="${@:2}"; - +CARGO_ARGS=("${@:2}"); +_CARGO_PROFILE_SET=0; export CARGO_PROFILE_DIR="debug"; -if [ "$BUILD_MODE" = "profiling" ]; then - # Optimized like release, but keeps the wasm name section so the Firefox - # profiler can resolve Rust frames (release strips them). `--profiling-funcs` - # preserves function names through wasm-opt without pulling in DWARF. - export CARGO_PARAMS="--profile profiling $CARGO_PARAMS"; - export CARGO_PROFILE_DIR="profiling"; - export EMCC_CFLAGS="-O3 -sASSERTIONS=0 --profiling-funcs $EMCC_CFLAGS"; -elif [ "$BUILD_MODE" = "release" ]; then - case "$RENDER_TARGET" in - frontend) - export CARGO_PARAMS="--release $CARGO_PARAMS"; +for (( _cargo_index=0; _cargo_index < ${#CARGO_ARGS[@]}; _cargo_index++ )); do + case "${CARGO_ARGS[_cargo_index]}" in + --release|-r) + _CARGO_PROFILE_SET=1; export CARGO_PROFILE_DIR="release"; - export EMCC_CFLAGS="-O3 -sASSERTIONS=0 $EMCC_CFLAGS"; ;; - export) - export CARGO_PARAMS="--profile size $CARGO_PARAMS"; - export CARGO_PROFILE_DIR="size"; - export EMCC_CFLAGS="-Oz -sASSERTIONS=0 $EMCC_CFLAGS"; + --profile) + _CARGO_PROFILE_SET=1; + export CARGO_PROFILE_DIR="${CARGO_ARGS[_cargo_index + 1]}"; + ;; + --profile=*) + _CARGO_PROFILE_SET=1; + export CARGO_PROFILE_DIR="${CARGO_ARGS[_cargo_index]#--profile=}"; + ;; + --target-dir) + export CARGO_TARGET_DIR="${CARGO_ARGS[_cargo_index + 1]:-$CARGO_TARGET_DIR}"; + ;; + --target-dir=*) + export CARGO_TARGET_DIR="${CARGO_ARGS[_cargo_index]#--target-dir=}"; ;; esac -else - # TODO: Extra parameters that could be good to look into: - # -gseparate-dwarf - # -gsplit-dwarf - # -gsource-map - export EMCC_CFLAGS="-g -sASSERTIONS=1 -sVERBOSE=1 $EMCC_CFLAGS" +done + +if [ "$_CARGO_PROFILE_SET" = "0" ]; then + if [ "$BUILD_MODE" = "profiling" ]; then + CARGO_ARGS=(--profile profiling "${CARGO_ARGS[@]}"); + export CARGO_PROFILE_DIR="profiling"; + elif [ "$BUILD_MODE" = "release" ]; then + case "$RENDER_TARGET" in + frontend) + CARGO_ARGS=(--release "${CARGO_ARGS[@]}"); + export CARGO_PROFILE_DIR="release"; + ;; + export) + CARGO_ARGS=(--profile size "${CARGO_ARGS[@]}"); + export CARGO_PROFILE_DIR="size"; + ;; + esac + fi fi -export FRONTEND_DEST="../frontend/resources/public/js"; -export EXPORT_DEST="../exporter/resources/wasm"; +case "$CARGO_PROFILE_DIR" in + profiling) + # Keep the wasm name section so profilers can resolve Rust frames. + export EMCC_CFLAGS="-O3 -sASSERTIONS=0 --profiling-funcs $EMCC_CFLAGS"; + ;; + release) + export EMCC_CFLAGS="-O3 -sASSERTIONS=0 $EMCC_CFLAGS"; + ;; + size) + export EMCC_CFLAGS="-Oz -sASSERTIONS=0 $EMCC_CFLAGS"; + ;; + *) + # TODO: Extra parameters that could be good to look into: + # -gseparate-dwarf + # -gsplit-dwarf + # -gsource-map + export EMCC_CFLAGS="-g -sASSERTIONS=1 $EMCC_CFLAGS"; + ;; +esac -export FRONTEND_SHARED_DEST="../frontend/src/app/render_wasm/api/shared.js"; -export EXPORT_SHARED_DEST="../exporter/src/app/wasm/shared.js"; +if [ "$CARGO_PROFILE_DIR" = "dev" ]; then + export CARGO_PROFILE_DIR="debug"; +fi + +export RENDER_WASM_BUILD_INFO="${RENDER_WASM_BUILD_INFO:-$CARGO_TARGET_DIR/build-info.json}"; + +export FRONTEND_DEST="${FRONTEND_DEST:-../frontend/resources/public/js}"; +export EXPORT_DEST="${EXPORT_DEST:-../exporter/resources/wasm}"; + +export FRONTEND_SHARED_DEST="${FRONTEND_SHARED_DEST:-../frontend/src/app/render_wasm/api/shared.js}"; +export EXPORT_SHARED_DEST="${EXPORT_SHARED_DEST:-../exporter/src/app/wasm/shared.js}"; function clean { cargo clean; } -function setup { +function setup_rust { # Setup default rust version rustup default $RUST_VERSION rustup target add wasm32-unknown-emscripten + rustup component add clippy rustfmt --toolchain $RUST_VERSION +} + +function setup { + setup_rust pnpm install; } function build { - cargo build $CARGO_PARAMS; + ( + set -o pipefail; + cargo build --message-format=json-render-diagnostics "${CARGO_ARGS[@]}" \ + | node ./scripts/cargo-build.cjs "$RENDER_WASM_BUILD_INFO" "$PWD/Cargo.toml"; + ) } function copy_artifacts { - DEST=$1; - SRC="$CARGO_TARGET_DIR/$CARGO_BUILD_TARGET/$CARGO_PROFILE_DIR"; + local DEST="$1"; + local SRC; + SRC=$(jq -r '.artifact_dir' -- "$RENDER_WASM_BUILD_INFO") + mkdir -p -- "$DEST"; - mkdir -p $DEST; - - cp $SRC/render_wasm.js $DEST/$BUILD_NAME.js; - cp $SRC/render_wasm.wasm $DEST/$BUILD_NAME.wasm; - if [ -f $SRC/render_wasm.wasm.map ]; then - cp $SRC/render_wasm.wasm.map $DEST/$BUILD_NAME.wasm.map; + cp -- "$SRC/render_wasm.js" "$DEST/$BUILD_NAME.js"; + cp -- "$SRC/render_wasm.wasm" "$DEST/$BUILD_NAME.wasm"; + if [ -f "$SRC/render_wasm.wasm.map" ]; then + cp -- "$SRC/render_wasm.wasm.map" "$DEST/$BUILD_NAME.wasm.map"; fi - sed -i "s/render_wasm.wasm/$BUILD_NAME.wasm?version=$VERSION_TAG/g" $DEST/$BUILD_NAME.js; + # Rewrite the generated JavaScript so it loads the renamed Wasm file with + # a version query, such as render-wasm.wasm?version=develop. + local WASM_URL="$BUILD_NAME.wasm?version=$VERSION_TAG"; + WASM_URL=${WASM_URL//\\/\\\\}; + WASM_URL=${WASM_URL//&/\\&}; + WASM_URL=${WASM_URL//|/\\|}; + sed -i "s|render_wasm\\.wasm|$WASM_URL|g" "$DEST/$BUILD_NAME.js"; # The worker bundle is a browser concern; the exporter imports the ESM # module directly under Node. if [ "$RENDER_TARGET" = "frontend" ]; then - pnpm exec esbuild $SRC/render_wasm.js \ + pnpm exec esbuild "$SRC/render_wasm.js" \ --log-level=error \ - --outfile=$DEST/worker/render.js \ + "--outfile=$DEST/worker/render.js" \ --platform=neutral \ --format=iife \ --global-name=WasmModule; @@ -141,10 +209,11 @@ function copy_artifacts { } function copy_shared_artifact { - DEST=$1; - SHARED_FILE=$(find $CARGO_TARGET_DIR/$CARGO_BUILD_TARGET -name render_wasm_shared.js | head -n 1); - - cp $SHARED_FILE $DEST; + local DEST="$1"; + local SHARED_FILE; + SHARED_FILE=$(jq -r '.shared_file' -- "$RENDER_WASM_BUILD_INFO") + mkdir -p -- "$(dirname -- "$DEST")"; + cp -- "$SHARED_FILE" "$DEST"; } # Copies whatever the current RENDER_TARGET produced to where that target's @@ -160,4 +229,8 @@ function copy_target_artifacts { copy_shared_artifact "$EXPORT_SHARED_DEST"; ;; esac + + if [ -n "${RENDER_WASM_SHARED_ARTIFACT:-}" ]; then + copy_shared_artifact "$RENDER_WASM_SHARED_ARTIFACT"; + fi } diff --git a/render-wasm/build b/render-wasm/build index 7b73aaf5a7..c4061068f2 100755 --- a/render-wasm/build +++ b/render-wasm/build @@ -23,14 +23,14 @@ case "$_RENDER_TARGET" in ;; esac -set -e +set -e -o pipefail EMSDK_QUIET=1 . /opt/emsdk/emsdk_env.sh pushd "$_SCRIPT_DIR" > /dev/null export RENDER_TARGET="$_RENDER_TARGET" -. ./_build_env "$_RENDER_TARGET" +. ./_build_env "$_RENDER_TARGET" "${@:2}" . ./_build_lock acquire_build_lock diff --git a/render-wasm/check-no-invoke-v b/render-wasm/check-no-invoke-v new file mode 100755 index 0000000000..b90502a235 --- /dev/null +++ b/render-wasm/check-no-invoke-v @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Usage: ./check-no-invoke-v [frontend|export] [glue-path] +# +# Fail if the Emscripten JS glue has any invoke_* wrapper. With Wasm EH and +# Wasm SjLj there should be none; invoke_*V (v128) throws a TypeError at the +# JS boundary. +set -euo pipefail + +_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +TARGET=${1:-frontend} +# _build_env reads NODE_ENV without a default; keep set -u happy. +NODE_ENV=${NODE_ENV:-} +RENDER_TARGET=$TARGET +# shellcheck source=/dev/null +. "$_SCRIPT_DIR/_build_env" "$TARGET" + +if [[ $# -ge 2 ]]; then + GLUE=$2 +elif [[ "$TARGET" = "export" ]]; then + GLUE="$_SCRIPT_DIR/$EXPORT_DEST/$BUILD_NAME.js" +else + GLUE="$_SCRIPT_DIR/$FRONTEND_DEST/$BUILD_NAME.js" +fi + +if [[ ! -f "$GLUE" ]]; then + echo "error: JS glue not found: $GLUE" >&2 + echo "hint: run ./build $TARGET first" >&2 + exit 1 +fi + +status=0 +grep -qE 'invoke_[A-Za-z0-9]+' "$GLUE" || status=$? + +if [[ $status -eq 0 ]]; then + echo "error: JS glue has invoke_* wrappers: $GLUE" >&2 + grep -oE 'invoke_[A-Za-z0-9]+' "$GLUE" | sort -u >&2 + exit 1 +elif [[ $status -gt 1 ]]; then + echo "error: grep failed on $GLUE" >&2 + exit "$status" +fi diff --git a/render-wasm/lint b/render-wasm/lint index 4ed7d6826d..097a4314cf 100755 --- a/render-wasm/lint +++ b/render-wasm/lint @@ -10,10 +10,6 @@ fi . ./_build_env frontend -export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"}; -export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.93.1/skia-binaries-319323662b1685a112f5-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp.tar.gz"} - - ALLOWED_RULES="-D static_mut_refs" # ./lint --fix diff --git a/render-wasm/package.json b/render-wasm/package.json index f1f4b196ab..a7a15fb5f0 100644 --- a/render-wasm/package.json +++ b/render-wasm/package.json @@ -4,7 +4,7 @@ "license": "MPL-2.0", "author": "Kaleidos INC Sucursal en España SL", "private": true, - "packageManager": "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1", + "packageManager": "pnpm@12.18.0+sha512.9d7882d5dc82147ecfc4e062aa34834e0ad0296710a1837a1115ae012127d36b7109e321ac1598a1dd0a65597681b1e99917e12ed0d749eae91187943363beb3", "repository": { "type": "git", "url": "https://github.com/penpot/penpot" diff --git a/render-wasm/pnpm-lock.yaml b/render-wasm/pnpm-lock.yaml index a0180171fa..452e972384 100644 --- a/render-wasm/pnpm-lock.yaml +++ b/render-wasm/pnpm-lock.yaml @@ -7,153 +7,153 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: 12.16.1 - version: 12.16.1 + specifier: 12.18.0 + version: 12.18.0 packages: - '@pnpm/exe.android-arm64@12.16.1': - resolution: {integrity: sha512-PKf78Ew+i0CBYf0Xc+eMcqQiqyBT+u8XCVg1F5roc98/6wWfdYlWhrUMmEBiKWaJ3JqHxrtt8l1NwZ7qfJt2/Q==} + '@pnpm/exe.android-arm64@12.18.0': + resolution: {integrity: sha512-fOF6IwgvnUKEmVedGqiAsNPfN2Ry8GvaqzSQOiF3RJFbpBfPKO/6YT54s1GjToyYinRxw9preHHPe4y2N3SzhA==} cpu: [arm64] os: [android] - '@pnpm/exe.android-x64@12.16.1': - resolution: {integrity: sha512-xdu7gMbB98pdvhwMeA6612Of515FDlRUtfA/1f7nWPZfPLnEdSRBO2u+1N0XBtQ/ennhPH/lrxB1gC6gnR/cdg==} + '@pnpm/exe.android-x64@12.18.0': + resolution: {integrity: sha512-6OiolnaCsB3/GcJ9XNrenbUH3zFHRJ+5QynrMUUEyThbgnnCzWxa199hpQlOmxN4rAXuYQ0IYgWFNUZxyzg31Q==} cpu: [x64] os: [android] - '@pnpm/exe.darwin-arm64@12.16.1': - resolution: {integrity: sha512-pVbmIIbhixiLvADLu1WA3TaW8FZy/D72R1De27WbbFx8/2mxvnsTyFVm4fO9kjpjMY7RzJd48CiQ96p8fO3ebQ==} + '@pnpm/exe.darwin-arm64@12.18.0': + resolution: {integrity: sha512-zkq+4YzVIX39ETYLyC3W7LY7MpHEU/J3nRhHl4i2yPld2Cf2+rEKRh3ejJ4AmaQ77HHJLCktY4Z3jq0fpcxFXg==} cpu: [arm64] os: [darwin] - '@pnpm/exe.darwin-x64@12.16.1': - resolution: {integrity: sha512-xZvQBFqcZjO7vFKTK3XsJpqY6lnBL9EWSLCzVUPq3kZ94+D96gwJCa76xxGNK8Cj32f4eeM91Qg+yNRR4sRpDw==} + '@pnpm/exe.darwin-x64@12.18.0': + resolution: {integrity: sha512-qMsL9qx92unOTa6hgkfiTJv8ceZaTx0IQg7InhYDVX2ZrTECujaVEEVypnJK5m3eGUsP1KD7Y4qIgleCwfRsYw==} cpu: [x64] os: [darwin] - '@pnpm/exe.freebsd-x64@12.16.1': - resolution: {integrity: sha512-zTtunfi9mBhRXiXEJYfwiJj0tk7MgTSfqDSRh+Az9FZ9THazVIiQiVNS8jpcsftDOqu7LKNffQdjmgwHDThfUg==} + '@pnpm/exe.freebsd-x64@12.18.0': + resolution: {integrity: sha512-/9vy/4ms52cU4MjZ5NhHd6N2d9L9W0kARf5/ff2tweAHu0VeUzs/0qp/7ZHFuddm2Wa/m0AKQoPum9AVUCqKsw==} cpu: [x64] os: [freebsd] - '@pnpm/exe.linux-arm64-musl@12.16.1': - resolution: {integrity: sha512-BJ3mOmcmQgcUo92Ee/WfEOt1pRiv3y2MMAkhEiYHd85UiPpHTfFhv+SpCbALmfExzXk3UlaRtMYwHlkQymc4VA==} + '@pnpm/exe.linux-arm64-musl@12.18.0': + resolution: {integrity: sha512-Vp+TIMPPyackVCXkOdEKH/RmAn/r07EcIyrgZYQK7VrkksqZOie0GnWIx+85mPqeFEYCRtXIZAFJ9L4ceTndDg==} cpu: [arm64] os: [linux] libc: [musl] - '@pnpm/exe.linux-arm64@12.16.1': - resolution: {integrity: sha512-JaRd5gNxwdNGBBkrZFt8bknrK4YmHVJBW0JMhUQ0fJ0dEYp6C2GT9ExaCqOO8EwYEyqM+jpKgJIK+8WFaJy3ew==} + '@pnpm/exe.linux-arm64@12.18.0': + resolution: {integrity: sha512-2dpyVBCHT+FfpuQh9i+7MXAqNWYLCvD5vYr5p6Eimxoga8USLnjbUgyj72AOrO0hjc5ri3TEFP3jiyBHjkSZpQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-ppc64@12.16.1': - resolution: {integrity: sha512-FnSV0Jv40S1cMv7vFNWwvu0X567Lec4L4HCRead3CFcD6zQDNbGw81s8N5bcKPahtPD9ApVuhsbmFCx4rw6RMw==} + '@pnpm/exe.linux-ppc64@12.18.0': + resolution: {integrity: sha512-BBOOOfNJ2QwIIya3eKqOjiN4WNTszfRz7HCvw9XggcT7+ftMjNP8XJ/GfW+ltCBeUFR9d8WBVsr2m/NLjX2CZQ==} cpu: [ppc64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-riscv64@12.16.1': - resolution: {integrity: sha512-C1Sww4FN5IttD4atRRhAKmQtSGWVp04ZAKNxO8pfu2bkRPwOW+7v+2DH1Xs1W+kZYYpO86BYDvUcadefF+AgaQ==} + '@pnpm/exe.linux-riscv64@12.18.0': + resolution: {integrity: sha512-7kMN77xSOYJX8GPSH5RbNAc5oM61LMmcSUukDDUwllnZPXm8GRPskCNsTWy8SCw01Kr1FB+5SwGNMiTdiziLNQ==} cpu: [riscv64] os: [linux] libc: [glibc] - '@pnpm/exe.linux-s390x@12.16.1': - resolution: {integrity: sha512-kn2fautsTcxIMODoovQVqZyfpDRdNgGkoeVqNdrKqbeBBvrEl+fXLkmZiq5xDht1LMr4sYiCNcsMfNMT6JPFmg==} + '@pnpm/exe.linux-s390x@12.18.0': + resolution: {integrity: sha512-htuSCpEcmDzhKoJqXbPkvfr176dBHU0QIF5mWcRiqxRXAB/1JoYiMYotfjWpk+ztgpB9qlx4XnWlLFAf7gzc6g==} cpu: [s390x] os: [linux] libc: [glibc] - '@pnpm/exe.linux-x64-musl@12.16.1': - resolution: {integrity: sha512-PM6ccHCOe/O9SsTflTJTYWfwogJZa6EmUvpnwZgiPguJBddh20wDQKgxUPXf48rDsPm9vMsIij0uHswwwbXL8g==} + '@pnpm/exe.linux-x64-musl@12.18.0': + resolution: {integrity: sha512-Uj+vzfIQ62kDP10x36yHZIXVs5ohdNtiPYwn5FhCLlNScHPo0OAI+ShGDtLofiBXKE7umu71Jmjm7N8a8zVcDw==} cpu: [x64] os: [linux] libc: [musl] - '@pnpm/exe.linux-x64@12.16.1': - resolution: {integrity: sha512-RK//8ZLcXGW8A2k8bQOXZDBN1Qu92zN4dYnG1bzj1zoQ1fkZ6jNzZ1AvXtsfGVma795CJtvoaF5bLXf4l/FIUw==} + '@pnpm/exe.linux-x64@12.18.0': + resolution: {integrity: sha512-OJ+X2wcraldCwj75pCIerWARQSRpdfj0eBTs/2ODyhfeW46ylOnjTIE/GKra6Mn6E72/gBmXS8igrUioa+F9fQ==} cpu: [x64] os: [linux] libc: [glibc] - '@pnpm/exe.win32-arm64@12.16.1': - resolution: {integrity: sha512-Ox3kNNVxtGw6pK7SVejmeOUgHhUTZgAM7NQQ6cU7frPw3ix9+j7d2MGbRg3fyEFAaKWq8ZAZLTQGcDyLnAu+qA==} + '@pnpm/exe.win32-arm64@12.18.0': + resolution: {integrity: sha512-wEY6jiACGcbhhi5yD59doHqNTndS7AaujaPmtWAu1DKkHru2hy5fJ9yUGecW72Ypaw2/I3BEprfMOopap2NQtw==} cpu: [arm64] os: [win32] - '@pnpm/exe.win32-x64@12.16.1': - resolution: {integrity: sha512-k+FhRJ0ttphFoFIVB1Sk4v76YJAulafmA/Ly57cysesUhhIzVlApeOShUz0jXubKx317TThlEfpQrukldnqORA==} + '@pnpm/exe.win32-x64@12.18.0': + resolution: {integrity: sha512-5FJkghO7RLRkQpWxeVEygW9XsmnNuxzPfXbgwFWCHsFyFhpDn/vOdL88ToQEM0Kt5igdW5s6U0/IDH8Dvkt3pg==} cpu: [x64] os: [win32] - pnpm@12.16.1: - resolution: {integrity: sha512-CR0d7nknV1Zhsl2rPCrjHK/BxU1rce1qZgDPDByxBDSkR+rhQBXxsj4ua+3u1535SpMuIWTSgyKA3OP7MvZW0Q==} + pnpm@12.18.0: + resolution: {integrity: sha512-nXiC1dyCFH7PxOBiqjSDTgrQKWcQoYN6ERWuASEn02txCeMhrBWYod0KZVl2gbHpmRfhLtDXSerpEYeUM2O+sw==} engines: {node: '>=18'} hasBin: true snapshots: - '@pnpm/exe.android-arm64@12.16.1': + '@pnpm/exe.android-arm64@12.18.0': optional: true - '@pnpm/exe.android-x64@12.16.1': + '@pnpm/exe.android-x64@12.18.0': optional: true - '@pnpm/exe.darwin-arm64@12.16.1': + '@pnpm/exe.darwin-arm64@12.18.0': optional: true - '@pnpm/exe.darwin-x64@12.16.1': + '@pnpm/exe.darwin-x64@12.18.0': optional: true - '@pnpm/exe.freebsd-x64@12.16.1': + '@pnpm/exe.freebsd-x64@12.18.0': optional: true - '@pnpm/exe.linux-arm64-musl@12.16.1': + '@pnpm/exe.linux-arm64-musl@12.18.0': optional: true - '@pnpm/exe.linux-arm64@12.16.1': + '@pnpm/exe.linux-arm64@12.18.0': optional: true - '@pnpm/exe.linux-ppc64@12.16.1': + '@pnpm/exe.linux-ppc64@12.18.0': optional: true - '@pnpm/exe.linux-riscv64@12.16.1': + '@pnpm/exe.linux-riscv64@12.18.0': optional: true - '@pnpm/exe.linux-s390x@12.16.1': + '@pnpm/exe.linux-s390x@12.18.0': optional: true - '@pnpm/exe.linux-x64-musl@12.16.1': + '@pnpm/exe.linux-x64-musl@12.18.0': optional: true - '@pnpm/exe.linux-x64@12.16.1': + '@pnpm/exe.linux-x64@12.18.0': optional: true - '@pnpm/exe.win32-arm64@12.16.1': + '@pnpm/exe.win32-arm64@12.18.0': optional: true - '@pnpm/exe.win32-x64@12.16.1': + '@pnpm/exe.win32-x64@12.18.0': optional: true - pnpm@12.16.1: + pnpm@12.18.0: optionalDependencies: - '@pnpm/exe.android-arm64': 12.16.1 - '@pnpm/exe.android-x64': 12.16.1 - '@pnpm/exe.darwin-arm64': 12.16.1 - '@pnpm/exe.darwin-x64': 12.16.1 - '@pnpm/exe.freebsd-x64': 12.16.1 - '@pnpm/exe.linux-arm64': 12.16.1 - '@pnpm/exe.linux-arm64-musl': 12.16.1 - '@pnpm/exe.linux-ppc64': 12.16.1 - '@pnpm/exe.linux-riscv64': 12.16.1 - '@pnpm/exe.linux-s390x': 12.16.1 - '@pnpm/exe.linux-x64': 12.16.1 - '@pnpm/exe.linux-x64-musl': 12.16.1 - '@pnpm/exe.win32-arm64': 12.16.1 - '@pnpm/exe.win32-x64': 12.16.1 + '@pnpm/exe.android-arm64': 12.18.0 + '@pnpm/exe.android-x64': 12.18.0 + '@pnpm/exe.darwin-arm64': 12.18.0 + '@pnpm/exe.darwin-x64': 12.18.0 + '@pnpm/exe.freebsd-x64': 12.18.0 + '@pnpm/exe.linux-arm64': 12.18.0 + '@pnpm/exe.linux-arm64-musl': 12.18.0 + '@pnpm/exe.linux-ppc64': 12.18.0 + '@pnpm/exe.linux-riscv64': 12.18.0 + '@pnpm/exe.linux-s390x': 12.18.0 + '@pnpm/exe.linux-x64': 12.18.0 + '@pnpm/exe.linux-x64-musl': 12.18.0 + '@pnpm/exe.win32-arm64': 12.18.0 + '@pnpm/exe.win32-x64': 12.18.0 --- lockfileVersion: '9.0' diff --git a/render-wasm/scripts/cargo-build.cjs b/render-wasm/scripts/cargo-build.cjs new file mode 100644 index 0000000000..035703a03c --- /dev/null +++ b/render-wasm/scripts/cargo-build.cjs @@ -0,0 +1,120 @@ +// Usage: cargo build --message-format=json-render-diagnostics ... | +// node scripts/cargo-build.cjs +const fs = require("node:fs"); +const path = require("node:path"); +const readline = require("node:readline"); + +async function main() { + const [infoFile, manifestFile] = process.argv.slice(2); + if (!infoFile || !manifestFile) { + throw new Error( + "Usage: cargo-build.cjs ", + ); + } + const manifest = path.resolve(manifestFile); + // A failed invocation must not leave facts from an earlier build. + fs.rmSync(infoFile, { force: true }); + + const artifacts = []; + const buildScripts = new Map(); + let succeeded = false; + const lines = readline.createInterface({ + input: process.stdin, + crlfDelay: Infinity, + }); + for await (const line of lines) { + if (!line.trim()) continue; + const message = JSON.parse(line); + if (!message || typeof message !== "object" || Array.isArray(message)) { + throw new Error("Invalid Cargo JSON record"); + } + switch (message.reason) { + case "compiler-message": + if (typeof message.message?.rendered === "string") { + process.stderr.write(message.message.rendered); + } + break; + case "compiler-artifact": + if ( + message.target?.name === "render_wasm" && + typeof message.manifest_path === "string" && + path.resolve(message.manifest_path) === manifest + ) { + artifacts.push(message); + } + break; + case "build-script-executed": { + const outDirs = buildScripts.get(message.package_id) || new Set(); + outDirs.add(message.out_dir); + buildScripts.set(message.package_id, outDirs); + break; + } + case "build-finished": + succeeded = message.success === true; + break; + } + } + + if (!succeeded) throw new Error("Cargo build did not finish successfully"); + if (artifacts.length !== 1) { + throw new Error("Expected one root render_wasm compiler-artifact"); + } + const artifact = artifacts[0]; + if ( + typeof artifact.package_id !== "string" || + !artifact.package_id || + !Array.isArray(artifact.features) || + !artifact.features.every((feature) => typeof feature === "string") + ) { + throw new Error("Missing root renderer package_id or actual features"); + } + if ( + !artifact.profile || + typeof artifact.profile !== "object" || + Array.isArray(artifact.profile) || + typeof artifact.profile.opt_level !== "string" || + typeof artifact.profile.debug_assertions !== "boolean" + ) { + throw new Error("Missing or malformed root renderer Cargo profile"); + } + const outDirs = [...(buildScripts.get(artifact.package_id) || [])]; + if (outDirs.length !== 1 || typeof outDirs[0] !== "string" || !outDirs[0]) { + throw new Error( + "Expected one matching root renderer build-script-executed out_dir", + ); + } + const jsFiles = ( + Array.isArray(artifact.filenames) ? artifact.filenames : [] + ).filter( + (file) => + typeof file === "string" && path.basename(file) === "render_wasm.js", + ); + if (jsFiles.length !== 1) + throw new Error("Missing root render_wasm.js artifact"); + + const outDir = path.resolve(outDirs[0]); + const sharedFile = path.join(outDir, "render_wasm_shared.js"); + fs.accessSync(sharedFile, fs.constants.R_OK); + const info = { + package_id: artifact.package_id, + features: artifact.features, + profile: artifact.profile, + out_dir: outDir, + artifact_dir: path.dirname(path.resolve(jsFiles[0])), + shared_file: sharedFile, + }; + fs.mkdirSync(path.dirname(infoFile), { recursive: true }); + const temporary = `${infoFile}.tmp`; + try { + fs.writeFileSync(temporary, JSON.stringify(info, null, 2) + "\n"); + fs.renameSync(temporary, infoFile); + } finally { + fs.rmSync(temporary, { force: true }); + } +} + +main().catch((error) => { + process.stderr.write(`ERROR: ${error.message}\n`); + process.exitCode = 1; + process.stdin.destroy(); +}); diff --git a/render-wasm/scripts/cargo-build.test.cjs b/render-wasm/scripts/cargo-build.test.cjs new file mode 100644 index 0000000000..e757c7cd95 --- /dev/null +++ b/render-wasm/scripts/cargo-build.test.cjs @@ -0,0 +1,349 @@ +const assert = require("node:assert/strict"); +const { spawn, spawnSync } = require("node:child_process"); +const { once } = require("node:events"); +const fs = require("node:fs"); +const os = require("node:os"); +const path = require("node:path"); +const { test } = require("node:test"); + +const helper = path.join(__dirname, "cargo-build.cjs"); +const finished = { reason: "build-finished", success: true }; + +function fixture(t) { + const root = fs.mkdtempSync( + path.join(os.tmpdir(), "render-wasm-cargo-build-"), + ); + t.after(() => fs.rmSync(root, { recursive: true, force: true })); + const manifest = path.join(root, "Cargo.toml"); + const artifactDir = path.join( + root, + "target with spaces", + "wasm32-unknown-emscripten", + "release", + ); + const outDir = path.join(artifactDir, "build", "render-active", "out"); + const staleOutDir = path.join( + artifactDir, + "build", + "000-render-stale", + "out", + ); + for (const dir of [outDir, staleOutDir]) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(manifest, '[package]\nname = "render"\n'); + fs.writeFileSync( + path.join(artifactDir, "render_wasm.js"), + "export default {};\n", + ); + fs.writeFileSync( + path.join(outDir, "render_wasm_shared.js"), + "active enum table\n", + ); + fs.writeFileSync( + path.join(staleOutDir, "render_wasm_shared.js"), + "stale enum table\n", + ); + + const artifact = { + reason: "compiler-artifact", + package_id: `path+file://${root}#render@0.1.0`, + manifest_path: manifest, + target: { name: "render_wasm", kind: ["bin"] }, + features: ["default", "profile", "profile-macros", "profile-raf"], + profile: { + opt_level: "3", + debuginfo: 0, + debug_assertions: false, + overflow_checks: false, + test: false, + }, + filenames: [path.join(artifactDir, "render_wasm.js")], + fresh: false, + }; + const buildScript = { + reason: "build-script-executed", + package_id: artifact.package_id, + out_dir: outDir, + }; + const otherScript = { + ...buildScript, + package_id: + "registry+https://github.com/rust-lang/crates.io-index#render@0.1.0", + out_dir: staleOutDir, + }; + const otherArtifact = { + ...artifact, + package_id: otherScript.package_id, + manifest_path: path.join(root, "dependency", "Cargo.toml"), + features: ["stats"], + profile: { ...artifact.profile, opt_level: "0", debug_assertions: true }, + }; + return { + root, + manifest, + artifactDir, + outDir, + staleOutDir, + artifact, + buildScript, + otherScript, + otherArtifact, + info: path.join(root, "build-info.json"), + }; +} + +function cargoOutput(records) { + return records.map((record) => JSON.stringify(record)).join("\n") + "\n"; +} + +function run(f, records) { + return spawnSync(process.execPath, [helper, f.info, f.manifest], { + cwd: f.root, + input: cargoOutput(records), + encoding: "utf8", + }); +} + +for (const fresh of [false, true]) { + test(`selects the root package's active enum table and actual features (fresh=${fresh})`, (t) => { + const f = fixture(t); + const result = run(f, [ + f.otherScript, + f.otherArtifact, + { ...f.artifact, fresh }, + f.buildScript, + { + ...f.otherScript, + out_dir: path.join(f.root, "another dependency out"), + }, + finished, + ]); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stdout, ""); + const info = JSON.parse(fs.readFileSync(f.info, "utf8")); + assert.deepEqual(info, { + package_id: f.artifact.package_id, + features: f.artifact.features, + profile: f.artifact.profile, + out_dir: f.outDir, + artifact_dir: f.artifactDir, + shared_file: path.join(f.outDir, "render_wasm_shared.js"), + }); + assert.equal( + fs.readFileSync(info.shared_file, "utf8"), + "active enum table\n", + ); + }); +} + +test("records an empty actual feature set", (t) => { + const f = fixture(t); + const result = run(f, [ + f.buildScript, + { ...f.artifact, features: [] }, + finished, + ]); + assert.equal(result.status, 0, result.stderr); + assert.deepEqual(JSON.parse(fs.readFileSync(f.info, "utf8")).features, []); +}); + +for (const profile of [ + { + opt_level: "0", + debuginfo: 2, + debug_assertions: true, + overflow_checks: true, + test: false, + }, + { + opt_level: "z", + debuginfo: 0, + debug_assertions: false, + overflow_checks: false, + test: false, + }, +]) { + test(`records the actual Cargo profile without changing fields (opt_level=${profile.opt_level})`, (t) => { + const f = fixture(t); + const result = run(f, [ + f.buildScript, + { ...f.artifact, profile }, + finished, + ]); + assert.equal(result.status, 0, result.stderr); + assert.deepEqual( + JSON.parse(fs.readFileSync(f.info, "utf8")).profile, + profile, + ); + }); +} + +test( + "streams rendered compiler messages to stderr before Cargo finishes", + { timeout: 10000 }, + async (t) => { + const f = fixture(t); + const child = spawn(process.execPath, [helper, f.info, f.manifest], { + cwd: f.root, + }); + t.after(() => { + if (child.exitCode === null) child.kill(); + }); + child.stdout.resume(); + const closed = once(child, "close"); + const diagnostic = "warning: renderer diagnostic\n"; + let stderr = ""; + const rendered = new Promise((resolve, reject) => { + child.stderr.on("data", (chunk) => { + stderr += chunk; + if (stderr.includes(diagnostic)) resolve(); + }); + child.on("error", reject); + child.on("close", () => { + if (!stderr.includes(diagnostic)) + reject(new Error(stderr || "Missing diagnostic")); + }); + }); + child.stdin.write( + cargoOutput([ + { reason: "compiler-message", message: { rendered: diagnostic } }, + ]), + ); + await rendered; + assert.equal(child.exitCode, null); + assert.equal(fs.existsSync(f.info), false); + child.stdin.end(cargoOutput([f.buildScript, f.artifact, finished])); + const [code] = await closed; + assert.equal(code, 0, stderr); + assert.equal(stderr, diagnostic); + }, +); + +const rejected = [ + [ + "failed Cargo build", + (f) => [f.buildScript, f.artifact, { ...finished, success: false }], + ], + ["missing build-finished record", (f) => [f.buildScript, f.artifact]], + ["missing root artifact", (f) => [f.otherScript, f.otherArtifact, finished]], + [ + "missing matching build script", + (f) => [f.otherScript, f.artifact, finished], + ], + [ + "missing out_dir", + (f) => [{ ...f.buildScript, out_dir: undefined }, f.artifact, finished], + ], + [ + "missing actual features", + (f) => [f.buildScript, { ...f.artifact, features: undefined }, finished], + ], + [ + "invalid actual features", + (f) => [f.buildScript, { ...f.artifact, features: [42] }, finished], + ], + [ + "missing profile", + (f) => [f.buildScript, { ...f.artifact, profile: undefined }, finished], + ], + [ + "null profile", + (f) => [f.buildScript, { ...f.artifact, profile: null }, finished], + ], + [ + "non-object profile", + (f) => [f.buildScript, { ...f.artifact, profile: "release" }, finished], + ], + [ + "array profile", + (f) => [f.buildScript, { ...f.artifact, profile: [] }, finished], + ], + [ + "missing profile opt_level", + (f) => [ + f.buildScript, + { ...f.artifact, profile: { debug_assertions: false } }, + finished, + ], + ], + [ + "invalid profile opt_level", + (f) => [ + f.buildScript, + { ...f.artifact, profile: { opt_level: 3, debug_assertions: false } }, + finished, + ], + ], + [ + "missing profile debug_assertions", + (f) => [ + f.buildScript, + { ...f.artifact, profile: { opt_level: "3" } }, + finished, + ], + ], + [ + "invalid profile debug_assertions", + (f) => [ + f.buildScript, + { ...f.artifact, profile: { opt_level: "3", debug_assertions: "false" } }, + finished, + ], + ], + [ + "missing JS artifact", + (f) => [f.buildScript, { ...f.artifact, filenames: [] }, finished], + ], + [ + "multiple root artifacts", + (f) => [ + f.buildScript, + f.artifact, + { ...f.artifact, features: [] }, + finished, + ], + ], + [ + "ambiguous root out_dir", + (f) => [ + f.buildScript, + { ...f.buildScript, out_dir: f.staleOutDir }, + f.artifact, + finished, + ], + ], +]; + +for (const [name, records] of rejected) { + test(`rejects ${name} and removes stale build facts`, (t) => { + const f = fixture(t); + fs.writeFileSync(f.info, '{"features":["stale"]}\n'); + const result = run(f, records(f)); + assert.equal(result.status, 1, result.stderr); + assert.match(result.stderr, /ERROR:/); + assert.equal(fs.existsSync(f.info), false); + }); +} + +test("rejects a missing active enum table even when a stale table exists", (t) => { + const f = fixture(t); + fs.unlinkSync(path.join(f.outDir, "render_wasm_shared.js")); + const result = run(f, [f.otherScript, f.buildScript, f.artifact, finished]); + assert.equal(result.status, 1, result.stderr); + assert.match(result.stderr, /render_wasm_shared\.js/); + assert.equal(fs.existsSync(f.info), false); +}); + +test("rejects malformed Cargo JSON and removes stale build facts", (t) => { + const f = fixture(t); + fs.writeFileSync(f.info, '{"features":["stale"]}\n'); + const result = spawnSync(process.execPath, [helper, f.info, f.manifest], { + cwd: f.root, + input: "not Cargo JSON\n", + encoding: "utf8", + }); + assert.equal(result.status, 1, result.stderr); + assert.match(result.stderr, /ERROR:/); + assert.equal(fs.existsSync(f.info), false); +}); diff --git a/render-wasm/src/globals.rs b/render-wasm/src/globals.rs index 26e149d97c..cc9e718bdf 100644 --- a/render-wasm/src/globals.rs +++ b/render-wasm/src/globals.rs @@ -16,7 +16,7 @@ static mut RENDER_RESOURCES: *mut RenderResources = std::ptr::null_mut(); pub(crate) fn get_design_state() -> &'static mut State { unsafe { - debug_assert!(!DESIGN_STATE.is_null(), "Design State is null"); + assert!(!DESIGN_STATE.is_null(), "Design State is null"); &mut *DESIGN_STATE } } @@ -35,7 +35,7 @@ pub(crate) fn get_gpu_state() -> &'static mut GpuState { #[inline(always)] pub(crate) fn get_render_state() -> &'static mut RenderState { unsafe { - debug_assert!(!RENDER_STATE.is_null(), "Render State is null"); + assert!(!RENDER_STATE.is_null(), "Render State is null"); &mut *RENDER_STATE } } @@ -64,7 +64,7 @@ pub(crate) fn has_render_resources() -> bool { #[inline(always)] pub(crate) fn get_resources() -> &'static mut RenderResources { unsafe { - debug_assert!(!RENDER_RESOURCES.is_null(), "Render Resources is null"); + assert!(!RENDER_RESOURCES.is_null(), "Render Resources is null"); &mut *RENDER_RESOURCES } } @@ -72,7 +72,7 @@ pub(crate) fn get_resources() -> &'static mut RenderResources { #[inline(always)] pub(crate) fn get_text_editor_state() -> &'static mut TextEditorState { unsafe { - debug_assert!(!TEXT_EDITOR_STATE.is_null(), "Text Editor state is null"); + assert!(!TEXT_EDITOR_STATE.is_null(), "Text Editor state is null"); &mut *TEXT_EDITOR_STATE } } @@ -80,7 +80,7 @@ pub(crate) fn get_text_editor_state() -> &'static mut TextEditorState { #[inline(always)] pub(crate) fn get_ui_state() -> &'static mut UIState { unsafe { - debug_assert!(!UI_STATE.is_null(), "UI State is null"); + assert!(!UI_STATE.is_null(), "UI State is null"); &mut *UI_STATE } } @@ -234,14 +234,28 @@ pub extern "C" fn init_headless(_width: i32, _height: i32) -> Result<()> { Ok(()) } +unsafe fn drop_global(slot: *mut *mut T) { + let ptr = std::ptr::replace(slot, std::ptr::null_mut()); + if !ptr.is_null() { + drop(Box::from_raw(ptr)); + } +} + #[no_mangle] #[wasm_error] pub extern "C" fn clean_up() -> Result<()> { - // Cancel the current animation frame if it exists so - // it won't try to render without context - let render_state = get_render_state(); - render_state.prepare_context_loss_cleanup(); - unsafe { DESIGN_STATE = std::ptr::null_mut() } + if has_render_state() { + // Abandons the GL context, so the drops below issue no GL calls. + get_render_state().prepare_context_loss_cleanup(); + } + unsafe { + drop_global(std::ptr::addr_of_mut!(DESIGN_STATE)); + drop_global(std::ptr::addr_of_mut!(TEXT_EDITOR_STATE)); + drop_global(std::ptr::addr_of_mut!(UI_STATE)); + drop_global(std::ptr::addr_of_mut!(RENDER_STATE)); + drop_global(std::ptr::addr_of_mut!(RENDER_RESOURCES)); + drop_global(std::ptr::addr_of_mut!(GPU_STATE)); + } mem::free_bytes()?; Ok(()) } diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 34f3aa8dad..fe38624844 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -354,6 +354,7 @@ pub extern "C" fn set_view_end() -> Result<()> { pub extern "C" fn set_modifiers_start() -> Result<()> { performance::begin_measure!("set_modifiers_start"); let render_state = get_render_state(); + render_state.moving_extrects.clear(); render_state.options.set_fast_mode(true); render_state.options.set_interactive_transform(true); performance::end_measure!("set_modifiers_start"); @@ -369,6 +370,7 @@ pub extern "C" fn set_modifiers_start() -> Result<()> { pub extern "C" fn set_modifiers_end() -> Result<()> { performance::begin_measure!("set_modifiers_end"); let render_state = get_render_state(); + render_state.moving_extrects.clear(); render_state.options.set_fast_mode(false); render_state.options.set_interactive_transform(false); performance::end_measure!("set_modifiers_end"); @@ -857,6 +859,69 @@ pub extern "C" fn render_shape_pdf(a: u32, b: u32, c: u32, d: u32) -> Result<*mu }) } +/// Multi-page PDF: shape UUIDs are read from the transfer buffer (16 bytes +/// each, same layout as `set_focus_mode`). Returns `[len u32 LE][pdf bytes]`. +#[no_mangle] +#[wasm_error] +pub extern "C" fn render_shapes_pdf() -> Result<*mut u8> { + let bytes = mem::bytes(); + let ids: Vec = bytes + .chunks(size_of::<::BytesType>()) + .map(|data| Uuid::try_from(data).map_err(|e| Error::RecoverableError(e.to_string()))) + .collect::>>()?; + + with_state!(state, { + let data = state.render_shapes_pdf(&ids)?; + + let len = data.len() as u32; + let mut buf = Vec::with_capacity(4 + data.len()); + buf.extend_from_slice(&len.to_le_bytes()); + buf.extend_from_slice(&data); + Ok(mem::write_bytes(buf)) + }) +} + +/// Starts a progressive multi-page PDF session (see `pdf_export_add_page` / +/// `pdf_export_finish`). Replaces any previous session. +#[no_mangle] +#[wasm_error] +pub extern "C" fn pdf_export_begin() -> Result<()> { + with_state!(state, { state.pdf_export_begin() }) +} + +/// Appends one shape as a page of the current PDF export session. +#[no_mangle] +#[wasm_error] +pub extern "C" fn pdf_export_add_page(a: u32, b: u32, c: u32, d: u32) -> Result<()> { + let id = uuid_from_u32_quartet(a, b, c, d); + with_state!(state, { state.pdf_export_add_page(&id) }) +} + +/// Finishes the PDF export session and returns `[len u32 LE][pdf bytes]`. +#[no_mangle] +#[wasm_error] +pub extern "C" fn pdf_export_finish() -> Result<*mut u8> { + with_state!(state, { + let data = state.pdf_export_finish()?; + + let len = data.len() as u32; + let mut buf = Vec::with_capacity(4 + data.len()); + buf.extend_from_slice(&len.to_le_bytes()); + buf.extend_from_slice(&data); + Ok(mem::write_bytes(buf)) + }) +} + +/// Aborts the current PDF export session, if any. +#[no_mangle] +#[wasm_error] +pub extern "C" fn pdf_export_abort() -> Result<()> { + with_state!(state, { + state.pdf_export_abort(); + }); + Ok(()) +} + #[no_mangle] #[wasm_error] pub extern "C" fn render_shape_svg(a: u32, b: u32, c: u32, d: u32, scale: f32) -> Result<*mut u8> { @@ -873,6 +938,28 @@ pub extern "C" fn render_shape_svg(a: u32, b: u32, c: u32, d: u32, scale: f32) - }) } +/// Multi-root SVG: shape UUIDs are read from the transfer buffer (16 bytes +/// each, same layout as `render_shapes_pdf`). Returns `[len u32 LE][svg bytes]`. +#[no_mangle] +#[wasm_error] +pub extern "C" fn render_shapes_svg(scale: f32) -> Result<*mut u8> { + let bytes = mem::bytes(); + let ids: Vec = bytes + .chunks(size_of::<::BytesType>()) + .map(|data| Uuid::try_from(data).map_err(|e| Error::RecoverableError(e.to_string()))) + .collect::>>()?; + + with_state!(state, { + let data = state.render_shapes_svg(&ids, scale)?; + + let len = data.len() as u32; + let mut buf = Vec::with_capacity(4 + data.len()); + buf.extend_from_slice(&len.to_le_bytes()); + buf.extend_from_slice(&data); + Ok(mem::write_bytes(buf)) + }) +} + /// Raster image via CPU (no GPU/WebGL). Returns `[len][width][height][bytes]` /// (LE), same layout as `render_shape_pixels`. `format` selects the encoder: /// 0 = PNG, 1 = JPEG, 2 = WEBP (see `RasterFormat`). diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 4f84df8599..5b47c8bce2 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -480,6 +480,8 @@ pub(crate) struct RenderState { /// GPU crops from `Backbuffer` or tile atlas keyed by shape id. Filled on full-frame completion; during /// drag, entries for the moved top-level selection are ensured here pub backbuffer_crop_cache: HashMap, + /// Extrect of each moved shape at the previous interactive frame; reset per gesture. + pub moving_extrects: HashMap, /// Whether we've already forced a GPU flush+submit before a tile-atlas /// snapshot this render. The first snapshot of a pass can otherwise capture /// a tile before its text glyph uploads complete (blank first/center tile). @@ -682,6 +684,7 @@ impl RenderState { interactive_target_seeded: false, preserve_target_during_render: false, backbuffer_crop_cache: HashMap::default(), + moving_extrects: HashMap::default(), tile_atlas_flushed: false, drop_shadows_ops_warmed: false, drop_shadow_filter_cache: shadows::DropShadowFilterCache::new(), @@ -772,8 +775,9 @@ impl RenderState { paint.set_style(skia::PaintStyle::Stroke); paint.set_stroke_width(stroke_outset * 2.0); - let mut outline = skia::Path::default(); + let mut outline = skia::PathBuilder::new(); if skia::path_utils::fill_path_with_paint(&base, &paint, &mut outline, None, None) { + let outline = outline.detach(); if let Some(united) = base.op(&outline, skia::PathOp::Union) { return united; } @@ -1215,7 +1219,9 @@ impl RenderState { // This avoids clearing Cache on renders that don't actually paint tiles (e.g. hover/UI), // while still preventing stale pixels from surviving across full-quality renders. if !self.cache_cleared_this_render { - self.surfaces.clear_cache(self.background_color); + if self.options.is_debug_visible() { + self.surfaces.clear_cache(self.background_color); + } self.cache_cleared_this_render = true; } let tile_rect = self.get_current_aligned_tile_bounds()?; @@ -1233,7 +1239,7 @@ impl RenderState { &self.tile_viewbox, ¤t_tile, &tile_rect, - false, + !self.options.is_debug_visible(), self.render_area, self.get_scale(), self.viewbox.area, @@ -2346,10 +2352,7 @@ impl RenderState { .is_none_or(|s| s.width() < win_w || s.height() < win_h); if needs_alloc { scratch_surface = get_gpu_state() - .create_surface_with_isize( - "drag_crop_scratch".to_string(), - skia::ISize::new(win_w, win_h), - ) + .create_surface_with_isize(skia::ISize::new(win_w, win_h)) .ok(); } let Some(scratch) = scratch_surface.as_mut() else { @@ -2525,11 +2528,13 @@ impl RenderState { s.canvas().scale((scale, scale)); }); - self.surfaces.resize_cache_from_viewbox( - &self.viewbox, - &self.cached_viewbox, - self.options.dpr_viewport_interest_area_threshold, - )?; + if self.options.is_debug_visible() { + self.surfaces.resize_cache_from_viewbox( + &self.viewbox, + &self.cached_viewbox, + self.options.dpr_viewport_interest_area_threshold, + )?; + } // FIXME - review debug // debug::render_debug_tiles_for_viewbox(self); @@ -4332,11 +4337,16 @@ impl RenderState { if !is_empty || self.current_tile_had_shapes { if self.options.is_interactive_transform() { // During drag, avoid snapshot-based caching. Draw Current directly - // into Target (and Cache) to reduce stalls. + // into Target (and Cache, for the debug views) to reduce stalls. + let draw_on_cache = if self.options.is_debug_visible() { + surfaces::DrawOnCache::Yes + } else { + surfaces::DrawOnCache::No + }; self.surfaces.draw_current_tile_into_backbuffer( &tile_rect, self.background_color, - surfaces::DrawOnCache::Yes, + draw_on_cache, ); } else { self.apply_render_to_final_canvas()?; @@ -4547,7 +4557,8 @@ impl RenderState { // // We intentionally skip this when there is NO modifier so that plain // zoom / pan tile-index rebuilds do NOT invalidate valid atlas content. - if tree.get_modifier(&shape.id).is_some() { + // Once per gesture: later frames only move away from pixels already cleared. + if tree.get_modifier(&shape.id).is_some() && !self.moving_extrects.contains_key(&shape.id) { if let Some(raw_shape) = tree.get_raw(&shape.id) { let old_extrect = raw_shape.extrect(tree, 1.0); self.surfaces @@ -4823,7 +4834,7 @@ impl RenderState { } if self.options.is_interactive_transform() { - self.update_tiles_shapes(ids, tree)?; + self.update_moving_tiles(ids, tree); } else { let ancestors = all_with_ancestors(ids, tree, false); self.update_tiles_shapes(&ancestors, tree)?; @@ -4831,6 +4842,34 @@ impl RenderState { Ok(()) } + /// Evicts each moved shape's previous-frame ∪ current coverage. Tiles it left + /// before the previous frame were already repainted without it. + fn update_moving_tiles(&mut self, ids: &[Uuid], tree: ShapesPoolRef) { + let mut next = HashMap::with_capacity(ids.len()); + for id in ids { + let Some(shape) = tree.get(id) else { + continue; + }; + let prev_extrect = self.moving_extrects.get(id).copied(); + self.invalidate_shape_and_update_tiles(shape, tree, prev_extrect); + let extrect = self.get_cached_extrect(shape, tree, 1.0); + next.insert(*id, extrect); + } + // Shapes that stopped moving (e.g. flex siblings no longer reflowed) leave their + // last position behind and return to one painted without them; dropping them also + // restarts their coverage if they re-enter. + let previous = std::mem::replace(&mut self.moving_extrects, next); + for (id, rect) in previous { + if !self.moving_extrects.contains_key(&id) { + self.surfaces.invalidate_cached_tiles_intersecting(rect); + if let Some(shape) = tree.get(&id) { + let extrect = self.get_cached_extrect(shape, tree, 1.0); + self.surfaces.invalidate_cached_tiles_intersecting(extrect); + } + } + } + } + pub fn get_scale(&self) -> f32 { // During export, use the export scale instead of the workspace zoom. if let Some((_, export_scale)) = self.export_context { diff --git a/render-wasm/src/render/fonts.rs b/render-wasm/src/render/fonts.rs index 78ae886064..b1a09531cc 100644 --- a/render-wasm/src/render/fonts.rs +++ b/render-wasm/src/render/fonts.rs @@ -33,7 +33,12 @@ pub struct FontStore { impl FontStore { pub fn try_new() -> Result { - let font_mgr = FontMgr::new(); + // custom_empty: FreeType-backed manager with no system scan. FontMgr::new() + // calls SkFontMgr_NewSystem, which on wasm tries to opendir font paths and + // aborts when Emscripten is built with -sFILESYSTEM=0 (exporter + browser). + let font_mgr = FontMgr::custom_empty().ok_or(Error::CriticalError( + "Failed to create empty FontMgr".to_string(), + ))?; let font_provider = load_default_provider(&font_mgr); let mut font_collection = skia::textlayout::FontCollection::new(); font_collection.set_default_font_manager(FontMgr::from(font_provider.clone()), None); @@ -47,7 +52,7 @@ impl FontStore { let debug_font = skia::Font::new(debug_typeface, 12.0); let ui_typeface = font_mgr - .new_from_data(UI_FONT_BYTES, None) + .new_from_data(skia::Data::new_copy(UI_FONT_BYTES), None) .ok_or(Error::CriticalError("Failed to load UI font".to_string()))?; let ui_font = skia::Font::new(ui_typeface, 12.0); @@ -96,7 +101,7 @@ impl FontStore { let typeface = self .font_mgr - .new_from_data(font_data, None) + .new_from_data(skia::Data::new_copy(font_data), None) .ok_or(Error::CriticalError( "Failed to create typeface".to_string(), ))?; @@ -265,7 +270,7 @@ fn load_default_provider(font_mgr: &FontMgr) -> skia::textlayout::TypefaceFontPr let family = FontFamily::new(default_font_uuid(), 400, FontStyle::Normal); let font = font_mgr - .new_from_data(DEFAULT_FONT_BYTES, None) + .new_from_data(skia::Data::new_copy(DEFAULT_FONT_BYTES), None) .expect("Failed to load font"); font_provider.register_typeface(font, family.alias().as_str()); diff --git a/render-wasm/src/render/gpu_state.rs b/render-wasm/src/render/gpu_state.rs index 7a158b5fc6..1c0bdcf314 100644 --- a/render-wasm/src/render/gpu_state.rs +++ b/render-wasm/src/render/gpu_state.rs @@ -88,93 +88,35 @@ impl GpuState { None } - fn delete_gl_texture(&mut self, texture_id: gl::types::GLuint) -> bool { - unsafe { - gl::DeleteTextures(1, &texture_id); - gl::GetError() == 0 - } - } - - fn create_gl_texture(&mut self, width: i32, height: i32) -> gl::types::GLuint { - let mut texture_id: gl::types::GLuint = 0; - - unsafe { - gl::GenTextures(1, &mut texture_id); - gl::BindTexture(gl::TEXTURE_2D, texture_id); - - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32); - - gl::TexImage2D( - gl::TEXTURE_2D, - 0, - gl::RGBA8 as i32, - width, - height, - 0, - gl::RGBA, - gl::UNSIGNED_BYTE, - std::ptr::null(), - ); - } - - texture_id - } - - pub fn delete_surface(&mut self, surface: &mut skia::Surface) -> bool { - let Some(texture) = skia::gpu::surfaces::get_backend_texture( - surface, - skia_safe::surface::BackendHandleAccess::FlushRead, - ) else { - return false; - }; - let Some(texture_info) = gpu::backend_textures::get_gl_texture_info(&texture) else { - return false; - }; - self.delete_gl_texture(texture_info.id) - } - - pub fn create_surface_with_isize( - &mut self, - label: String, - size: ISize, - ) -> Result { - self.create_surface_with_dimensions(label, size.width, size.height) + pub fn create_surface_with_isize(&mut self, size: ISize) -> Result { + self.create_surface_with_dimensions(size.width, size.height) } pub fn create_surface_with_dimensions( &mut self, - label: String, width: i32, height: i32, ) -> Result { - let backend_texture = unsafe { - let texture_id = self.create_gl_texture(width, height); - let texture_info = TextureInfo { - target: gl::TEXTURE_2D, - id: texture_id, - format: gl::RGBA8, - protected: skia::gpu::Protected::No, - }; - gpu::backend_textures::make_gl((width, height), gpu::Mipmapped::No, texture_info, label) - }; + let image_info = skia::ImageInfo::new( + (width, height), + skia::ColorType::RGBA8888, + skia::AlphaType::Premul, + None, + ); - let surface = gpu::surfaces::wrap_backend_texture( + gpu::surfaces::render_target( &mut self.context, - &backend_texture, + gpu::Budgeted::No, + &image_info, + None, gpu::SurfaceOrigin::BottomLeft, None, - skia::ColorType::RGBA8888, - None, + false, None, ) .ok_or(Error::CriticalError( "Failed to create Skia surface".to_string(), - ))?; - - Ok(surface) + )) } /// Create a Skia surface that will be used for rendering. diff --git a/render-wasm/src/render/images.rs b/render-wasm/src/render/images.rs index 4b1ce66754..bcbf557d0c 100644 --- a/render-wasm/src/render/images.rs +++ b/render-wasm/src/render/images.rs @@ -21,15 +21,7 @@ pub fn get_dest_rect(container: &MathRect, delta: f32) -> MathRect { } pub fn get_image_dest_rect(container: &MathRect, image_fill: &ImageFill) -> MathRect { - match image_fill.transform() { - Some(tf) => MathRect::from_xywh( - container.left + tf.x * container.width(), - container.top + tf.y * container.height(), - tf.width * container.width(), - tf.height * container.height(), - ), - None => *container, - } + image_fill.dest_rect(container) } pub fn get_source_rect(size: ISize, container: &MathRect, image_fill: &ImageFill) -> MathRect { @@ -186,8 +178,9 @@ const DEFAULT_SVG_SIZE: f32 = 512.0; fn parse_svg(raw_data: &[u8]) -> Option<(skia::svg::Dom, Size)> { // An empty font manager: elements inside SVG image fills won't // resolve typefaces. Wire the render state's font provider here if that - // ever becomes a need. - let font_mgr = skia::FontMgr::new(); + // ever becomes a need. Must be custom_empty (not FontMgr::new): NewSystem + // scans font dirs and aborts under -sFILESYSTEM=0. + let font_mgr = skia::FontMgr::custom_empty()?; let mut dom = skia::svg::Dom::from_bytes(raw_data, font_mgr).ok()?; let mut size = dom.root().intrinsic_size(); diff --git a/render-wasm/src/render/pdf.rs b/render-wasm/src/render/pdf.rs index d70f353ef4..746b26242d 100644 --- a/render-wasm/src/render/pdf.rs +++ b/render-wasm/src/render/pdf.rs @@ -1,6 +1,8 @@ +use std::mem::ManuallyDrop; + use skia_safe as skia; -use crate::error::Result; +use crate::error::{Error, Result}; use crate::state::ShapesPoolRef; use crate::uuid::Uuid; @@ -10,50 +12,291 @@ use super::RenderResources; mod glyph_text; mod to_unicode; -/// Renders a shape tree to a PDF document and returns the raw PDF bytes. -/// -/// This is a dedicated vector-PDF render path that draws directly to a Skia -/// PDF canvas, bypassing the GPU surface system entirely. The result is a -/// true vector PDF — paths, text and fills are represented as PDF drawing -/// operations rather than rasterised bitmaps. Effects that are inherently -/// pixel-based (blur, shadows with blur) are rasterised internally by Skia's -/// PDF backend +/// In-flight multi-page PDF. The byte buffer and metadata are heap-pinned so +/// Skia's `Document` can hold `'static` write/metadata references across FFI +/// calls (skia-safe ties the document lifetime to both). Stored on +/// [`crate::state::State`], not a process-wide static. +pub(crate) struct PdfExportSession { + bytes: *mut Vec, + metadata: *mut skia::pdf::Metadata<'static>, + document: ManuallyDrop>, + roots: Vec, +} + +fn pdf_metadata() -> skia::pdf::Metadata<'static> { + skia::pdf::Metadata { + creator: "Penpot".to_string(), + producer: "Penpot (Skia PDF)".to_string(), + raster_dpi: Some(300.0), + ..Default::default() + } +} + +/// Starts a multi-page PDF session. Aborts any previous session in `slot` first. +pub fn export_begin(slot: &mut Option) -> Result<()> { + export_abort(slot); + let bytes = Box::into_raw(Box::new(Vec::new())); + let metadata = Box::into_raw(Box::new(pdf_metadata())); + // SAFETY: `bytes` and `metadata` stay pinned until finish/abort. Document + // writes through `&mut Vec`; reallocating the Vec's heap buffer is + // fine because the reference targets the Vec, not its buffer. + let document = unsafe { + let bytes_ref: &'static mut Vec = &mut *bytes; + let metadata_ref: &'static skia::pdf::Metadata<'static> = &*metadata; + skia::pdf::new_document(bytes_ref, Some(metadata_ref)) + }; + *slot = Some(PdfExportSession { + bytes, + metadata, + document: ManuallyDrop::new(document), + roots: Vec::new(), + }); + Ok(()) +} + +/// Appends one shape as a new page. Call between `export_begin` and +/// `export_finish`. +pub fn export_add_page( + slot: &mut Option, + shared: &mut RenderResources, + id: &Uuid, + tree: ShapesPoolRef, +) -> Result<()> { + let session = slot + .as_mut() + .ok_or_else(|| Error::CriticalError("PDF export session not started".to_string()))?; + + let shape = tree + .get(id) + .ok_or_else(|| Error::CriticalError(format!("Shape not found for PDF: {id}")))?; + // Boards crop overflowing children (classic board box) but keep their own + // strokes/shadows/blur. Other shapes use full extrect. + let bounds = shape.export_root_bounds(tree, 1.0); + + // SAFETY: document was created against session.bytes; we put it back. + let document = unsafe { ManuallyDrop::take(&mut session.document) }; + let mut on_page = document.begin_page((bounds.width(), bounds.height()), None); + { + let page_canvas = on_page.canvas(); + page_canvas.translate((-bounds.left(), -bounds.top())); + if let Err(err) = vector::render_tree_pdf(shared, page_canvas, id, tree, bounds) { + // Restore the document so abort/finish can still run; do not + // record this root — the page content failed. + let document = on_page.end_page(); + session.document = ManuallyDrop::new(document); + return Err(err); + } + } + let document = on_page.end_page(); + session.document = ManuallyDrop::new(document); + session.roots.push(*id); + Ok(()) +} + +fn drop_session_buffers(bytes: *mut Vec, metadata: *mut skia::pdf::Metadata<'static>) { + unsafe { + drop(Box::from_raw(metadata)); + drop(Box::from_raw(bytes)); + } +} + +/// Closes the session and returns the PDF bytes (with ToUnicode fixup). +pub fn export_finish(slot: &mut Option, tree: ShapesPoolRef) -> Result> { + let session = slot + .take() + .ok_or_else(|| Error::CriticalError("PDF export session not started".to_string()))?; + + if session.roots.is_empty() { + let document = ManuallyDrop::into_inner(session.document); + document.close(); + drop_session_buffers(session.bytes, session.metadata); + return Err(Error::CriticalError( + "No shapes for multi-page PDF".to_string(), + )); + } + + let roots = session.roots; + let document = ManuallyDrop::into_inner(session.document); + document.close(); + let pdf_bytes = unsafe { *Box::from_raw(session.bytes) }; + unsafe { + drop(Box::from_raw(session.metadata)); + } + + // Skia maps ligatures and other substituted glyphs to U+0000; give them + // back their text so it can be copied from the PDF. + let glyph_texts = glyph_text::collect_many(tree, &roots); + Ok(to_unicode::fix_to_unicode(pdf_bytes, &glyph_texts)) +} + +/// Drops an in-flight session without returning bytes. +pub fn export_abort(slot: &mut Option) { + let Some(session) = slot.take() else { + return; + }; + let document = ManuallyDrop::into_inner(session.document); + document.close(); + drop_session_buffers(session.bytes, session.metadata); +} + +/// Renders a shape tree to a single-page PDF and returns the raw PDF bytes. pub fn render_to_pdf( shared: &mut RenderResources, id: &Uuid, tree: ShapesPoolRef, ) -> Result> { - let shape = tree - .get(id) - .ok_or_else(|| crate::error::Error::CriticalError("Shape not found for PDF".to_string()))?; - // Boards crop overflowing children (classic board box) but keep their own - // strokes/shadows/blur. Other shapes use full extrect. - let bounds = shape.export_root_bounds(tree, 1.0); + render_to_pdf_multi(shared, &[*id], tree) +} - let mut pdf_bytes: Vec = Vec::new(); +/// Renders each shape root as a page of one PDF document, in `ids` order. +/// Uses a local session so it does not disturb a progressive export in +/// [`crate::state::State`]. +pub fn render_to_pdf_multi( + shared: &mut RenderResources, + ids: &[Uuid], + tree: ShapesPoolRef, +) -> Result> { + if ids.is_empty() { + return Err(Error::CriticalError( + "No shapes for multi-page PDF".to_string(), + )); + } + let mut session = None; + export_begin(&mut session)?; + for id in ids { + if let Err(err) = export_add_page(&mut session, shared, id, tree) { + export_abort(&mut session); + return Err(err); + } + } + match export_finish(&mut session, tree) { + Ok(bytes) => Ok(bytes), + Err(err) => { + export_abort(&mut session); + Err(err) + } + } +} - let metadata = skia::pdf::Metadata { - creator: "Penpot".to_string(), - producer: "Penpot (Skia PDF)".to_string(), - raster_dpi: Some(300.0), - ..Default::default() - }; +#[cfg(test)] +mod tests { + use super::*; - let document = skia::pdf::new_document(&mut pdf_bytes, Some(&metadata)); + use crate::shapes::{Fill, Rect, SolidColor, Type}; + use crate::state::ShapesPool; + use crate::utils::uuid_from_u32_quartet; - let mut on_page = document.begin_page((bounds.width(), bounds.height()), None); + use skia_safe as skia; - { - let page_canvas = on_page.canvas(); - page_canvas.translate((-bounds.left(), -bounds.top())); - vector::render_tree_pdf(shared, page_canvas, id, tree, bounds)?; + fn uid(n: u32) -> Uuid { + uuid_from_u32_quartet(0, 0, 0, n) } - let document = on_page.end_page(); - document.close(); + fn add_solid_rect( + pool: &mut ShapesPool, + id: Uuid, + (l, t, r, b): (f32, f32, f32, f32), + color: skia::Color, + ) { + let shape = pool.add_shape(id); + shape.set_parent(Uuid::nil()); + shape.set_shape_type(Type::Rect(Rect::default())); + shape.set_selrect(l, t, r, b); + shape.set_fills(vec![Fill::Solid(SolidColor(color))]); + } - // Skia maps ligatures and other substituted glyphs to U+0000; give them - // back their text so it can be copied from the PDF. - let glyph_texts = glyph_text::collect(tree, id); - Ok(to_unicode::fix_to_unicode(pdf_bytes, &glyph_texts)) + /// Counts page objects: `/Type /Page` entries that are not `/Pages`. + fn page_count(pdf: &[u8]) -> usize { + let text = String::from_utf8_lossy(pdf); + text.match_indices("/Type /Page") + .filter(|(i, _)| !text[*i..].starts_with("/Type /Pages")) + .count() + } + + #[test] + fn multi_page_pdf_has_one_page_per_shape() { + let mut pool = ShapesPool::new(); + let a = uid(1); + let b = uid(2); + add_solid_rect( + &mut pool, + a, + (0.0, 0.0, 100.0, 80.0), + skia::Color::from_rgb(255, 0, 0), + ); + add_solid_rect( + &mut pool, + b, + (0.0, 0.0, 200.0, 120.0), + skia::Color::from_rgb(0, 0, 255), + ); + + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = crate::globals::TestRenderResourcesGuard::install(&mut resources); + + let pdf = render_to_pdf_multi(&mut resources, &[a, b], &pool).expect("pdf"); + assert!(pdf.starts_with(b"%PDF"), "missing PDF header"); + assert_eq!(page_count(&pdf), 2, "expected two pages"); + } + + #[test] + fn session_api_reports_progress_sized_pages() { + let mut pool = ShapesPool::new(); + let a = uid(1); + let b = uid(2); + add_solid_rect( + &mut pool, + a, + (0.0, 0.0, 100.0, 80.0), + skia::Color::from_rgb(255, 0, 0), + ); + add_solid_rect( + &mut pool, + b, + (0.0, 0.0, 200.0, 120.0), + skia::Color::from_rgb(0, 0, 255), + ); + + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = crate::globals::TestRenderResourcesGuard::install(&mut resources); + + let mut session = None; + export_begin(&mut session).expect("begin"); + export_add_page(&mut session, &mut resources, &a, &pool).expect("page a"); + export_add_page(&mut session, &mut resources, &b, &pool).expect("page b"); + let pdf = export_finish(&mut session, &pool).expect("finish"); + assert_eq!(page_count(&pdf), 2); + } + + #[test] + fn empty_id_list_is_an_error() { + let pool = ShapesPool::new(); + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = crate::globals::TestRenderResourcesGuard::install(&mut resources); + + let err = render_to_pdf_multi(&mut resources, &[], &pool).expect_err("empty"); + assert!( + matches!(err, Error::CriticalError(_)), + "empty ids must fail critically: {err:?}" + ); + } + + #[test] + fn single_page_wrapper_still_works() { + let mut pool = ShapesPool::new(); + let id = uid(1); + add_solid_rect( + &mut pool, + id, + (0.0, 0.0, 50.0, 50.0), + skia::Color::from_rgb(0, 128, 0), + ); + + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = crate::globals::TestRenderResourcesGuard::install(&mut resources); + + let pdf = render_to_pdf(&mut resources, &id, &pool).expect("pdf"); + assert!(pdf.starts_with(b"%PDF")); + assert_eq!(page_count(&pdf), 1); + } } diff --git a/render-wasm/src/render/pdf/glyph_text.rs b/render-wasm/src/render/pdf/glyph_text.rs index d3623bf87f..71ba721343 100644 --- a/render-wasm/src/render/pdf/glyph_text.rs +++ b/render-wasm/src/render/pdf/glyph_text.rs @@ -197,6 +197,25 @@ pub fn collect(tree: ShapesPoolRef, root: &Uuid) -> GlyphTexts { glyph_texts } +/// Merges glyph texts from every root in `ids` (export-frames / multi PDF). +pub fn collect_many(tree: ShapesPoolRef, ids: &[Uuid]) -> GlyphTexts { + let mut result = GlyphTexts::default(); + for id in ids { + result.merge(collect(tree, id)); + } + result +} + +impl GlyphTexts { + fn merge(&mut self, other: GlyphTexts) { + for (font, glyphs) in other.fonts { + for (glyph, value) in glyphs { + self.insert(&font, glyph, value); + } + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/render-wasm/src/render/strokes.rs b/render-wasm/src/render/strokes.rs index 0b12f33482..a54eac73e9 100644 --- a/render-wasm/src/render/strokes.rs +++ b/render-wasm/src/render/strokes.rs @@ -490,7 +490,7 @@ fn expand_side_patterns( line_pb.detach() }; - let mut outline = skia::Path::default(); + let mut outline = skia::PathBuilder::new(); if skia::path_utils::fill_path_with_paint( &line_path, &stamp_paint, @@ -498,7 +498,7 @@ fn expand_side_patterns( None, None, ) { - pb.add_path(&outline); + pb.add_path(&outline.detach(), None); expanded = true; } } diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index 9d07a6362e..2c60832b45 100644 --- a/render-wasm/src/render/surfaces.rs +++ b/render-wasm/src/render/surfaces.rs @@ -165,8 +165,7 @@ impl DocAtlas { pub fn try_new() -> Result { // Keep atlas as a regular surface like the rest. Start with a tiny // transparent surface and grow it on demand. - let mut surface = - get_gpu_state().create_surface_with_dimensions("atlas".to_string(), 1, 1)?; + let mut surface = get_gpu_state().create_surface_with_dimensions(1, 1)?; surface.canvas().clear(skia::Color::TRANSPARENT); @@ -282,8 +281,7 @@ impl DocAtlas { return Ok(()); } - let mut new_surface = - gpu_state.create_surface_with_dimensions("atlas".to_string(), new_w, new_h)?; + let mut new_surface = gpu_state.create_surface_with_dimensions(new_w, new_h)?; new_surface.canvas().clear(skia::Color::TRANSPARENT); // Copy old atlas into the new one with offset. @@ -313,7 +311,6 @@ impl DocAtlas { self.origin = skia::Point::new(new_left, new_top); self.size = skia::ISize::new(new_w, new_h); self.scale = new_scale; - gpu_state.delete_surface(&mut self.surface); self.surface = new_surface; Ok(()) } @@ -534,35 +531,25 @@ impl Surfaces { let margins = skia::ISize::new(extra_tile_dims.width / 4, extra_tile_dims.height / 4); let target = gpu_state.create_target_surface(width, height)?; - let filter = gpu_state.create_surface_with_isize("filter".to_string(), extra_tile_dims)?; - let cache = gpu_state.create_surface_with_dimensions("cache".to_string(), width, height)?; - let backbuffer = - gpu_state.create_surface_with_dimensions("backbuffer".to_string(), width, height)?; + let filter = gpu_state.create_surface_with_isize(extra_tile_dims)?; + let cache = gpu_state.create_surface_with_dimensions(width, height)?; + let backbuffer = gpu_state.create_surface_with_dimensions(width, height)?; let max_texture_size = gpu_state.max_texture_size(); - let tile_atlas = gpu_state.create_surface_with_dimensions( - "tile_atlas".to_string(), - max_texture_size, - max_texture_size, - )?; + let tile_atlas = + gpu_state.create_surface_with_dimensions(max_texture_size, max_texture_size)?; - let current = - gpu_state.create_surface_with_isize("current".to_string(), extra_tile_dims)?; + let current = gpu_state.create_surface_with_isize(extra_tile_dims)?; - let drop_shadows = - gpu_state.create_surface_with_isize("drop_shadows".to_string(), extra_tile_dims)?; - let inner_shadows = - gpu_state.create_surface_with_isize("inner_shadows".to_string(), extra_tile_dims)?; - let text_drop_shadows = gpu_state - .create_surface_with_isize("text_drop_shadows".to_string(), extra_tile_dims)?; - let shape_fills = - gpu_state.create_surface_with_isize("shape_fills".to_string(), extra_tile_dims)?; - let shape_strokes = - gpu_state.create_surface_with_isize("shape_strokes".to_string(), extra_tile_dims)?; - let export = gpu_state.create_surface_with_isize("export".to_string(), extra_tile_dims)?; + let drop_shadows = gpu_state.create_surface_with_isize(extra_tile_dims)?; + let inner_shadows = gpu_state.create_surface_with_isize(extra_tile_dims)?; + let text_drop_shadows = gpu_state.create_surface_with_isize(extra_tile_dims)?; + let shape_fills = gpu_state.create_surface_with_isize(extra_tile_dims)?; + let shape_strokes = gpu_state.create_surface_with_isize(extra_tile_dims)?; + let export = gpu_state.create_surface_with_isize(extra_tile_dims)?; - let ui = gpu_state.create_surface_with_dimensions("ui".to_string(), width, height)?; - let debug = gpu_state.create_surface_with_dimensions("debug".to_string(), width, height)?; + let ui = gpu_state.create_surface_with_dimensions(width, height)?; + let debug = gpu_state.create_surface_with_dimensions(width, height)?; let tiles = TileTextureCache::new(tile_atlas.width(), tile_atlas.height()); let atlas = DocAtlas::try_new()?; @@ -683,12 +670,10 @@ impl Surfaces { )); canvas.scale((s / scale, s / scale)); - self.atlas.surface.draw( - canvas, - (0.0, 0.0), - self.sampling_options, - Some(&skia::Paint::default()), - ); + let sampling = skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::None); + self.atlas + .surface + .draw(canvas, (0.0, 0.0), sampling, Some(&skia::Paint::default())); canvas.restore(); } diff --git a/render-wasm/src/render/svg/fixtures.rs b/render-wasm/src/render/svg/fixtures.rs index db7759102b..a112752b6b 100644 --- a/render-wasm/src/render/svg/fixtures.rs +++ b/render-wasm/src/render/svg/fixtures.rs @@ -15,7 +15,7 @@ use crate::state::ShapesPool; use crate::utils::uuid_from_u32_quartet; use crate::uuid::Uuid; -use super::render_tree_to_svg; +use super::{render_tree_to_svg, render_trees_to_svg}; /// Font URL referenced in exported SVG `@font-face` rules. pub(super) const TEST_FONT_URL: &str = "fonts/sourcesanspro-regular.ttf"; @@ -335,23 +335,39 @@ pub(super) fn add_text_with_fills( text: &str, font_size: f32, fills: Vec, +) { + add_text_with_spans(pool, id, (l, t, r, b), font_size, vec![(text, fills)]); +} + +/// Fixed text with one paragraph of `spans`, each a text and its fills. +pub(super) fn add_text_with_spans( + pool: &mut ShapesPool, + id: Uuid, + (l, t, r, b): (f32, f32, f32, f32), + font_size: f32, + spans: Vec<(&str, Vec)>, ) { let bounds = skia::Rect::from_ltrb(l, t, r, b); let mut content = TextContent::new(bounds, GrowType::Fixed); let line_height = 1.2; - let span = TextSpan::new( - text.to_string(), - FontFamily::new(Uuid::nil(), 400, FontStyle::Normal), - font_size, - line_height, - 0.0, - None, - None, - TextDirection::LTR, - 400, - Uuid::nil(), - fills, - ); + let spans = spans + .into_iter() + .map(|(text, fills)| { + TextSpan::new( + text.to_string(), + FontFamily::new(Uuid::nil(), 400, FontStyle::Normal), + font_size, + line_height, + 0.0, + None, + None, + TextDirection::LTR, + 400, + Uuid::nil(), + fills, + ) + }) + .collect(); content.add_paragraph(Paragraph::new( TextAlign::Left, TextDirection::LTR, @@ -359,7 +375,7 @@ pub(super) fn add_text_with_fills( None, line_height, 0.0, - vec![span], + spans, )); let shape = pool.add_shape(id); @@ -591,6 +607,18 @@ pub(super) fn render(pool: &ShapesPool, root: Uuid) -> String { render_with(pool, root, |_resources| {}) } +/// Multi-root SVG export for tests (one document, joined viewBox). +pub(super) fn render_multi(pool: &ShapesPool, roots: &[Uuid]) -> String { + let _serial = svg_export_test_lock() + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()); + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + register_test_font_urls(&mut resources.fonts); + let _guard = TestRenderResourcesGuard::install(&mut resources); + let bytes = render_trees_to_svg(&mut resources, roots, pool, 1.0).expect("svg multi export"); + String::from_utf8(bytes).expect("utf8 svg") +} + /// SVG export installs a process-wide resources pointer; serialize tests that /// call this so parallel rustc threads do not race / SIGSEGV. fn svg_export_test_lock() -> &'static Mutex<()> { diff --git a/render-wasm/src/render/svg/mod.rs b/render-wasm/src/render/svg/mod.rs index ae4fdd3c08..997a68e71c 100644 --- a/render-wasm/src/render/svg/mod.rs +++ b/render-wasm/src/render/svg/mod.rs @@ -104,6 +104,88 @@ pub(crate) fn render_tree_to_svg( collect_font_aliases(tree, id, &mut aliases); let font_css = shared.fonts.font_face_css_for_aliases(&aliases); + Ok(emit_svg_document(page_w, page_h, &font_css, &defs, &body)) +} + +/// Multi-root SVG export: one document whose viewBox covers every visible root, +/// with relative placement preserved (inspect / copy-as-svg multi-select). +pub fn render_to_svg_multi( + shared: &mut RenderResources, + ids: &[Uuid], + tree: ShapesPoolRef, + scale: f32, +) -> Result> { + render_trees_to_svg(shared, ids, tree, scale) +} + +/// Core multi-root SVG export for headless tests and the WASM bridge. +pub(crate) fn render_trees_to_svg( + shared: &mut RenderResources, + ids: &[Uuid], + tree: ShapesPoolRef, + scale: f32, +) -> Result> { + if ids.is_empty() { + return Ok(emit_svg_document(0.0, 0.0, "", "", "")); + } + + let mut joined: Option = None; + let mut roots: Vec = Vec::with_capacity(ids.len()); + for id in ids { + let Some(shape) = tree.get(id) else { + continue; + }; + if shape.hidden { + continue; + } + let bounds = svg_page_bounds(shape, tree, scale); + match &mut joined { + None => joined = Some(bounds), + Some(acc) => { + acc.join(bounds); + } + } + roots.push(*id); + } + + // Missing or hidden-only roots: empty document (same as empty `ids`), not + // CriticalError — inspect/copy must not crash the host when nothing is drawable. + let Some(bounds) = joined else { + return Ok(emit_svg_document(0.0, 0.0, "", "", "")); + }; + + let page_w = bounds.width() * scale; + let page_h = bounds.height() * scale; + let rect = skia::Rect::from_xywh(0., 0., page_w, page_h); + let tx = -bounds.left(); + let ty = -bounds.top(); + + let mut builder = SvgLayerCanvas::new(scale, rect, tx, ty); + for id in &roots { + if let Some(shape) = tree.get(id) { + builder.nested_fills.push(shape.inherited_fills(tree)); + } + render_tree(&mut builder, shared, id, tree, scale)?; + builder.nested_fills.pop(); + } + builder.flush(); + + let mut aliases = HashSet::new(); + for id in &roots { + collect_font_aliases(tree, id, &mut aliases); + } + let font_css = shared.fonts.font_face_css_for_aliases(&aliases); + + Ok(emit_svg_document( + page_w, + page_h, + &font_css, + &builder.defs, + &builder.out, + )) +} + +fn emit_svg_document(page_w: f32, page_h: f32, font_css: &str, defs: &str, body: &str) -> Vec { let mut out = String::with_capacity(body.len() + defs.len() + font_css.len() + 256); out.push_str("\n"); out.push_str(&format!( @@ -118,14 +200,13 @@ pub(crate) fn render_tree_to_svg( "" )); } - out.push_str(&defs); + out.push_str(defs); out.push_str(""); } - out.push_str(&body); + out.push_str(body); out.push_str(""); - - Ok(out.into_bytes()) + out.into_bytes() } mod background_blur; diff --git a/render-wasm/src/render/svg/tests.rs b/render-wasm/src/render/svg/tests.rs index 02eda33b79..490d5ef27a 100644 --- a/render-wasm/src/render/svg/tests.rs +++ b/render-wasm/src/render/svg/tests.rs @@ -58,6 +58,75 @@ fn exports_a_solid_rect() { insta::assert_snapshot!(render(&pool, id)); } +#[test] +fn exports_multiple_roots_in_one_svg_with_relative_placement() { + let mut pool = ShapesPool::new(); + let left = uid(1); + let right = uid(2); + add_solid_rect( + &mut pool, + left, + Uuid::nil(), + (0.0, 0.0, 50.0, 50.0), + skia::Color::from_rgb(255, 0, 0), + ); + add_solid_rect( + &mut pool, + right, + Uuid::nil(), + (100.0, 0.0, 150.0, 50.0), + skia::Color::from_rgb(0, 0, 255), + ); + + let svg = render_multi(&pool, &[left, right]); + assert_eq!( + svg.matches(": {svg}" + ); + assert_eq!( + svg.matches("").count(), + 1, + "multi-root export must emit one : {svg}" + ); + // Joined page is 150×50; both fills present and the right rect keeps x=100. + assert!( + svg.contains("viewBox=\"0 0 150 50\""), + "joined viewBox should span both rects: {svg}" + ); + assert!(svg.contains("fill=\"red\""), "missing left red fill: {svg}"); + assert!( + svg.contains("fill=\"blue\"") && svg.contains("x=\"100\""), + "missing right blue fill at x=100: {svg}" + ); +} + +#[test] +fn exports_multi_root_all_hidden_or_missing_as_empty_svg() { + let mut pool = ShapesPool::new(); + let hidden = uid(1); + add_solid_rect( + &mut pool, + hidden, + Uuid::nil(), + (0.0, 0.0, 50.0, 50.0), + skia::Color::from_rgb(255, 0, 0), + ); + pool.get_mut(&hidden).unwrap().set_hidden(true); + + let missing = uid(99); + let svg = render_multi(&pool, &[hidden, missing]); + assert_eq!(svg.matches(" skia_safe::Rect { - let image_width = image_fill.width() as f32; - let image_height = image_fill.height() as f32; - let scale_x = selrect.width() / image_width; - let scale_y = selrect.height() / image_height; - let scale = scale_x.max(scale_y); - let scaled_width = image_width * scale; - let scaled_height = image_height * scale; - let pos_x = selrect.left() - (scaled_width - selrect.width()) / 2.0; - let pos_y = selrect.top() - (scaled_height - selrect.height()) / 2.0; - skia_safe::Rect::from_xywh(pos_x, pos_y, scaled_width, scaled_height) -} - fn push_glyph_clip( builder: &mut SvgLayerCanvas, element: &Shape, @@ -365,12 +351,12 @@ fn linked_image_fills_at_layer<'a>( layer: usize, shared: &RenderResources, ) -> Vec<&'a ImageFill> { - let mut out = Vec::new(); - let mut seen = HashSet::new(); + // Spans can share an image with different transforms: one element each. + let mut out: Vec<&ImageFill> = Vec::new(); for paragraph in text_content.paragraphs() { for span in paragraph.children() { if let Some(Fill::Image(img)) = span.fills_from_bottom(layer) { - if shared.images.source_url(&img.id()).is_some() && seen.insert(img.id()) { + if shared.images.source_url(&img.id()).is_some() && !out.contains(&img) { out.push(img); } } @@ -412,7 +398,7 @@ fn emit_text_image_fill( let clip_id = builder.unique("txtimgclip"); let text_content = shape.get_text_content().new_bounds(shape.selrect()); let mut paragraph_builders = - text_content.paragraph_builder_group_opaque_for_image_layer(layer, image_fill.id()); + text_content.paragraph_builder_group_opaque_for_image_layer(layer, image_fill); let canvas = builder.new_fragment(); { @@ -428,7 +414,7 @@ fn emit_text_image_fill( emit_linked_image_element( builder, image_fill, - shape.selrect(), + image_fill.dest_rect(&shape.selrect()), &href, Some(&clip_id), true, diff --git a/render-wasm/src/render/text_editor.rs b/render-wasm/src/render/text_editor.rs index 7adfa5d692..fdf9adb037 100644 --- a/render-wasm/src/render/text_editor.rs +++ b/render-wasm/src/render/text_editor.rs @@ -3,7 +3,7 @@ use crate::shapes::{vertical_align_offset, Shape, TextContent, Type}; use crate::state::{TextEditorState, TextSelection}; use crate::view::Viewbox; use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle}; -use skia_safe::{BlendMode, Canvas, Color, Paint, Rect}; +use skia_safe::{BlendMode, Canvas, Color, Paint, Point, Rect}; pub fn render_overlay( canvas: &Canvas, @@ -228,10 +228,40 @@ fn calculate_selection_rects( text_content: &TextContent, shape: &Shape, ) -> Vec { - let mut rects = Vec::new(); - let start = selection.start(); let end = selection.end(); + range_rects( + (start.paragraph, start.offset), + (end.paragraph, end.offset), + text_content, + shape, + ) +} + +/// Top-left corner of the first line box of the text between `start` and `end` +/// (paragraph, char offset), shape-local; `None` when the range covers no text. +pub(crate) fn range_origin( + start: (usize, usize), + end: (usize, usize), + text_content: &TextContent, + shape: &Shape, +) -> Option { + range_rects(start, end, text_content, shape) + .first() + .map(|rect| Point::new(rect.left(), rect.top())) +} + +/// Line boxes, shape-local, of the text between `start` and `end` (paragraph, +/// char offset). +fn range_rects( + start: (usize, usize), + end: (usize, usize), + text_content: &TextContent, + shape: &Shape, +) -> Vec { + let mut rects = Vec::new(); + let (start_paragraph, start_offset) = start; + let (end_paragraph, end_offset) = end; let paragraphs = text_content.paragraphs(); let layout_paragraphs: Vec<_> = text_content.layout.paragraphs.iter().flatten().collect(); @@ -242,7 +272,7 @@ fn calculate_selection_rects( let para_height = laid_out_para.height(); // Check if this paragraph is in selection range - if para_idx < start.paragraph || para_idx > end.paragraph { + if para_idx < start_paragraph || para_idx > end_paragraph { y_offset += para_height; continue; } @@ -255,14 +285,14 @@ fn calculate_selection_rects( .map(|span| span.text.chars().count()) .sum(); - let range_start = if para_idx == start.paragraph { - start.offset + let range_start = if para_idx == start_paragraph { + start_offset } else { 0 }; - let range_end = if para_idx == end.paragraph { - end.offset + let range_end = if para_idx == end_paragraph { + end_offset } else { para_char_count }; @@ -291,3 +321,91 @@ fn calculate_selection_rects( rects } + +#[cfg(test)] +mod tests { + use super::*; + use crate::globals::TestRenderResourcesGuard; + use crate::render::RenderResources; + use crate::shapes::{ + FontFamily, FontStyle, GrowType, Paragraph, TextAlign, TextDirection, TextSpan, + }; + use crate::uuid::Uuid; + + /// A laid-out fixed text, `width` wide, with a single span of `text`. + fn text_shape(text: &str, width: f32) -> Shape { + let span = TextSpan::new( + text.to_string(), + FontFamily::new(Uuid::nil(), 400, FontStyle::Normal), + 14.0, + 1.2, + 0.0, + None, + None, + TextDirection::LTR, + 400, + Uuid::nil(), + vec![], + ); + let mut content = + TextContent::new(Rect::from_xywh(0.0, 0.0, width, 100.0), GrowType::Fixed); + content.add_paragraph(Paragraph::new( + TextAlign::Left, + TextDirection::LTR, + None, + None, + 1.2, + 0.0, + vec![span], + )); + + let mut shape = Shape::new(Uuid::nil()); + shape.set_shape_type(Type::Text(content)); + shape.set_selrect(0.0, 0.0, width, 100.0); + shape + } + + fn origin_of(shape: &Shape, start: usize, end: usize) -> Option { + let Type::Text(content) = &shape.shape_type else { + unreachable!() + }; + range_origin((0, start), (0, end), content, shape) + } + + #[test] + fn range_origin_is_where_the_text_starts() { + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = TestRenderResourcesGuard::install(&mut resources); + let shape = text_shape("hello world", 500.0); + + let hello = origin_of(&shape, 0, 5).expect("hello has text"); + let world = origin_of(&shape, 6, 11).expect("world has text"); + + assert!(hello.x.abs() < 0.01, "{hello:?}"); + assert!((0.0..1.0).contains(&hello.y), "{hello:?}"); + assert!(world.x > hello.x, "{world:?}"); + assert!((world.y - hello.y).abs() < 0.01, "{world:?}"); + } + + #[test] + fn range_origin_of_wrapped_text_is_on_its_first_line() { + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = TestRenderResourcesGuard::install(&mut resources); + // Too narrow for both words on one line. + let shape = text_shape("hello world", 45.0); + + let world = origin_of(&shape, 6, 11).expect("world has text"); + + assert!(world.x.abs() < 0.01, "{world:?}"); + assert!(world.y > 10.0, "{world:?}"); + } + + #[test] + fn range_origin_is_none_for_an_empty_range() { + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = TestRenderResourcesGuard::install(&mut resources); + let shape = text_shape("hello", 500.0); + + assert!(origin_of(&shape, 2, 2).is_none()); + } +} diff --git a/render-wasm/src/render/ui/rulers.rs b/render-wasm/src/render/ui/rulers.rs index 16bf6f4acc..18e9d0a4e3 100644 --- a/render-wasm/src/render/ui/rulers.rs +++ b/render-wasm/src/render/ui/rulers.rs @@ -364,14 +364,12 @@ fn draw_mask(ctx: &RenderCtx, rect: Rect, axis: MaskAxis, fade_to_end: bool) { MaskAxis::Horizontal => (rect.right, rect.top), MaskAxis::Vertical => (rect.left, rect.bottom), }; - let shader = skia::gradient_shader::linear( - ((rect.left, rect.top), end), - &colors[..], - Some(&offsets[..]), - skia::TileMode::Clamp, - None, - None, - ); + let colors4f: Vec = colors.iter().map(|c| skia::Color4f::from(*c)).collect(); + let gradient_colors = + skia::gradient::Colors::new(&colors4f, Some(&offsets[..]), skia::TileMode::Clamp, None); + let gradient = + skia::gradient::Gradient::new(gradient_colors, skia::gradient::Interpolation::default()); + let shader = skia::shaders::linear_gradient(((rect.left, rect.top), end), &gradient, None); let mut paint = Paint::default(); paint.set_anti_alias(false); paint.set_style(PaintStyle::Fill); diff --git a/render-wasm/src/shapes/fills.rs b/render-wasm/src/shapes/fills.rs index 1faeb54185..15afbbbba7 100644 --- a/render-wasm/src/shapes/fills.rs +++ b/render-wasm/src/shapes/fills.rs @@ -74,14 +74,20 @@ impl Gradient { rect.left + self.end.0 * rect.width(), rect.top + self.end.1 * rect.height(), ); - skia::gradient_shader::linear( - (start, end), - self.colors.as_slice(), + let colors4f: Vec = self + .colors + .iter() + .map(|c| skia::Color4f::from(*c)) + .collect(); + let colors = skia::gradient::Colors::new( + &colors4f, Some(self.offsets.as_slice()), skia::TileMode::Clamp, None, - None, - ) + ); + let gradient = + skia::gradient::Gradient::new(colors, skia::gradient::Interpolation::default()); + skia::shaders::linear_gradient((start, end), &gradient, None) } pub fn to_radial_shader(&self, rect: &Rect) -> Option { @@ -106,15 +112,20 @@ impl Gradient { transform.pre_scale((self.width * rect.width() / rect.height(), 1.), None); transform.pre_translate((-center.x, -center.y)); - skia::gradient_shader::radial( - center, - distance, - self.colors.as_slice(), + let colors4f: Vec = self + .colors + .iter() + .map(|c| skia::Color4f::from(*c)) + .collect(); + let colors = skia::gradient::Colors::new( + &colors4f, Some(self.offsets.as_slice()), skia::TileMode::Clamp, None, - Some(&transform), - ) + ); + let gradient = + skia::gradient::Gradient::new(colors, skia::gradient::Interpolation::default()); + skia::shaders::radial_gradient((center, distance), &gradient, Some(&transform)) } } @@ -134,6 +145,8 @@ pub struct ImageFill { height: i32, keep_aspect_ratio: bool, transform: Option, + // Opaque media type code, kept so the frontend can read it back. + mtype: u16, } impl ImageFill { @@ -145,6 +158,7 @@ impl ImageFill { height, keep_aspect_ratio, transform: None, + mtype: 0, } } @@ -163,9 +177,14 @@ impl ImageFill { height, keep_aspect_ratio, transform, + mtype: 0, } } + pub fn with_mtype(self, mtype: u16) -> Self { + Self { mtype, ..self } + } + pub fn id(&self) -> Uuid { self.id } @@ -186,9 +205,42 @@ impl ImageFill { self.height } + pub fn mtype(&self) -> u16 { + self.mtype + } + pub fn transform(&self) -> Option<&ImageFillTransform> { self.transform.as_ref() } + + /// The part of `container` the image is laid over: its transform, else all of it. + pub fn dest_rect(&self, container: &Rect) -> Rect { + match &self.transform { + Some(tf) => Rect::from_xywh( + container.left + tf.x * container.width(), + container.top + tf.y * container.height(), + tf.width * container.width(), + tf.height * container.height(), + ), + None => *container, + } + } + + /// Where the image is drawn to cover its dest rect in `container`, centered. + pub fn cover_rect(&self, container: &Rect) -> Rect { + let dest = self.dest_rect(container); + let image_width = self.width as f32; + let image_height = self.height as f32; + let scale = (dest.width() / image_width).max(dest.height() / image_height); + let scaled_width = image_width * scale; + let scaled_height = image_height * scale; + Rect::from_xywh( + dest.left - (scaled_width - dest.width()) / 2.0, + dest.top - (scaled_height - dest.height()) / 2.0, + scaled_width, + scaled_height, + ) + } } #[derive(Debug, Clone, PartialEq, Copy)] @@ -287,20 +339,14 @@ pub fn get_fill_shader(fill: &Fill, bounding_box: &Rect) -> Option let sampling_options = skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest); - // FIXME no image ratio applied, centered to the current rect + // Covers the dest rect (transform, else the whole box), centered. + // keep_aspect_ratio is not applied: the image always covers. let tile_modes = (skia::TileMode::Clamp, skia::TileMode::Clamp); - let image_width = image_fill.width as f32; - let image_height = image_fill.height as f32; - let scale_x = bounding_box.width() / image_width; - let scale_y = bounding_box.height() / image_height; - let scale = scale_x.max(scale_y); - let scaled_width = image_width * scale; - let scaled_height = image_height * scale; - let pos_x = bounding_box.left() - (scaled_width - bounding_box.width()) / 2.0; - let pos_y = bounding_box.top() - (scaled_height - bounding_box.height()) / 2.0; + let cover = image_fill.cover_rect(bounding_box); + let scale = cover.width() / image_fill.width as f32; let mut matrix = skia::Matrix::new_identity(); - matrix.pre_translate((pos_x, pos_y)); + matrix.pre_translate((cover.left, cover.top)); matrix.pre_scale((scale, scale), None); let opacity = image_fill.opacity(); @@ -371,3 +417,44 @@ pub fn set_paint_fill(paint: &mut Paint, fill: &Fill, bounding_box: &Rect, remov paint.set_shader(shader); } } + +#[cfg(test)] +mod tests { + use super::*; + + fn image_fill(transform: Option) -> ImageFill { + ImageFill::new_with_transform(Uuid::nil(), 255, 100, 50, true, transform) + } + + #[test] + fn an_image_without_transform_covers_the_whole_container() { + let container = Rect::from_xywh(10.0, 20.0, 100.0, 100.0); + + assert_eq!(image_fill(None).dest_rect(&container), container); + // 100x50 scaled by 2 to fill the height, centered horizontally. + assert_eq!( + image_fill(None).cover_rect(&container), + Rect::from_xywh(-40.0, 20.0, 200.0, 100.0) + ); + } + + #[test] + fn an_image_with_transform_covers_its_part_of_the_container() { + let container = Rect::from_xywh(10.0, 20.0, 200.0, 100.0); + let fill = image_fill(Some(ImageFillTransform { + x: 0.5, + y: -0.5, + width: 0.5, + height: 1.0, + })); + + assert_eq!( + fill.dest_rect(&container), + Rect::from_xywh(110.0, -30.0, 100.0, 100.0) + ); + assert_eq!( + fill.cover_rect(&container), + Rect::from_xywh(60.0, -30.0, 200.0, 100.0) + ); + } +} diff --git a/render-wasm/src/shapes/modifiers/grid_layout.rs b/render-wasm/src/shapes/modifiers/grid_layout.rs index a9749b9d8f..e28c6dbb46 100644 --- a/render-wasm/src/shapes/modifiers/grid_layout.rs +++ b/render-wasm/src/shapes/modifiers/grid_layout.rs @@ -314,10 +314,7 @@ fn set_flex_multi_span( // Distribute the space between flex tracks in proportion to the division for track in tracks[start..end].iter_mut() { if track.track_type == GridTrackType::Flex { - let new_size = alloc.clamp(track.size, track.max_size); - let aloc = new_size - track.size; - dist -= aloc; - track.size = new_size; + track.size = alloc.clamp(track.size, track.max_size); } } } diff --git a/render-wasm/src/shapes/stroke_paths.rs b/render-wasm/src/shapes/stroke_paths.rs index 52dc58e762..bcdbdbeac4 100644 --- a/render-wasm/src/shapes/stroke_paths.rs +++ b/render-wasm/src/shapes/stroke_paths.rs @@ -48,7 +48,7 @@ pub fn stroke_to_path( paint.set_stroke_cap(cap); } - let mut stroke_outline = skia::Path::default(); + let mut stroke_outline = skia::PathBuilder::new(); let success = skia::path_utils::fill_path_with_paint( &transformed_shape_path, &paint, @@ -60,6 +60,7 @@ pub fn stroke_to_path( if !success { return None; } + let stroke_outline = stroke_outline.detach(); // For inner/outer strokes, use boolean ops to clip // the 2×-width stroke outline to the correct region. @@ -198,9 +199,9 @@ fn stroke_region(path: &skia::Path, width: f32) -> Option { paint.set_style(skia::PaintStyle::Stroke); paint.set_stroke_width(width); - let mut outline = skia::Path::default(); + let mut outline = skia::PathBuilder::new(); skia::path_utils::fill_path_with_paint(path, &paint, &mut outline, None, None) - .then_some(outline) + .then(|| outline.detach()) } /// Filled geometry of a single stroke cap, matching what `handle_stroke_caps` diff --git a/render-wasm/src/shapes/text.rs b/render-wasm/src/shapes/text.rs index 14eb84d347..a8328be0d1 100644 --- a/render-wasm/src/shapes/text.rs +++ b/render-wasm/src/shapes/text.rs @@ -5,7 +5,6 @@ use crate::{ utils::Browser, }; -use core::f32; use macros::ToJs; use skia_safe::textlayout::{RectHeightStyle, RectWidthStyle}; use skia_safe::{ @@ -425,6 +424,9 @@ pub struct TextContent { /// into cached Skia paragraphs. Kept across move clones so paint can /// translate glyphs + shaders together. See `cached_layout_paint_offset`. layout_paint_origin: Option, + /// Box size the gradient and image shaders were built for. They are relative + /// to the whole box, so a new size must rebuild them. + layout_paint_size: Option<(f32, f32)>, } impl PartialEq for TextContent { @@ -449,6 +451,7 @@ impl TextContent { layout_version: 0, layout_width: None, layout_paint_origin: None, + layout_paint_size: None, } } @@ -465,6 +468,7 @@ impl TextContent { layout_version: 0, layout_width: None, layout_paint_origin: None, + layout_paint_size: None, } } @@ -879,18 +883,18 @@ impl TextContent { } /// Opaque black glyphs only for spans whose fill at `layer_from_bottom` is - /// the given image — used as an SVG `` for linked image fills. + /// `image_fill` — used as an SVG `` for linked image fills. pub fn paragraph_builder_group_opaque_for_image_layer( &self, layer_from_bottom: usize, - image_id: Uuid, + image_fill: &shapes::ImageFill, ) -> Vec { self.paragraph_builders( None, false, None, None, - Some((layer_from_bottom, image_id)), + Some((layer_from_bottom, image_fill)), None, ) } @@ -901,7 +905,7 @@ impl TextContent { opaque: bool, align_override: Option, fill_layer: Option, - opaque_image_layer: Option<(usize, Uuid)>, + opaque_image_layer: Option<(usize, &shapes::ImageFill)>, skip_image_ids: Option<&HashSet>, ) -> Vec { let fonts = get_font_collection(); @@ -916,7 +920,7 @@ impl TextContent { let mut builder = ParagraphBuilder::new(¶graph_style, fonts); let mut has_text = false; for span in paragraph.children() { - let text_style = if let Some((layer, image_id)) = opaque_image_layer { + let text_style = if let Some((layer, image_fill)) = opaque_image_layer { let mut style = span.to_style( &self.bounds(), fallback_fonts, @@ -925,7 +929,7 @@ impl TextContent { ); let mut paint = paint::Paint::default(); match span.fills_from_bottom(layer) { - Some(shapes::Fill::Image(img)) if img.id() == image_id => { + Some(shapes::Fill::Image(img)) if img == image_fill => { paint.set_color(skia::Color::BLACK); paint.set_alpha(255); } @@ -1115,7 +1119,28 @@ impl TextContent { if self.layout.needs_update() || self.layout_version != self.content_version { return false; } - self.layout_matches_paint_container(shape) + self.layout_matches_paint_container(shape) && self.paint_size_matches(&shape.selrect()) + } + + /// True when some span has a fill laid over the whole box (gradient or image). + fn has_box_relative_fills(&self) -> bool { + self.paragraphs.iter().any(|paragraph| { + paragraph.children().iter().any(|span| { + span.fills + .iter() + .any(|fill| !matches!(fill, shapes::Fill::Solid(_))) + }) + }) + } + + /// True when the cached fill shaders fit a box of `selrect`'s size. + fn paint_size_matches(&self, selrect: &Rect) -> bool { + if !self.has_box_relative_fills() { + return true; + } + self.layout_paint_size.is_some_and(|(width, height)| { + (width - selrect.width()).abs() < 0.01 && (height - selrect.height()).abs() < 0.01 + }) } pub(crate) fn layout_cache_versions_match(&self) -> bool { @@ -1157,11 +1182,13 @@ impl TextContent { // Paragraph paints (incl. absolute image/gradient shaders) were built // against `self.bounds()` in `paragraph_builder_group_from_text`. self.layout_paint_origin = Some(Point::new(self.bounds.x(), self.bounds.y())); + self.layout_paint_size = Some((self.bounds.width(), self.bounds.height())); } pub fn force_next_layout_update(&mut self) { self.layout_width = None; self.layout_paint_origin = None; + self.layout_paint_size = None; self.layout.cached_extrect.set(None); // Bump the content version so update_layout can't early-return: auto-width // shapes always match their container and clearing the cache above doesn't @@ -1183,6 +1210,7 @@ impl TextContent { if !self.layout.needs_update() && self.layout_version == self.content_version && layout_matches_container + && self.paint_size_matches(&selrect) { return self.size; } @@ -1352,6 +1380,7 @@ impl Default for TextContent { layout_version: 0, layout_width: None, layout_paint_origin: None, + layout_paint_size: None, } } } @@ -1995,6 +2024,8 @@ pub fn calculate_position_data( #[cfg(test)] mod tests { use super::*; + use crate::globals::TestRenderResourcesGuard; + use crate::render::RenderResources; #[test] fn vertical_align_top_keeps_the_content_at_the_origin() { @@ -2346,6 +2377,69 @@ mod tests { ); } + fn gradient_fill() -> shapes::Fill { + shapes::Fill::LinearGradient(shapes::Gradient::new( + (0.5, 0.0), + (0.5, 1.0), + 255, + 1.0, + &[(skia::Color::RED, 0.0), (skia::Color::BLUE, 1.0)], + )) + } + + fn solid_fill() -> shapes::Fill { + shapes::Fill::Solid(shapes::SolidColor(skia::Color::RED)) + } + + /// A laid-out text shape at 200x100 whose only span has `fills`. + fn laid_out_text_shape(grow_type: GrowType, fills: Vec) -> Shape { + let mut paragraph = test_paragraph(&["hello"]); + paragraph.children_mut()[0].fills = fills; + let mut content = TextContent::new(Rect::from_xywh(0.0, 0.0, 200.0, 100.0), grow_type); + content.add_paragraph(paragraph); + + let mut shape = Shape::new(Uuid::nil()); + shape.set_shape_type(shapes::Type::Text(content)); + shape.set_selrect(0.0, 0.0, 200.0, 100.0); + shape + } + + /// Whether resizing `shape` to `width`x`height`, with no content change, + /// rebuilt its cached Skia paragraphs. + fn resize_rebuilds_paragraphs(shape: &mut Shape, width: f32, height: f32) -> bool { + let before = Rc::clone(&shape.get_text_content().layout.paragraphs); + assert!(!before.is_empty(), "the shape should be laid out"); + shape.set_selrect(0.0, 0.0, width, height); + !Rc::ptr_eq(&before, &shape.get_text_content().layout.paragraphs) + } + + // Gradient and image shaders are built into the cached paragraphs against + // the box size, so a new box size must rebuild them. + + #[test] + fn resizing_a_fixed_text_height_rebuilds_box_relative_fills() { + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = TestRenderResourcesGuard::install(&mut resources); + let mut shape = laid_out_text_shape(GrowType::Fixed, vec![gradient_fill()]); + assert!(resize_rebuilds_paragraphs(&mut shape, 200.0, 300.0)); + } + + #[test] + fn resizing_an_auto_width_text_rebuilds_box_relative_fills() { + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = TestRenderResourcesGuard::install(&mut resources); + let mut shape = laid_out_text_shape(GrowType::AutoWidth, vec![gradient_fill()]); + assert!(resize_rebuilds_paragraphs(&mut shape, 400.0, 300.0)); + } + + #[test] + fn resizing_a_fixed_text_height_keeps_the_cache_for_solid_fills() { + let mut resources = RenderResources::try_new_headless().expect("headless resources"); + let _guard = TestRenderResourcesGuard::install(&mut resources); + let mut shape = laid_out_text_shape(GrowType::Fixed, vec![solid_fill()]); + assert!(!resize_rebuilds_paragraphs(&mut shape, 200.0, 300.0)); + } + #[test] fn cached_layout_paint_offset_tracks_selrect_move() { let mut content = sample_text_content(); diff --git a/render-wasm/src/shapes/text_paths.rs b/render-wasm/src/shapes/text_paths.rs index 777181a3a4..44cc21a115 100644 --- a/render-wasm/src/shapes/text_paths.rs +++ b/render-wasm/src/shapes/text_paths.rs @@ -68,7 +68,7 @@ impl TextPaths { let Some(glyph_path) = font.get_path(*glyph) else { continue; }; - builder.add_path(&glyph_path.with_offset(run_origin + *position)); + builder.add_path(&glyph_path.with_offset(run_origin + *position), None); has_glyphs = true; } diff --git a/render-wasm/src/state.rs b/render-wasm/src/state.rs index 9efce2b024..97296058b0 100644 --- a/render-wasm/src/state.rs +++ b/render-wasm/src/state.rs @@ -28,6 +28,8 @@ pub(crate) struct State { pub saved_shapes: Option, /// True while the first bulk load of shapes is in progress. pub loading: bool, + /// Progressive multi-page PDF export (begin / add-page / finish). + pub pdf_export: Option, } impl State { @@ -38,6 +40,7 @@ impl State { shapes: ShapesPool::new(), saved_shapes: None, loading: false, + pdf_export: None, } } @@ -50,18 +53,16 @@ impl State { .to_string(), )); } - self.saved_shapes = Some(self.shapes.clone()); - self.shapes = ShapesPool::new(); + self.saved_shapes = Some(std::mem::take(&mut self.shapes)); Ok(()) } // Disposes of the temporary shapes pool restoring the normal pool // Will panic if a there is no temporary pool. pub fn end_temp_objects(&mut self) -> Result<()> { - self.shapes = self.saved_shapes.clone().ok_or(Error::CriticalError( + self.shapes = self.saved_shapes.take().ok_or(Error::CriticalError( "Tried to end temp objects but not content to be restored is present".to_string(), ))?; - self.saved_shapes = None; Ok(()) } @@ -105,10 +106,34 @@ impl State { crate::render::pdf::render_to_pdf(get_resources(), id, &self.shapes) } + pub fn render_shapes_pdf(&mut self, ids: &[Uuid]) -> Result> { + crate::render::pdf::render_to_pdf_multi(get_resources(), ids, &self.shapes) + } + + pub fn pdf_export_begin(&mut self) -> Result<()> { + crate::render::pdf::export_begin(&mut self.pdf_export) + } + + pub fn pdf_export_add_page(&mut self, id: &Uuid) -> Result<()> { + crate::render::pdf::export_add_page(&mut self.pdf_export, get_resources(), id, &self.shapes) + } + + pub fn pdf_export_finish(&mut self) -> Result> { + crate::render::pdf::export_finish(&mut self.pdf_export, &self.shapes) + } + + pub fn pdf_export_abort(&mut self) { + crate::render::pdf::export_abort(&mut self.pdf_export) + } + pub fn render_shape_svg(&mut self, id: &Uuid, scale: f32) -> Result> { crate::render::svg::render_to_svg(get_resources(), id, &self.shapes, scale) } + pub fn render_shapes_svg(&mut self, ids: &[Uuid], scale: f32) -> Result> { + crate::render::svg::render_to_svg_multi(get_resources(), ids, &self.shapes, scale) + } + /// GPU-free counterpart of [`State::render_shape_pixels`]: encodes to /// `format` on a CPU raster surface, no GPU/WebGL. pub fn render_shape_raster( diff --git a/render-wasm/src/state/shapes_pool.rs b/render-wasm/src/state/shapes_pool.rs index 60824bd42f..cd9542068a 100644 --- a/render-wasm/src/state/shapes_pool.rs +++ b/render-wasm/src/state/shapes_pool.rs @@ -308,10 +308,11 @@ impl ShapesPoolImpl { } } - // Expand every root modifier to its full descendant subtree. + // Expand every pure-move root modifier to the descendants without one. // When CLJS sends only root shapes (translation on drag), descendants - // need the same matrix. - // For resize/rotate, propagate-modifiers already includes all descendants. + // need the same matrix. Other matrices come from propagate-modifiers, + // where a missing child means the child stays put (e.g. an auto-size + // layout parent or a layout child that does not move). // Descendants are NOT pushed into `ids` / `modifier_uuids`: rebuild_modifier_tiles // runs for roots, and drops the non-clipping ancestors' extrects separately. let root_pairs: Vec<(usize, skia::Matrix)> = ids @@ -319,41 +320,54 @@ impl ShapesPoolImpl { .filter_map(|uuid| { let idx = self.uuid_to_idx.get(uuid).copied()?; let matrix = modifiers_with_idx.get(&idx).copied()?; - Some((idx, matrix)) + math::is_move_only_matrix(&matrix).then_some((idx, matrix)) }) .collect(); - let mut descendants_idxs: Vec = Vec::new(); for (root_idx, matrix) in root_pairs { - for descendant_idx in self.collect_all_descendants(root_idx) { - if let std::collections::hash_map::Entry::Vacant(e) = - modifiers_with_idx.entry(descendant_idx) - { - e.insert(matrix); - descendants_idxs.push(descendant_idx); - } + for descendant_idx in self.collect_inheriting_descendants(root_idx, &modifiers_with_idx) + { + modifiers_with_idx.insert(descendant_idx, matrix); } } self.modifiers = modifiers_with_idx; - for descendant_idx in descendants_idxs { - self.modified_shape_cache - .insert(descendant_idx, OnceCell::new()); + for &idx in self.modifiers.keys() { + self.modified_shape_cache.insert(idx, OnceCell::new()); } - // Compute ancestors before consuming `ids` so we can move it into - // `modifier_uuids` without a clone. - let all_ids = shapes::all_with_ancestors(&ids, self, true); + // Snapped translations arrive propagated to every descendant. Keep only the shapes + // that move relative to their parent, so tiles are rebuilt per moved subtree. + let roots: Vec = ids + .into_iter() + .filter(|uuid| !self.moves_with_parent(uuid)) + .collect(); - for uuid in all_ids { + for uuid in shapes::all_with_ancestors(&roots, self, true) { if let Some(idx) = self.uuid_to_idx.get(&uuid).copied() { self.modified_shape_cache.insert(idx, OnceCell::new()); } } // rebuild_modifier_tiles doesn't process every descendant individually. - self.modifier_uuids = ids; + self.modifier_uuids = roots; + } + + fn moves_with_parent(&self, id: &Uuid) -> bool { + let Some(&idx) = self.uuid_to_idx.get(id) else { + return false; + }; + let Some(&parent_idx) = self.shapes[idx] + .parent_id + .as_ref() + .and_then(|parent_id| self.uuid_to_idx.get(parent_id)) + else { + return false; + }; + self.modifiers + .get(&parent_idx) + .is_some_and(|parent| self.modifiers.get(&idx) == Some(parent)) } pub fn set_structure(&mut self, structure: HashMap>) { @@ -477,7 +491,13 @@ impl ShapesPoolImpl { } } - fn collect_all_descendants(&self, idx: usize) -> Vec { + /// Descendants of `idx` without a modifier of their own, stopping at the subtrees of + /// those that have one: they inherit from their nearest ancestor with a modifier. + fn collect_inheriting_descendants( + &self, + idx: usize, + modifiers: &HashMap, + ) -> Vec { let mut result = Vec::new(); let mut queue: VecDeque<&Uuid> = VecDeque::new(); let shape = &self.shapes[idx]; @@ -486,6 +506,9 @@ impl ShapesPoolImpl { } while let Some(child_id) = queue.pop_front() { if let Some(&child_idx) = self.uuid_to_idx.get(child_id) { + if modifiers.contains_key(&child_idx) { + continue; + } result.push(child_idx); let child_shape = &self.shapes[child_idx]; for grandchild_id in child_shape.children_ids_iter(false) { @@ -577,4 +600,33 @@ mod tests { pool.get_mut(&id).expect("shape").set_opacity(0.5); assert_eq!(pool.get(&id).expect("shape").opacity, 0.5); } + + fn pool_with_parent_and_child() -> (ShapesPool, Uuid, Uuid) { + let mut pool = ShapesPool::new(); + pool.initialize(2); + let parent = Uuid::new_v4(); + let child = Uuid::new_v4(); + pool.add_shape(parent).add_child(child); + pool.add_shape(child).set_parent(parent); + (pool, parent, child) + } + + #[test] + fn set_modifiers_passes_a_move_down_to_children() { + let (mut pool, parent, child) = pool_with_parent_and_child(); + let translate = skia::Matrix::translate((10.0, 0.0)); + + pool.set_modifiers(HashMap::from([(parent, translate)])); + + assert_eq!(pool.get_modifier(&child), Some(&translate)); + } + + #[test] + fn set_modifiers_keeps_a_resize_on_the_parent() { + let (mut pool, parent, child) = pool_with_parent_and_child(); + + pool.set_modifiers(HashMap::from([(parent, skia::Matrix::scale((1.0, 2.4)))])); + + assert_eq!(pool.get_modifier(&child), None); + } } diff --git a/render-wasm/src/wasm/fills.rs b/render-wasm/src/wasm/fills.rs index 5d2a6e29e2..28ff005bec 100644 --- a/render-wasm/src/wasm/fills.rs +++ b/render-wasm/src/wasm/fills.rs @@ -179,7 +179,8 @@ mod tests { #[test] fn test_image_fill_round_trip() { - let image_fill = shapes::ImageFill::new(crate::uuid::Uuid::nil(), 0x80, 300, 200, true); + let image_fill = + shapes::ImageFill::new(crate::uuid::Uuid::nil(), 0x80, 300, 200, true).with_mtype(0x02); let fill = shapes::Fill::Image(image_fill); let raw_fill = RawFillData::try_from(&fill).expect("image fill must be serializable"); let bytes = <[u8; RAW_FILL_DATA_SIZE]>::from(raw_fill); diff --git a/render-wasm/src/wasm/fills/image.rs b/render-wasm/src/wasm/fills/image.rs index 72befd5e55..301e5cfd1a 100644 --- a/render-wasm/src/wasm/fills/image.rs +++ b/render-wasm/src/wasm/fills/image.rs @@ -81,7 +81,7 @@ pub struct RawImageFillData { d: u32, opacity: u8, flags: u8, - _pad: u16, + mtype: u16, width: i32, height: i32, transform_x: f32, @@ -113,7 +113,7 @@ impl From<&ImageFill> for RawImageFillData { d, opacity: image_fill.opacity(), flags, - _pad: 0, + mtype: image_fill.mtype(), width: image_fill.width(), height: image_fill.height(), transform_x: tx, @@ -147,6 +147,7 @@ impl From for ImageFill { keep_aspect_ratio, transform, ) + .with_mtype(value.mtype) } } diff --git a/render-wasm/src/wasm/text_editor.rs b/render-wasm/src/wasm/text_editor.rs index 43725727f1..9853876249 100644 --- a/render-wasm/src/wasm/text_editor.rs +++ b/render-wasm/src/wasm/text_editor.rs @@ -3,6 +3,7 @@ use macros::{wasm_error, ToJs}; use crate::globals::{get_render_state, get_text_editor_state}; use crate::math::{Matrix, Point}; use crate::mem; +use crate::render::text_editor::range_origin; use crate::shapes::{TextAlign, TextContent, TextPositionWithAffinity, Type, VerticalAlign}; use crate::state::{State, TextEditorEvent, TextEditorState}; use crate::utils::uuid_from_u32_quartet; @@ -1032,3 +1033,56 @@ pub extern "C" fn text_editor_get_selection(buffer_ptr: *mut u32) -> bool { true }) } + +/// Writes where the text between the UTF-16 offsets starts in the text shape +/// (x, y, shape-local) and its box size (width, height); false when no text. +#[no_mangle] +#[wasm_error] +#[allow(clippy::too_many_arguments)] +pub extern "C" fn text_get_range_origin( + a: u32, + b: u32, + c: u32, + d: u32, + start_paragraph: u32, + start_offset: u32, + end_paragraph: u32, + end_offset: u32, + buffer_ptr: *mut f32, +) -> Result { + with_state!(state, { + let Some(shape) = state.shapes.get(&uuid_from_u32_quartet(a, b, c, d)) else { + return Ok(false); + }; + let Type::Text(text_content) = &shape.shape_type else { + return Ok(false); + }; + + let paragraphs = text_content.paragraphs(); + let to_char = |paragraph: u32, offset: u32| { + let paragraph = paragraph as usize; + let offset = paragraphs + .get(paragraph) + .map(|para| para.utf16_offset_to_char(offset as usize)) + .unwrap_or(offset as usize); + (paragraph, offset) + }; + let Some(origin) = range_origin( + to_char(start_paragraph, start_offset), + to_char(end_paragraph, end_offset), + text_content, + shape, + ) else { + return Ok(false); + }; + let selrect = shape.selrect(); + + unsafe { + *buffer_ptr = origin.x; + *buffer_ptr.add(1) = origin.y; + *buffer_ptr.add(2) = selrect.width(); + *buffer_ptr.add(3) = selrect.height(); + } + Ok(true) + }) +} diff --git a/render-wasm/test b/render-wasm/test index 957ac14bed..3f680ac09a 100755 --- a/render-wasm/test +++ b/render-wasm/test @@ -1,14 +1,21 @@ #!/usr/bin/env bash -set -x +set -ex -export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.93.1/skia-binaries-319323662b1685a112f5-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp.tar.gz"} -export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"x86_64-unknown-linux-gnu"}; +case "$(uname -m)" in + aarch64|arm64) _ARCH="aarch64" ;; + *) _ARCH="x86_64" ;; +esac + +export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.153.3/skia-binaries-b7f043e0b1e2a850e702-${_ARCH}-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp-pdf.tar.gz"} +export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"${_ARCH}-unknown-linux-gnu"}; _SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) pushd "$_SCRIPT_DIR" > /dev/null . ./_build_env frontend +node --test scripts/cargo-build.test.cjs || exit $? + setup cargo test --bin render_wasm -- --show-output diff --git a/render-wasm/watch b/render-wasm/watch index f9ad114bb8..c1177186e2 100755 --- a/render-wasm/watch +++ b/render-wasm/watch @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Usage: ./watch [frontend|export] +# Usage: ./watch [frontend|export] [extra cargo params...] set -e @@ -9,19 +9,26 @@ pushd "$_SCRIPT_DIR" > /dev/null _RENDER_TARGET=${1:-${RENDER_TARGET:-frontend}} export RENDER_TARGET="$_RENDER_TARGET" -. ./_build_env "$_RENDER_TARGET" "${@:2}" +_BUILD_COMMAND=("$_SCRIPT_DIR/build" "$RENDER_TARGET" "${@:2}") # The initial build uses the protected build entry point. Its lock is released # before cargo watch starts waiting for source changes. -"$_SCRIPT_DIR/build" "$RENDER_TARGET" $CARGO_PARAMS +"${_BUILD_COMMAND[@]}" + +# cargo watch runs shell commands; single-quote each original argument for sh. +_WATCH_BUILD_COMMAND="" +for _arg in "${_BUILD_COMMAND[@]}"; do + _arg=${_arg//\'/\'\\\'\'} + _WATCH_BUILD_COMMAND+=" '$_arg'" +done set -x cargo watch \ --why \ + --postpone \ -i "_tmp*" \ - -x "build $CARGO_PARAMS" \ - -s "\"$_SCRIPT_DIR/build\" \"$RENDER_TARGET\" $CARGO_PARAMS" \ + -s "$_WATCH_BUILD_COMMAND" \ -s "echo 'DONE\n'"; popd > /dev/null diff --git a/render-wasm/watch_test b/render-wasm/watch_test index 798eb84bf0..3422891457 100755 --- a/render-wasm/watch_test +++ b/render-wasm/watch_test @@ -1,7 +1,13 @@ #!/usr/bin/env bash _SCRIPT_DIR=$(dirname $0); -export SKIA_BINARIES_URL="https://github.com/penpot/skia-binaries/releases/download/0.93.1/skia-binaries-319323662b1685a112f5-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp.tar.gz" +case "$(uname -m)" in + aarch64|arm64) _ARCH="aarch64" ;; + *) _ARCH="x86_64" ;; +esac + +export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.153.3/skia-binaries-b7f043e0b1e2a850e702-${_ARCH}-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp-pdf.tar.gz"} +export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"${_ARCH}-unknown-linux-gnu"}; pushd $_SCRIPT_DIR; cargo watch -x "test --bin render_wasm -- --show-output" diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000000..1d7acb30de --- /dev/null +++ b/renovate.json @@ -0,0 +1,42 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["github>penpot/renovate-config"], + "labels": ["dependencies", "team infra", "no issue required"], + "semanticCommits": "disabled", + "commitMessagePrefix": ":arrow_up:", + "commitMessageAction": "Update", + "pinDigest": { + "commitMessageAction": "Pin" + }, + "nvm": { + "enabled": false + }, + "customManagers": [ + { + "customType": "regex", + "description": "Keep .nvmrc in sync with the DHI Node image", + "managerFilePatterns": ["/^\\.nvmrc$/"], + "matchStrings": ["v(?\\d+\\.\\d+\\.\\d+)"], + "depNameTemplate": "dhi.io/node", + "datasourceTemplate": "docker", + "extractVersionTemplate": "^(?\\d+\\.\\d+\\.\\d+)-debian13$" + }, + { + "customType": "regex", + "description": "Keep the devenv Node in sync with the DHI Node image", + "managerFilePatterns": ["/^docker/devenv/Dockerfile$/"], + "matchStrings": ["NODE_VERSION=v(?\\d+\\.\\d+\\.\\d+)"], + "depNameTemplate": "dhi.io/node", + "datasourceTemplate": "docker", + "extractVersionTemplate": "^(?\\d+\\.\\d+\\.\\d+)-debian13$" + } + ], + "packageRules": [ + { + "description": "Node versions in plain text have no digest to pin", + "matchManagers": ["custom.regex"], + "matchDatasources": ["docker"], + "pinDigests": false + } + ] +}